diff --git a/.gitea/workflows/update-thirdparty-superpowers.yml b/.gitea/workflows/update-thirdparty-superpowers.yml index a642cc1..ead8a5f 100644 --- a/.gitea/workflows/update-thirdparty-superpowers.yml +++ b/.gitea/workflows/update-thirdparty-superpowers.yml @@ -1,4 +1,4 @@ -name: Update and Sync Superpowers +name: 🪄 Update and Sync Superpowers on: push: @@ -8,10 +8,6 @@ on: schedule: - cron: "@daily" -concurrency: - group: superpowers-update-sync-${{ github.repository }} - cancel-in-progress: true - env: WORKSPACE_DIR: "/home/workspace" THIRDPARTY_BRANCH: "thirdparty/skill" @@ -21,19 +17,18 @@ env: SUPERPOWERS_LIST: "codex/skills/.sources/superpowers.list" jobs: - update: - name: Update thirdparty/skill snapshot + update_and_sync: + name: ♻️ Update thirdparty and sync main runs-on: ubuntu-22.04 - outputs: - snapshot_changed: ${{ steps.update_snapshot.outputs.snapshot_changed }} - env: - TARGET_BRANCH: "${{ env.THIRDPARTY_BRANCH }}" steps: - - name: Prepare repo + - name: 🧰 Prepare repo + shell: bash run: | + set -euo pipefail + echo "========================================" - echo "Prepare repo to WORKSPACE_DIR" + echo "🧰 Prepare repo in WORKSPACE_DIR" echo "========================================" REPO_NAME="${{ github.event.repository.name }}" @@ -48,6 +43,9 @@ jobs: if [ -d "$REPO_DIR" ]; then if [ -d "$REPO_DIR/.git" ]; then cd "$REPO_DIR" + git ls-files -v | awk '/^[a-zS] / {sub(/^[a-zS] /, ""); print}' | while IFS= read -r path; do + git update-index --no-assume-unchanged --no-skip-worktree -- "$path" + done git clean -fdx git reset --hard git fetch --all --tags --force --prune --prune-tags @@ -66,84 +64,48 @@ jobs: git checkout -B main origin/main git config --global --add safe.directory "$REPO_DIR" - echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV + echo "REPO_DIR=$REPO_DIR" >> "$GITHUB_ENV" - - name: Update thirdparty/skill snapshot - id: update_snapshot + - name: ♻️ Update thirdparty and sync main shell: bash run: | set -euo pipefail cd "$REPO_DIR" + + echo "========================================" + echo "📦 Refresh thirdparty/skill snapshot" + echo "========================================" + before_ref="" - if git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then - before_ref="$(git rev-parse "origin/$TARGET_BRANCH")" - fi - bash .gitea/ci/update_thirdparty_superpowers.sh - git fetch origin "$TARGET_BRANCH" - after_ref="$(git rev-parse "origin/$TARGET_BRANCH")" - if [ "$after_ref" != "$before_ref" ]; then - echo "snapshot_changed=true" >> "$GITHUB_OUTPUT" + if git show-ref --verify --quiet "refs/remotes/origin/$THIRDPARTY_BRANCH"; then + before_ref="$(git rev-parse "origin/$THIRDPARTY_BRANCH")" + echo "📌 Previous $THIRDPARTY_BRANCH: $before_ref" else - echo "snapshot_changed=false" >> "$GITHUB_OUTPUT" + echo "📌 Previous $THIRDPARTY_BRANCH: " + fi + + TARGET_BRANCH="$THIRDPARTY_BRANCH" bash .gitea/ci/update_thirdparty_superpowers.sh + + git fetch origin "$THIRDPARTY_BRANCH" + after_ref="$(git rev-parse "origin/$THIRDPARTY_BRANCH")" + echo "📌 Current $THIRDPARTY_BRANCH: $after_ref" + + if [ "$after_ref" = "$before_ref" ]; then + echo "✅ No thirdparty snapshot change; skip main sync." + exit 0 fi - sync: - name: Sync skills to main - needs: update - if: ${{ needs.update.outputs.snapshot_changed == 'true' }} - runs-on: ubuntu-22.04 - env: - TARGET_BRANCH: "main" - SUPERPOWERS_BRANCH: "${{ env.THIRDPARTY_BRANCH }}" - SUPERPOWERS_DIR: "superpowers" - SUPERPOWERS_LIST: "codex/skills/.sources/superpowers.list" - steps: - - name: Prepare repo - run: | echo "========================================" - echo "Prepare repo to WORKSPACE_DIR" + echo "🚀 Sync skills into main" echo "========================================" - REPO_NAME="${{ github.event.repository.name }}" - REPO_DIR="${{ env.WORKSPACE_DIR }}/$REPO_NAME" - TOKEN="${{ secrets.WORKFLOW }}" - if [ -n "$TOKEN" ]; then - REPO_URL="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git" - else - REPO_URL="${GITHUB_SERVER_URL}/${{ github.repository }}.git" - fi + git fetch origin main + git checkout -B main origin/main - if [ -d "$REPO_DIR" ]; then - if [ -d "$REPO_DIR/.git" ]; then - cd "$REPO_DIR" - # Clear local index flags so checked-out workflow/script files - # always refresh from the latest main branch contents. - git ls-files -v | awk '/^[a-zS] / {sub(/^[a-zS] /, ""); print}' | while IFS= read -r path; do - git update-index --no-assume-unchanged --no-skip-worktree -- "$path" - done - git clean -fdx - git reset --hard - git fetch --all --tags --force --prune --prune-tags - else - rm -rf "$REPO_DIR" - fi - fi - - if [ ! -d "$REPO_DIR/.git" ]; then - mkdir -p "${{ env.WORKSPACE_DIR }}" - git clone "$REPO_URL" "$REPO_DIR" - cd "$REPO_DIR" - fi - - git fetch origin "$TARGET_BRANCH" - git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH" - - git config --global --add safe.directory "$REPO_DIR" - echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV - - - name: Sync superpowers skills to main - shell: bash - run: | - set -euo pipefail - cd "$REPO_DIR" + TARGET_BRANCH="main" \ + SUPERPOWERS_BRANCH="$THIRDPARTY_BRANCH" \ + SUPERPOWERS_DIR="$SUPERPOWERS_DIR" \ + SUPERPOWERS_LIST="$SUPERPOWERS_LIST" \ bash .gitea/ci/sync_superpowers.sh + + echo "🎉 Update and sync finished." diff --git a/tests/test_superpowers_workflows.py b/tests/test_superpowers_workflows.py index 1e91701..752d598 100644 --- a/tests/test_superpowers_workflows.py +++ b/tests/test_superpowers_workflows.py @@ -14,7 +14,7 @@ class SuperpowersWorkflowTests(unittest.TestCase): def test_auto_update_workflow_name_describes_full_pipeline(self): text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8") - self.assertIn("name: Update and Sync Superpowers", text) + self.assertIn("Update and Sync Superpowers", text) def test_auto_update_workflow_triggers_on_main_push(self): text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8") @@ -31,11 +31,13 @@ class SuperpowersWorkflowTests(unittest.TestCase): text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8") self.assertIn("bash .gitea/ci/update_thirdparty_superpowers.sh", text) - def test_auto_update_workflow_runs_sync_after_update(self): + def test_auto_update_workflow_uses_single_serial_job(self): text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8") - self.assertIn("update:", text) - self.assertIn("sync:", text) - self.assertIn("needs: update", text) + self.assertIn("update_and_sync:", text) + self.assertNotIn("\n update:\n", text) + self.assertNotIn("\n sync:\n", text) + self.assertNotIn("needs: update", text) + self.assertNotIn("outputs:", text) self.assertIn("bash .gitea/ci/sync_superpowers.sh", text) def test_auto_update_workflow_sync_job_clears_stale_index_flags(self): @@ -43,11 +45,13 @@ class SuperpowersWorkflowTests(unittest.TestCase): self.assertIn("git update-index --no-assume-unchanged", text) self.assertIn("--no-skip-worktree", text) - def test_auto_update_workflow_sync_job_runs_from_latest_main(self): + def test_auto_update_workflow_sync_step_runs_from_latest_main(self): text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8") - self.assertIn('TARGET_BRANCH: "main"', text) - self.assertIn('git fetch origin "$TARGET_BRANCH"', text) - self.assertIn('git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH"', text) + self.assertIn('TARGET_BRANCH="$THIRDPARTY_BRANCH" bash .gitea/ci/update_thirdparty_superpowers.sh', text) + self.assertIn('TARGET_BRANCH="main" \\', text) + self.assertIn('SUPERPOWERS_BRANCH="$THIRDPARTY_BRANCH" \\', text) + self.assertIn('SUPERPOWERS_DIR="$SUPERPOWERS_DIR" \\', text) + self.assertIn('SUPERPOWERS_LIST="$SUPERPOWERS_LIST" \\', text) def test_auto_update_workflow_sync_job_uses_literal_superpowers_paths(self): text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8") @@ -56,6 +60,15 @@ class SuperpowersWorkflowTests(unittest.TestCase): self.assertNotIn('SUPERPOWERS_DIR: "${{ env.SUPERPOWERS_DIR }}"', text) self.assertNotIn('SUPERPOWERS_LIST: "${{ env.SUPERPOWERS_LIST }}"', text) + def test_auto_update_workflow_logs_clear_serial_flow(self): + text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8") + self.assertIn("name: 🪄 Update and Sync Superpowers", text) + self.assertIn("- name: 🧰 Prepare repo", text) + self.assertIn('- name: ♻️ Update thirdparty and sync main', text) + self.assertIn('echo "📦 Refresh thirdparty/skill snapshot"', text) + self.assertIn('echo "✅ No thirdparty snapshot change; skip main sync."', text) + self.assertIn('echo "🚀 Sync skills into main"', text) + def test_auto_update_script_targets_thirdparty_branch(self): text = AUTO_UPDATE_SCRIPT.read_text(encoding="utf-8") self.assertIn('TARGET_BRANCH="${TARGET_BRANCH:-thirdparty/skill}"', text)