Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions web/api/site.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Small reads for the pages in one function. vercel.json rewrites /api/city, /api/gallery, /api/stars, /api/live
// and /api/pulse here as ?op=<name>.
// Small reads for the pages in one function. vercel.json rewrites /api/city, /api/gallery, /api/stars, /api/live,
// /api/pulse and /api/version here as ?op=<name>.
import { dispatch } from '../lib/http.js';
import * as city from '../lib/routes/city.js';
import * as gallery from '../lib/routes/gallery.js';
import * as live from '../lib/routes/live.js';
import * as pulse from '../lib/routes/pulse.js';
import * as stars from '../lib/routes/stars.js';
import * as version from '../lib/routes/version.js';

const routes = { city, gallery, stars, live, pulse };
const routes = { city, gallery, stars, live, pulse, version };
export const GET = dispatch(routes, 'GET');
10 changes: 10 additions & 0 deletions web/lib/routes/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Which commit this deployment was built from, so a deploy can be read back and checked against the history.
// Vercel sets VERCEL_GIT_COMMIT_SHA for deployments made from git; a deployment without it says so rather than guess.
import { handle, HttpError, json } from '../http.js';

export const GET = handle(async () => {
const commit = process.env.VERCEL_GIT_COMMIT_SHA;
if (!commit || !/^[0-9a-f]{40}$/.test(commit)) throw new HttpError(503, 'this deployment does not know its commit');
const ref = process.env.VERCEL_GIT_COMMIT_REF;
return json({ commit, source: 'env:VERCEL_GIT_COMMIT_SHA', ...(ref ? { ref } : {}) });
});
22 changes: 22 additions & 0 deletions web/test/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ test('a GitHub URL with commit.city swapped in lands on the city', () => {
['/orgs/vercel', 'redirect /vercel'],
['/orgs/vercel/repositories', 'redirect /vercel'],
// the site's own paths are left alone
['/api/version', 'rewrite /api/site?op=version'],
['/api/build/repo', null],
['/s/f/inter.woff2', null],
['/v/app.a63cdb5695.js', null],
Expand All @@ -43,3 +44,24 @@ test('a GitHub URL with commit.city swapped in lands on the city', () => {
];
for (const [p, want] of cases) assert.equal(route(p), want, p);
});

test('/api/version names the commit this deployment was built from, and never guesses', async () => {
const { GET } = await import('../lib/routes/version');
const saved = { sha: process.env.VERCEL_GIT_COMMIT_SHA, ref: process.env.VERCEL_GIT_COMMIT_REF };
try {
process.env.VERCEL_GIT_COMMIT_SHA = 'a'.repeat(40);
process.env.VERCEL_GIT_COMMIT_REF = 'main';
const r = await GET(new Request('https://commit.city/api/site?op=version'));
assert.equal(r.status, 200);
assert.equal(r.headers.get('cache-control'), 'no-store');
assert.deepEqual(await r.json(), { commit: 'a'.repeat(40), source: 'env:VERCEL_GIT_COMMIT_SHA', ref: 'main' });
for (const bad of [undefined, '', 'abc1234']) {
if (bad === undefined) delete process.env.VERCEL_GIT_COMMIT_SHA; else process.env.VERCEL_GIT_COMMIT_SHA = bad;
assert.equal((await GET(new Request('https://commit.city/api/site?op=version'))).status, 503);
}
} finally {
for (const [k, v] of [['VERCEL_GIT_COMMIT_SHA', saved.sha], ['VERCEL_GIT_COMMIT_REF', saved.ref]] as const) {
if (v === undefined) delete process.env[k]; else process.env[k] = v;
}
}
});
2 changes: 1 addition & 1 deletion web/vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"rewrites": [
{ "source": "/api/auth/:op(login|callback|logout)", "destination": "/api/account?op=:op" },
{ "source": "/api/:op(me|settings)", "destination": "/api/account?op=:op" },
{ "source": "/api/:op(city|gallery|stars|live|pulse)", "destination": "/api/site?op=:op" },
{ "source": "/api/:op(city|gallery|stars|live|pulse|version)", "destination": "/api/site?op=:op" },
{ "source": "/:owner((?!(?:api|v|s|u|fonts|local-store|explore|events|readme|about|404|index)(?:/|$))[A-Za-z0-9][A-Za-z0-9-]{0,38})", "destination": "/api/page?t=:owner" },
{ "source": "/:owner((?!(?:api|v|s|u|fonts|local-store|explore|events|readme|about|404|index)(?:/|$))[A-Za-z0-9][A-Za-z0-9-]{0,38})/:repo([A-Za-z0-9._-]{1,100})", "destination": "/api/page?t=:owner/:repo" }
],
Expand Down
Loading