* Initial plan
* chore(security): harden workflow permissions and pin Docker base image digests
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2ddc8f2b-7355-48cf-9a0b-c06df66c3f47
* fix(security): restore permissions: {} on publish + release-candidate workflows
These two release-publishing workflows had permissions: {} (the strictest valid form) before PR #1454, which replaced it with permissions: read-all. Every job in both files already declares its own permissions block, so the workflow-level default is only the safety net for future jobs added without one — read-all weakens that net for no benefit. Restore {} and the explanatory comment.
Scorecard's TokenPermissions check accepts both forms, so this preserves U9 compliance.
* fix(security): narrow permissions: read-all to contents: read on 13 workflows
PR #1454 added permissions: read-all to 13 workflows that previously had no top-level permissions block. read-all is Scorecard-compliant but unnecessarily broad — every job in scope only needs contents:read at the workflow level (job-level blocks already grant the writes that any job actually performs).
Snapshot of every job in the 13 workflows confirms contents:read is sufficient:
- ci.yml: quality/tests/scope-parity have explicit contents:read job blocks; save-pr-meta uses upload-artifact only (no token scopes needed); ci-status is pure shell.
- ci-e2e.yml, ci-quality.yml, ci-scope-parity.yml, ci-tests.yml: all jobs do checkout + npm + tsc/vitest/playwright/upload-artifact only; no API token scopes required.
- claude.yml, codeql.yml, dependency-review.yml, docker.yml, gitleaks.yml, pr-labeler.yml, trivy.yml, workflow-lint.yml: all jobs already declare their own job-level blocks (security-events:write, pull-requests:write, packages:write, etc.) so the workflow-level default does not gate them.
zizmor (--min-severity high) is clean on the resulting tree. Pre-existing medium findings (secrets-inherit, artipacked) are in unrelated workflows and untouched by this commit.
scorecard.yml also uses read-all but pre-existed PR #1454 and is deferred to a follow-up PR per the plan's scope boundary.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
release-drafter v7 (merged in #852) removed the `disable-releaser`
input, causing the autolabel job to attempt creating a release and
fail with "Resource not accessible by integration". Replace with
`dry-run: true` which achieves the same label-only behavior.
Also update stale version comments for release-drafter and
action-semantic-pull-request to match the actual pinned versions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: standardize workflow concurrency and automate release-note labeling
Concurrency — prevent racing CI jobs
- Every top-level workflow now declares an explicit concurrency block.
- PR runs cancel-in-progress on supersede; main/push/workflow_call/publish
runs queue instead of cancelling so every commit and every release is
validated end-to-end.
- ci.yml uses a literal `CI-` prefix (not `${{ github.workflow }}`) and a
per-run nested group for workflow_call invocations, avoiding a potential
deadlock with publish.yml and release-candidate.yml callers whose own
concurrency groups could otherwise collide with the called workflow.
- ci-report.yml falls back to `<head-repo>/<head-branch>` for fork PRs
(stable across reruns) instead of the per-run-unique workflow_run.id
which did not actually serialize anything.
- ci-quality.yml enforces the convention: fails CI if any non-reusable
workflow lacks a concurrency block or a reusable workflow declares one.
Release-note automation
- New pr-labeler.yml: amannn/action-semantic-pull-request enforces
conventional-commit PR titles on pull_request (fork-safe, read-only);
release-drafter/release-drafter with disable-releaser: true applies the
matching label under pull_request_target (write-scoped). sync-labels in
.github/release-drafter.yml removes managed autolabels that no longer
match (e.g. when `!` or `BREAKING CHANGE:` is dropped from a PR).
- .github/release.yml (unchanged) continues to map labels to categorized
release-notes sections.
- dependabot.yml added for the github-actions ecosystem so pinned SHAs
auto-refresh on a weekly cadence.
Docs
- CONTRIBUTING.md documents the concurrency convention, the
conventional-commit PR-title rules, and the reusable-workflow exception.
Follow-up to verify before relying on the labeler in anger
- gh api repos/amannn/action-semantic-pull-request/git/refs/tags/v5.5.3
- gh api repos/release-drafter/release-drafter/git/refs/tags/v6.0.0
- Confirm release-drafter reads its config from the base ref (not fork
head) when invoked via pull_request_target.
* ci: address PR review feedback on concurrency and labeler workflows
Two blocking fixes
- pr-labeler.yml: separate concurrency slots for pull_request and
pull_request_target. Previously both triggers shared a single group
with cancel-in-progress: true, so the privileged autolabel run could
cancel the title-validation check mid-run and leave a required status
in a permanent cancelled state.
- pr-labeler.yml autolabel job: add contents: read. release-drafter's
context.config() reads .github/release-drafter.yml from the default
branch via the repo-contents API and 403s without the scope. Job-level
permissions nullify all unlisted scopes so an explicit grant is needed.
Two non-blocking improvements
- Replace the hardcoded reusable-workflow allowlist in ci-quality.yml
with dynamic on:-block parsing. New workflow_call-only workflows no
longer produce false-positive convention failures.
- Implement actual group-key validation. The check now also asserts that
every concurrency.group expression references either ${{ github.workflow }}
or the literal CI- prefix (the documented ci.yml exception).
- Script extracted to .github/scripts/check-workflow-concurrency.py so
it is runnable locally and independently testable.