Repository navigation
220 lines (202 loc) · 10.1 KB
/
Copy pathrelease-please.yml
File metadata and controls
220 lines (202 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
name: Release Please
# 트렁크 기반 릴리즈: main 머지는 Release PR만 갱신하고,
# 그 Release PR을 머지할 때만 실제 릴리즈/발행이 일어난다.
on:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: release-please
cancel-in-progress: false
jobs:
# ── 1) Release PR 유지 / Release PR 머지 시 태그·릴리즈 생성 ──────────
release-please:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
release_created: ${{ steps.rp.outputs.release_created }}
tag_name: ${{ steps.rp.outputs.tag_name }}
steps:
# GitHub App 토큰을 써야 Release PR에 CI(verify/CodeRabbit)가 돈다.
# (GITHUB_TOKEN이 만든 PR/커밋은 다른 워크플로를 트리거하지 않음)
- name: GitHub App 토큰 발급
id: app-token
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Release Please
id: rp
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
# ── 2) 릴리즈 생성됨 → 빌드 & GitHub Release 자산 첨부 (승인 없이 자동) ──
publish:
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
zip_sha256: ${{ steps.zip.outputs.sha256 }}
steps:
- name: 리포지토리 체크아웃
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4
with:
persist-credentials: false
- name: Node.js 설정
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '24'
cache: npm
- name: 의존성 설치
run: npm ci
- name: 프로덕션 빌드
run: npm run build
- name: 릴리즈 manifest 검사 (key 미포함)
run: node .github/scripts/check-release-manifest.mjs
- name: dist 압축
id: zip
env:
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
(cd dist && zip -r "../dotbugi-${TAG}.zip" .)
echo "sha256=$(sha256sum "dotbugi-${TAG}.zip" | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: 릴리즈에 zip 첨부
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release-please.outputs.tag_name }}
run: gh release upload "$TAG" "dotbugi-${TAG}.zip" --repo "$GITHUB_REPOSITORY" --clobber
- name: Discord 릴리즈 알림 (스테이블=초록)
# 웹훅 미설정 여부는 스크립트에서 판단한다 (단계 자신의 env 는 같은 단계의 if 에서 보이지 않음)
if: always()
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
STORE_ENABLED: ${{ vars.ENABLE_STORE_PUBLISH == 'true' }}
TAG: ${{ needs.release-please.outputs.tag_name }}
JOB_STATUS: ${{ job.status }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const webhook = process.env.DISCORD_WEBHOOK_URL;
if (!webhook) return;
const tag = process.env.TAG;
const success = process.env.JOB_STATUS === 'success';
const awaitingStore = success && process.env.STORE_ENABLED === 'true';
const repoUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}`;
// 스토어 게시가 켜져 있으면 같은 실행의 store 잡이 승인 대기 중 → 승인 화면(실행 페이지)으로 안내
const url = awaitingStore ? `${repoUrl}/actions/runs/${context.runId}` : `${repoUrl}/releases/tag/${tag}`;
// 승인자가 직전 스테이블 대비 변경 내역을 바로 검토할 수 있도록 비교 링크를 만든다.
// 베타(프리릴리즈)·드래프트·현재 태그는 제외. 조회 실패는 알림 자체를 막지 않도록 무시한다.
let compareLink = '';
if (success) {
try {
const { data: releases } = await github.rest.repos.listReleases({ ...context.repo, per_page: 20 });
const prev = releases.find((r) => !r.prerelease && !r.draft && r.tag_name !== tag);
if (prev) compareLink = `\n\n[변경 사항 보기](${repoUrl}/compare/${prev.tag_name}...${tag})`;
} catch (e) {
core.warning(`이전 릴리즈 조회 실패: ${e.message}`);
}
}
const res = await fetch(webhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
embeds: [{
title: !success
? `⚠️ 릴리즈 발행 실패 (${tag})`
: awaitingStore
? `⏸️ ${tag} 스토어 게시 승인 대기`
: `✅ 스테이블 릴리즈 ${tag}`,
url,
description: !success
? '워크플로 로그를 확인해주세요.'
: awaitingStore
? 'GitHub Release가 생성되었습니다. 제목을 눌러 **Review deployments → Approve** 하면 스토어 업로드·심사 제출이 진행됩니다.' + compareLink
: '새 버전이 발행되었습니다.' + compareLink,
color: !success ? 0xed4245 : awaitingStore ? 0xfee75c : 0x57f287,
}],
allowed_mentions: { parse: [] },
}),
});
if (!res.ok) core.warning(`Discord 알림 실패: ${res.status}`);
# ── 3) Chrome Web Store 업로드 + 심사 제출 (stable 환경 승인 후) ─────────
# 활성화: Variables(GCP_WIF_PROVIDER, GCP_SERVICE_ACCOUNT, CWS_PUBLISHER_ID,
# STABLE_EXTENSION_ID) + ENABLE_STORE_PUBLISH=true
# 승인 전에는 GCP 토큰 자체가 발급되지 않는다(WIF 조건이 stable/beta 환경을 요구).
# 별도 잡으로 분리한 이유: 승인 대기가 GitHub Release 자산 첨부까지 막지 않도록.
store:
needs: [release-please, publish]
if: needs.release-please.outputs.release_created == 'true' && vars.ENABLE_STORE_PUBLISH == 'true'
environment: stable
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # WIF(OIDC) 토큰 발급용
steps:
- name: 리포지토리 체크아웃
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4
with:
persist-credentials: false
# 다시 빌드하지 않고 GitHub Release에 첨부된 바로 그 zip을 올린다 (릴리즈 자산 = 스토어 패키지)
# Release 자산은 쓰기 권한자가 승인 대기 중에 바꿔치기할 수 있으므로,
# publish 잡이 만든 해시(잡 출력은 실행 중 변경 불가)와 일치할 때만 진행한다.
- name: 릴리즈 zip 내려받기 + 무결성 검증
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release-please.outputs.tag_name }}
EXPECTED_SHA256: ${{ needs.publish.outputs.zip_sha256 }}
run: |
gh release download "$TAG" --pattern "dotbugi-${TAG}.zip" --repo "$GITHUB_REPOSITORY"
echo "${EXPECTED_SHA256} dotbugi-${TAG}.zip" | sha256sum -c -
- name: GCP 인증 (WIF → 서비스 계정)
id: gcp
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
token_format: access_token
access_token_scopes: https://www.googleapis.com/auth/chromewebstore
create_credentials_file: false # 액세스 토큰만 쓰므로 작업 디렉터리에 자격증명 파일을 남기지 않음
- name: Chrome Web Store 업로드 + 심사 제출
env:
ACCESS_TOKEN: ${{ steps.gcp.outputs.access_token }}
CWS_PUBLISHER_ID: ${{ vars.CWS_PUBLISHER_ID }}
CWS_EXTENSION_ID: ${{ vars.STABLE_EXTENSION_ID }}
TAG: ${{ needs.release-please.outputs.tag_name }}
run: .github/scripts/cws-publish.sh "dotbugi-${TAG}.zip"
- name: Discord 스토어 결과 알림
# 웹훅 미설정 여부는 스크립트에서 판단한다 (단계 자신의 env 는 같은 단계의 if 에서 보이지 않음)
if: always()
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
TAG: ${{ needs.release-please.outputs.tag_name }}
JOB_STATUS: ${{ job.status }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const webhook = process.env.DISCORD_WEBHOOK_URL;
if (!webhook) return;
const tag = process.env.TAG;
const success = process.env.JOB_STATUS === 'success';
const res = await fetch(webhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
embeds: [{
title: success ? `📤 ${tag} 스토어 심사 제출 완료` : `⚠️ ${tag} 스토어 게시 실패`,
url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
description: success
? 'Google 심사를 통과하면 사용자에게 자동 배포됩니다.'
: '워크플로 로그를 확인한 뒤 잡을 재실행해주세요.',
color: success ? 0x57f287 : 0xed4245,
}],
allowed_mentions: { parse: [] },
}),
});
if (!res.ok) core.warning(`Discord 알림 실패: ${res.status}`);