Skip to content

ThirdParty Dependency Update #7

ThirdParty Dependency Update

ThirdParty Dependency Update #7

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
env:
GITHUB_TOKEN: ${{ github.token }}
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