Skip to content

Commit a05567a

Browse files
committed
feat: add xivstrings update workflow and trigger script
1 parent 3ea5d2a commit a05567a

2 files changed

Lines changed: 140 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ jobs:
7272
.ixion-config.ci.json
7373
package.json
7474
75+
update-xivstrings:
76+
runs-on: ubuntu-latest
77+
needs: build
78+
steps:
79+
- name: Checkout code
80+
uses: actions/checkout@v4
81+
82+
- name: Setup Node.js
83+
uses: actions/setup-node@v4
84+
with:
85+
node-version: 24
86+
87+
- name: Trigger xivstrings update
88+
env:
89+
XIVSTRINGS_ROOT: ${{ secrets.XIVSTRINGS_ROOT }}
90+
XIVSTRINGS_UPDATE_TOKEN: ${{ secrets.XIVSTRINGS_UPDATE_TOKEN }}
91+
run: node scripts/trigger-xivstrings-update.mjs
92+
7593
datamining-cn:
7694
runs-on: ubuntu-latest
7795
needs: build
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
const root = process.env.XIVSTRINGS_ROOT?.trim()
2+
const token = process.env.XIVSTRINGS_UPDATE_TOKEN?.trim()
3+
const pollIntervalMs = Number(process.env.XIVSTRINGS_POLL_INTERVAL_MS ?? 10_000)
4+
const timeoutMs = Number(process.env.XIVSTRINGS_TIMEOUT_MS ?? 30 * 60 * 1000)
5+
6+
if (!root) {
7+
throw new Error('Missing XIVSTRINGS_ROOT environment variable')
8+
}
9+
10+
if (!token) {
11+
throw new Error('Missing XIVSTRINGS_UPDATE_TOKEN environment variable')
12+
}
13+
14+
const startedAt = Date.now()
15+
16+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
17+
18+
const getVersionUrl = () => new URL('/api/version', root)
19+
20+
const getPostUrl = () => {
21+
const url = getVersionUrl()
22+
url.searchParams.set('token', token)
23+
return url
24+
}
25+
26+
const requestJson = async (url, init) => {
27+
const response = await fetch(url, init)
28+
const text = await response.text()
29+
30+
let body
31+
try {
32+
body = text ? JSON.parse(text) : {}
33+
} catch {
34+
body = { raw: text }
35+
}
36+
37+
if (!response.ok) {
38+
const message = body?.error ?? body?.message ?? `HTTP ${response.status}`
39+
throw new Error(`${response.status} ${message}`)
40+
}
41+
42+
return {
43+
status: response.status,
44+
body,
45+
}
46+
}
47+
48+
const formatStatus = (payload) => {
49+
const update = payload?.update ?? {}
50+
const version = payload?.version ? ` version=${payload.version}` : ''
51+
const state = update.state ? ` state=${update.state}` : ''
52+
const startedAt = update.startedAt ? ` startedAt=${update.startedAt}` : ''
53+
const finishedAt = update.finishedAt ? ` finishedAt=${update.finishedAt}` : ''
54+
const updated =
55+
typeof update.updated === 'boolean'
56+
? ` updated=${String(update.updated)}`
57+
: ''
58+
const error = update.error ? ` error=${update.error}` : ''
59+
return `${version}${state}${startedAt}${finishedAt}${updated}${error}`.trim()
60+
}
61+
62+
const waitForCompletion = async (expectedStartedAt) => {
63+
while (true) {
64+
if (Date.now() - startedAt > timeoutMs) {
65+
throw new Error(
66+
`Timed out waiting for xivstrings update after ${timeoutMs}ms`,
67+
)
68+
}
69+
70+
const { body: payload } = await requestJson(getVersionUrl())
71+
const state = payload?.update?.state ?? 'idle'
72+
const currentStartedAt = payload?.update?.startedAt
73+
74+
console.log(`xivstrings status:${formatStatus(payload)}`)
75+
76+
if (expectedStartedAt && currentStartedAt !== expectedStartedAt) {
77+
throw new Error(
78+
`Observed unexpected update job while waiting for ${expectedStartedAt}: ${formatStatus(payload)}`,
79+
)
80+
}
81+
82+
if (state === 'success') {
83+
return payload
84+
}
85+
86+
if (state === 'error') {
87+
throw new Error(payload?.update?.error ?? 'xivstrings update failed')
88+
}
89+
90+
if (state !== 'running') {
91+
throw new Error(`Unexpected update state while polling: ${state}`)
92+
}
93+
94+
await sleep(pollIntervalMs)
95+
}
96+
}
97+
98+
const { status: triggerStatus, body: triggerPayload } = await requestJson(
99+
getPostUrl(),
100+
{
101+
method: 'POST',
102+
},
103+
)
104+
105+
if (triggerStatus !== 202) {
106+
throw new Error(
107+
`Expected 202 Accepted from update trigger, got ${triggerStatus}`,
108+
)
109+
}
110+
111+
if (triggerPayload?.update?.state !== 'running') {
112+
throw new Error(
113+
`Expected running update state from trigger response, got ${triggerPayload?.update?.state ?? 'missing'}`,
114+
)
115+
}
116+
117+
console.log(
118+
`xivstrings update request accepted: ${triggerPayload.message ?? 'no message'} ${formatStatus(triggerPayload)}`.trim(),
119+
)
120+
121+
const finalPayload = await waitForCompletion(triggerPayload.update.startedAt)
122+
console.log(`xivstrings update finished:${formatStatus(finalPayload)}`)

0 commit comments

Comments
 (0)