Skip to content

Commit 8a710bf

Browse files
committed
ci: make Git release builds reusable
The release workflow owns the platform matrix, PGO training, and packaging checks. Accept a source commit, version, upstream tag, and recipe pin through workflow_call so callers can reuse those build jobs. Keep public pushes on the controller-output check and publish only from that path. Stamp requested builds with the source commit being compiled and include the version in uploaded artifact names. Share package checksum and size checks with a manifest helper. Record one receipt per platform, verify all six against the requested source, recipe, run, and archive bytes, and attach the manifest to the release. Give the preview channel its own version suffix. Signed-off-by: Taylor Blau <ttaylorr@openai.com>
1 parent 34df167 commit 8a710bf

3 files changed

Lines changed: 233 additions & 37 deletions

File tree

.github/workflows/codex-release.yml

Lines changed: 84 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
name: Codex Git release
22

33
on:
4+
workflow_call:
5+
inputs:
6+
source_sha:
7+
description: Full source commit in the caller repository; build only.
8+
required: true
9+
type: string
10+
version:
11+
required: true
12+
type: string
13+
upstream_tag:
14+
required: true
15+
type: string
16+
recipe_sha:
17+
description: Full commit used to call this workflow.
18+
required: true
19+
type: string
420
push:
521
branches:
622
- codex
@@ -10,14 +26,14 @@ permissions:
1026
contents: read
1127

1228
concurrency:
13-
group: codex-git-release-${{ github.sha }}
29+
group: codex-git-release-${{ github.repository }}-${{ inputs.source_sha || github.sha }}
1430
cancel-in-progress: false
1531

1632
jobs:
1733
publication:
1834
name: Verify controller publication
1935
runs-on: ubuntu-24.04
20-
if: github.event.deleted == false
36+
if: inputs.source_sha == '' && github.event.deleted == false
2137
outputs:
2238
published: ${{ steps.verify.outputs.published }}
2339
steps:
@@ -62,19 +78,21 @@ jobs:
6278
version:
6379
name: Determine version
6480
needs: publication
65-
if: needs.publication.outputs.published == 'true'
81+
if: ${{ !cancelled() && (inputs.source_sha != '' || needs.publication.outputs.published == 'true') }}
6682
runs-on: ubuntu-24.04
6783
outputs:
6884
describe: ${{ steps.version.outputs.describe }}
69-
upstream_tag: ${{ steps.version.outputs.upstream_tag }}
70-
version: ${{ steps.version.outputs.version }}
85+
upstream_tag: ${{ inputs.upstream_tag || steps.version.outputs.upstream_tag }}
86+
version: ${{ inputs.version || steps.version.outputs.version }}
7187
steps:
7288
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
89+
if: inputs.source_sha == ''
7390
with:
7491
fetch-depth: 0
7592
persist-credentials: false
7693

7794
- name: Derive OpenAI version from git describe
95+
if: inputs.source_sha == ''
7896
id: version
7997
shell: bash
8098
run: |
@@ -95,16 +113,33 @@ jobs:
95113
upstream_tag=
96114
version="openai-$describe"
97115
fi
116+
if test "$GITHUB_REF_NAME" = codex-unstable
117+
then version="$version.codex-unstable"
118+
fi
98119
git check-ref-format "refs/tags/$version"
99120
printf 'describe=%s\n' "$describe" | tee -a "$GITHUB_OUTPUT"
100121
printf 'upstream_tag=%s\n' "$upstream_tag" | tee -a "$GITHUB_OUTPUT"
101122
printf 'version=%s\n' "$version" | tee -a "$GITHUB_OUTPUT"
102123
124+
- name: Validate requested build inputs
125+
if: inputs.source_sha != ''
126+
env:
127+
SOURCE_SHA: ${{ inputs.source_sha }}
128+
RECIPE_SHA: ${{ inputs.recipe_sha }}
129+
VERSION: ${{ inputs.version }}
130+
run: |
131+
[[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ && "$RECIPE_SHA" =~ ^[0-9a-f]{40}$ ]]
132+
[[ "$VERSION" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]
133+
103134
build:
135+
if: ${{ !cancelled() && needs.version.result == 'success' }}
104136
name: ${{ matrix.name }}
105137
needs: version
106138
runs-on: ${{ matrix.os }}
107139
timeout-minutes: 60
140+
env:
141+
SOURCE_SHA: ${{ inputs.source_sha || github.sha }}
142+
RECIPE_SHA: ${{ inputs.recipe_sha || github.sha }}
108143
strategy:
109144
fail-fast: false
110145
matrix:
@@ -120,7 +155,6 @@ jobs:
120155
lto: thin
121156
profile_format: LLVM
122157
llvm_profdata: xcrun llvm-profdata
123-
max_tar_bytes: 67108864
124158
- name: macOS x64
125159
os: macos-15-intel
126160
target_platform: macOS
@@ -132,7 +166,6 @@ jobs:
132166
lto: thin
133167
profile_format: LLVM
134168
llvm_profdata: xcrun llvm-profdata
135-
max_tar_bytes: 67108864
136169
# Keep arm64 builds native so release smoke tests can execute them.
137170
- name: Linux arm64
138171
os: ubuntu-22.04-arm
@@ -144,7 +177,6 @@ jobs:
144177
has_gcm: false
145178
lto: auto
146179
profile_format: GCC
147-
max_tar_bytes: 67108864
148180
- name: Linux x64
149181
os: ubuntu-22.04
150182
target_platform: ubuntu
@@ -155,7 +187,6 @@ jobs:
155187
has_gcm: false
156188
lto: auto
157189
profile_format: GCC
158-
max_tar_bytes: 67108864
159190
- name: Windows arm64
160191
os: windows-11-arm
161192
target_platform: win32
@@ -167,7 +198,6 @@ jobs:
167198
lto: thin
168199
profile_format: LLVM
169200
llvm_profdata: llvm-profdata
170-
max_tar_bytes: 134217728
171201
sdk_arch: aarch64
172202
sdk_flavor: full
173203
mingw_dir: clangarm64
@@ -185,7 +215,6 @@ jobs:
185215
has_gcm: true
186216
lto: auto
187217
profile_format: GCC
188-
max_tar_bytes: 134217728
189218
sdk_arch: x86_64
190219
sdk_flavor: full
191220
mingw_dir: mingw64
@@ -195,21 +224,31 @@ jobs:
195224
mingit_sha256: e3ea2944cea4b3fabcd69c7c1669ef69b1b66c05ac7806d81224d0abad2dec31
196225

197226
steps:
227+
- name: Check out the manifest helper from the recipe
228+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
229+
with:
230+
repository: openai/git
231+
ref: ${{ inputs.recipe_sha || github.sha }}
232+
path: release-tooling
233+
sparse-checkout: .github/workflows/git-release-manifest.sh
234+
sparse-checkout-cone-mode: false
235+
persist-credentials: false
236+
198237
# Keep the packaging contract, dependency pins, and platform build logic
199238
# aligned with the artifacts already consumed by Codex and GitHub Desktop.
200239
- name: Check out Dugite Native
201240
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
202241
with:
203-
repository: desktop/dugite-native
204-
ref: f97e50add48cdcff053a69d95aa343a4a4a258c2
242+
repository: dreynaud-oai/dugite-native
243+
ref: b6f4473557acb85433fdf9deffe0854a34fd9cc5
205244
path: dugite-native
206245
fetch-depth: 0
207246
persist-credentials: false
208247

209248
- name: Check out this Git revision
210249
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
211250
with:
212-
ref: ${{ github.sha }}
251+
ref: ${{ inputs.source_sha || github.sha }}
213252
path: dugite-native/git
214253
fetch-depth: 1
215254
persist-credentials: false
@@ -411,7 +450,7 @@ jobs:
411450
412451
file "$GIT_BINARY" | tee /tmp/git-file-type
413452
grep -E "$FILE_PATTERN" /tmp/git-file-type
414-
strings "$GIT_BINARY" | grep -F "$GITHUB_SHA"
453+
strings "$GIT_BINARY" | grep -F "$SOURCE_SHA"
415454
grep -F -- "-flto=$LTO" dugite-native/git/GIT-CFLAGS
416455
if test "$PROFILE_FORMAT" = LLVM
417456
then
@@ -456,7 +495,7 @@ jobs:
456495
457496
build_options="$(env "${git_env[@]}" "$git_binary" --version --build-options)"
458497
printf '%s\n' "$build_options"
459-
grep -F "built from commit: $GITHUB_SHA" <<<"$build_options"
498+
grep -F "built from commit: $SOURCE_SHA" <<<"$build_options"
460499
env "${git_env[@]}" "$git_binary" lfs version
461500
if test "$HAS_GCM" = true
462501
then
@@ -480,7 +519,6 @@ jobs:
480519
TARGET_PLATFORM: ${{ matrix.target_platform }}
481520
TARGET_ARCH: ${{ matrix.arch }}
482521
VERSION: ${{ needs.version.outputs.version }}
483-
MAX_TAR_BYTES: ${{ matrix.max_tar_bytes }}
484522
run: |
485523
set -euo pipefail
486524
script/package.sh
@@ -498,28 +536,13 @@ jobs:
498536
mv "${matches[0]}.sha256" "$destination.sha256"
499537
done
500538
501-
for checksum in output/*.sha256
502-
do
503-
archive="${checksum%.sha256}"
504-
expected="$(tr -d '\r\n' <"$checksum")"
505-
if command -v sha256sum >/dev/null 2>&1
506-
then
507-
actual="$(sha256sum "$archive" | awk '{print $1}')"
508-
else
509-
actual="$(shasum -a 256 "$archive" | awk '{print $1}')"
510-
fi
511-
test "$actual" = "$expected"
512-
done
513-
514-
tarball="output/git-$VERSION-$ASSET_PLATFORM-$TARGET_ARCH.tar.gz"
515-
tar_bytes="$(wc -c <"$tarball")"
516-
printf '%s bytes: %s\n' "$tar_bytes" "$tarball"
517-
test "$tar_bytes" -le "$MAX_TAR_BYTES"
539+
bash ../release-tooling/.github/workflows/git-release-manifest.sh \
540+
record output "$ASSET_PLATFORM-$TARGET_ARCH"
518541
519542
- name: Upload release assets
520543
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
521544
with:
522-
name: git-${{ matrix.asset_platform }}-${{ matrix.arch }}
545+
name: git-${{ needs.version.outputs.version }}-${{ matrix.asset_platform }}-${{ matrix.arch }}
523546
path: dugite-native/output/git-*
524547
if-no-files-found: error
525548
retention-days: 7
@@ -534,6 +557,7 @@ jobs:
534557
Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
535558
536559
release:
560+
if: inputs.source_sha == ''
537561
name: Publish GitHub prerelease
538562
needs:
539563
- version
@@ -543,13 +567,31 @@ jobs:
543567
permissions:
544568
contents: write
545569
steps:
570+
- name: Check out the manifest helper
571+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
572+
with:
573+
ref: ${{ github.sha }}
574+
sparse-checkout: .github/workflows/git-release-manifest.sh
575+
sparse-checkout-cone-mode: false
576+
persist-credentials: false
577+
546578
- name: Download release assets
547579
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
548580
with:
549-
pattern: git-*
581+
pattern: git-${{ needs.version.outputs.version }}-*
550582
path: artifacts
551583
merge-multiple: true
552584

585+
- name: Combine the verified native receipts
586+
env:
587+
SOURCE_SHA: ${{ github.sha }}
588+
RECIPE_SHA: ${{ github.sha }}
589+
VERSION: ${{ needs.version.outputs.version }}
590+
UPSTREAM_TAG: ${{ needs.version.outputs.upstream_tag }}
591+
CHANNEL: ${{ github.ref_name }}
592+
VISIBILITY: public
593+
run: bash .github/workflows/git-release-manifest.sh collect artifacts >manifest.json
594+
553595
- name: Publish immutable prerelease
554596
env:
555597
GH_TOKEN: ${{ github.token }}
@@ -558,7 +600,12 @@ jobs:
558600
VERSION: ${{ needs.version.outputs.version }}
559601
run: |
560602
set -euo pipefail
561-
assets=(artifacts/git-*)
603+
assets=(manifest.json)
604+
for file in artifacts/git-*
605+
do
606+
case "$file" in *.build.json) continue ;; esac
607+
assets+=("$file")
608+
done
562609
release_notes=$(
563610
printf '%s\n' \
564611
"source_ref=$SOURCE_REF" \
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env bash
2+
# Record native artifacts, then combine only matching receipts into a manifest.
3+
set -euo pipefail
4+
test "$#" -ge 2
5+
mode=$1 directory=$2
6+
scratch=$(mktemp -d)
7+
trap 'rm -rf "$scratch"' EXIT
8+
[[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ && "$RECIPE_SHA" =~ ^[0-9a-f]{40}$ ]]
9+
[[ "$VERSION" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]
10+
hash () {
11+
if command -v sha256sum >/dev/null
12+
then sha256sum "$1"
13+
else shasum -a 256 "$1"
14+
fi | awk '{print $1}'
15+
}
16+
record () {
17+
local target=$1 archive file sum bytes limit=67108864
18+
case "$target" in
19+
windows-arm64|windows-x64) limit=134217728 ;;
20+
macOS-arm64|macOS-x64|ubuntu-arm64|ubuntu-x64) ;;
21+
*) return 1 ;;
22+
esac
23+
: >"$scratch/assets"
24+
for extension in tar.gz lzma
25+
do
26+
archive=$directory/git-$VERSION-$target.$extension
27+
for file in "$archive" "$archive.sha256"
28+
do
29+
test -f "$file" && test ! -L "$file"
30+
sum=$(hash "$file"); bytes=$(wc -c <"$file")
31+
test "$bytes" -gt 0
32+
jq -n --arg name "${file##*/}" --arg hash "$sum" --argjson size "$bytes" \
33+
'{name:$name,sha256:$hash,size:$size}' >>"$scratch/assets"
34+
done
35+
test "$(tr -d '\r\n' <"$archive.sha256")" = "$(hash "$archive")"
36+
done
37+
test "$(wc -c <"$directory/git-$VERSION-$target.tar.gz")" -le "$limit"
38+
jq -S -a -s --arg repository "$GITHUB_REPOSITORY" --arg version "$VERSION" \
39+
--arg source "$SOURCE_SHA" --arg recipe "$RECIPE_SHA" --arg run "$GITHUB_RUN_ID" \
40+
--arg target "$target" '{repository:$repository,source_sha:$source,version:$version,
41+
recipe:{repository:"openai/git",sha:$recipe},run_id:$run,target:$target,assets:.}' "$scratch/assets"
42+
}
43+
case "$mode" in
44+
record) record "${3:?target required}" >"$directory/git-$VERSION-$3.build.json" ;;
45+
collect)
46+
for target in macOS-arm64 macOS-x64 ubuntu-arm64 ubuntu-x64 windows-arm64 windows-x64
47+
do
48+
receipt=$directory/git-$VERSION-$target.build.json
49+
test -f "$receipt" && test ! -L "$receipt"
50+
record "$target" >"$scratch/$target.json"
51+
cmp "$scratch/$target.json" "$receipt"
52+
done
53+
shopt -s nullglob dotglob
54+
files=("$directory"/*)
55+
test "${#files[@]}" -eq 30
56+
jq -S -a -s --arg channel "$CHANNEL" --arg tag "$UPSTREAM_TAG" --arg visibility "$VISIBILITY" '
57+
.[0] as $first | {schema_version:1,repository:$first.repository,visibility:$visibility,
58+
channel:$channel,version:$first.version,source_sha:$first.source_sha,upstream_tag:$tag,
59+
recipe:$first.recipe,targets:(map({key:.target,value:.assets}) | from_entries),
60+
builds:(map({key:.target,value:.}) | from_entries)}' "$scratch/"*.json
61+
;;
62+
*) echo 'usage: git-release-manifest.sh record <directory> <target> | collect <directory>' >&2; exit 129 ;;
63+
esac

0 commit comments

Comments
 (0)