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
4 changes: 3 additions & 1 deletion scripts/build-release.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ done

[[ -x "${CASE_DIR}/release/scripts/configure.sh" ]] || fail 'configure.sh lost executable mode'
[[ -x "${CASE_DIR}/release/scripts/teardown.sh" ]] || fail 'teardown.sh lost executable mode'
[[ -x "${CASE_DIR}/release/scripts/scrub-lockfile.sh" ]] || fail 'scrub-lockfile.sh lost executable mode'
# The GraphQL publisher creates new files as 100644. This helper is invoked via
# bash (including the packaged controller below), so it needs no executable bit.
[[ ! -x "${CASE_DIR}/release/scripts/scrub-lockfile.sh" ]] || fail 'scrub-lockfile.sh must be non-executable for signed publication'

grep -Fq 'bash "$GITHUB_ACTION_PATH/scripts/configure.sh"' "${CASE_DIR}/release/action.yml" || fail 'root action does not invoke its shipped configure script'
grep -Fq 'bash "$GITHUB_ACTION_PATH/../scripts/teardown.sh"' "${CASE_DIR}/release/teardown/action.yml" || fail 'teardown action does not invoke its shipped teardown script'
Expand Down
80 changes: 78 additions & 2 deletions scripts/publish-release.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ fi
case "$endpoint" in
graphql)
cp "$input" "${FAKE_GH_STATE}/graphql-payload.json"
if [[ -n "${FAKE_BASE_TREE:-}" ]]; then
# Model FileAddition: preserve an existing file's mode; new files are
# regular 100644 blobs. Build the result from the actual mutation payload,
# independently of the publisher's desired tree hash.
export GIT_INDEX_FILE="${FAKE_GH_STATE}/api-index"
git -C "$FAKE_GIT_ROOT" read-tree "$FAKE_BASE_TREE"
while IFS= read -r path; do
git -C "$FAKE_GIT_ROOT" update-index --force-remove -- "$path"
done < <(jq -r '.variables.input.fileChanges.deletions[].path' "$input")
while IFS=$'\t' read -r path contents; do
entry="$(git -C "$FAKE_GIT_ROOT" ls-files --stage -- "$path")"
mode="${entry%% *}"
mode="${mode:-100644}"
blob="$(printf '%s' "$contents" | base64 --decode | git -C "$FAKE_GIT_ROOT" hash-object -w --stdin)"
git -C "$FAKE_GIT_ROOT" update-index --add --cacheinfo "${mode},${blob},${path}"
done < <(jq -r '.variables.input.fileChanges.additions[] | [.path, .contents] | @tsv' "$input")
git -C "$FAKE_GIT_ROOT" write-tree >"${FAKE_GH_STATE}/api-tree"
fi
printf '{"data":{"createCommitOnBranch":{"commit":{"oid":"5555555555555555555555555555555555555555","signature":{"isValid":%s,"wasSignedByGitHub":%s,"state":"%s"}}}}}\n' \
"${FAKE_SIGNATURE_VALID:-true}" "${FAKE_SIGNED_BY_GITHUB:-true}" "${FAKE_SIGNATURE_STATE:-VALID}"
;;
Expand All @@ -103,10 +121,21 @@ case "$endpoint" in
printf '%s\n' "${FAKE_EXISTING_TREE:-4444444444444444444444444444444444444444}"
;;
*/git/commits/5555555555555555555555555555555555555555)
printf '%s\n' "${FAKE_PUBLISHED_TREE:-${FAKE_DESIRED_TREE}}"
if [[ -n "${FAKE_BASE_TREE:-}" ]]; then
cat "${FAKE_GH_STATE}/api-tree"
else
printf '%s\n' "${FAKE_PUBLISHED_TREE:-${FAKE_DESIRED_TREE}}"
fi
;;
*/git/trees/*)
printf '{"truncated":false,"tree":[{"path":"obsolete.txt","type":"blob","mode":"100644","sha":"9999999999999999999999999999999999999999"}]}\n'
if [[ -n "${FAKE_BASE_TREE:-}" ]]; then
git -C "$FAKE_GIT_ROOT" ls-tree -r "$FAKE_BASE_TREE" | jq -Rn '
{truncated: false, tree: [inputs | split("\t") as $row |
($row[0] | split(" ")) as $object |
{mode: $object[0], type: $object[1], sha: $object[2], path: $row[1]}]}'
else
printf '{"truncated":false,"tree":[{"path":"obsolete.txt","type":"blob","mode":"100644","sha":"9999999999999999999999999999999999999999"}]}\n'
fi
;;
*/git/refs | */git/refs/*)
printf '{}\n'
Expand Down Expand Up @@ -169,6 +198,53 @@ jq -e --arg parent "$EXISTING_SHA" '.variables.input.expectedHeadOid == $parent'
grep -Fq 'PATCH repos/workos/setup-socket-firewall/git/refs/heads/action-release/v1' "${STATE_DIR}/calls" || fail 'release branch was not moved'
grep -Fq 'PATCH repos/workos/setup-socket-firewall/git/refs/tags/v1' "${STATE_DIR}/calls" || fail 'v1 tag was not moved'

# Reproduce the five-file pre-scrub release: the older shell helpers already
# have executable modes; every other runtime file must be added by the API.
base_index="${CASE_DIR}/base-index"
GIT_INDEX_FILE="$base_index" git -C "$ROOT" read-tree --empty
for path in LICENSE action.yml scripts/configure.sh scripts/teardown.sh teardown/action.yml; do
read -r mode _ blob _ < <(git -C "$ROOT" ls-tree -r "$DESIRED_TREE" -- "$path")
GIT_INDEX_FILE="$base_index" git -C "$ROOT" update-index --add --cacheinfo "${mode},${blob},${path}"
done
base_tree="$(GIT_INDEX_FILE="$base_index" git -C "$ROOT" write-tree)"

publish_from_previous_release() {
FAKE_BASE_TREE="$base_tree" \
FAKE_GIT_ROOT="$ROOT" \
FAKE_EXISTING_RELEASE=true \
FAKE_EXISTING_TREE="$base_tree" \
FAKE_EXISTING_TAG=true \
GH_BIN="$fake_gh" \
GITHUB_REPOSITORY=workos/setup-socket-firewall \
"${ROOT}/scripts/publish-release.sh" "$1" "$SOURCE_SHA" v1
}

# The current package must match the mode-aware API result without relaxing the
# exact-tree check. This fails on the original 100755 scrub helper from PR #10.
new_state
publish_from_previous_release "$release_dir" >/dev/null
assert_line "$GITHUB_OUTPUT" "release_sha=${NEW_SHA}"
assert_line "$GITHUB_OUTPUT" 'released=true'
assert_line "${STATE_DIR}/api-tree" "$DESIRED_TREE"
assert_line "${STATE_DIR}/calls" 'PATCH repos/workos/setup-socket-firewall/git/refs/heads/action-release/v1'
assert_line "${STATE_DIR}/calls" 'PATCH repos/workos/setup-socket-firewall/git/refs/tags/v1'

# Reintroducing the unnecessary executable bit reproduces the production error
# and must leave both discovery refs untouched, while cleaning the staging ref.
broken_release="${CASE_DIR}/executable-scrub-release"
cp -R "$release_dir" "$broken_release"
chmod +x "${broken_release}/scripts/scrub-lockfile.sh"
new_state
if publish_from_previous_release "$broken_release" >"${STATE_DIR}/error" 2>&1; then
fail 'new executable scrub helper unexpectedly published'
fi
grep -Fq 'GitHub-signed release commit tree differs from the staged tree' "${STATE_DIR}/error" || fail 'wrong failure for executable scrub helper'
[[ ! -s "$GITHUB_OUTPUT" ]] || fail 'mismatched release emitted success outputs'
if grep -Eq 'PATCH repos/.*/git/refs/(heads/action-release/v1|tags/v1)' "${STATE_DIR}/calls"; then
fail 'executable mode mismatch moved a release ref'
fi
assert_line "${STATE_DIR}/calls" 'DELETE repos/workos/setup-socket-firewall/git/refs/heads/action-release-staging/v1-12345-1'

# Never publish release refs when GitHub does not sign the staged commit.
new_state
set +e
Expand Down
Empty file modified scripts/scrub-lockfile.sh
100755 → 100644
Empty file.
Loading