mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-06 02:49:56 +00:00
* 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.
121 lines
4.8 KiB
YAML
121 lines
4.8 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths-ignore: ['**.md', 'docs/**', 'LICENSE']
|
|
pull_request:
|
|
branches: [main]
|
|
paths-ignore: ['**.md', 'docs/**', 'LICENSE']
|
|
workflow_call:
|
|
|
|
# Concurrency convention: see CONTRIBUTING.md → "GitHub Actions — Concurrency Convention".
|
|
# Hardcoded `CI-` prefix (not `${{ github.workflow }}`) because this workflow is
|
|
# invoked as a reusable workflow from publish.yml and release-candidate.yml. In
|
|
# called-workflow context `github.workflow` evaluation is ambiguous across GitHub
|
|
# Actions versions, and a prefix that could resolve to the caller's name would
|
|
# share a concurrency group with the caller → deadlock. A literal prefix is
|
|
# immune. Direct `push`/`pull_request` invocations use `CI-<ref>`; invocations
|
|
# from a reusable-workflow caller fall into a per-run-unique group that never
|
|
# serializes with the caller.
|
|
# cancel-in-progress is event-aware: cancel superseded PR runs, queue every other
|
|
# event (push to main, workflow_call from publish.yml, etc.).
|
|
concurrency:
|
|
group: ${{ (github.event_name == 'pull_request' || github.event_name == 'push') && format('CI-{0}', github.ref) || format('CI-nested-{0}', github.run_id) }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
# ── Reusable workflow orchestration ─────────────────────────────────
|
|
# Each concern lives in its own workflow file for maintainability:
|
|
# ci-quality.yml — typecheck (tsc --noEmit)
|
|
# ci-tests.yml — unit + integration tests with coverage + cross-platform
|
|
# ci-e2e.yml — E2E tests (only when gitnexus-web/ changes)
|
|
#
|
|
# Shared setup is DRY via .github/actions/setup-gitnexus composite action.
|
|
|
|
jobs:
|
|
quality:
|
|
uses: ./.github/workflows/ci-quality.yml
|
|
permissions:
|
|
contents: read
|
|
|
|
tests:
|
|
uses: ./.github/workflows/ci-tests.yml
|
|
permissions:
|
|
contents: read
|
|
|
|
e2e:
|
|
uses: ./.github/workflows/ci-e2e.yml
|
|
permissions:
|
|
contents: read
|
|
|
|
# ── Save PR metadata for the reporting workflow ─────────────────
|
|
# The ci-report.yml workflow (triggered by workflow_run) needs the
|
|
# PR number and job results to post a comment. We save them as an
|
|
# artifact because workflow_run context doesn't reliably carry PR
|
|
# info for fork PRs.
|
|
save-pr-meta:
|
|
name: Save PR Metadata
|
|
if: always() && github.event_name == 'pull_request'
|
|
needs: [quality, tests, e2e]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Write metadata
|
|
shell: bash
|
|
env:
|
|
PR_NUMBER: ${{ github.event.number }}
|
|
QUALITY: ${{ needs.quality.result }}
|
|
TESTS: ${{ needs.tests.result }}
|
|
E2E: ${{ needs.e2e.result }}
|
|
run: |
|
|
mkdir -p pr-meta
|
|
echo "$PR_NUMBER" > pr-meta/pr_number
|
|
echo "$QUALITY" > pr-meta/quality_result
|
|
echo "$TESTS" > pr-meta/tests_result
|
|
echo "$E2E" > pr-meta/e2e_result
|
|
# TODO(post-merge): remove backward-compat copies once ci-report.yml
|
|
# on main reads underscore names.
|
|
# Backward-compat: ci-report.yml on main still reads hyphenated
|
|
# names. workflow_run always executes from the default branch, so
|
|
# the main-branch reader won't find the underscore variants until
|
|
# this PR is merged. Write both until then.
|
|
cp pr-meta/pr_number pr-meta/pr-number
|
|
cp pr-meta/quality_result pr-meta/quality-result
|
|
cp pr-meta/tests_result pr-meta/tests-result
|
|
cp pr-meta/e2e_result pr-meta/e2e-result
|
|
|
|
- name: Upload PR metadata
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: pr-meta
|
|
path: pr-meta/
|
|
retention-days: 1
|
|
|
|
# ── Unified CI gate ──────────────────────────────────────────────
|
|
# Single required check for branch protection.
|
|
ci-status:
|
|
name: CI Gate
|
|
needs: [quality, tests, e2e]
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Check all jobs passed
|
|
shell: bash
|
|
env:
|
|
QUALITY: ${{ needs.quality.result }}
|
|
TESTS: ${{ needs.tests.result }}
|
|
E2E: ${{ needs.e2e.result }}
|
|
run: |
|
|
echo "Quality: $QUALITY"
|
|
echo "Tests: $TESTS"
|
|
echo "E2E: $E2E"
|
|
if [[ "$QUALITY" != "success" ]] ||
|
|
[[ "$TESTS" != "success" ]]; then
|
|
echo "::error::Quality or test jobs failed"
|
|
exit 1
|
|
fi
|
|
if [[ "$E2E" != "success" && "$E2E" != "skipped" ]]; then
|
|
echo "::error::E2E job failed"
|
|
exit 1
|
|
fi
|