Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9f27317
Harden deployment readiness and validation environment cleanup (#5119)
JayDoubleu Oct 13, 2026
2b1bf7b
Fix bundle execution and preserve uninstall failures (#5119)
JayDoubleu Oct 13, 2026
8c834dc
Allow uninstall cleanup after confirmed Porter absence (#5228)
JayDoubleu Oct 13, 2026
7fe897d
Stabilise certificate provisioning and verify identity TLS (#5119)
JayDoubleu Oct 13, 2026
a7bb4b8
Fix Airlock VM prerequisites and shared-service test cleanup (#5119)
JayDoubleu Oct 13, 2026
b3aac8b
Select bundle tests and require successful smoke tests (#5119)
JayDoubleu Oct 13, 2026
34f29eb
Validate base workspace and Azure SQL lifecycles (#5119)
JayDoubleu Oct 13, 2026
e4a2879
Verify Airlock draft deletion with independent storage checks (#5119)
JayDoubleu Oct 13, 2026
e461a1e
Separate CI and E2E validation from runtime changes (#5119)
JayDoubleu Oct 13, 2026
f23259d
Verify certificate reuse after reinstallation (#5245)
JayDoubleu Oct 14, 2026
f46d307
Clarify E2E helper documentation (#5119)
JayDoubleu Oct 14, 2026
2040c98
Keep E2E polling logs free of response content (#5119)
JayDoubleu Oct 14, 2026
9ad8d12
Validate shared-service command arguments before dispatch (#5119)
JayDoubleu Oct 14, 2026
7d63e09
Fix certificate lifecycle template lookup (#5158)
JayDoubleu Oct 15, 2026
4f99399
Read public certificate evidence through the resource processor (#5158)
JayDoubleu Oct 15, 2026
2ce0cdf
Allow slower Azure health checks in smoke tests (#5119)
JayDoubleu Oct 15, 2026
585ecb2
Set an explicit admin username for Linux VM validation (#5145)
JayDoubleu Oct 16, 2026
9d68738
Update validation branch and consolidate documentation (#5255)
JayDoubleu Oct 16, 2026
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
3 changes: 3 additions & 0 deletions .github/linters/actionlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# Remove these exact parser exceptions when actionlint supports queue.
# ci_environment_workflow.test.js validates all reference group settings.
paths:
.github/workflows/deploy_tre_branch.yml:
ignore:
- '^unexpected key "queue" for "concurrency" section\. expected one of "cancel-in-progress", "group"$'
.github/workflows/clean_validation_envs.yml:
ignore:
- '^unexpected key "queue" for "concurrency" section\. expected one of "cancel-in-progress", "group"$'
Expand Down
38 changes: 35 additions & 3 deletions .github/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
const { createHash } = require('crypto');

const SKIP_DEPLOYMENT_FLAG = "skip_deployment";
const ACCEPT_NEXUS_EULA_FLAG = "accept_nexus_eula";

async function getCommandFromComment({ core, context, github }) {
const commentUsername = context.payload.comment.user.login;
Expand Down Expand Up @@ -54,6 +55,7 @@ async function getCommandFromComment({ core, context, github }) {
const commentFirstLine = commentBody.split("\n")[0];
let command = "none";
let skipDeployment = false;
let acceptNexusEula = false;
const trimmedFirstLine = commentFirstLine.trim();
if (trimmedFirstLine[0] === "/") {
// only allow actions for users with write access
Expand Down Expand Up @@ -122,9 +124,24 @@ async function getCommandFromComment({ core, context, github }) {
break;
}

const consentFlags = parts.slice(1).filter(part => part.startsWith(ACCEPT_NEXUS_EULA_FLAG));
if (consentFlags.length !== 1 || consentFlags[0] !== ACCEPT_NEXUS_EULA_FLAG) {
const message = `:warning: Nexus CE EULA acceptance is required for shared service tests. No deployment or tests were started. After accepting the EULA, re-run \`${commandText} [<sha>] ${ACCEPT_NEXUS_EULA_FLAG}\`.`;
await addActionComment({ github }, repoOwner, repoName, prNumber, commentUsername, commentLink, message);
break;
}

const shaParts = parts.slice(1).filter(part => part !== ACCEPT_NEXUS_EULA_FLAG);
if (shaParts.length > 1 || shaParts.some(part => !/^[0-9a-f]{7,40}$/i.test(part))) {
const message = `:warning: Invalid shared-service arguments. No deployment or tests were started. Use \`${commandText} [<sha>] ${ACCEPT_NEXUS_EULA_FLAG}\` with one optional SHA and one exact consent flag.`;
await addActionComment({ github }, repoOwner, repoName, prNumber, commentUsername, commentLink, message);
break;
}

const runTests = await handleTestCommand({ core, github }, parts, "shared service tests", runId, { number: prNumber, authorUsername: prAuthorUsername, repoOwner, repoName, headSha: prHeadSha, refId: prRefId, details: pr }, { username: commentUsername, link: commentLink });
if (runTests) {
command = "run-tests-shared-services";
acceptNexusEula = true;
}
break;
}
Expand All @@ -145,9 +162,23 @@ async function getCommandFromComment({ core, context, github }) {

case "/test-airlock":
{
if (commandHasSkipDeploymentFlag(parts)) {
await addUnsupportedSkipDeploymentFlagComment({ github }, repoOwner, repoName, prNumber, commentUsername, commentLink, commandText);
break;
}

const consentFlags = parts.slice(1).filter(part => part === ACCEPT_NEXUS_EULA_FLAG);
const shaParts = parts.slice(1).filter(part => part !== ACCEPT_NEXUS_EULA_FLAG);
if (consentFlags.length > 1 || shaParts.length > 1 || shaParts.some(part => !/^[0-9a-f]{7,40}$/i.test(part))) {
const message = `:warning: Invalid Airlock arguments. No deployment or tests were started. Use \`${commandText} [<sha>] [${ACCEPT_NEXUS_EULA_FLAG}]\` with one optional SHA and one exact consent flag.`;
await addActionComment({ github }, repoOwner, repoName, prNumber, commentUsername, commentLink, message);
break;
}

const runTests = await handleTestCommand({ core, github }, parts, "airlock tests", runId, { number: prNumber, authorUsername: prAuthorUsername, repoOwner, repoName, headSha: prHeadSha, refId: prRefId, details: pr }, { username: commentUsername, link: commentLink });
if (runTests) {
command = "run-tests-airlock";
acceptNexusEula = consentFlags.length === 1;
}
break;
}
Expand Down Expand Up @@ -177,6 +208,7 @@ async function getCommandFromComment({ core, context, github }) {
}
}
logAndSetOutput(core, "skipDeployment", skipDeployment.toString());
logAndSetOutput(core, "acceptNexusEula", acceptNexusEula.toString());
logAndSetOutput(core, "command", command);
return command;
}
Expand Down Expand Up @@ -227,7 +259,7 @@ function commandHasSkipDeploymentFlag(commandParts) {
}

function getShaFromCommandParts(commandParts) {
return commandParts.slice(1).find(part => part !== SKIP_DEPLOYMENT_FLAG);
return commandParts.slice(1).find(part => part !== SKIP_DEPLOYMENT_FLAG && part !== ACCEPT_NEXUS_EULA_FLAG);
}

async function addUnsupportedSkipDeploymentFlagComment({ github }, repoOwner, repoName, prNumber, commentUser, commentLink, command) {
Expand Down Expand Up @@ -301,9 +333,9 @@ You can use the following commands:
&nbsp;&nbsp;&nbsp;&nbsp;/test [<sha>] [skip_deployment] - build, deploy and run smoke tests on a PR
&nbsp;&nbsp;&nbsp;&nbsp;/test-extended [<sha>] [skip_deployment] - build, deploy and run smoke & extended tests on a PR
&nbsp;&nbsp;&nbsp;&nbsp;/test-extended-aad - build, deploy and run smoke & extended AAD tests on a PR
&nbsp;&nbsp;&nbsp;&nbsp;/test-shared-services - test the deployment of shared services on a PR build
&nbsp;&nbsp;&nbsp;&nbsp;/test-shared-services [<sha>] accept_nexus_eula - build, deploy and test shared services with explicit Nexus CE EULA acceptance
&nbsp;&nbsp;&nbsp;&nbsp;/test-backups - build, deploy and run backup tests on a PR
&nbsp;&nbsp;&nbsp;&nbsp;/test-airlock - build, deploy and run Airlock tests on a PR
&nbsp;&nbsp;&nbsp;&nbsp;/test-airlock [<sha>] [accept_nexus_eula] - build, deploy and run Airlock tests with optional explicit Nexus CE EULA acceptance
&nbsp;&nbsp;&nbsp;&nbsp;/test-force-approve - force approval of the PR tests (i.e. skip the deployment checks)
&nbsp;&nbsp;&nbsp;&nbsp;/test-destroy-env - delete the validation environment for a PR (e.g. to enable testing a deployment from a clean start after previous tests)
&nbsp;&nbsp;&nbsp;&nbsp;/help - show this help`;
Expand Down
206 changes: 199 additions & 7 deletions .github/scripts/build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,16 +476,17 @@ describe('getCommandFromComment', () => {
test(`should set command to 'run-tests-shared-services'`, async () => {
const context = createCommentContext({
username: 'admin',
body: '/test-shared-services',
body: '/test-shared-services accept_nexus_eula',
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('run-tests-shared-services');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('true');
});

test(`should add comment with run link`, async () => {
const context = createCommentContext({
username: 'admin',
body: '/test-shared-services',
body: '/test-shared-services accept_nexus_eula',
pullRequestNumber: PR_NUMBER.UPSTREAM_NON_DOCS_CHANGES,
});
await getCommandFromComment({ core, context, github });
Expand All @@ -497,10 +498,111 @@ describe('getCommandFromComment', () => {
});
});

test.each([
'/test-shared-services 2345678 accept_nexus_eula',
'/test-shared-services accept_nexus_eula 2345678',
'/test-shared-services ' + 'a'.repeat(40) + ' accept_nexus_eula',
])('accepts one optional SHA on an internal PR: %s', async body => {
const context = createCommentContext({ body });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('run-tests-shared-services');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('true');
});

describe.each([
{ description: 'internal', pullRequestNumber: PR_NUMBER.UPSTREAM_NON_DOCS_CHANGES, authorUsername: 'admin' },
{ description: 'external', pullRequestNumber: PR_NUMBER.FORK_NON_DOCS_CHANGES, authorUsername: 'non-contributor' },
])('argument validation for $description PRs', ({ pullRequestNumber, authorUsername }) => {
test.each([
'/test-shared-services typo accept_nexus_eula',
'/test-shared-services accept_nexus_eula typo',
'/test-shared-services 234567 accept_nexus_eula',
'/test-shared-services ' + 'a'.repeat(41) + ' accept_nexus_eula',
'/test-shared-services 2345678 2345678 accept_nexus_eula',
'/test-shared-services 2345678 accept_nexus_eula unexpected',
])('does not dispatch malformed arguments: %s', async body => {
const context = createCommentContext({ body, pullRequestNumber, authorUsername });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
expect(mockGithubRestIssuesCreateComment).toHaveComment({
owner: 'someOwner', repo: 'someRepo', issue_number: pullRequestNumber,
bodyMatcher: /Invalid shared-service arguments.*No deployment or tests were started/,
});
});
});

test.each([
'/test-shared-services',
'/test-shared-services accept_nexus_eula=false',
'/test-shared-services accept_nexus_eula=true',
'/test-shared-services accept_nexus_eula accept_nexus_eula=false',
'/test-shared-services accept_nexus_eula accept_nexus_eula',
'/test-shared-services\naccept_nexus_eula',
])('does not dispatch without the explicit flag: %s', async body => {
const context = createCommentContext({ body });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
expect(mockGithubRestIssuesCreateComment).toHaveComment({
owner: 'someOwner', repo: 'someRepo', issue_number: PR_NUMBER.UPSTREAM_NON_DOCS_CHANGES,
bodyMatcher: /Nexus CE EULA acceptance is required.*No deployment or tests were started/,
});
});

test.each([
'/test-shared-services 2345678 accept_nexus_eula',
'/test-shared-services accept_nexus_eula 2345678',
])('keeps external-PR SHA validation with the consent flag: %s', async body => {
const context = createCommentContext({
body, pullRequestNumber: PR_NUMBER.FORK_NON_DOCS_CHANGES, authorUsername: 'non-contributor',
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('run-tests-shared-services');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('true');
});

test.each([
'/test-shared-services accept_nexus_eula',
'/test-shared-services outdated123 accept_nexus_eula',
'/test-shared-services 234567 accept_nexus_eula',
])('does not accept consent when external-PR SHA validation fails: %s', async body => {
const context = createCommentContext({
body, pullRequestNumber: PR_NUMBER.FORK_NON_DOCS_CHANGES, authorUsername: 'non-contributor',
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
});

test('does not allow the consent flag to bypass collaborator permissions', async () => {
const context = createCommentContext({
body: '/test-shared-services accept_nexus_eula', username: 'non-contributor',
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).not.toBe('true');
});

test('does not accept consent for an unmergeable PR', async () => {
const context = createCommentContext({
body: '/test-shared-services accept_nexus_eula', pullRequestNumber: PR_NUMBER.UPSTREAM_NON_MERGEABLE,
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
});

test('does not grant consent to another command', async () => {
const context = createCommentContext({ body: '/test-extended accept_nexus_eula' });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
});

test(`should warn and not run if skip_deployment is supplied`, async () => {
const context = createCommentContext({
username: 'admin',
body: '/test-shared-services skip_deployment',
body: '/test-shared-services skip_deployment accept_nexus_eula',
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
Expand All @@ -514,13 +616,103 @@ describe('getCommandFromComment', () => {
});

describe(`for '/test-airlock'`, () => {
test(`should set command to 'run-tests-airlock'`, async () => {
test.each([
'/test-airlock',
'/test-airlock 2345678',
'/test-airlock\naccept_nexus_eula',
])('preserves the existing route without granting consent: %s', async body => {
const context = createCommentContext({ body });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('run-tests-airlock');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
expect(outputFor(mockCoreSetOutput, 'skipDeployment')).toBe('false');
});

test.each([
'/test-airlock accept_nexus_eula',
'/test-airlock 2345678 accept_nexus_eula',
'/test-airlock accept_nexus_eula 2345678',
])('forwards explicit consent: %s', async body => {
const context = createCommentContext({ body });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('run-tests-airlock');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('true');
expect(outputFor(mockCoreSetOutput, 'skipDeployment')).toBe('false');
});

test.each([
'/test-airlock accept_nexus_eula=true',
'/test-airlock accept_nexus_eula=false',
'/test-airlock accept_nexus_eula accept_nexus_eula=false',
'/test-airlock accept_nexus_eula accept_nexus_eula',
'/test-airlock accept_nexus_eula unexpected',
'/test-airlock accept_nexus_eula 2345678 2345678',
'/test-airlock ACCEPT_NEXUS_EULA',
'/test-airlock accept_nexus_eula 234567',
'/test-airlock accept_nexus_eula ' + 'a'.repeat(41),
])('rejects invalid arguments without dispatching: %s', async body => {
const context = createCommentContext({ body });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
expect(mockGithubRestIssuesCreateComment).toHaveComment({
owner: 'someOwner', repo: 'someRepo', issue_number: PR_NUMBER.UPSTREAM_NON_DOCS_CHANGES,
bodyMatcher: /Invalid Airlock arguments.*No deployment or tests were started/,
});
});

test.each([
'/test-airlock 2345678 accept_nexus_eula',
'/test-airlock accept_nexus_eula 2345678',
])('keeps reviewed SHA validation for external PRs: %s', async body => {
const context = createCommentContext({
username: 'admin',
body: '/test-airlock',
body, pullRequestNumber: PR_NUMBER.FORK_NON_DOCS_CHANGES, authorUsername: 'non-contributor',
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('run-tests-airlock');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('true');
});

test.each([
'/test-airlock accept_nexus_eula',
'/test-airlock 1234567 accept_nexus_eula',
])('does not dispatch an external PR without its reviewed SHA: %s', async body => {
const context = createCommentContext({
body, pullRequestNumber: PR_NUMBER.FORK_NON_DOCS_CHANGES, authorUsername: 'non-contributor',
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
});

test('does not let consent bypass collaborator permissions', async () => {
const context = createCommentContext({ body: '/test-airlock accept_nexus_eula', username: 'non-contributor' });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).not.toBe('true');
});

test('does not accept consent for an unmergeable PR', async () => {
const context = createCommentContext({
body: '/test-airlock accept_nexus_eula', pullRequestNumber: PR_NUMBER.UPSTREAM_NON_MERGEABLE,
});
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
});

test.each([
'/test-airlock skip_deployment',
'/test-airlock accept_nexus_eula skip_deployment',
])('rejects unsupported deployment reuse: %s', async body => {
const context = createCommentContext({ body });
await getCommandFromComment({ core, context, github });
expect(outputFor(mockCoreSetOutput, 'command')).toBe('none');
expect(outputFor(mockCoreSetOutput, 'acceptNexusEula')).toBe('false');
expect(mockGithubRestIssuesCreateComment).toHaveComment({
owner: 'someOwner', repo: 'someRepo', issue_number: PR_NUMBER.UPSTREAM_NON_DOCS_CHANGES,
bodyMatcher: /`skip_deployment` is only supported/,
});
});
});

Expand Down Expand Up @@ -752,7 +944,7 @@ describe('getCommandFromComment', () => {
owner: 'someOwner',
repo: 'someRepo',
issue_number: PR_NUMBER.UPSTREAM_NON_DOCS_CHANGES,
bodyMatcher: /Hello!\n\nYou can use the following commands:[\s\S]*\/test-airlock/,
bodyMatcher: /Hello!\n\nYou can use the following commands:[\s\S]*\/test-airlock \[<sha>\] \[accept_nexus_eula\]/,
});
});
});
Expand Down
Loading