-
Notifications
You must be signed in to change notification settings - Fork 6
186 lines (174 loc) · 7.62 KB
/
release-published.yml
File metadata and controls
186 lines (174 loc) · 7.62 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
name: Notify Docs on Release Published
# Configuration (Settings → Secrets and variables → Actions):
# Variables (vars.*):
# DOCS_REPO docs 仓库 owner/name,例如 huanfeng/WindInputDocs,未配置则跳过文档通知
# R2_BUCKET Cloudflare R2 bucket 名(未配置则跳过 R2 同步)
# R2_BASE_URL R2 自定义域名,例如 https://dl.example.com(未配置则跳过 R2 同步)
# Secrets:
# DOCS_REPO_PAT 触发 docs 仓库 dispatch 的 PAT
# CLOUDFLARE_API_TOKEN 具备 R2 Edit 权限的 Token
# CLOUDFLARE_ACCOUNT_ID Cloudflare 账户 ID
on:
release:
types: [published]
jobs:
notify-docs:
runs-on: ubuntu-latest
env:
DOCS_REPO: ${{ vars.DOCS_REPO }}
PAT: ${{ secrets.DOCS_REPO_PAT }}
TAG: ${{ github.event.release.tag_name }}
steps:
- name: Notify docs repo to sync changelog
run: |
if [ -z "$DOCS_REPO" ] || [ -z "$PAT" ]; then
echo "DOCS_REPO or DOCS_REPO_PAT not configured, skipping notification"
exit 0
fi
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $PAT" \
"https://api.github.com/repos/${DOCS_REPO}/dispatches" \
-d "{\"event_type\":\"changelog-updated\",\"client_payload\":{\"tag\":\"$TAG\"}}"
sync-to-r2:
name: Sync installer + release notes to Cloudflare R2
runs-on: ubuntu-latest
env:
R2_BUCKET: ${{ vars.R2_BUCKET }}
R2_BASE_URL: ${{ vars.R2_BASE_URL }}
TAG: ${{ github.event.release.tag_name }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
steps:
- name: Skip if R2 configuration is incomplete
id: gate
run: |
if [ -z "$R2_BUCKET" ] || [ -z "$R2_BASE_URL" ] \
|| [ -z "$CLOUDFLARE_API_TOKEN" ] || [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then
echo "R2 configuration incomplete (vars.R2_BUCKET / vars.R2_BASE_URL / CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID), skipping R2 sync"
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Setup Node
if: steps.gate.outputs.skip != 'true'
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install wrangler
if: steps.gate.outputs.skip != 'true'
run: npm install -g wrangler@4
- name: Download installer + release notes from GitHub Release
if: steps.gate.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${TAG#v}"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
mkdir -p dl
gh release download "$TAG" \
--repo "${{ github.repository }}" \
--pattern "WindInput-${VERSION}-Setup.exe" \
--dir ./dl
gh release download "$TAG" \
--repo "${{ github.repository }}" \
--pattern "WindInput-${VERSION}-macOS.pkg" \
--dir ./dl
gh release view "$TAG" --repo "${{ github.repository }}" --json body -q .body \
> "./dl/WindInput-${VERSION}-Release.md"
ls -lh ./dl
- name: Delete previous version's objects in R2
if: steps.gate.outputs.skip != 'true'
run: |
PREV_JSON=$(curl -fsSL "${R2_BASE_URL}/latest.json" || echo "")
if [ -z "$PREV_JSON" ]; then
echo "No previous latest.json found, skipping delete"
exit 0
fi
PREV_VERSION=$(echo "$PREV_JSON" | jq -r .version)
if [ -z "$PREV_VERSION" ] || [ "$PREV_VERSION" = "null" ]; then
echo "Previous version not parseable, skipping delete"
exit 0
fi
if [ "$PREV_VERSION" = "$VERSION" ]; then
echo "Same version as previous ($VERSION), nothing to delete"
exit 0
fi
echo "Purging previous version: $PREV_VERSION"
wrangler r2 object delete --remote "$R2_BUCKET/WindInput-${PREV_VERSION}-Setup.exe" || true
wrangler r2 object delete --remote "$R2_BUCKET/WindInput-${PREV_VERSION}-macOS.pkg" || true
wrangler r2 object delete --remote "$R2_BUCKET/WindInput-${PREV_VERSION}-Release.md" || true
- name: Upload installer to R2
if: steps.gate.outputs.skip != 'true'
run: |
wrangler r2 object put --remote "$R2_BUCKET/WindInput-${VERSION}-Setup.exe" \
--file "./dl/WindInput-${VERSION}-Setup.exe" \
--content-type "application/octet-stream"
wrangler r2 object put --remote "$R2_BUCKET/WindInput-${VERSION}-macOS.pkg" \
--file "./dl/WindInput-${VERSION}-macOS.pkg" \
--content-type "application/octet-stream"
- name: Upload release notes to R2
if: steps.gate.outputs.skip != 'true'
run: |
wrangler r2 object put --remote "$R2_BUCKET/WindInput-${VERSION}-Release.md" \
--file "./dl/WindInput-${VERSION}-Release.md" \
--content-type "text/markdown; charset=utf-8"
- name: Update latest.json on R2
if: steps.gate.outputs.skip != 'true'
run: |
PUBLISHED_AT='${{ github.event.release.published_at }}'
cat > latest.json <<EOF
{
"version": "${VERSION}",
"tag": "${TAG}",
"exeUrl": "${R2_BASE_URL}/WindInput-${VERSION}-Setup.exe",
"pkgUrl": "${R2_BASE_URL}/WindInput-${VERSION}-macOS.pkg",
"releaseNotesUrl": "${R2_BASE_URL}/WindInput-${VERSION}-Release.md",
"publishedAt": "${PUBLISHED_AT}"
}
EOF
cat latest.json
wrangler r2 object put --remote "$R2_BUCKET/latest.json" \
--file latest.json \
--content-type "application/json; charset=utf-8"
- name: Verify objects are reachable on R2 public URL
if: steps.gate.outputs.skip != 'true'
run: |
set -e
EXPECTED_EXE_SIZE=$(stat -c %s "./dl/WindInput-${VERSION}-Setup.exe")
EXPECTED_PKG_SIZE=$(stat -c %s "./dl/WindInput-${VERSION}-macOS.pkg")
check() {
local url="$1"
local expected_size="$2"
echo "Verifying: $url"
local attempt=0
while [ $attempt -lt 6 ]; do
attempt=$((attempt + 1))
local headers
if headers=$(curl -fsSL -I "$url" 2>&1); then
echo "$headers"
if [ -n "$expected_size" ]; then
local actual
actual=$(printf '%s\n' "$headers" | awk 'BEGIN{IGNORECASE=1} /^content-length:/ {gsub(/\r/,""); print $2}' | tail -n1)
if [ "$actual" = "$expected_size" ]; then
echo "OK: size matches ($actual bytes)"
return 0
fi
echo "Size mismatch: expected=$expected_size actual=$actual, retrying..."
else
echo "OK: reachable"
return 0
fi
else
echo "HEAD failed (attempt $attempt/6), retrying in 5s..."
fi
sleep 5
done
echo "ERROR: $url not reachable or size mismatch after 6 attempts" >&2
return 1
}
check "${R2_BASE_URL}/WindInput-${VERSION}-Setup.exe" "$EXPECTED_EXE_SIZE"
check "${R2_BASE_URL}/WindInput-${VERSION}-macOS.pkg" "$EXPECTED_PKG_SIZE"
check "${R2_BASE_URL}/WindInput-${VERSION}-Release.md" ""
check "${R2_BASE_URL}/latest.json" ""
echo "All R2 objects verified."