diff --git a/.github/workflows/deploy-check.yml b/.github/workflows/deploy-check.yml index 534aebd7d9..11e3e1acbf 100644 --- a/.github/workflows/deploy-check.yml +++ b/.github/workflows/deploy-check.yml @@ -1,5 +1,8 @@ name: Check Deploy +# Always reports a result so it can be a required check on `main`, and so the +# promotion's head SHA carries a success for the release App's direct push. +# See apps/cli/docs/release-process.md. on: pull_request: types: @@ -15,9 +18,33 @@ permissions: jobs: check: - if: github.head_ref != 'develop' && !startsWith(github.head_ref, 'hotfix/') + name: Check deploy runs-on: ubuntu-latest + env: + HEAD_REF: ${{ github.head_ref }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + BASE_REPO: ${{ github.repository }} + PR_TITLE: ${{ github.event.pull_request.title }} steps: - - run: | - echo "Pull requests to main branch are only allowed from develop or hotfix/* branches." + - name: Verify head branch and title + run: | + set -euo pipefail + if [ "$HEAD_REF" = "develop" ] && [ "$HEAD_REPO" = "$BASE_REPO" ]; then + echo "Head branch is develop: scheduled promotion, nothing to check." + exit 0 + fi + case "$HEAD_REF" in + hotfix/*) ;; + *) + echo "::error ::Pull requests into main are only allowed from this repository's develop branch or from hotfix/* branches, got '$HEAD_REPO:$HEAD_REF'. Rename the branch (git branch -m hotfix/) and reopen the PR, or target develop instead." + exit 1 + ;; + esac + # semantic-release publishes a patch only for fix, perf, and revert. + # A `!` before the colon would publish a major. + if printf '%s' "$PR_TITLE" | grep -Eq '^(fix|perf|revert)(\([^)]*\))?: '; then + echo "Hotfix title '$PR_TITLE' publishes a patch release." + exit 0 + fi + echo "::error ::Hotfix title '$PR_TITLE' would not publish a patch release. Start it with fix, perf, or revert, with an optional (scope) and no '!' before the colon, for example 'fix(cli): …'. Edit the PR title and this check re-runs." exit 1 diff --git a/.github/workflows/release-smoke-test.yml b/.github/workflows/release-smoke-test.yml index a9581a9e1e..459ae4df67 100644 --- a/.github/workflows/release-smoke-test.yml +++ b/.github/workflows/release-smoke-test.yml @@ -1,6 +1,9 @@ name: Release Smoke Test on: + pull_request: + branches: + - main workflow_dispatch: inputs: version: @@ -18,6 +21,10 @@ on: - beta default: beta +concurrency: + group: release-smoke-test-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + permissions: # release-shared.yml declares privileged publish jobs. They are gated by # dry_run here, but GitHub validates nested-workflow permissions at startup. @@ -29,10 +36,11 @@ permissions: jobs: smoke: name: Run release smoke tests + if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'hotfix/') uses: ./.github/workflows/release-shared.yml with: - version: ${{ inputs.version }} - npm_tag: ${{ inputs.npm_tag }} + version: ${{ inputs.version || '0.0.0-smoke' }} + npm_tag: ${{ inputs.npm_tag || 'beta' }} channel: beta prerelease: true dry_run: true diff --git a/.github/workflows/run-ci.yml b/.github/workflows/run-ci.yml index 49abc78abd..24d3e180e9 100644 --- a/.github/workflows/run-ci.yml +++ b/.github/workflows/run-ci.yml @@ -1,8 +1,9 @@ name: run-ci # Opt-in full develop CI for PRs that Test.yml does not already cover: stacked -# PRs (base is not develop) and drafts. Ready develop PRs stay on the existing -# workflows so required check names and concurrency are unchanged. +# PRs (base is neither develop nor main) and drafts. Ready develop and main PRs +# stay on the existing workflows so required check names and concurrency are +# unchanged. # # Add the `run-ci` label to start the suite; remove it to cancel in-progress # runs via this workflow's concurrency group. Other labels do not retrigger. @@ -41,7 +42,8 @@ jobs: if: | !startsWith(github.head_ref, 'release-notes/') && contains(github.event.pull_request.labels.*.name, 'run-ci') && - (github.event.pull_request.draft || github.base_ref != 'develop') && + (github.event.pull_request.draft || + (github.base_ref != 'develop' && github.base_ref != 'main')) && ((github.event.action != 'labeled' && github.event.action != 'unlabeled') || github.event.label.name == 'run-ci') diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c934ecae5..c5c6265026 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,8 +3,10 @@ name: Test # Release-notes PRs (head ref `release-notes/*`) only add markdown under # `release-notes/` and are published via approval — skip the full CI suite. # -# Default path: ready (non-draft) PRs targeting `develop`, plus the merge queue. -# `run-ci.yml` calls this workflow for drafts and stacked / non-develop PRs. +# Default path: ready (non-draft) PRs targeting `develop` or `main`, plus the +# merge queue. PRs into `main` from `develop` are the scheduled promotion and +# skip the suite: that head already passed through the merge queue. Hotfix PRs +# into `main` run it. `run-ci.yml` calls this workflow for drafts and stacked PRs. on: pull_request: types: @@ -15,6 +17,7 @@ on: - converted_to_draft branches: - develop + - main merge_group: types: - checks_requested @@ -45,6 +48,7 @@ jobs: check: if: | !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false) @@ -80,6 +84,7 @@ jobs: test-unit: if: | !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false) @@ -103,6 +108,7 @@ jobs: test-integration: if: | !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false) @@ -126,6 +132,7 @@ jobs: test-stack-ports: if: | !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false) @@ -178,6 +185,7 @@ jobs: if: | always() && !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false) @@ -201,6 +209,7 @@ jobs: test-e2e: if: | !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false) @@ -272,6 +281,7 @@ jobs: test-stack-e2e: if: | !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false) @@ -311,6 +321,7 @@ jobs: if: | always() && !startsWith(github.head_ref, 'release-notes/') && + !(github.base_ref == 'main' && github.head_ref == 'develop') && (github.event_name == 'merge_group' || inputs.force || github.event.pull_request.draft == false)