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
71 changes: 61 additions & 10 deletions .github/local-actions/branch-manager/main.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions .github/local-actions/labels-sync/lib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ async function syncLabelsInRepo(github: Octokit, repoName: string, managedLabels
}

async function main() {
/** The repositories to sync the labels in, from the provided config. */
const repos = core.getMultilineInput('repos', {required: true, trimWhitespace: true});
/** The Github API instance to use for requests. */
const github = new Octokit({auth: await getAuthTokenFor(ANGULAR_ROBOT)});
const github = new Octokit({auth: await getAuthTokenFor(ANGULAR_ROBOT, {repositories: repos})});

try {
/** The list of managed labels. */
const labels = [...Object.values(allLabels)];
/** The repositories to sync the labels in, from the provided config. */
const repos = core.getMultilineInput('repos', {required: true, trimWhitespace: true});

await core.group('Repos being synced:', async () =>
repos.forEach((repo) => core.info(`- ${repo}`)),
Expand Down
75 changes: 63 additions & 12 deletions .github/local-actions/labels-sync/main.js

Large diffs are not rendered by default.

71 changes: 61 additions & 10 deletions .github/local-actions/lock-closed/main.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .github/local-actions/stale-cleanup/lib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ export async function closeStaleDraftPrs(github: Octokit, repo: string): Promise
}

async function main() {
const github = new Octokit({auth: await getAuthTokenFor(ANGULAR_ROBOT)});
const repos = core.getMultilineInput('repos', {required: true, trimWhitespace: true});
const github = new Octokit({auth: await getAuthTokenFor(ANGULAR_ROBOT, {repositories: repos})});
try {
const repos = core.getMultilineInput('repos', {required: true, trimWhitespace: true});
await core.group('Repos being cleaned:', async () =>
repos.forEach((repo) => core.info(`- ${repo}`)),
);
Expand Down
75 changes: 63 additions & 12 deletions .github/local-actions/stale-cleanup/main.js

Large diffs are not rendered by default.

25 changes: 24 additions & 1 deletion github-actions/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
load("@devinfra_npm//:defs.bzl", "npm_link_all_packages")
load("//tools:defaults.bzl", "ts_config", "ts_project")
load("//tools:defaults.bzl", "jasmine_test", "ts_config", "ts_project")

package(default_visibility = ["//github-actions:__subpackages__"])

Expand Down Expand Up @@ -43,3 +43,26 @@ ts_project(
":node_modules/@types/node",
],
)

ts_project(
name = "test_lib",
testonly = True,
srcs = [
"utils.spec.ts",
],
tsconfig = ":tsconfig_test",
deps = [
":node_modules/@actions/github",
":node_modules/@octokit/rest",
":node_modules/@types/jasmine",
":node_modules/@types/node",
":utils",
],
)

jasmine_test(
name = "test",
data = [
":test_lib",
],
)
4 changes: 3 additions & 1 deletion github-actions/branch-manager/lib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ let _github: Octokit | null = null;
/** Get the shared instance of Octokit, first creating the instance if necessary. */
async function github() {
if (_github === null) {
const token = await getAuthTokenFor(ANGULAR_ROBOT);
const token = await getAuthTokenFor(ANGULAR_ROBOT, {
repositories: Array.from(new Set([context.repo.repo, 'dev-infra'])),
});
_github = new Octokit({auth: token});
}
return _github;
Expand Down
75 changes: 64 additions & 11 deletions github-actions/branch-manager/main.js

Large diffs are not rendered by default.

52 changes: 42 additions & 10 deletions github-actions/labeling/issue/main.js

Large diffs are not rendered by default.

52 changes: 42 additions & 10 deletions github-actions/labeling/pull-request/main.js

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion github-actions/org-file-sync/lib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,13 @@ export async function runOrgFileSync(
) {
assertValidCommitSha(sha);

const github = octokit ?? new Octokit({auth: await getAuthTokenFor(ANGULAR_ROBOT)});
const github =
octokit ??
new Octokit({
auth: await getAuthTokenFor(ANGULAR_ROBOT, {
repositories: [context.repo.repo, ...reposToSync],
}),
});
try {
const goldenFiles: Files = await getFilesForRepo(github, context.repo.repo, filesToSync, sha);

Expand Down
77 changes: 66 additions & 11 deletions github-actions/org-file-sync/main.js

Large diffs are not rendered by default.

52 changes: 42 additions & 10 deletions github-actions/post-approval-changes/main.js

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion github-actions/release/publish/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ esbuild_checked_in(
ts_project(
name = "test_lib",
testonly = True,
srcs = ["lib/publish-ci.spec.ts"],
srcs = [
"lib/main.spec.ts",
"lib/publish-ci.spec.ts",
],
tsconfig = "//github-actions:tsconfig_test",
deps = [
":lib",
Expand Down
103 changes: 103 additions & 0 deletions github-actions/release/publish/lib/main.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import {AuthenticatedGitClient} from '../../../../ng-dev/utils/git/authenticated-git-client.js';
import {setConfig} from '../../../../ng-dev/utils/config.js';
import {ANGULAR_ROBOT, utils} from '../../../utils.js';
import {PublishCiTool} from './publish-ci.js';
import {run} from './main.js';

describe('release publish action main', () => {
let getAuthTokenForSpy: jasmine.Spy;
let revokeActiveInstallationTokenSpy: jasmine.Spy;
let toolRunSpy: jasmine.Spy;
let stdoutWriteSpy: jasmine.Spy;

beforeEach(() => {
getAuthTokenForSpy = spyOn(utils, 'getAuthTokenFor').and.resolveTo('test-token-12345');
revokeActiveInstallationTokenSpy = spyOn(
utils,
'revokeActiveInstallationToken',
).and.resolveTo();

process.env['INPUT_WOMBOT-TOKEN'] = 'mock-wombot-token';
process.env['INPUT_BUILT-PACKAGES-DIR'] = '/tmp/mock-built-packages';
process.env['INPUT_EXPECTED-SHA'] = '0123456789abcdef0123456789abcdef01234567';
process.env['INPUT_DRY-RUN'] = 'false';

setConfig({
github: {
mergeMode: 'caretaker-only' as any,
owner: 'angular',
name: 'angular',
mainBranchName: 'main',
},
release: {
representativeNpmPackage: '@angular/core',
npmPackages: [{name: '@angular/core'}],
buildPackages: async () => [],
},
});

spyOn(AuthenticatedGitClient, 'configure');
spyOn(AuthenticatedGitClient, 'get').and.resolveTo({
baseDir: '/tmp/mock-repo',
} as any);

toolRunSpy = spyOn(PublishCiTool.prototype, 'run').and.resolveTo();
stdoutWriteSpy = spyOn(process.stdout, 'write').and.callThrough();
});

afterEach(() => {
delete process.env['INPUT_WOMBOT-TOKEN'];
delete process.env['INPUT_BUILT-PACKAGES-DIR'];
delete process.env['INPUT_EXPECTED-SHA'];
delete process.env['INPUT_DRY-RUN'];
delete process.env['WOMBOT_TOKEN'];
process.exitCode = 0;
});

it('should revoke active installation token on successful run', async () => {
await run();

expect(getAuthTokenForSpy).toHaveBeenCalledWith(ANGULAR_ROBOT);
expect(toolRunSpy).toHaveBeenCalled();
expect(revokeActiveInstallationTokenSpy).toHaveBeenCalledWith('test-token-12345');
expect(stdoutWriteSpy).not.toHaveBeenCalledWith(jasmine.stringContaining('::error::'));
});

it('should revoke active installation token when PublishCiTool throws', async () => {
toolRunSpy.and.rejectWith(new Error('Publish failed due to network timeout'));

await run();

expect(toolRunSpy).toHaveBeenCalled();
expect(stdoutWriteSpy).toHaveBeenCalledWith(
jasmine.stringContaining('::error::Publish failed due to network timeout'),
);
expect(revokeActiveInstallationTokenSpy).toHaveBeenCalledWith('test-token-12345');
});

it('should catch and warn if revokeActiveInstallationToken throws, preserving failure state', async () => {
toolRunSpy.and.rejectWith(new Error('Primary publish failure'));
revokeActiveInstallationTokenSpy.and.rejectWith(new Error('GitHub API 500 revocation error'));

await run();

expect(stdoutWriteSpy).toHaveBeenCalledWith(
jasmine.stringContaining('::error::Primary publish failure'),
);
expect(revokeActiveInstallationTokenSpy).toHaveBeenCalledWith('test-token-12345');
expect(stdoutWriteSpy).toHaveBeenCalledWith(
jasmine.stringMatching(/::warning::Failed to revoke active installation token/),
);
});

it('should not attempt revocation if getAuthTokenFor fails before token is minted', async () => {
getAuthTokenForSpy.and.rejectWith(new Error('Private key invalid'));

await run();

expect(stdoutWriteSpy).toHaveBeenCalledWith(
jasmine.stringContaining('::error::Private key invalid'),
);
expect(revokeActiveInstallationTokenSpy).not.toHaveBeenCalled();
});
});
20 changes: 15 additions & 5 deletions github-actions/release/publish/lib/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as core from '@actions/core';
import {getAuthTokenFor, ANGULAR_ROBOT} from '../../../utils.js';
import {utils, ANGULAR_ROBOT} from '../../../utils.js';
import {AuthenticatedGitClient} from '../../../../ng-dev/utils/git/authenticated-git-client.js';
import {getConfig, assertValidGithubConfig} from '../../../../ng-dev/utils/config.js';
import {assertValidReleaseConfig} from '../../../../ng-dev/release/config/index.js';
import {PublishCiTool} from './publish-ci.js';

async function run() {
export async function run(): Promise<void> {
let token: string | null = null;
try {
const token = await getAuthTokenFor(ANGULAR_ROBOT);
core.setSecret(token);
token = await utils.getAuthTokenFor(ANGULAR_ROBOT);

const wombotToken = core.getInput('wombot-token', {required: true});
process.env['WOMBOT_TOKEN'] = wombotToken;
Expand Down Expand Up @@ -42,7 +42,17 @@ async function run() {
} else {
core.setFailed(`Unknown error: ${e}`);
}
} finally {
if (token !== null) {
try {
await utils.revokeActiveInstallationToken(token);
} catch (err) {
core.warning(`Failed to revoke active installation token: ${err}`);
}
}
}
}

await run();
if (process.env['JASMINE_RUNNER'] === undefined && !process.env['TEST_TARGET']) {
await run();
}
Loading
Loading