Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions .github/workflows/codex-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -852,12 +852,53 @@ automation_workflow_matches () {
"$tmp_dir/actual-automation.yml"
}

extract_release_trigger () {
# Compare the push-only projection of a reusable workflow. Calls may skip
# provenance checks to build another source, but must never publish it.
read_release_workflow () (
head_oid=$1
output=$2
make_tmp_dir
git show "$head_oid:.github/workflows/codex-release.yml" \
>"$tmp_dir/codex-release.yml" 2>/dev/null || return 1
>"$output" 2>/dev/null || return 1
grep -F -x ' workflow_call:' "$output" >/dev/null || return 0
while IFS="$tab" read -r job condition
do
extract_release_job "$output" "$job" "$tmp_dir/call-job" ||
return 1
actual=$(sed -n '/^ if:/p' "$tmp_dir/call-job")
test "$actual" = " if: $condition" || return 1
done <<-'EOF'
publication inputs.source_sha == '' && github.event.deleted == false
version ${{ !cancelled() && (inputs.source_sha != '' || needs.publication.outputs.published == 'true') }}
release inputs.source_sha == ''
EOF
awk '
$0 == "on:" { in_trigger = 1; skip = 0 }
in_trigger && /^ [^[:space:]]/ {
skip = ($0 == " workflow_call:")
}
in_trigger && /^[^[:space:]]/ && $0 != "on:" {
in_trigger = skip = 0
}
skip { next }
$0 ~ /^ [^[:space:]]/ { job = $0 }
/^ if:/ && job == " publication:" {
$0 = " if: github.event.deleted == false"
}
/^ if:/ && job == " version:" {
$0 = " if: needs.publication.outputs.published == \047true\047"
}
{ print }
' "$output" >"$output.normalized" &&
mv "$output.normalized" "$output"
)

extract_release_trigger () {
head_oid=$1
output=$2
make_tmp_dir
read_release_workflow "$head_oid" "$tmp_dir/codex-release.yml" ||
return 1
test "$(grep -c '^on:$' "$tmp_dir/codex-release.yml")" = 1 || return 1
awk '
$0 == "on:" && !found {
Expand Down Expand Up @@ -1005,17 +1046,17 @@ release_publication_controls_preserved () (
make_tmp_dir
old_workflow=$tmp_dir/published-release.yml
new_workflow=$tmp_dir/candidate-release.yml
if ! git show "$published:.github/workflows/codex-release.yml" \
>"$old_workflow" 2>/dev/null
if ! git cat-file -e \
"$published:.github/workflows/codex-release.yml" 2>/dev/null
then
return 0
fi
read_release_workflow "$published" "$old_workflow" || return 1
if ! grep -F -x ' publication:' "$old_workflow" >/dev/null
then
return 0
fi
git show "$candidate:.github/workflows/codex-release.yml" \
>"$new_workflow" 2>/dev/null || return 1
read_release_workflow "$candidate" "$new_workflow" || return 1
extract_release_job "$old_workflow" publication \
"$tmp_dir/published-publication" || return 1
extract_release_job "$new_workflow" publication \
Expand Down
123 changes: 102 additions & 21 deletions t/t9905-codex-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,38 @@ write_dual_guarded_release_workflow () {
EOF
}

write_callable_release_workflow () {
write_dual_guarded_release_workflow "$1" &&
awk '
$0 == "on:" {
print
print " workflow_call:"
print " inputs:"
print " source_sha:"
print " required: true"
print " type: string"
next
}
$0 == " if: github.event.deleted == false" {
$0 = " if: inputs.source_sha == \047\047 && github.event.deleted == false"
}
$0 == " if: needs.publication.outputs.published == \047true\047" {
$0 = " if: ${{ !cancelled() && (inputs.source_sha != \047\047 || needs.publication.outputs.published == \047true\047) }}"
}
{ print }
END {
print ""
print " release:"
print " if: inputs.source_sha == \047\047"
print " needs: version"
print " runs-on: ubuntu-24.04"
print " steps:"
print " - run: echo publish"
}
' "$1" >"$1.callable" &&
mv "$1.callable" "$1"
}

install_bootstrap_pin_guard_gh () {
directory=$1 &&
mkdir -p "$directory" &&
Expand Down Expand Up @@ -2733,7 +2765,7 @@ test_expect_success 'published release provenance gates cannot be removed' '
done
'

test_expect_success 'release publication guard has one exact dual-lane upgrade' '
test_expect_success 'release publication guard permits reviewed workflow upgrades' '
git init --bare release-upgrade.git &&
test_create_repo release-upgrade-source &&
(
Expand Down Expand Up @@ -2761,6 +2793,43 @@ test_expect_success 'release publication guard has one exact dual-lane upgrade'
git add .github/workflows/codex-release.yml &&
git commit -m "publish dual release guard" &&

git switch -c call-valid dual-valid &&
write_callable_release_workflow \
.github/workflows/codex-release.yml &&
git add .github/workflows/codex-release.yml &&
git commit -m "allow build-only workflow calls" &&
for job in publication version release
do
git switch -c "call-bad-$job" call-valid &&
awk -v target=" $job:" '\''
/^ [^[:space:]]/ { job = $0 }
/^ if:/ && job == target {
$0 = " if: always()"
}
{ print }
'\'' .github/workflows/codex-release.yml >release.tmp &&
mv release.tmp .github/workflows/codex-release.yml &&
git add .github/workflows/codex-release.yml &&
git commit -m "drop $job call guard" || return 1
done &&
git switch -c call-extra-trigger call-valid &&
awk '\''
{ print }
$0 == "on:" { print " workflow_dispatch:" }
'\'' .github/workflows/codex-release.yml >release.tmp &&
mv release.tmp .github/workflows/codex-release.yml &&
git add .github/workflows/codex-release.yml &&
git commit -m "add an unreviewed release trigger" &&

git switch -c call-duplicate-trigger call-valid &&
awk '\''
$0 == " push:" { print "on:" }
{ print }
'\'' .github/workflows/codex-release.yml >release.tmp &&
mv release.tmp .github/workflows/codex-release.yml &&
git add .github/workflows/codex-release.yml &&
git commit -m "duplicate the release trigger mapping" &&

git switch -c dual-old-guard "$legacy" &&
awk '\''
{ print }
Expand All @@ -2787,31 +2856,43 @@ test_expect_success 'release publication guard has one exact dual-lane upgrade'

git push origin master meta codex codex-unstable \
dual-valid:aa/codex/release \
dual-valid dual-old-guard dual-no-delete dual-wrong-key
dual-valid dual-old-guard dual-no-delete dual-wrong-key \
call-valid call-bad-publication call-bad-version \
call-bad-release call-extra-trigger call-duplicate-trigger
) &&
git clone release-upgrade.git release-upgrade-runner &&
(
cd release-upgrade-runner &&
fetch_all &&
snapshot_refs ../release-upgrade.git >before &&
sh "$codex_branch" rewrite --remote origin \
--base master --codex codex \
--result result --updates updates \
--inputs inputs --failure failure &&
candidate=$(cat result) &&
unstable=$(updated_tip codex-unstable updates) &&
write_dual_guarded_release_workflow expected-release.yml &&
git show "$candidate:.github/workflows/codex-release.yml" \
>actual-release.yml &&
test_cmp expected-release.yml actual-release.yml &&
stable_release=$(git rev-parse \
"$candidate:.github/workflows/codex-release.yml") &&
unstable_release=$(git rev-parse \
"$unstable:.github/workflows/codex-release.yml") &&
test "$stable_release" = "$unstable_release" &&
snapshot_refs ../release-upgrade.git >after &&
test_cmp before after &&
for variant in dual-old-guard dual-no-delete dual-wrong-key
for variant in dual-valid call-valid
do
oid=$(git rev-parse "origin/$variant") &&
git --git-dir=../release-upgrade.git update-ref \
refs/heads/aa/codex/release "$oid" &&
fetch_all &&
snapshot_refs ../release-upgrade.git >before &&
sh "$codex_branch" rewrite --remote origin \
--base master --codex codex \
--result result --updates updates \
--inputs inputs --failure failure &&
candidate=$(cat result) &&
unstable=$(updated_tip codex-unstable updates) &&
git show "$oid:.github/workflows/codex-release.yml" \
>expected-release.yml &&
git show "$candidate:.github/workflows/codex-release.yml" \
>actual-release.yml &&
test_cmp expected-release.yml actual-release.yml &&
stable_release=$(git rev-parse \
"$candidate:.github/workflows/codex-release.yml") &&
unstable_release=$(git rev-parse \
"$unstable:.github/workflows/codex-release.yml") &&
test "$stable_release" = "$unstable_release" &&
snapshot_refs ../release-upgrade.git >after &&
test_cmp before after || return 1
done &&
for variant in dual-old-guard dual-no-delete dual-wrong-key \
call-bad-publication call-bad-version call-bad-release \
call-extra-trigger call-duplicate-trigger
do
oid=$(git rev-parse "origin/$variant") &&
git --git-dir=../release-upgrade.git update-ref \
Expand Down