diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index f2f488a..280af12 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -5,7 +5,7 @@ on: pull_request: branches: - main - types: [synchronize, ready_for_review] + types: [opened, reopened, synchronize, ready_for_review] concurrency: # Key off the PR number (works across pull_request and pull_request_review diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90f8462..fada972 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,8 +14,15 @@ jobs: - name: 🏗 Setup Repo uses: actions/checkout@v4 with: + # Need full history so the no-op probe below can locate the last + # release boundary commit and grep for qualifying commits since. + fetch-depth: 0 # PAT lets release-it push the version-bump commit + tag back to # main past branch protection (GITHUB_TOKEN can't bypass rules). + # NOTE: GH_PAT was failing with "Permission denied" on push (likely + # expired/missing contents:write). We keep this here in case it gets + # rotated, but `.release-it.js` now sets `git.push: false` so the + # workflow no longer depends on it actually working. token: ${{ secrets.GH_PAT }} persist-credentials: true @@ -64,20 +71,50 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - # release-it prints the next version it WOULD publish. If it matches - # the version already on disk (== last published), there is nothing - # to ship. We disable npm/github side-effects on this probe so it - # only computes the version from conventional commits. + # We can't trust `release-it --release-version` here: when no commits + # match the `(modal)` scope filter, the conventional-changelog plugin + # returns no recommendation and release-it's Version plugin falls + # back to a CI-default `patch` of `latestVersion` (which is `0.0.0` + # because there's no `magic-modal-*` git tag yet). That made the + # probe print `0.0.1` even with zero qualifying commits, so the + # script proceeded to publish. + # + # Instead, decide skip deterministically from git history. The + # boundary is the most recent of: + # - last `magic-modal-*` tag (preferred, once we ever push one) + # - last `chore(release):` commit (release-it's own bump commit) + # - last `chore(modal): sync version` commit (manual sync fallback) + # We then look for any commit since the boundary whose subject is + # `(modal)`-scoped OR whose body has a `BREAKING CHANGE:` footer. + # This mirrors the commitFilter in packages/modal/.release-it.js. CURRENT=$(node -p "require('./package.json').version") - NEXT=$(pnpm exec release-it --release-version --ci --no-npm --no-github 2>&1 | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' | tail -n 1 | tr -d '[:space:]') echo "current=$CURRENT" - echo "next=$NEXT" echo "current=$CURRENT" >> "$GITHUB_OUTPUT" - echo "next=$NEXT" >> "$GITHUB_OUTPUT" - if [ -z "$NEXT" ] || [ "$NEXT" = "$CURRENT" ]; then + + TAG_BOUND=$(git describe --tags --abbrev=0 --match='magic-modal-*' 2>/dev/null || true) + REL_BOUND=$(git log -1 --pretty=%H --grep='^chore(release)' 2>/dev/null || true) + SYNC_BOUND=$(git log -1 --pretty=%H --grep='^chore(modal): sync version' 2>/dev/null || true) + CANDIDATES=$(printf '%s\n%s\n%s\n' "$TAG_BOUND" "$REL_BOUND" "$SYNC_BOUND" | grep -v '^$' || true) + + if [ -z "$CANDIDATES" ]; then + # No boundary at all (fresh repo) — let the release flow run. + echo "No release boundary found; proceeding." + echo "skip=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + BOUNDARY=$(echo "$CANDIDATES" | xargs -I{} git log -1 --pretty="%ct %H" {} 2>/dev/null | sort -rn | head -1 | awk '{print $2}') + echo "boundary=$BOUNDARY ($(git log -1 --pretty=%s "$BOUNDARY"))" + + # `\(modal\)` matches scoped commits; `BREAKING[- ]CHANGE` matches + # the footer added by conventional-commits for breaking changes. + QUALIFY=$(git log "$BOUNDARY..HEAD" -E --grep='\(modal\)|BREAKING[- ]CHANGE' --pretty=%s || true) + if [ -z "$QUALIFY" ]; then + echo "No qualifying commits since boundary. Skipping release." echo "skip=true" >> "$GITHUB_OUTPUT" - echo "No new version to release. Skipping." else + echo "Qualifying commits found:" + echo "$QUALIFY" echo "skip=false" >> "$GITHUB_OUTPUT" fi diff --git a/packages/modal/.release-it.js b/packages/modal/.release-it.js index 76c80b3..4e63187 100644 --- a/packages/modal/.release-it.js +++ b/packages/modal/.release-it.js @@ -76,7 +76,22 @@ export default { pushArgs: ["-o ci.skip"], commit: true, tag: true, - push: true, + // We intentionally do NOT push the release commit/tag back to main from + // CI. The repository's GH_PAT secret (dated 2024) is currently rejected + // by branch protection ("Permission to GSTJ/react-native-magic-modal.git + // denied to GSTJ"), which causes the entire publish workflow to fail + // AFTER npm publish has already happened — leaving npm and main out of + // sync and bricking the workflow forever after. + // + // Keeping `commit: true` and `tag: true` so the @release-it/github plugin + // still has a tag to attach the GitHub Release to within the runner. + // The bump commit + tag exist only on the runner and are discarded when + // the job ends; main stays at the pre-release version, and we sync via + // a follow-up PR (same pattern used for #192). + // + // TODO: once GH_PAT is rotated with `contents: write` and granted + // bypass on branch protection, flip `push` back to `true`. + push: false, requireCleanWorkingDir: false, tagName: "magic-modal-${version}", },