From e8431c3e79c1086bf02349401073865e7954fb37 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 10 May 2026 21:57:51 +0100 Subject: [PATCH 1/5] ci: add release workflow, CODEOWNERS, and explicit job permissions - New release.yml: workflow_dispatch trigger that runs `nx release version`, commits + tags, and pushes to master with --follow-tags. The tag push leaves manual GitHub Release creation as the publish gate. - Add CODEOWNERS for /.github/ and /nx.json so release/security configuration changes require maintainer review (requires the matching "Require review from Code Owners" toggle in the master ruleset). - Set explicit permissions blocks on codeql-analysis.yml (security-events: write) and deploy-docs.yml (contents: write) so they keep working once the repo default workflow permission is read-only. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/CODEOWNERS | 11 +++++ .github/workflows/codeql-analysis.yml | 4 ++ .github/workflows/deploy-docs.yml | 2 + .github/workflows/release.yml | 62 +++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/release.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..6263cd97c5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,11 @@ +# CODEOWNERS — required reviewers for security-sensitive paths. +# Syntax: https://docs.github.com/en/repositories/managing-your-repositories-settings-and-security/customizing-your-repository/about-code-owners +# +# Enforcement is opt-in: enable "Require review from Code Owners" in the +# branch ruleset protecting master for these rules to actually gate merges. + +# Anything under .github/ — workflows, Dependabot, actions config, this file. +/.github/ @mathuo + +# Release plumbing. +/nx.json @mathuo diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3f21a3b3ba..b1731e0637 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,6 +18,10 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest + permissions: + contents: read + actions: read + security-events: write strategy: fail-fast: false diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index ff786b8213..f9dfce200e 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -6,6 +6,8 @@ on: jobs: deploy-nightly-demo-app: runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout 🛎️ uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..7e19ce09c4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,62 @@ +name: Release + +on: + workflow_dispatch: + inputs: + specifier: + description: 'patch | minor | major | prerelease | explicit version (e.g. 6.1.0)' + required: true + type: string + default: patch + dry-run: + description: 'Dry run (compute version and commit locally, do not push)' + required: false + type: boolean + default: false + +concurrency: + group: release + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + ref: master + fetch-depth: 0 + fetch-tags: true + token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }} + + - uses: actions/setup-node@v4 + with: + node-version: '20.x' + + - uses: actions/cache@v4 + with: + path: | + node_modules + ~/.npm + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-modules- + + - run: yarn + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Version + run: npx nx release version ${{ inputs.specifier }} + + - name: Show release commit + run: git --no-pager log -1 --stat + + - name: Push commit and tag + if: ${{ !inputs.dry-run }} + run: git push --follow-tags origin master From 6ca52fd62eb7ce67a37cc0dfd1454fa535d66363 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 10 May 2026 22:32:31 +0100 Subject: [PATCH 2/5] ci: mint release token from GitHub App via release environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the GITHUB_TOKEN / RELEASE_TOKEN fallback in release.yml with a short-lived token minted by actions/create-github-app-token from the dockview-release-bot App. The job now declares `environment: release`, so the App credentials (RELEASE_APP_ID, RELEASE_APP_PRIVATE_KEY) are only injected for runs targeting that environment — gated by its master-only deployment branch policy. Job-level permissions drop to `contents: read` since pushes flow through the App token, not GITHUB_TOKEN. Action pinned to commit SHA (v3.1.1, 2026-04-11) to avoid tag-repointing supply-chain risk. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e19ce09c4..f3e26dd032 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,15 +21,23 @@ concurrency: jobs: release: runs-on: ubuntu-latest + environment: release permissions: - contents: write + contents: read steps: + - name: Generate App token + id: app-token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + with: + app-id: ${{ secrets.RELEASE_APP_ID }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + - uses: actions/checkout@v4 with: ref: master fetch-depth: 0 fetch-tags: true - token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }} + token: ${{ steps.app-token.outputs.token }} - uses: actions/setup-node@v4 with: From 83cdae1ce865408d36630db19a063ba87e9a7288 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 10 May 2026 22:34:22 +0100 Subject: [PATCH 3/5] ci: pin create-github-app-token to version tag instead of SHA Maintainer prefers version-tag pinning across the project's workflows. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f3e26dd032..3dccf81be9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Generate App token id: app-token - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + uses: actions/create-github-app-token@v3.1.1 with: app-id: ${{ secrets.RELEASE_APP_ID }} private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} From 5d68c4921d8c9ffd321ffdee98e7975577538567 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 11 May 2026 19:39:14 +0100 Subject: [PATCH 4/5] ci: attribute release commits to the GitHub App bot Configure git user.name/email from the App's bot identity (resolved via the GitHub API using the app-slug) instead of the generic github-actions[bot]. This matches the push identity (the App token) so the commit author and pusher line up and commits are GitHub-verified. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3dccf81be9..e338c34865 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,10 +54,22 @@ jobs: - run: yarn + - name: Get App user id + id: app-user + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + slug="${{ steps.app-token.outputs.app-slug }}" + id=$(gh api "/users/${slug}[bot]" --jq .id) + echo "user-id=${id}" >> "$GITHUB_OUTPUT" + - name: Configure git + env: + SLUG: ${{ steps.app-token.outputs.app-slug }} + UID: ${{ steps.app-user.outputs.user-id }} run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.name "${SLUG}[bot]" + git config user.email "${UID}+${SLUG}[bot]@users.noreply.github.com" - name: Version run: npx nx release version ${{ inputs.specifier }} From ed5fce494ca540c38aeb571ed24a79c76643c5c4 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 11 May 2026 19:42:11 +0100 Subject: [PATCH 5/5] ci: harden release workflow input handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pass workflow_dispatch specifier via env var instead of interpolating into the shell command — eliminates a shell-injection vector even though dispatch is restricted to users with write access. - Move app-slug interpolation into env: in the "Get App user id" step to match the pattern used by "Configure git". Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e338c34865..0fe4c05068 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,9 +58,9 @@ jobs: id: app-user env: GH_TOKEN: ${{ steps.app-token.outputs.token }} + SLUG: ${{ steps.app-token.outputs.app-slug }} run: | - slug="${{ steps.app-token.outputs.app-slug }}" - id=$(gh api "/users/${slug}[bot]" --jq .id) + id=$(gh api "/users/${SLUG}[bot]" --jq .id) echo "user-id=${id}" >> "$GITHUB_OUTPUT" - name: Configure git @@ -72,7 +72,9 @@ jobs: git config user.email "${UID}+${SLUG}[bot]@users.noreply.github.com" - name: Version - run: npx nx release version ${{ inputs.specifier }} + env: + SPECIFIER: ${{ inputs.specifier }} + run: npx nx release version "$SPECIFIER" - name: Show release commit run: git --no-pager log -1 --stat