forked from ilysenko/codex-desktop-linux
-
Notifications
You must be signed in to change notification settings - Fork 0
281 lines (249 loc) · 10.7 KB
/
Copy pathupstream-build-app.yml
File metadata and controls
281 lines (249 loc) · 10.7 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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
name: Upstream Build App
on:
schedule:
- cron: '30 * * * *'
pull_request:
paths:
- install.sh
- Makefile
- scripts/patch-linux-window-ui.js
- scripts/patch-linux-window-ui.test.js
- scripts/patches/**
- scripts/ci/validate-patch-report.js
- scripts/ci/upstream-dmg-*.js
- scripts/validate-upstream-dmg.js
- scripts/lib/candidate-install.sh
- scripts/lib/bundled-plugins.sh
- scripts/lib/browser-client-node-repl-runtime.test.js
- scripts/lib/patch-browser-client-iab-socket-scope.js
- scripts/lib/patch-validation.js
- scripts/lib/upstream-dmg-acceptance.js
- scripts/lib/upstream-dmg-release-profile.js
- scripts/lib/linux-features.js
- computer-use-linux/src/bin/codex-chrome-extension-host.rs
- linux-features/**
- .github/workflows/upstream-build-app.yml
push:
branches:
- main
paths:
- install.sh
- Makefile
- scripts/patch-linux-window-ui.js
- scripts/patch-linux-window-ui.test.js
- scripts/patches/**
- scripts/ci/validate-patch-report.js
- scripts/ci/upstream-dmg-*.js
- scripts/validate-upstream-dmg.js
- scripts/lib/candidate-install.sh
- scripts/lib/bundled-plugins.sh
- scripts/lib/browser-client-node-repl-runtime.test.js
- scripts/lib/patch-browser-client-iab-socket-scope.js
- scripts/lib/patch-validation.js
- scripts/lib/upstream-dmg-acceptance.js
- scripts/lib/upstream-dmg-release-profile.js
- scripts/lib/linux-features.js
- computer-use-linux/src/bin/codex-chrome-extension-host.rs
- linux-features/**
- .github/workflows/upstream-build-app.yml
workflow_dispatch:
permissions:
contents: read
concurrency:
group: upstream-dmg-acceptance-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false
env:
UPSTREAM_DMG_URL: https://persistent.oaistatic.com/codex-app-prod/ChatGPT.dmg
UPSTREAM_DMG_PATH: /tmp/codex-upstream-ci/Codex.dmg
DMG_CACHE_SCHEMA_VERSION: v1
jobs:
build-app-upstream-dmg:
name: Build App Against Upstream DMG
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install build dependencies
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y p7zip-full make g++ python3 curl unzip
- name: Capture upstream DMG metadata
id: upstream-metadata
run: |
set -euo pipefail
headers_file="$(mktemp)"
trap 'rm -f "$headers_file"' EXIT
curl -fsSLI "$UPSTREAM_DMG_URL" > "$headers_file"
# Match header names case-insensitively without gawk's IGNORECASE,
# which is a no-op under mawk (the default awk on ubuntu runners).
last_modified="$(awk 'tolower($0) ~ /^last-modified:/ {sub(/\r$/,""); sub(/^[^:]+: /,""); print; exit}' "$headers_file")"
etag="$(awk 'tolower($0) ~ /^etag:/ {sub(/\r$/,""); sub(/^[^:]+: /,""); gsub(/"/,""); print; exit}' "$headers_file")"
content_length="$(awk 'tolower($0) ~ /^content-length:/ {sub(/\r$/,""); sub(/^[^:]+: /,""); print; exit}' "$headers_file")"
if [ -z "$last_modified" ]; then
last_modified="unknown"
fi
if [ -z "$etag" ]; then
etag="no-etag"
fi
if [ -z "$content_length" ]; then
content_length="unknown"
fi
cache_segment="$(printf '%s|%s|%s\n' "$last_modified" "$etag" "$content_length" | sha256sum | cut -d' ' -f1)"
{
echo "last_modified=$last_modified"
echo "etag=$etag"
echo "content_length=$content_length"
echo "cache_segment=$cache_segment"
} >> "$GITHUB_OUTPUT"
- name: Restore cached upstream DMG
id: dmg-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /tmp/codex-upstream-ci/Codex.dmg
key: upstream-dmg-${{ env.DMG_CACHE_SCHEMA_VERSION }}-${{ steps.upstream-metadata.outputs.cache_segment }}
- name: Download upstream DMG
if: steps.dmg-cache.outputs.cache-hit != 'true'
run: |
set -euo pipefail
mkdir -p "$(dirname "$UPSTREAM_DMG_PATH")"
curl -fL --retry 3 -o "$UPSTREAM_DMG_PATH" "$UPSTREAM_DMG_URL"
- name: Record local DMG fingerprint
id: local-dmg
run: |
set -euo pipefail
test -s "$UPSTREAM_DMG_PATH"
dmg_sha256="$(sha256sum "$UPSTREAM_DMG_PATH" | cut -d' ' -f1)"
dmg_size_bytes="$(stat -c '%s' "$UPSTREAM_DMG_PATH")"
tested_at_utc="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
{
echo "sha256=$dmg_sha256"
echo "size_bytes=$dmg_size_bytes"
echo "tested_at_utc=$tested_at_utc"
} >> "$GITHUB_OUTPUT"
cat > upstream-dmg-metadata.json <<JSON
{
"url": "$UPSTREAM_DMG_URL",
"path": "$UPSTREAM_DMG_PATH",
"last_modified": "${{ steps.upstream-metadata.outputs.last_modified }}",
"etag": "${{ steps.upstream-metadata.outputs.etag }}",
"content_length": "${{ steps.upstream-metadata.outputs.content_length }}",
"sha256": "$dmg_sha256",
"size_bytes": "$dmg_size_bytes",
"tested_at_utc": "$tested_at_utc",
"cache_schema_version": "${{ env.DMG_CACHE_SCHEMA_VERSION }}"
}
JSON
- name: Build app from upstream DMG
id: acceptance-build
continue-on-error: true
env:
CODEX_ACCEPTANCE_DECISION_JSON: ${{ github.workspace }}/upstream-dmg-decision.json
CODEX_ACCEPTANCE_OVERRIDE: '0'
CODEX_ACCEPTANCE_SOURCE: github-actions
CODEX_UPSTREAM_DMG_METADATA_JSON: ${{ github.workspace }}/upstream-dmg-metadata.json
REBUILD_REPORT_DIR: ${{ github.workspace }}/upstream-acceptance
run: make build-app DMG=/tmp/codex-upstream-ci/Codex.dmg
- name: Import staged Browser clients through node_repl
if: steps.acceptance-build.outcome == 'success'
env:
CODEX_NODE_REPL_PATH: ${{ github.workspace }}/codex-app/resources/node_repl
CODEX_STAGED_BUNDLED_PLUGINS_ROOT: ${{ github.workspace }}/codex-app/resources/plugins/openai-bundled/plugins
run: node --test scripts/lib/browser-client-node-repl-runtime.test.js
- name: Upload upstream DMG metadata artifact
# The transactional installer records a decision before returning a
# rejected status, so diagnostics remain available on failed runs.
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: upstream-dmg-metadata
path: |
upstream-dmg-metadata.json
upstream-dmg-decision.json
upstream-acceptance/**/*.json
if-no-files-found: ignore
- name: Record missing decision in the summary
if: always()
run: |
if [ ! -f upstream-dmg-decision.json ]; then
{
echo "## Upstream DMG acceptance"
echo
echo "No decision artifact was produced; the run is inconclusive."
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: Enforce shared acceptance decision
if: always()
run: |
test -f upstream-dmg-decision.json
verdict="$(node -e 'console.log(require("./upstream-dmg-decision.json").verdict)')"
case "$verdict" in
accepted|accepted_with_warnings) exit 0 ;;
*) echo "Upstream DMG verdict: $verdict" >&2; exit 1 ;;
esac
reconcile-upstream-dmg-issue:
name: Reconcile Upstream DMG Drift Issue
needs: build-app-upstream-dmg
if: always() && github.event_name == 'schedule'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
issues: write
steps:
- name: Check out trusted reconciliation code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- name: Download acceptance artifact
id: download
continue-on-error: true
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: upstream-dmg-metadata
path: acceptance-artifact
- name: Capture current upstream identity
id: current-identity
if: steps.download.outcome == 'success' && hashFiles('acceptance-artifact/upstream-dmg-decision.json') != ''
continue-on-error: true
run: |
set -euo pipefail
headers_file="$(mktemp)"
trap 'rm -f "$headers_file"' EXIT
curl -fsSLI "$UPSTREAM_DMG_URL" > "$headers_file"
node - "$headers_file" > current-upstream-identity.json <<'NODE'
const fs = require("node:fs");
const headers = fs.readFileSync(process.argv[2], "utf8").split(/\r?\n/);
const metadata = {};
for (const line of headers) {
const match = line.match(/^([^:]+):\s*(.*)$/);
if (!match) continue;
const key = match[1].toLowerCase();
if (key === "etag") metadata.etag = match[2].replace(/^"|"$/g, "");
if (key === "last-modified") metadata.last_modified = match[2];
if (key === "content-length") metadata.content_length = match[2];
}
process.stdout.write(`${JSON.stringify(metadata, null, 2)}\n`);
NODE
- name: Create, update, or close the drift issue
if: steps.current-identity.outcome == 'success'
uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0
with:
script: |
const fs = require('fs');
const { httpIdentity } = require('./scripts/lib/upstream-dmg-acceptance.js');
const { reconcileUpstreamDmgIssue } = require('./scripts/ci/upstream-dmg-issue.js');
const decision = JSON.parse(fs.readFileSync('acceptance-artifact/upstream-dmg-decision.json', 'utf8'));
const currentMetadata = JSON.parse(fs.readFileSync('current-upstream-identity.json', 'utf8'));
const result = await reconcileUpstreamDmgIssue({
github,
repo: context.repo,
decision,
currentHttpIdentityKey: httpIdentity(currentMetadata)?.key ?? null,
});
core.info(`Upstream DMG issue reconciliation: ${JSON.stringify(result)}`);