Переглянути джерело

Move release flow to PR-based and add CODEOWNERS for branch protection (#649)

* Move release flow to PR-based and add CODEOWNERS for branch protection

- Add .github/CODEOWNERS
- Cut a release branch in release.sh and open two PRs (sync to dev,
  release to main) instead of merging dev into main locally
- Move tag creation into a workflow that fires on push to main
- Skip auto_version_dev when Config.xcconfig was changed in this push,
  so merging the release sync-PR into dev does not double-bump

* Make workflow guards portable via fork check

Replace the hardcoded 'loopandlearn' owner check in workflows with a
fork check, so the workflows run on any non-fork repository (including
a test org) while still skipping on contributor forks.

* Allow skipping sister repo updates in release.sh

Set SKIP_SISTER_REPOS=1 to bypass the LoopFollow_Second / LoopFollow_Third
update_follower steps. Default behavior is unchanged: both sister repos
are updated as today and missing directories still cause a hard error,
so a forgotten clone in production fails fast.

* Revert "Allow skipping sister repo updates in release.sh"

This reverts commit c2792b8363d6057e02700663b411ca9c71aadb3d.

* Skip patch hunks for files missing from sister repos

Sister repos (LoopFollow_Second / LoopFollow_Third) are intentionally
stripped of dev-only files like release.sh, auto_version_dev.yml,
lint.yml, and warn_main_pr.yml. Any release patch that touches one of
those files used to abort the sister-repo update with 'No such file or
directory'. Now update_follower runs git apply --check first, parses
the missing-file errors, and re-applies with --exclude for each, so the
sister patch covers the files that actually exist.

* Revert "Skip patch hunks for files missing from sister repos"

This reverts commit 45b9871af675f51ee49037a812c66fad8bd5717d.
Jonas Björkert 2 місяців тому
батько
коміт
e6327c79da

+ 6 - 0
.github/CODEOWNERS

@@ -0,0 +1,6 @@
+# Code owners for LoopFollow.
+# Owners listed here are automatically requested for review on PRs and,
+# when "Require review from Code Owners" is enabled in branch protection,
+# their approval is required before a PR can be merged.
+
+* @marionbarker @bjorkert @codebymini

+ 21 - 1
.github/workflows/auto_version_dev.yml

@@ -35,7 +35,7 @@ on:
 
 jobs:
   bump-version:
-    if: github.repository_owner == 'loopandlearn'
+    if: ${{ !github.event.repository.fork }}
     runs-on: ubuntu-latest
 
     steps:
@@ -43,13 +43,32 @@ jobs:
         uses: actions/checkout@v5
         with:
             token: ${{ secrets.LOOPFOLLOW_TOKEN_AUTOBUMP }}
+            fetch-depth: 0
+
+      - name: Skip if Config.xcconfig was changed in this push
+        id: check
+        run: |
+          BEFORE="${{ github.event.before }}"
+          if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
+            echo "skip=false" >> "$GITHUB_OUTPUT"
+            echo "No previous SHA on this push; not skipping."
+            exit 0
+          fi
+          if git diff "$BEFORE..HEAD" -- Config.xcconfig | grep -qE '^\+LOOP_FOLLOW_MARKETING_VERSION'; then
+            echo "skip=true" >> "$GITHUB_OUTPUT"
+            echo "LOOP_FOLLOW_MARKETING_VERSION was set in this push (likely a release sync); skipping auto-bump."
+          else
+            echo "skip=false" >> "$GITHUB_OUTPUT"
+          fi
 
       - name: Set up Git
+        if: steps.check.outputs.skip != 'true'
         run: |
           git config --global user.name "github-actions[bot]"
           git config --global user.email "github-actions[bot]@users.noreply.github.com"
 
       - name: Bump dev version number in Config.xcconfig
+        if: steps.check.outputs.skip != 'true'
         run: |
           FILE=Config.xcconfig
 
@@ -85,6 +104,7 @@ jobs:
           echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
 
       - name: Commit and push changes
+        if: steps.check.outputs.skip != 'true'
         run: |
           git add Config.xcconfig
           git commit -m "CI: Bump dev version to $NEW_VERSION [skip ci]"

+ 68 - 0
.github/workflows/tag_on_main.yml

@@ -0,0 +1,68 @@
+# -----------------------------------------------------------------------------
+# Workflow: Tag release on push to main
+#
+# Description:
+# Creates an annotated git tag whenever main advances to a release version
+# (X.Y.0). The version is read from LOOP_FOLLOW_MARKETING_VERSION in
+# Config.xcconfig and the tag name is `v<version>`.
+#
+# Triggered by: any push to main (release PR merge).
+# Skips if: the version on main is not X.Y.0 (e.g. a hotfix that didn't bump
+# minor/major), or if the tag already exists.
+# -----------------------------------------------------------------------------
+
+name: Tag release on main
+
+on:
+  push:
+    branches:
+      - main
+
+jobs:
+  tag:
+    if: ${{ !github.event.repository.fork }}
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v5
+        with:
+          fetch-depth: 0
+
+      - name: Extract version from Config.xcconfig
+        id: version
+        run: |
+          VERSION=$(grep -E "^LOOP_FOLLOW_MARKETING_VERSION[[:space:]]*=" Config.xcconfig | awk '{print $3}')
+          if [ -z "$VERSION" ]; then
+            echo "::error::Could not find LOOP_FOLLOW_MARKETING_VERSION in Config.xcconfig"
+            exit 1
+          fi
+          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+          echo "Found version: $VERSION"
+
+      - name: Skip non-release versions (only X.Y.0 is tagged)
+        id: check
+        run: |
+          VERSION="${{ steps.version.outputs.version }}"
+          if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.0$ ]]; then
+            echo "is_release=true" >> "$GITHUB_OUTPUT"
+          else
+            echo "is_release=false" >> "$GITHUB_OUTPUT"
+            echo "Version $VERSION is not a release version (X.Y.0); skipping tag."
+          fi
+
+      - name: Create and push tag if missing
+        if: steps.check.outputs.is_release == 'true'
+        run: |
+          TAG="v${{ steps.version.outputs.version }}"
+          git config user.name "github-actions[bot]"
+          git config user.email "github-actions[bot]@users.noreply.github.com"
+          if git rev-parse "$TAG" >/dev/null 2>&1; then
+            echo "Tag $TAG already exists; skipping."
+          else
+            git tag -a "$TAG" -m "$TAG"
+            git push origin "$TAG"
+            echo "Created and pushed tag $TAG"
+          fi

+ 1 - 1
.github/workflows/warn_main_pr.yml

@@ -8,7 +8,7 @@ on:
 
 jobs:
   warn:
-    if: github.repository_owner == 'loopandlearn'
+    if: ${{ !github.event.repository.fork }}
     runs-on: ubuntu-latest
 
     permissions:

+ 37 - 29
release.sh

@@ -24,11 +24,6 @@ echo_run()  { echo "+ $*"; "$@"; }
 push_cmds=()
 queue_push() { push_cmds+=("git -C \"$(pwd)\" $*"); echo "+ [queued] (in $(pwd)) git $*"; }
 
-queue_push_tag () {
-  local tag="$1"
-  queue_push push origin "refs/tags/$tag"
-}
-
 update_follower () {
   local DIR="$1"
   echo; echo "🔄  Updating $DIR …"
@@ -97,32 +92,30 @@ esac
 
 echo "🔢  Bumping version: $old_ver  →  $new_ver"
 
-# --- switch to dev branch ----
+# --- switch to dev so the release branch is cut from latest dev ----
 echo_run git switch "$DEV_BRANCH"
 echo_run git fetch
 echo_run git pull
 
-# --- update version number ----
+# --- create release branch from dev's tip ----
+RELEASE_BRANCH="release/v${new_ver}"
+echo_run git switch -c "$RELEASE_BRANCH"
+
+# --- bump version on the release branch ----
 sed -i '' "s/${MARKETING_KEY}[[:space:]]*=.*/${MARKETING_KEY} = ${new_ver}/" "$VERSION_FILE"
 echo_run git diff "$VERSION_FILE"; pause
 echo_run git commit -m "update version to ${new_ver} [skip ci]" "$VERSION_FILE"
 
-echo "💻  Build & test dev branch now."; pause
-queue_push push origin "$DEV_BRANCH"
+echo "💻  Build & test release branch now."; pause
+queue_push push origin "$RELEASE_BRANCH"
 
-# --- create a patch  ---------------------------
+# --- create a patch from main..release branch (includes the bump) -----
 mkdir -p "$PATCH_DIR"
 PATCH_FILE="${PATCH_DIR}/LF_diff_${old_ver}_to_${new_ver}.patch"
 
-git diff -M --binary "$MAIN_BRANCH" "$DEV_BRANCH"  \
+git diff -M --binary "$MAIN_BRANCH" "$RELEASE_BRANCH"  \
   > "$PATCH_FILE"
 
-# --- merge dev into main for new release
-echo_run git switch "$MAIN_BRANCH"
-echo_run git merge "$DEV_BRANCH"
-echo "💻  Build & test main branch now."; pause
-queue_push push origin "$MAIN_BRANCH"
-
 cd ..
 update_follower "$SECOND_DIR"
 update_follower "$THIRD_DIR"
@@ -136,24 +129,39 @@ pause
 cd ${PRIMARY_ABS_PATH}
 
 # ---------- push queue ----------
-echo; echo "🚀  Ready to tag and push changes upstream."
+echo; echo "🚀  Ready to push changes upstream and open the release PR."
 echo_run git log --oneline -2
 
-read -rp "▶▶  Ready to tag? (y/n): " confirm
-if [[ $confirm =~ ^[Yy]$ ]]; then
-  git tag -a "v${new_ver}" -m "v${new_ver}"
-  queue_push_tag "v${new_ver}"
-  echo_run git log --oneline -2
-else
-  echo "🚫  tag skipped, can add later"
-fi
-
 read -rp "▶▶  Push everything now? (y/n): " confirm
 if [[ $confirm =~ ^[Yy]$ ]]; then
   for cmd in "${push_cmds[@]}"; do echo "+ $cmd"; bash -c "$cmd"; done
   echo "🎉  All pushes completed."
-  echo; echo "🎉  All repos updated to v${new_ver} (local)."
-  echo "👉  Remember to create a GitHub release for tag v${new_ver}."
+
+  echo; echo "📝  Opening sync PR ${RELEASE_BRANCH} → ${DEV_BRANCH} …"
+  gh pr create \
+    --base "$DEV_BRANCH" \
+    --head "$RELEASE_BRANCH" \
+    --title "Sync v${new_ver} version bump to dev" \
+    --body "Syncs the v${new_ver} version bump from the release branch back to \`dev\` so subsequent auto-bumps on \`dev\` continue from the released minor.
+
+\`auto_version_dev\` detects that \`Config.xcconfig\` was changed in this push and skips re-bumping.
+
+⚠️ **Use rebase-merge** (not squash or merge-commit) so \`dev\` and \`main\` end up at the same commit SHA after the release."
+
+  echo; echo "📝  Opening release PR ${RELEASE_BRANCH} → ${MAIN_BRANCH} …"
+  gh pr create \
+    --base "$MAIN_BRANCH" \
+    --head "$RELEASE_BRANCH" \
+    --title "Release v${new_ver}" \
+    --body "Release v${new_ver}.
+
+Merging this PR triggers the tagging workflow, which creates tag \`v${new_ver}\` from \`LOOP_FOLLOW_MARKETING_VERSION\` in \`Config.xcconfig\`.
+
+⚠️ **Use rebase-merge** (not squash or merge-commit) so \`dev\` and \`main\` end up at the same commit SHA after the release."
+
+  echo; echo "🎉  All repos updated to v${new_ver} (local). Release PRs opened (sync → dev, release → main)."
+  echo "👉  Review and merge both PRs — the tag will be created automatically by .github/workflows/tag_on_main.yml."
+  echo "👉  Remember to create a GitHub release for tag v${new_ver} after the tag exists."
 else
   echo "🚫  Pushes skipped.  Run manually if needed:"; printf '   %s\n' "${push_cmds[@]}"
   echo "🚫  Release not completed, pushes to GitHub were skipped"