LiuShen revisó este gist 16 hours ago. Ir a la revisión
2 files changed, 312 insertions
renovate-app-version.yml(archivo creado)
| @@ -0,0 +1,212 @@ | |||
| 1 | + | name: Update app version in Renovate Branches | |
| 2 | + | ||
| 3 | + | on: | |
| 4 | + | push: | |
| 5 | + | branches: [ 'renovate/*' ] | |
| 6 | + | workflow_dispatch: | |
| 7 | + | inputs: | |
| 8 | + | manual-trigger: | |
| 9 | + | description: 'Manually trigger Renovate' | |
| 10 | + | default: '' | |
| 11 | + | ||
| 12 | + | # 并发控制:确保同一时间只有一个 PR 合并操作在进行,避免竞争条件 | |
| 13 | + | concurrency: | |
| 14 | + | group: merge-renovate-pr | |
| 15 | + | cancel-in-progress: false | |
| 16 | + | ||
| 17 | + | jobs: | |
| 18 | + | update-app-version: | |
| 19 | + | runs-on: ubuntu-latest | |
| 20 | + | steps: | |
| 21 | + | - name: Checkout | |
| 22 | + | uses: actions/checkout@v5 | |
| 23 | + | with: | |
| 24 | + | fetch-depth: 0 | |
| 25 | + | ||
| 26 | + | - name: Check if triggered by self (prevent circular trigger) | |
| 27 | + | id: check-circular | |
| 28 | + | run: | | |
| 29 | + | # 检查最后一个 commit 的 author,如果是自己则跳过 | |
| 30 | + | last_author=$(git log -1 --format='%an') | |
| 31 | + | last_message=$(git log -1 --format='%s') | |
| 32 | + | ||
| 33 | + | echo "Last commit author: $last_author" | |
| 34 | + | echo "Last commit message: $last_message" | |
| 35 | + | ||
| 36 | + | # 如果是 github-action 自己 commit 的,说明是工作流触发的,跳过避免循环 | |
| 37 | + | if [[ "$last_author" == "github-action update-app-version" ]] || \ | |
| 38 | + | [[ "$last_author" == "github-action[bot]" ]] || \ | |
| 39 | + | [[ "$last_message" == *"[auto-version-bump]"* ]]; then | |
| 40 | + | echo "⚠️ Skipping: triggered by own commit (preventing circular trigger)" | |
| 41 | + | echo "skip=true" >> $GITHUB_OUTPUT | |
| 42 | + | else | |
| 43 | + | echo "✅ Proceeding: triggered by external commit" | |
| 44 | + | echo "skip=false" >> $GITHUB_OUTPUT | |
| 45 | + | fi | |
| 46 | + | ||
| 47 | + | - name: Configure git | |
| 48 | + | if: steps.check-circular.outputs.skip != 'true' | |
| 49 | + | run: | | |
| 50 | + | git config --local user.email "githubaction@githubaction.com" | |
| 51 | + | git config --local user.name "github-action update-app-version" | |
| 52 | + | ||
| 53 | + | - name: Get list of updated files by the last commit | |
| 54 | + | if: steps.check-circular.outputs.skip != 'true' | |
| 55 | + | id: updated-files | |
| 56 | + | run: | | |
| 57 | + | echo "files=$(git diff-tree --no-commit-id --name-only -r ${{ github.sha }} | tr '\n' ' ')" >> $GITHUB_OUTPUT | |
| 58 | + | ||
| 59 | + | - name: Run renovate-app-version.sh on updated files | |
| 60 | + | if: steps.check-circular.outputs.skip != 'true' | |
| 61 | + | id: rename | |
| 62 | + | run: | | |
| 63 | + | set -e | |
| 64 | + | chmod +x .github/workflows/renovate-app-version.sh | |
| 65 | + | ||
| 66 | + | files="${{ steps.updated-files.outputs.files }}" | |
| 67 | + | declare -a changed_apps=() | |
| 68 | + | ||
| 69 | + | echo "Updated files: $files" | |
| 70 | + | ||
| 71 | + | for file in $files; do | |
| 72 | + | if [[ $file == *"docker-compose.yml"* ]]; then | |
| 73 | + | echo "Processing file: $file" | |
| 74 | + | ||
| 75 | + | app_name=$(echo $file | cut -d'/' -f 2) | |
| 76 | + | old_version=$(echo $file | cut -d'/' -f 3) | |
| 77 | + | echo "App name: $app_name, old version: $old_version" | |
| 78 | + | ||
| 79 | + | # 获取所有服务名 | |
| 80 | + | services=$(yq '.services | keys | .[]' "$file") | |
| 81 | + | service="" | |
| 82 | + | image_line="" | |
| 83 | + | ||
| 84 | + | for s in $services; do | |
| 85 | + | # 通过awk获取服务下的image行(包含注释) | |
| 86 | + | image_line=$(awk "/services:/{flag=0} /^\s*$s:/{flag=1} flag && /^\s*image:/{print; exit}" "$file") | |
| 87 | + | echo "Service $s image line: $image_line" | |
| 88 | + | if [[ "$image_line" != *"[ignore]"* ]]; then | |
| 89 | + | service="$s" | |
| 90 | + | break | |
| 91 | + | else | |
| 92 | + | echo "Skipping service $s due to [ignore]" | |
| 93 | + | fi | |
| 94 | + | done | |
| 95 | + | ||
| 96 | + | if [[ -z "$service" ]]; then | |
| 97 | + | echo "No valid service found in $file, skipping..." | |
| 98 | + | continue | |
| 99 | + | fi | |
| 100 | + | ||
| 101 | + | # 提取image纯字符串,去除注释和多余空格 | |
| 102 | + | image=$(echo "$image_line" | sed -E 's/^\s*image:\s*([^ #]+).*/\1/') | |
| 103 | + | echo "Selected service: $service" | |
| 104 | + | echo "Extracted image: $image" | |
| 105 | + | ||
| 106 | + | if [[ "$image" == *":"* ]]; then | |
| 107 | + | new_version=$(cut -d ":" -f2- <<< "$image") | |
| 108 | + | trimmed_version=${new_version/#"v"/} | |
| 109 | + | echo "Parsed new version: $trimmed_version" | |
| 110 | + | else | |
| 111 | + | trimmed_version="" | |
| 112 | + | echo "No version tag found in image." | |
| 113 | + | fi | |
| 114 | + | ||
| 115 | + | changed_apps+=("${app_name}:${old_version}:${trimmed_version}") | |
| 116 | + | echo "Calling renovate-app-version.sh with: $app_name, $old_version, $trimmed_version" | |
| 117 | + | .github/workflows/renovate-app-version.sh "$app_name" "$old_version" "$trimmed_version" | |
| 118 | + | fi | |
| 119 | + | done | |
| 120 | + | ||
| 121 | + | echo "All changed apps: ${changed_apps[*]}" | |
| 122 | + | echo "apps=$(IFS=, ; echo "${changed_apps[*]}")" >> $GITHUB_OUTPUT | |
| 123 | + | ||
| 124 | + | - name: Commit & Push Changes | |
| 125 | + | if: steps.check-circular.outputs.skip != 'true' | |
| 126 | + | run: | | |
| 127 | + | set -e | |
| 128 | + | IFS=',' read -r -a apps <<< "${{ steps.rename.outputs.apps }}" | |
| 129 | + | for item in "${apps[@]}"; do | |
| 130 | + | app_name=$(cut -d':' -f1 <<< "$item") | |
| 131 | + | old_version=$(cut -d':' -f2 <<< "$item") | |
| 132 | + | new_version=$(cut -d':' -f3 <<< "$item") | |
| 133 | + | ||
| 134 | + | if [[ -n "$app_name" && -n "$new_version" ]]; then | |
| 135 | + | git add "apps/$app_name/*" | |
| 136 | + | git commit -m "📈将应用 $app_name 的版本从 $old_version 升级到 $new_version" --no-verify || echo "无内容可提交" | |
| 137 | + | fi | |
| 138 | + | done | |
| 139 | + | ||
| 140 | + | git push || echo "无内容可推送" | |
| 141 | + | ||
| 142 | + | - name: Rebase to latest main and merge PR | |
| 143 | + | if: steps.check-circular.outputs.skip != 'true' && github.ref_name != 'main' | |
| 144 | + | env: | |
| 145 | + | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| 146 | + | run: | | |
| 147 | + | set -e | |
| 148 | + | branch_name=$(git rev-parse --abbrev-ref HEAD) | |
| 149 | + | echo "Current branch: $branch_name" | |
| 150 | + | ||
| 151 | + | # 获取 PR 编号 | |
| 152 | + | pr_number=$(gh pr list --state open --head "$branch_name" --json number -q '.[0].number') | |
| 153 | + | if [ -z "$pr_number" ]; then | |
| 154 | + | echo "No PR found for branch $branch_name" | |
| 155 | + | exit 0 | |
| 156 | + | fi | |
| 157 | + | ||
| 158 | + | echo "Found PR #$pr_number, preparing to merge..." | |
| 159 | + | ||
| 160 | + | # Fetch 最新的 main 分支 | |
| 161 | + | echo "Fetching latest main branch..." | |
| 162 | + | git fetch origin main | |
| 163 | + | ||
| 164 | + | # Rebase 当前分支到最新的 main | |
| 165 | + | # 由于 renovate PR 修改的是不同的应用文件夹,不会有真正的冲突 | |
| 166 | + | echo "Rebasing $branch_name onto origin/main..." | |
| 167 | + | git rebase origin/main | |
| 168 | + | ||
| 169 | + | # Force push 更新后的分支 | |
| 170 | + | echo "Force pushing rebased branch..." | |
| 171 | + | git push --force-with-lease | |
| 172 | + | ||
| 173 | + | # 等待 GitHub 重新计算 mergeable 状态 | |
| 174 | + | echo "Waiting for GitHub to recalculate mergeable status..." | |
| 175 | + | sleep 15 | |
| 176 | + | ||
| 177 | + | # 检查 mergeable 状态,最多等待 2 分钟 | |
| 178 | + | max_attempts=12 | |
| 179 | + | attempt=0 | |
| 180 | + | while [ $attempt -lt $max_attempts ]; do | |
| 181 | + | mergeable=$(gh pr view "$pr_number" --json mergeable -q '.mergeable' 2>/dev/null || echo "UNKNOWN") | |
| 182 | + | echo "Mergeable status: $mergeable (attempt $((attempt+1))/$max_attempts)" | |
| 183 | + | ||
| 184 | + | if [ "$mergeable" = "MERGEABLE" ]; then | |
| 185 | + | echo "PR is mergeable!" | |
| 186 | + | break | |
| 187 | + | fi | |
| 188 | + | ||
| 189 | + | # 如果是 CONFLICTING,说明有真正的冲突(不应该发生) | |
| 190 | + | if [ "$mergeable" = "CONFLICTING" ]; then | |
| 191 | + | echo "Warning: PR has conflicts, but proceeding with admin merge..." | |
| 192 | + | break | |
| 193 | + | fi | |
| 194 | + | ||
| 195 | + | sleep 10 | |
| 196 | + | attempt=$((attempt+1)) | |
| 197 | + | done | |
| 198 | + | ||
| 199 | + | # 合并 PR | |
| 200 | + | echo "Merging PR #$pr_number..." | |
| 201 | + | gh pr merge "$pr_number" --merge --delete-branch --admin | |
| 202 | + | ||
| 203 | + | echo "Successfully merged PR #$pr_number!" | |
| 204 | + | ||
| 205 | + | - name: Trigger sync to CNB mirror | |
| 206 | + | if: steps.check-circular.outputs.skip != 'true' && github.ref_name != 'main' | |
| 207 | + | env: | |
| 208 | + | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| 209 | + | run: | | |
| 210 | + | echo "Triggering sync-to-cnb workflow..." | |
| 211 | + | gh workflow run sync-to-cnb.yml --ref main | |
| 212 | + | echo "Sync workflow triggered successfully!" | |
sync-to-cnb.yml(archivo creado)
| @@ -0,0 +1,100 @@ | |||
| 1 | + | name: Sync commits to CNB (force) | |
| 2 | + | ||
| 3 | + | on: | |
| 4 | + | schedule: | |
| 5 | + | - cron: "0 0 * * *" # 每天凌晨 0 点强制同步(兜底) | |
| 6 | + | - cron: "0 */6 * * *" # 每 6 小时检查一次 | |
| 7 | + | push: | |
| 8 | + | branches: | |
| 9 | + | - main | |
| 10 | + | workflow_call: # 允许被其他工作流调用 | |
| 11 | + | workflow_dispatch: | |
| 12 | + | inputs: | |
| 13 | + | reason: | |
| 14 | + | description: 'Manual trigger reason' | |
| 15 | + | required: false | |
| 16 | + | default: 'Manual run' | |
| 17 | + | force_sync: | |
| 18 | + | description: 'Force sync without checking for new commits' | |
| 19 | + | required: false | |
| 20 | + | default: 'false' | |
| 21 | + | ||
| 22 | + | jobs: | |
| 23 | + | sync: | |
| 24 | + | runs-on: ubuntu-latest | |
| 25 | + | outputs: | |
| 26 | + | synced: ${{ steps.sync.outputs.synced }} | |
| 27 | + | ||
| 28 | + | steps: | |
| 29 | + | - name: Checkout GitHub repository | |
| 30 | + | uses: actions/checkout@v5 | |
| 31 | + | with: | |
| 32 | + | ref: main | |
| 33 | + | fetch-depth: 0 | |
| 34 | + | ||
| 35 | + | - name: Determine sync mode | |
| 36 | + | id: mode | |
| 37 | + | run: | | |
| 38 | + | # 每天凌晨的定时任务强制同步 | |
| 39 | + | if [[ "${{ github.event.schedule }}" == "0 0 * * *" ]]; then | |
| 40 | + | echo "Daily scheduled sync - forcing sync" | |
| 41 | + | echo "force=true" >> $GITHUB_OUTPUT | |
| 42 | + | exit 0 | |
| 43 | + | fi | |
| 44 | + | ||
| 45 | + | # 手动触发且指定强制同步 | |
| 46 | + | if [[ "${{ github.event.inputs.force_sync }}" == "true" ]]; then | |
| 47 | + | echo "Manual force sync requested" | |
| 48 | + | echo "force=true" >> $GITHUB_OUTPUT | |
| 49 | + | exit 0 | |
| 50 | + | fi | |
| 51 | + | ||
| 52 | + | # 被 workflow_call 调用时强制同步 | |
| 53 | + | if [[ "${{ github.event_name }}" == "workflow_call" ]]; then | |
| 54 | + | echo "Called by another workflow - forcing sync" | |
| 55 | + | echo "force=true" >> $GITHUB_OUTPUT | |
| 56 | + | exit 0 | |
| 57 | + | fi | |
| 58 | + | ||
| 59 | + | echo "force=false" >> $GITHUB_OUTPUT | |
| 60 | + | ||
| 61 | + | - name: Check if there are new commits | |
| 62 | + | if: steps.mode.outputs.force != 'true' | |
| 63 | + | id: check | |
| 64 | + | run: | | |
| 65 | + | NEW_COMMITS=$(git log --since="6 hours ago" --oneline) | |
| 66 | + | if [ -z "$NEW_COMMITS" ]; then | |
| 67 | + | echo "No new commits in the last 6 hours, skip sync." | |
| 68 | + | echo "skip=true" >> $GITHUB_OUTPUT | |
| 69 | + | else | |
| 70 | + | echo "New commits found:" | |
| 71 | + | echo "$NEW_COMMITS" | |
| 72 | + | echo "skip=false" >> $GITHUB_OUTPUT | |
| 73 | + | fi | |
| 74 | + | ||
| 75 | + | - name: Sync to CNB | |
| 76 | + | id: sync | |
| 77 | + | if: steps.mode.outputs.force == 'true' || steps.check.outputs.skip == 'false' | |
| 78 | + | env: | |
| 79 | + | CNB_USERNAME: ${{ secrets.CNB_USERNAME }} | |
| 80 | + | CNB_TOKEN: ${{ secrets.CNB_TOKEN }} | |
| 81 | + | run: | | |
| 82 | + | echo "Syncing to CNB mirror..." | |
| 83 | + | rm -rf .git | |
| 84 | + | git init | |
| 85 | + | git config --global user.name 'GitHub Action' | |
| 86 | + | git config --global user.email 'action@github.com' | |
| 87 | + | git add . | |
| 88 | + | git commit -m "Sync commit ==> [$(date +"%Y-%m-%d %H:%M:%S")]" | |
| 89 | + | git branch -M main | |
| 90 | + | git remote add origin "https://${{ secrets.CNB_USERNAME }}:${{ secrets.CNB_TOKEN }}@cnb.cool/liiiu/appstore.git" | |
| 91 | + | git push --force --quiet origin main | |
| 92 | + | echo "synced=true" >> $GITHUB_OUTPUT | |
| 93 | + | ||
| 94 | + | - name: Report result | |
| 95 | + | run: | | |
| 96 | + | if [[ "${{ steps.sync.outputs.synced }}" == "true" ]]; then | |
| 97 | + | echo "✅ Successfully synced to CNB mirror" | |
| 98 | + | else | |
| 99 | + | echo "⏭️ Skipped sync (no new commits)" | |
| 100 | + | fi | |
Siguiente
Anterior