Skip to content
Open
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
91 changes: 91 additions & 0 deletions lib/getBuildContext.test.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import assert from 'node:assert/strict';
import { describe, it, beforeEach, afterEach } from 'node:test';
import getBuildContext from './getBuildContext.ts';

const ENV_KEYS = [
'BUILD_ENV',
'DEPLOY_PRIME_URL',
'HEAD',
'BRANCH',
'GITHUB_HEAD_REF',
'GITHUB_REF_NAME',
'REPOSITORY_URL',
] as const;
const savedEnv: Record<string, string | undefined> = {};

beforeEach(() => {
for (const key of ENV_KEYS) savedEnv[key] = process.env[key];
for (const key of ENV_KEYS) delete process.env[key];
});

afterEach(() => {
for (const key of ENV_KEYS) {
if (savedEnv[key] === undefined) delete process.env[key];
else process.env[key] = savedEnv[key];
}
});

describe('getBuildContext', () => {
it('defaults to production when BUILD_ENV is unset', () => {
const ctx = getBuildContext('https://github.com/cncf/techdocs');
assert.strictEqual(ctx.buildEnv, 'production');
assert.strictEqual(ctx.isProd, true);
assert.strictEqual(ctx.isDeployPreview, false);
assert.strictEqual(ctx.assetBranch, 'main');
assert.strictEqual(ctx.assetRepo, 'https://github.com/cncf/techdocs');
assert.strictEqual(
ctx.assetBaseUrl,
'https://github.com/cncf/techdocs/blob/main',
);
});

it('is not a deploy preview when BUILD_ENV is non-production but DEPLOY_PRIME_URL is unset', () => {
process.env.BUILD_ENV = 'dev';
const ctx = getBuildContext('https://github.com/cncf/techdocs');
assert.strictEqual(ctx.isProd, false);
assert.strictEqual(ctx.isDeployPreview, false);
assert.strictEqual(ctx.assetBranch, 'main');
assert.strictEqual(ctx.assetRepo, 'https://github.com/cncf/techdocs');
});

it('builds a deploy-preview context from the current branch and repo', () => {
process.env.BUILD_ENV = 'dev';
process.env.DEPLOY_PRIME_URL =
'https://deploy-preview-1--example.netlify.app';
process.env.HEAD = 'my-feature-branch';
process.env.REPOSITORY_URL = 'https://github.com/someone/techdocs-fork';
const ctx = getBuildContext('https://github.com/cncf/techdocs');
assert.strictEqual(ctx.buildEnv, 'dev');
assert.strictEqual(ctx.isProd, false);
assert.strictEqual(ctx.isDeployPreview, true);
assert.strictEqual(ctx.assetBranch, 'my-feature-branch');
assert.strictEqual(
ctx.assetRepo,
'https://github.com/someone/techdocs-fork',
);
assert.strictEqual(
ctx.assetBaseUrl,
'https://github.com/someone/techdocs-fork/blob/my-feature-branch',
);
});

it('falls back to main/repo arg in deploy-preview mode when branch/remote cannot be resolved', () => {
process.env.BUILD_ENV = 'dev';
process.env.DEPLOY_PRIME_URL =
'https://deploy-preview-1--example.netlify.app';
// No HEAD/BRANCH/GITHUB_* env vars, no REPOSITORY_URL: getCurrentBranch()
// and getGitHubRepoUrl() fall back to git plumbing, which may or may not
// resolve in this environment, so assetBranch/assetRepo must still be
// non-empty strings built from the '|| fallback' defaults.
const ctx = getBuildContext('https://github.com/cncf/techdocs');
assert.strictEqual(ctx.isDeployPreview, true);
assert.ok(
typeof ctx.assetBranch === 'string' && ctx.assetBranch.length > 0,
);
assert.ok(typeof ctx.assetRepo === 'string' && ctx.assetRepo.length > 0);
assert.strictEqual(
ctx.assetBaseUrl,
`${ctx.assetRepo}/blob/${ctx.assetBranch}`,
);
});
});
4 changes: 2 additions & 2 deletions lib/getBuildContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import getCurrentBranch from './getCurrentBranch';
import getGitHubRepoUrl from './getGitHubRepoUrl';
import getCurrentBranch from './getCurrentBranch.ts';
import getGitHubRepoUrl from './getGitHubRepoUrl.ts';

export interface BuildContext {
buildEnv: string;
Expand Down
66 changes: 66 additions & 0 deletions lib/getCurrentBranch.test.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import assert from 'node:assert/strict';
import { describe, it, beforeEach, afterEach } from 'node:test';
import getCurrentBranch from './getCurrentBranch.ts';

const ENV_KEYS = [
'HEAD',
'BRANCH',
'GITHUB_HEAD_REF',
'GITHUB_REF_NAME',
] as const;
const savedEnv: Record<string, string | undefined> = {};

beforeEach(() => {
for (const key of ENV_KEYS) savedEnv[key] = process.env[key];
});

afterEach(() => {
for (const key of ENV_KEYS) {
if (savedEnv[key] === undefined) delete process.env[key];
else process.env[key] = savedEnv[key];
}
});

function clearEnv() {
for (const key of ENV_KEYS) delete process.env[key];
}

describe('getCurrentBranch', () => {
it('prefers HEAD over BRANCH and the GitHub Actions refs', () => {
clearEnv();
process.env.HEAD = 'head-branch';
process.env.BRANCH = 'branch-branch';
process.env.GITHUB_HEAD_REF = 'gh-head-branch';
process.env.GITHUB_REF_NAME = 'gh-ref-branch';
assert.strictEqual(getCurrentBranch(), 'head-branch');
});

it('prefers BRANCH over the GitHub Actions refs', () => {
clearEnv();
process.env.BRANCH = 'branch-branch';
process.env.GITHUB_HEAD_REF = 'gh-head-branch';
process.env.GITHUB_REF_NAME = 'gh-ref-branch';
assert.strictEqual(getCurrentBranch(), 'branch-branch');
});

it('prefers GITHUB_HEAD_REF over GITHUB_REF_NAME', () => {
clearEnv();
process.env.GITHUB_HEAD_REF = 'gh-head-branch';
process.env.GITHUB_REF_NAME = 'gh-ref-branch';
assert.strictEqual(getCurrentBranch(), 'gh-head-branch');
});

it('falls back to GITHUB_REF_NAME when nothing else is set', () => {
clearEnv();
process.env.GITHUB_REF_NAME = 'gh-ref-branch';
assert.strictEqual(getCurrentBranch(), 'gh-ref-branch');
});

it('falls back to `git branch --show-current` when no env vars are set', () => {
clearEnv();
// Running inside a real git checkout, this resolves to a non-empty
// string or undefined (detached HEAD); either is a valid result, but the
// call must not throw and must not read any of the env vars above.
assert.doesNotThrow(() => getCurrentBranch());
});
});
62 changes: 62 additions & 0 deletions lib/getGitHubRepoUrl.test.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import assert from 'node:assert/strict';
import { describe, it, beforeEach, afterEach } from 'node:test';
import getGitHubRepoUrl from './getGitHubRepoUrl.ts';

const ENV_KEYS = ['REPOSITORY_URL'] as const;
const savedEnv: Record<string, string | undefined> = {};

beforeEach(() => {
for (const key of ENV_KEYS) savedEnv[key] = process.env[key];
});

afterEach(() => {
for (const key of ENV_KEYS) {
if (savedEnv[key] === undefined) delete process.env[key];
else process.env[key] = savedEnv[key];
}
});

describe('getGitHubRepoUrl', () => {
it('normalizes an HTTPS REPOSITORY_URL', () => {
process.env.REPOSITORY_URL = 'https://github.com/cncf/techdocs';
assert.strictEqual(getGitHubRepoUrl(), 'https://github.com/cncf/techdocs');
});

it('normalizes an HTTPS REPOSITORY_URL with a .git suffix', () => {
process.env.REPOSITORY_URL = 'https://github.com/cncf/techdocs.git';
assert.strictEqual(getGitHubRepoUrl(), 'https://github.com/cncf/techdocs');
});

it('normalizes an SSH REPOSITORY_URL', () => {
process.env.REPOSITORY_URL = 'git@github.com:cncf/techdocs.git';
assert.strictEqual(getGitHubRepoUrl(), 'https://github.com/cncf/techdocs');
});

it('normalizes an SSH REPOSITORY_URL without a .git suffix', () => {
process.env.REPOSITORY_URL = 'git@github.com:cncf/techdocs';
assert.strictEqual(getGitHubRepoUrl(), 'https://github.com/cncf/techdocs');
});

it('falls back to the git remote when REPOSITORY_URL is unset', () => {
delete process.env.REPOSITORY_URL;
// The checkout's origin remote points at some fork or upstream of
// techdocs on GitHub; don't hard-code the owner, which varies by fork/CI.
assert.match(
getGitHubRepoUrl() ?? '',
/^https:\/\/github\.com\/[^/]+\/techdocs$/,
);
});

it('falls back to the git remote when REPOSITORY_URL does not parse', () => {
process.env.REPOSITORY_URL = 'not-a-github-url';
assert.match(
getGitHubRepoUrl() ?? '',
/^https:\/\/github\.com\/[^/]+\/techdocs$/,
);
});

it('returns undefined for a non-existent remote', () => {
delete process.env.REPOSITORY_URL;
assert.strictEqual(getGitHubRepoUrl('does-not-exist'), undefined);
});
});
Loading