ThirdParty Dependency Update #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: ThirdParty Dependency Update | |
| on: | |
| schedule: | |
| - cron: "0 15 * * 0" | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "후보 탐색·AI 판정·빌드만 수행하고 원격 branch와 PR은 생성하거나 변경하지 않음" | |
| required: true | |
| default: true | |
| type: boolean | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| concurrency: | |
| group: thirdparty-dependency-update-develop | |
| cancel-in-progress: false | |
| env: | |
| BASE_BRANCH: develop | |
| UPDATE_BRANCH: chore/dependency-updates | |
| WORKSPACE: DevLog.xcworkspace | |
| SCHEME: App | |
| XCODE_VERSION: "26.3" | |
| MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} | |
| MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} | |
| jobs: | |
| update: | |
| runs-on: macos-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| ref: develop | |
| fetch-depth: 0 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: "22" | |
| - name: Set up Tuist | |
| uses: jdx/mise-action@v4 | |
| with: | |
| install: true | |
| cache: true | |
| - name: Install SwiftLint | |
| env: | |
| HOMEBREW_REQUIRE_TAP_TRUST: "1" | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| brew list swiftlint >/dev/null 2>&1 || brew install swiftlint | |
| swiftlint version | |
| - name: Install private config files | |
| uses: ./.github/actions/install-private-config | |
| with: | |
| git_url: ${{ env.MATCH_GIT_URL }} | |
| git_basic_authorization: ${{ env.MATCH_GIT_BASIC_AUTHORIZATION }} | |
| environment: staging | |
| - name: Prepare report directory | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dependency-update-report | |
| - name: Prepare update branch | |
| id: branch | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| PR_NUMBER="$(gh pr list \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --base "$BASE_BRANCH" \ | |
| --head "$UPDATE_BRANCH" \ | |
| --state open \ | |
| --json number \ | |
| --jq '.[0].number // empty')" | |
| if [ -n "$PR_NUMBER" ]; then | |
| git fetch origin "$BASE_BRANCH" "$UPDATE_BRANCH" | |
| git switch --create "$UPDATE_BRANCH" --force "origin/$UPDATE_BRANCH" | |
| BASE_COMMIT="$(git merge-base "origin/$BASE_BRANCH" HEAD)" | |
| CHANGED_PATHS="$(git diff --name-only "$BASE_COMMIT"...HEAD)" | |
| if [ -n "$CHANGED_PATHS" ] && [ "$CHANGED_PATHS" != "Libraries/ThirdParty/Project.swift" ]; then | |
| echo "자동 갱신 branch에 허용되지 않은 변경이 있음" >&2 | |
| echo "$CHANGED_PATHS" >&2 | |
| exit 1 | |
| fi | |
| git merge --no-edit "origin/$BASE_BRANCH" | |
| else | |
| if git ls-remote --exit-code --heads origin "$UPDATE_BRANCH" >/dev/null; then | |
| MERGED_PR="$(gh pr list \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --base "$BASE_BRANCH" \ | |
| --head "$UPDATE_BRANCH" \ | |
| --state merged \ | |
| --json number,headRefOid \ | |
| --jq 'if length == 0 then empty else .[0].number + " " + .[0].headRefOid end')" | |
| read -r MERGED_PR_NUMBER MERGED_PR_HEAD_SHA <<< "$MERGED_PR" | |
| if [ -z "$MERGED_PR_NUMBER" ] || [ -z "$MERGED_PR_HEAD_SHA" ]; then | |
| echo "병합되지 않은 자동 갱신 branch가 남아 있어 수동 확인 필요" >&2 | |
| exit 1 | |
| fi | |
| git fetch origin "$UPDATE_BRANCH" | |
| REMOTE_HEAD_SHA="$(git rev-parse "origin/$UPDATE_BRANCH")" | |
| if [ "$REMOTE_HEAD_SHA" != "$MERGED_PR_HEAD_SHA" ]; then | |
| echo "현재 원격 branch가 마지막 병합 PR과 달라 수동 확인 필요" >&2 | |
| exit 1 | |
| fi | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "dry-run에서는 병합된 자동 갱신 branch를 삭제하지 않음" | |
| else | |
| git push origin --delete "$UPDATE_BRANCH" | |
| fi | |
| fi | |
| git switch --create "$UPDATE_BRANCH" "origin/$BASE_BRANCH" | |
| fi | |
| echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" | |
| - name: Discover candidates | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| node .github/scripts/dependency-update.mjs discover \ | |
| --manifest Libraries/ThirdParty/Project.swift \ | |
| --output dependency-update-report/discovery.json | |
| - name: Decide candidates from release notes | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| node .github/scripts/dependency-update.mjs decide \ | |
| --discovery dependency-update-report/discovery.json \ | |
| --output dependency-update-report/decisions.json | |
| - name: Apply approved updates locally | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| node .github/scripts/dependency-update.mjs apply \ | |
| --manifest Libraries/ThirdParty/Project.swift \ | |
| --updates dependency-update-report/decisions.json \ | |
| --output dependency-update-report/Project.swift | |
| if ! cmp --silent Libraries/ThirdParty/Project.swift dependency-update-report/Project.swift; then | |
| cp dependency-update-report/Project.swift Libraries/ThirdParty/Project.swift | |
| echo "changed=true" >> "$GITHUB_ENV" | |
| else | |
| echo "changed=false" >> "$GITHUB_ENV" | |
| fi | |
| - name: Select Xcode | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| XCODE_APP="/Applications/Xcode_${XCODE_VERSION}.app" | |
| if [ ! -d "$XCODE_APP" ]; then | |
| XCODE_APP="/Applications/Xcode-${XCODE_VERSION}.app" | |
| fi | |
| if [ ! -d "$XCODE_APP" ]; then | |
| echo "Requested Xcode not found for version: $XCODE_VERSION" >&2 | |
| exit 1 | |
| fi | |
| sudo xcode-select -s "$XCODE_APP/Contents/Developer" | |
| xcodebuild -version | |
| - name: Generate Xcode workspace | |
| shell: bash | |
| run: | | |
| set -o pipefail | |
| tuist generate --no-open 2>&1 | tee dependency-update-report/tuist-generate.log | |
| - name: Resolve Swift package dependencies | |
| shell: bash | |
| run: | | |
| set -o pipefail | |
| xcodebuild \ | |
| -workspace "$WORKSPACE" \ | |
| -scheme "$SCHEME" \ | |
| -configuration Debug \ | |
| -clonedSourcePackagesDirPath .spm \ | |
| -resolvePackageDependencies \ | |
| -skipPackagePluginValidation \ | |
| -skipMacroValidation \ | |
| 2>&1 | tee dependency-update-report/package-resolve.log | |
| - name: Build App | |
| shell: bash | |
| run: | | |
| set -o pipefail | |
| xcodebuild \ | |
| -workspace "$WORKSPACE" \ | |
| -scheme "$SCHEME" \ | |
| -configuration Debug \ | |
| -destination "generic/platform=iOS Simulator" \ | |
| -clonedSourcePackagesDirPath .spm \ | |
| -skipPackagePluginValidation \ | |
| -skipMacroValidation \ | |
| -resultBundlePath dependency-update-report/dependency-update.xcresult \ | |
| build \ | |
| 2>&1 | tee dependency-update-report/xcodebuild.log | |
| - name: Render PR body section | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f dependency-update-report/decisions.json ]; then | |
| node .github/scripts/dependency-update.mjs render-pr-body \ | |
| --decisions dependency-update-report/decisions.json \ | |
| --run-id "$GITHUB_RUN_ID" \ | |
| --output "$RUNNER_TEMP/pr-body-section.md" | |
| else | |
| cat > "$RUNNER_TEMP/pr-body-section.md" <<EOF | |
| <!-- dependency-update:$GITHUB_RUN_ID --> | |
| ### 의존성 갱신 실행 | |
| 후보 탐색 또는 AI 판정 결과를 만들지 못했으므로 workflow artifact 확인 필요. | |
| EOF | |
| fi | |
| - name: Update pull request | |
| if: success() && (github.event_name != 'workflow_dispatch' || inputs.dry_run == false) | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ steps.branch.outputs.pr_number }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ "$changed" = "true" ]; then | |
| git diff --check | |
| git add Libraries/ThirdParty/Project.swift | |
| git commit -m "chore: ThirdParty 의존성 갱신" | |
| git push origin "HEAD:$UPDATE_BRANCH" | |
| fi | |
| if [ -z "$PR_NUMBER" ] && [ "$changed" = "false" ]; then | |
| exit 0 | |
| fi | |
| if [ -z "$PR_NUMBER" ]; then | |
| cat > "$RUNNER_TEMP/initial-pr-body.md" <<EOF | |
| ## 🔗 연관된 이슈 | |
| - #821 | |
| ## 🎯 의도 | |
| `ThirdParty`에 직접 선언된 의존성의 검증된 갱신 제안. | |
| ## 📝 작업 내용 | |
| ### 📌 요약 | |
| ### 🔍 상세 | |
| EOF | |
| cat "$RUNNER_TEMP/pr-body-section.md" >> "$RUNNER_TEMP/initial-pr-body.md" | |
| PR_NUMBER="$(gh pr create \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --base "$BASE_BRANCH" \ | |
| --head "$UPDATE_BRANCH" \ | |
| --title "chore: ThirdParty 의존성 갱신" \ | |
| --body-file "$RUNNER_TEMP/initial-pr-body.md")" | |
| exit 0 | |
| fi | |
| CURRENT_BODY="$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json body --jq .body)" | |
| MARKER="<!-- dependency-update:$GITHUB_RUN_ID -->" | |
| if [[ "$CURRENT_BODY" == *"$MARKER"* ]]; then | |
| exit 0 | |
| fi | |
| { | |
| printf '%s\n\n' "$CURRENT_BODY" | |
| cat "$RUNNER_TEMP/pr-body-section.md" | |
| } > "$RUNNER_TEMP/next-pr-body.md" | |
| gh pr edit "$PR_NUMBER" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --body-file "$RUNNER_TEMP/next-pr-body.md" | |
| - name: Write workflow summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| echo "## ThirdParty 의존성 갱신" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- dry-run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- manifest 변경: ${changed:-false}" >> "$GITHUB_STEP_SUMMARY" | |
| if [ -f dependency-update-report/decisions.json ]; then | |
| jq -r '.packages[] | "- \(.repository): \(.action)"' dependency-update-report/decisions.json >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Upload update report | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: thirdparty-dependency-update-${{ github.run_id }} | |
| path: | | |
| dependency-update-report/discovery.json | |
| dependency-update-report/decisions.json | |
| dependency-update-report/Project.swift | |
| dependency-update-report/tuist-generate.log | |
| dependency-update-report/package-resolve.log | |
| dependency-update-report/xcodebuild.log | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Upload build result bundle | |
| if: failure() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: thirdparty-dependency-update-xcresult-${{ github.run_id }} | |
| path: dependency-update-report/dependency-update.xcresult | |
| if-no-files-found: ignore | |
| retention-days: 14 |