From 8d5b4022b5cbafee40ea6a39a5bbf789ef41a1f4 Mon Sep 17 00:00:00 2001 From: whqtker Date: Tue, 9 Jun 2026 10:27:31 +0900 Subject: [PATCH 1/3] =?UTF-8?q?chore:=20develop=20->=20master=20ff-only=20?= =?UTF-8?q?=EB=A8=B8=EC=A7=80=20=EC=9E=90=EB=8F=99=ED=99=94=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ff-merge.yml | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/ff-merge.yml diff --git a/.github/workflows/ff-merge.yml b/.github/workflows/ff-merge.yml new file mode 100644 index 00000000..17958c17 --- /dev/null +++ b/.github/workflows/ff-merge.yml @@ -0,0 +1,59 @@ +name: FF-Only Merge to Master + +on: + pull_request_review: + types: [submitted] + +jobs: + ff-merge: + if: | + github.event.review.state == 'approved' && + github.event.pull_request.base.ref == 'master' && + github.event.pull_request.head.ref == 'develop' && + github.event.pull_request.state == 'open' + runs-on: ubuntu-latest + + steps: + - name: 승인 상태 확인 + id: approvals + uses: actions/github-script@v7 + with: + script: | + const { data: reviews } = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + + const latest = {}; + for (const r of reviews) { + if (r.state !== 'COMMENTED') { + latest[r.user.login] = r.state; + } + } + + const values = Object.values(latest); + const approved = values.filter(s => s === 'APPROVED').length >= 1; + const blocked = values.some(s => s === 'CHANGES_REQUESTED'); + + core.setOutput('ready', approved && !blocked ? 'true' : 'false'); + + - name: Checkout + if: steps.approvals.outputs.ready == 'true' + uses: actions/checkout@v4 + with: + token: ${{ secrets.PAT }} + fetch-depth: 0 + + - name: Git 설정 + if: steps.approvals.outputs.ready == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: FF-Only merge develop → master + if: steps.approvals.outputs.ready == 'true' + run: | + git checkout master + git merge --ff-only origin/develop + git push origin master From 47a0ba035dc2a814592e41bd8670060b73cddf23 Mon Sep 17 00:00:00 2001 From: whqtker Date: Tue, 9 Jun 2026 10:36:05 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=EC=8A=B9=EC=9D=B8=20=EC=8B=9C?= =?UTF-8?q?=EC=A0=90=20=EC=9D=B4=ED=9B=84=20=ED=91=B8=EC=8B=9C=EB=90=9C=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B=20=EA=B4=80=EB=A0=A8=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ff-merge.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ff-merge.yml b/.github/workflows/ff-merge.yml index 17958c17..bccee2fc 100644 --- a/.github/workflows/ff-merge.yml +++ b/.github/workflows/ff-merge.yml @@ -51,9 +51,21 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" + - name: develop 변경 여부 검증 + if: steps.approvals.outputs.ready == 'true' + run: | + APPROVED_SHA="${{ github.event.pull_request.head.sha }}" + CURRENT_SHA=$(git rev-parse origin/develop) + if [ "$APPROVED_SHA" != "$CURRENT_SHA" ]; then + echo "develop이 승인 이후 변경되었습니다." + echo " 승인된 SHA: $APPROVED_SHA" + echo " 현재 SHA: $CURRENT_SHA" + exit 1 + fi + - name: FF-Only merge develop → master if: steps.approvals.outputs.ready == 'true' run: | git checkout master - git merge --ff-only origin/develop + git merge --ff-only ${{ github.event.pull_request.head.sha }} git push origin master From d7d6f93fe5e6c45123e7727216ac653134319ae3 Mon Sep 17 00:00:00 2001 From: whqtker Date: Tue, 9 Jun 2026 10:42:15 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20CI=20=EC=83=81=ED=83=9C=20=EB=98=90?= =?UTF-8?q?=ED=95=9C=20=EA=B3=A0=EB=A0=A4=ED=95=98=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ff-merge.yml | 91 ++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ff-merge.yml b/.github/workflows/ff-merge.yml index bccee2fc..a9b91f85 100644 --- a/.github/workflows/ff-merge.yml +++ b/.github/workflows/ff-merge.yml @@ -3,26 +3,64 @@ name: FF-Only Merge to Master on: pull_request_review: types: [submitted] + check_suite: + types: [completed] jobs: ff-merge: if: | - github.event.review.state == 'approved' && - github.event.pull_request.base.ref == 'master' && - github.event.pull_request.head.ref == 'develop' && - github.event.pull_request.state == 'open' + github.event_name == 'pull_request_review' || + (github.event_name == 'check_suite' && github.event.check_suite.conclusion == 'success') runs-on: ubuntu-latest steps: - - name: 승인 상태 확인 - id: approvals + - name: PR 조회 및 조건 검증 + id: validate uses: actions/github-script@v7 with: script: | + let prNumber, headSha; + + if (context.eventName === 'pull_request_review') { + const pr = context.payload.pull_request; + if (pr.base.ref !== 'master' || pr.head.ref !== 'develop' || pr.state !== 'open') { + core.setOutput('ready', 'false'); + return; + } + if (context.payload.review.state !== 'approved') { + core.setOutput('ready', 'false'); + return; + } + prNumber = pr.number; + headSha = pr.head.sha; + } else { + const { data: prs } = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + base: 'master', + head: `${context.repo.owner}:develop`, + }); + + if (prs.length === 0) { + core.setOutput('ready', 'false'); + return; + } + + prNumber = prs[0].number; + headSha = prs[0].head.sha; + + if (context.payload.check_suite.head_sha !== headSha) { + core.setOutput('ready', 'false'); + return; + } + } + + // 승인 상태 확인 const { data: reviews } = await github.rest.pulls.listReviews({ owner: context.repo.owner, repo: context.repo.repo, - pull_number: context.payload.pull_request.number, + pull_number: prNumber, }); const latest = {}; @@ -36,25 +74,50 @@ jobs: const approved = values.filter(s => s === 'APPROVED').length >= 1; const blocked = values.some(s => s === 'CHANGES_REQUESTED'); - core.setOutput('ready', approved && !blocked ? 'true' : 'false'); + if (!approved || blocked) { + core.setOutput('ready', 'false'); + return; + } + + // CI 상태 확인 + const { data: { check_runs } } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: headSha, + per_page: 100, + }); + + const ciRuns = check_runs.filter(r => r.name !== context.workflow); + const allPassed = ciRuns.length > 0 && ciRuns.every(r => + r.status === 'completed' && + ['success', 'skipped', 'neutral'].includes(r.conclusion) + ); + + if (!allPassed) { + core.setOutput('ready', 'false'); + return; + } + + core.setOutput('ready', 'true'); + core.setOutput('head_sha', headSha); - name: Checkout - if: steps.approvals.outputs.ready == 'true' + if: steps.validate.outputs.ready == 'true' uses: actions/checkout@v4 with: token: ${{ secrets.PAT }} fetch-depth: 0 - name: Git 설정 - if: steps.approvals.outputs.ready == 'true' + if: steps.validate.outputs.ready == 'true' run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: develop 변경 여부 검증 - if: steps.approvals.outputs.ready == 'true' + if: steps.validate.outputs.ready == 'true' run: | - APPROVED_SHA="${{ github.event.pull_request.head.sha }}" + APPROVED_SHA="${{ steps.validate.outputs.head_sha }}" CURRENT_SHA=$(git rev-parse origin/develop) if [ "$APPROVED_SHA" != "$CURRENT_SHA" ]; then echo "develop이 승인 이후 변경되었습니다." @@ -64,8 +127,8 @@ jobs: fi - name: FF-Only merge develop → master - if: steps.approvals.outputs.ready == 'true' + if: steps.validate.outputs.ready == 'true' run: | git checkout master - git merge --ff-only ${{ github.event.pull_request.head.sha }} + git merge --ff-only ${{ steps.validate.outputs.head_sha }} git push origin master