Sitelet https://github.com/limitless-angular/limitless-angular/pull/60/files
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
6 changes: 6 additions & 0 deletions .github/workflows/release-and-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,14 @@ jobs:
- name: Verify environment
run: |
echo "Node version: $(node -v)"
echo "npm version: $(npm -v)"
echo "pnpm version: $(pnpm -v)"
echo "Workspace directory: $(pwd)"
if [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ]; then
echo "::error::GitHub Actions OIDC request environment is missing; verify permissions.id-token: write."
exit 1
fi
echo "GitHub Actions OIDC request environment: available"

- name: Configure release paths
run: echo "RELEASE_PLAN_PATH=$RUNNER_TEMP/release-plan.json" >> "$GITHUB_ENV"
Expand Down
22 changes: 16 additions & 6 deletions tools/angular-compat/orchestration-contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,24 @@ test('release-only Turbo task settings are scoped to the release package', () =>
'root turbo.json should keep shared tasks only',
);

for (const task of [
'release',
'release:dry-run',
'release:plan',
'release:publish',
]) {
for (const task of ['release:dry-run', 'release:plan']) {
assert.deepEqual(releaseTurbo.tasks[task], { cache: false });
}
const trustedPublishTaskConfig = {
cache: false,
passThroughEnv: [
'ACTIONS_ID_TOKEN_REQUEST_*',
'GITHUB_*',
'NPM_CONFIG_PROVENANCE',
'RELEASE_BRANCH',
],
};

assert.deepEqual(releaseTurbo.tasks.release, trustedPublishTaskConfig);
assert.deepEqual(
releaseTurbo.tasks['release:publish'],
trustedPublishTaskConfig,
);
});

function readWorkspaceJson(path) {
Expand Down
6 changes: 5 additions & 1 deletion tools/release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,8 @@ The npm package must trust the GitHub Actions publisher for
environment `npm-release`; the publish job uses npm trusted publishing instead
of a long-lived npm token. The package `repository.url` must exactly match
`https://github.com/limitless-angular/limitless-angular` so npm can match the
OIDC publisher to the package metadata.
OIDC publisher to the package metadata. Because the publish-capable release
tasks run through Turborepo strict environment mode, their task configuration
must pass through `ACTIONS_ID_TOKEN_REQUEST_*`, `GITHUB_*`, and
`NPM_CONFIG_PROVENANCE` so npm can exchange the GitHub OIDC request for trusted
publish authorization.
13 changes: 10 additions & 3 deletions tools/release/src/pipeline.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import {
runReleasePipeline,
} from './pipeline.mjs';

const trustedPublishingEnv = {
ACTIONS_ID_TOKEN_REQUEST_TOKEN: 'github-oidc-token',
ACTIONS_ID_TOKEN_REQUEST_URL: 'https://actions.example/id-token',
GITHUB_REF: 'refs/heads/main',
GITHUB_TOKEN: 'token',
};

test('dry-run validates the planned artifact version and restores release files', () => {
const fixture = createReleaseFixture();
const commands = [];
Expand Down Expand Up @@ -67,7 +74,7 @@ test('publish mode validates before starting release side effects', () => {
tarballPath: '/tmp/release.tgz',
},
capture: createCapture(),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: 'token' },
env: trustedPublishingEnv,
mode: releaseModes.publish,
now: new Date('2026-06-08T00:00:00.000Z'),
paths: fixture.paths,
Expand Down Expand Up @@ -135,7 +142,7 @@ test('publish mode tags npm and GitHub prereleases', () => {
gitLog:
'abc1234\x01feat(sanity): add release validation\x01\x01Alfonso\x02',
}),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: 'token' },
env: trustedPublishingEnv,
mode: releaseModes.publish,
now: new Date('2026-06-08T00:00:00.000Z'),
paths: fixture.paths,
Expand Down Expand Up @@ -186,7 +193,7 @@ test('artifact version mismatch fails before publish side effects', () => {
tarballPath: '/tmp/release.tgz',
},
capture: createCapture(),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: 'token' },
env: trustedPublishingEnv,
mode: releaseModes.publish,
now: new Date('2026-06-08T00:00:00.000Z'),
paths: fixture.paths,
Expand Down
12 changes: 12 additions & 0 deletions tools/release/src/preflight.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function assertPublishPreconditions(plan, options = {}) {
const releaseBranch = getReleaseBranch(env, options);

assertGitHubReleaseToken(env);
assertGitHubOidcRequest(env);
assertReleaseRef({ capture: commandCapture, env, releaseBranch });
assertTrustedPublishingRepository(plan);
assertCleanWorktree(commandCapture);
Expand Down Expand Up @@ -45,6 +46,17 @@ function assertGitHubReleaseToken(env) {
}
}

function assertGitHubOidcRequest(env) {
if (
!env.ACTIONS_ID_TOKEN_REQUEST_URL ||
!env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
) {
throw new Error(
'Refusing to publish without GitHub Actions OIDC request environment variables; verify id-token: write and the release:publish Turbo passThroughEnv configuration.',
);
}
}

function assertReleaseRef({ capture, env, releaseBranch }) {
const expectedGitHubRef = `refs/heads/${releaseBranch}`;

Expand Down
37 changes: 31 additions & 6 deletions tools/release/src/preflight.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,22 @@ const plan = {
releaseTag: 'sanity@1.1.0',
};

const trustedPublishingEnv = {
ACTIONS_ID_TOKEN_REQUEST_TOKEN: 'github-oidc-token',
ACTIONS_ID_TOKEN_REQUEST_URL: 'https://actions.example/id-token',
GITHUB_REF: 'refs/heads/main',
GITHUB_TOKEN: 'token',
};

test('publish preflight rejects non-main GitHub refs', () => {
assert.throws(
() =>
assertPublishPreconditions(plan, {
capture: createCapture(),
env: { GITHUB_REF: 'refs/heads/release-test', GITHUB_TOKEN: 'token' },
env: {
...trustedPublishingEnv,
GITHUB_REF: 'refs/heads/release-test',
},
run: recordRun(),
}),
/expected refs\/heads\/main/,
Expand All @@ -31,19 +41,34 @@ test('publish preflight rejects missing GitHub release tokens', () => {
() =>
assertPublishPreconditions(plan, {
capture: createCapture(),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: '' },
env: { ...trustedPublishingEnv, GITHUB_TOKEN: '' },
run: recordRun(),
}),
/without GITHUB_TOKEN/,
);
});

test('publish preflight rejects missing GitHub Actions OIDC request variables', () => {
assert.throws(
() =>
assertPublishPreconditions(plan, {
capture: createCapture(),
env: {
...trustedPublishingEnv,
ACTIONS_ID_TOKEN_REQUEST_TOKEN: '',
},
run: recordRun(),
}),
/without GitHub Actions OIDC request environment variables/,
);
});

test('publish preflight rejects dirty worktrees', () => {
assert.throws(
() =>
assertPublishPreconditions(plan, {
capture: createCapture({ status: ' M packages/sanity/package.json\n' }),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: 'token' },
env: trustedPublishingEnv,
run: recordRun(),
}),
/uncommitted workspace changes/,
Expand All @@ -61,7 +86,7 @@ test('publish preflight rejects repository URLs that cannot satisfy npm trust',
},
{
capture: createCapture(),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: 'token' },
env: trustedPublishingEnv,
run: recordRun(),
},
),
Expand All @@ -74,7 +99,7 @@ test('publish preflight rejects already-published npm versions', () => {
() =>
assertPublishPreconditions(plan, {
capture: createCapture({ npmVersions: ['1.0.0', '1.1.0'] }),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: 'token' },
env: trustedPublishingEnv,
run: recordRun(),
}),
/already exists on npm/,
Expand All @@ -89,7 +114,7 @@ test('final publish preflight rejects a moved release branch', () => {
mergeBase: 'old-main-sha',
remoteHead: 'new-main-sha',
}),
env: { GITHUB_REF: 'refs/heads/main', GITHUB_TOKEN: 'token' },
env: trustedPublishingEnv,
run: recordRun(),
}),
/origin\/main moved after validation/,
Expand Down
16 changes: 14 additions & 2 deletions tools/release/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
"extends": ["//"],
"tasks": {
"release": {
"cache": false
"cache": false,
"passThroughEnv": [
"ACTIONS_ID_TOKEN_REQUEST_*",
"GITHUB_*",
"NPM_CONFIG_PROVENANCE",
"RELEASE_BRANCH"
]
},
"release:dry-run": {
"cache": false
Expand All @@ -12,7 +18,13 @@
"cache": false
},
"release:publish": {
"cache": false
"cache": false,
"passThroughEnv": [
"ACTIONS_ID_TOKEN_REQUEST_*",
"GITHUB_*",
"NPM_CONFIG_PROVENANCE",
"RELEASE_BRANCH"
]
}
}
}
Loading