Skip to content

Commit ecd16f7

Browse files
authored
Add redirect support based on Oak (#2)
* Add initial redirect support based on Oak * Add tests * Change default from master to main
1 parent af73ee4 commit ecd16f7

File tree

9 files changed

+377
-40
lines changed

9 files changed

+377
-40
lines changed

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,17 @@ google_cloud_project: <AppId>
9797
site: <SiteId>
9898

9999
# Specify a launch schedule. The schedule maps timestamps to branches or commit
100-
# shas. If blank, `master` is used for the default deployment.
100+
# shas. If blank, `main` is used for the default deployment.
101101
schedule:
102-
default: master
102+
default: main
103+
104+
redirects:
105+
- from: /foo
106+
to: /bar
107+
- from: /intl/:locale/
108+
to: /$locale/
109+
- from: /intl/:locale/*wildcard
110+
to: /$locale/$wildcard
103111
```
104112
105113
2. Generate your files.
@@ -209,12 +217,12 @@ Git branch is determined by inspecting the local Git environment when the
209217
The best way to understand how this works is by following the examples below:
210218

211219
```bash
212-
# master branch
220+
# main branch
213221
# ✓ public
214222
# ✓ production URL
215223
# ✓ also available from staging URL (restricted)
216224
217-
(master) $ fileset upload build
225+
(main) $ fileset upload build
218226
...
219227
Public URL: https://appid.appspot.com
220228
Staging URL: https://default-f3a9abb-dot-fileset-dot-appid.appspot.com

example/fileset.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
google_cloud_project: <AppId>
22
site: <SiteId>
33
schedule:
4-
default: master
4+
default: main
5+
redirects:
6+
- from: /foo
7+
to: /bar
8+
permanent: True
9+
- from: /intl/:locale/
10+
to: /$locale/
11+
- from: /intl/:locale/*wildcard
12+
to: /$locale/$wildcard

src/commands/upload.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import * as fs from 'fs';
22
import * as fsPath from 'path';
3+
import * as manifest from '../manifest';
34
import * as upload from '../upload';
45
import * as yaml from 'js-yaml';
56

6-
import {Manifest} from '../manifest';
77
import {getGitData} from '../gitdata';
88

99
interface UploadOptions {
@@ -29,7 +29,7 @@ function findConfig(path: string) {
2929
// TODO: Validate config schema.
3030
const config = yaml.safeLoad(fs.readFileSync(configPath, 'utf8')) as Record<
3131
string,
32-
string
32+
unknown
3333
>;
3434
return config;
3535
}
@@ -63,20 +63,23 @@ export class UploadCommand {
6363
throw new Error('Unable to determine the Google Cloud project.');
6464
}
6565

66-
const manifest = new Manifest(
67-
site,
66+
const manifestObj = new manifest.Manifest(
67+
site as string,
6868
this.options.ref || gitData.ref,
6969
this.options.branch || gitData.branch || ''
7070
);
71-
manifest.createFromDirectory(path);
72-
if (!manifest.files.length) {
71+
manifestObj.createFromDirectory(path);
72+
if (config.redirects) {
73+
manifestObj.setRedirects(config.redirects as manifest.Redirect[]);
74+
}
75+
if (!manifestObj.files.length) {
7376
console.log(`No files found in -> ${path}`);
7477
return;
7578
}
7679
upload.uploadManifest(
77-
googleCloudProject,
80+
googleCloudProject as string,
7881
bucket,
79-
manifest,
82+
manifestObj,
8083
this.options.force,
8184
ttl
8285
);

src/manifest.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export interface ManifestFile {
2323
cleanPath: string;
2424
}
2525

26+
export interface Redirect {
27+
from: string;
28+
to: string;
29+
permanent?: boolean;
30+
}
31+
2632
export interface PathToHash {
2733
path?: string;
2834
}
@@ -32,10 +38,12 @@ export class Manifest {
3238
ref: string;
3339
branch?: string;
3440
files: ManifestFile[];
41+
redirects: Redirect[];
3542
shortSha: string;
3643

3744
constructor(site: string, ref: string, branch?: string) {
3845
this.files = [];
46+
this.redirects = [];
3947
this.site = site;
4048
this.ref = ref;
4149
this.shortSha = ref.slice(0, 7);
@@ -49,6 +57,10 @@ export class Manifest {
4957
});
5058
}
5159

60+
setRedirects(redirects: Redirect[]) {
61+
this.redirects = redirects;
62+
}
63+
5264
createHash(path: string) {
5365
const contents = fs.readFileSync(path);
5466
const hash = crypto.createHash('sha1');
@@ -60,7 +72,9 @@ export class Manifest {
6072

6173
async addFile(path: string, dir: string) {
6274
const hash = this.createHash(path);
63-
const cleanPath = path.replace(dir.replace(/^\\+|\\+$/g, ''), '/').replace('//', '/');
75+
const cleanPath = path
76+
.replace(dir.replace(/^\\+|\\+$/g, ''), '/')
77+
.replace('//', '/');
6478
const manifestFile: ManifestFile = {
6579
cleanPath: cleanPath,
6680
hash: hash,
@@ -70,7 +84,16 @@ export class Manifest {
7084
this.files.push(manifestFile);
7185
}
7286

73-
toJSON() {
87+
async addRedirect(from: string, to: string, permanent: boolean) {
88+
const redirect: Redirect = {
89+
from: from,
90+
to: to,
91+
permanent: permanent,
92+
};
93+
this.redirects.push(redirect);
94+
}
95+
96+
pathsToJSON() {
7497
const pathsToHashes: any = {};
7598
this.files.forEach(file => {
7699
pathsToHashes[file.cleanPath] = file.hash;

src/redirects.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import * as manifest from './manifest';
2+
import * as redirects from './redirects';
3+
4+
import {ExecutionContext} from 'ava';
5+
import test from 'ava';
6+
7+
test('Test redirects', (t: ExecutionContext) => {
8+
let numTestsRun = 0;
9+
const config: manifest.Redirect[] = [
10+
{
11+
from: '/foo',
12+
to: '/bar',
13+
permanent: true,
14+
},
15+
{
16+
from: '/intl/:locale/',
17+
to: '/$locale/',
18+
},
19+
{
20+
from: '/intl/:locale/*wildcard',
21+
to: '/$locale/$wildcard',
22+
},
23+
];
24+
25+
const routeTrie = new redirects.RouteTrie();
26+
config.forEach(redirect => {
27+
const code = redirect.permanent ? 301 : 302;
28+
const route = new redirects.RedirectRoute(code, redirect.to);
29+
routeTrie.add(redirect.from, route);
30+
});
31+
32+
let [route, params] = routeTrie.get('/foo');
33+
if (route instanceof redirects.RedirectRoute) {
34+
const [code, destination] = route.getRedirect(params);
35+
t.is(code, 301);
36+
t.is(destination, '/bar');
37+
numTestsRun++;
38+
}
39+
40+
[route, params] = routeTrie.get('/intl/de');
41+
if (route instanceof redirects.RedirectRoute) {
42+
const [code, destination] = route.getRedirect(params);
43+
t.is(code, 302);
44+
t.is(destination, '/de/');
45+
numTestsRun++;
46+
}
47+
48+
[route, params] = routeTrie.get('/intl/ja/foo');
49+
if (route instanceof redirects.RedirectRoute) {
50+
const [code, destination] = route.getRedirect(params);
51+
t.is(code, 302);
52+
t.is(destination, '/ja/foo');
53+
numTestsRun++;
54+
}
55+
56+
t.is(numTestsRun, 3);
57+
});

0 commit comments

Comments
 (0)