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
106 changes: 106 additions & 0 deletions .github/scripts/store-codegen-pull-request.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash

set -euo pipefail

: "${GH_TOKEN:?GH_TOKEN is required}"
: "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}"
: "${DRIFT_FILES_PATH:?DRIFT_FILES_PATH is required}"

base_branch="${BASE_BRANCH:-dev}"
automation_branch="${AUTOMATION_BRANCH:-automation/store-codegen-drift}"
pr_title="chore: refresh store codegen snapshot"

codegen_targets=(
"packages/store/scripts/api-schema/schema.json"
"packages/store/src/gateway/AUTO_GENERATED/"
)

is_codegen_path() {
local candidate="$1"

[[ "${candidate}" == "packages/store/scripts/api-schema/schema.json" ]] ||
[[ "${candidate}" == packages/store/src/gateway/AUTO_GENERATED/* ]]
}

collect_changed_paths() {
git diff -z --name-only HEAD
git ls-files -z --others --exclude-standard
}

unexpected_paths=()
while IFS= read -r -d '' changed_path; do
if [[ -n "${changed_path}" ]] && ! is_codegen_path "${changed_path}"; then
unexpected_paths+=("${changed_path}")
fi
done < <(collect_changed_paths)

if (( ${#unexpected_paths[@]} > 0 )); then
echo "Refusing to create an automation PR with changes outside the codegen allowlist:" >&2
printf ' %s\n' "${unexpected_paths[@]}" >&2
exit 1
fi

drift_paths=()
while IFS= read -r -d '' changed_path; do
if [[ -n "${changed_path}" ]] && is_codegen_path "${changed_path}"; then
drift_paths+=("${changed_path}")
fi
done < <(collect_changed_paths)

if (( ${#drift_paths[@]} == 0 )); then
echo "No store codegen drift found."
exit 0
fi

git checkout -B "${automation_branch}"
git add -- "${codegen_targets[@]}"
git \
-c user.name="github-actions[bot]" \
-c user.email="41898282+github-actions[bot]@users.noreply.github.com" \
commit -m "${pr_title}"

gh auth setup-git

remote_sha="$(git ls-remote --heads origin "refs/heads/${automation_branch}" | awk '{print $1}')"
if [[ -n "${remote_sha}" ]]; then
git push \
--force-with-lease="refs/heads/${automation_branch}:${remote_sha}" \
origin "HEAD:refs/heads/${automation_branch}"
else
git push origin "HEAD:refs/heads/${automation_branch}"
fi
Comment thread
JOY marked this conversation as resolved.

body_file="$(mktemp)"
trap 'rm -f "${body_file}"' EXIT
{
echo "The Store Codegen Drift workflow detected an upstream schema change and regenerated the checked-in gateway client snapshot."
echo
echo "Changed generated files:"
sed 's/\(.*\)/- `\1`/' "${DRIFT_FILES_PATH}"
echo
echo "This PR is automation-created, but it is intentionally not auto-merged."
} > "${body_file}"

pr_number="$(
gh pr list \
--repo "${GITHUB_REPOSITORY}" \
--base "${base_branch}" \
--head "${automation_branch}" \
--state open \
--json number \
--jq '.[0].number // empty'
)"

if [[ -n "${pr_number}" ]]; then
gh pr edit "${pr_number}" \
--repo "${GITHUB_REPOSITORY}" \
--title "${pr_title}" \
--body-file "${body_file}"
else
gh pr create \
--repo "${GITHUB_REPOSITORY}" \
--base "${base_branch}" \
--head "${automation_branch}" \
--title "${pr_title}" \
--body-file "${body_file}"
fi
137 changes: 137 additions & 0 deletions .github/scripts/tests/store-codegen-pull-request.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env bash

set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
script_path="$(cd "${script_dir}/.." && pwd)/store-codegen-pull-request.sh"
test_root="$(mktemp -d)"

cleanup() {
rm -rf "${test_root}"
}
trap cleanup EXIT

fail() {
echo "FAIL: $*" >&2
exit 1
}

assert_contains() {
local needle="$1"
local file="$2"
grep -Fq -- "${needle}" "${file}" || fail "Expected ${file} to contain: ${needle}"
}

remote_path="${test_root}/remote.git"
repo_path="${test_root}/repo"
stub_bin="${test_root}/bin"
gh_log="${test_root}/gh.log"
drift_files="${test_root}/drift-files.txt"

codegen_paths=(
"packages/store/scripts/api-schema/schema.json"
"packages/store/src/gateway/AUTO_GENERATED/.schema-hash"
"packages/store/src/gateway/AUTO_GENERATED/auth.ts"
"packages/store/src/gateway/AUTO_GENERATED/relay.ts"
"packages/store/src/gateway/AUTO_GENERATED/spaces.ts"
"packages/store/src/gateway/AUTO_GENERATED/transactions.ts"
)
new_codegen_path="packages/store/src/gateway/AUTO_GENERATED/added.ts"

git init --bare "${remote_path}" >/dev/null
git init --initial-branch=dev "${repo_path}" >/dev/null
git -C "${repo_path}" config user.name "Test User"
git -C "${repo_path}" config user.email "test@example.com"

for path in "${codegen_paths[@]}"; do
mkdir -p "${repo_path}/$(dirname "${path}")"
printf 'initial\n' > "${repo_path}/${path}"
done
printf 'baseline\n' > "${repo_path}/README.md"
git -C "${repo_path}" add .
git -C "${repo_path}" commit -m "initial" >/dev/null
git -C "${repo_path}" remote add origin "${remote_path}"
git -C "${repo_path}" push --set-upstream origin dev >/dev/null

mkdir -p "${stub_bin}"
printf '%s\n' '#!/usr/bin/env bash' > "${stub_bin}/gh"
printf '%s\n' 'set -euo pipefail' >> "${stub_bin}/gh"
printf '%s\n' 'printf "%s\\n" "$*" >> "${GH_STUB_LOG}"' >> "${stub_bin}/gh"
printf '%s\n' 'if [[ "${1:-}" == "pr" && "${2:-}" == "list" ]]; then' >> "${stub_bin}/gh"
printf '%s\n' ' printf "%s" "${GH_STUB_PR_NUMBER:-}"' >> "${stub_bin}/gh"
printf '%s\n' 'elif [[ "${1:-}" == "pr" && "${2:-}" == "create" ]]; then' >> "${stub_bin}/gh"
printf '%s\n' ' printf "%s\\n" "https://github.com/DOS/Safe-Wallet/pull/123"' >> "${stub_bin}/gh"
printf '%s\n' 'elif [[ "${1:-}" == "pr" && "${2:-}" == "edit" ]]; then' >> "${stub_bin}/gh"
printf '%s\n' ' printf "%s\\n" "https://github.com/DOS/Safe-Wallet/pull/${3:-42}"' >> "${stub_bin}/gh"
printf '%s\n' 'fi' >> "${stub_bin}/gh"
chmod +x "${stub_bin}/gh"

printf '%s\n' "${codegen_paths[@]}" > "${drift_files}"

run_script() {
(
cd "${repo_path}"
PATH="${stub_bin}:${PATH}" \
GH_TOKEN="test-token" \
GH_STUB_LOG="${gh_log}" \
GH_STUB_PR_NUMBER="${GH_STUB_PR_NUMBER:-}" \
GITHUB_REPOSITORY="DOS/Safe-Wallet" \
BASE_BRANCH="dev" \
AUTOMATION_BRANCH="automation/store-codegen-drift" \
DRIFT_FILES_PATH="${drift_files}" \
bash "${script_path}"
)
}

for path in "${codegen_paths[@]}"; do
printf 'first refresh\n' > "${repo_path}/${path}"
done

run_script

first_remote_sha="$(git --git-dir="${remote_path}" rev-parse refs/heads/automation/store-codegen-drift)"
[[ -n "${first_remote_sha}" ]] || fail "Automation branch was not created"
[[ "$(git --git-dir="${remote_path}" show "${first_remote_sha}:${codegen_paths[0]}")" == "first refresh" ]] || fail "Generated content was not pushed"
assert_contains "pr create" "${gh_log}"
if grep -Fq "pr edit" "${gh_log}"; then
fail "First run must create a PR, not edit one"
fi

git -C "${repo_path}" checkout dev >/dev/null
for path in "${codegen_paths[@]}"; do
printf 'second refresh\n' > "${repo_path}/${path}"
done
: > "${gh_log}"
GH_STUB_PR_NUMBER=42 run_script

second_remote_sha="$(git --git-dir="${remote_path}" rev-parse refs/heads/automation/store-codegen-drift)"
[[ "${second_remote_sha}" != "${first_remote_sha}" ]] || fail "Automation branch was not updated"
[[ "$(git --git-dir="${remote_path}" show "${second_remote_sha}:${codegen_paths[0]}")" == "second refresh" ]] || fail "Updated generated content was not pushed"
assert_contains "pr edit 42" "${gh_log}"

git -C "${repo_path}" checkout dev >/dev/null
printf '%s\n' "${codegen_paths[@]}" "${new_codegen_path}" > "${drift_files}"
printf 'added by schema refresh\n' > "${repo_path}/${new_codegen_path}"
: > "${gh_log}"
GH_STUB_PR_NUMBER=42 run_script

third_remote_sha="$(git --git-dir="${remote_path}" rev-parse refs/heads/automation/store-codegen-drift)"
[[ "${third_remote_sha}" != "${second_remote_sha}" ]] || fail "Automation branch was not updated for an untracked generated file"
[[ "$(git --git-dir="${remote_path}" show "${third_remote_sha}:${new_codegen_path}")" == "added by schema refresh" ]] || fail "Untracked generated file was not pushed"
assert_contains "pr edit 42" "${gh_log}"

git -C "${repo_path}" checkout dev >/dev/null
printf '%s\n' "${codegen_paths[@]}" > "${drift_files}"
printf 'unexpected change\n' > "${repo_path}/README.md"
printf 'third refresh\n' > "${repo_path}/${codegen_paths[0]}"
: > "${gh_log}"
if GH_STUB_PR_NUMBER=42 run_script; then
fail "Script accepted a change outside the codegen allowlist"
fi

final_remote_sha="$(git --git-dir="${remote_path}" rev-parse refs/heads/automation/store-codegen-drift)"
[[ "${final_remote_sha}" == "${third_remote_sha}" ]] || fail "Rejected run changed the remote branch"
[[ "$(git -C "${repo_path}" config user.name)" == "Test User" ]] || fail "Script changed the local git user.name"
[[ "$(git -C "${repo_path}" config user.email)" == "test@example.com" ]] || fail "Script changed the local git user.email"

echo "PASS: store codegen PR automation"
12 changes: 12 additions & 0 deletions .github/scripts/tests/store-generated-endpoints.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash

set -euo pipefail

generated_spaces="packages/store/src/gateway/AUTO_GENERATED/spaces.ts"

grep -Fq 'entitlementsGetEntitlementsV1: build.query<' "${generated_spaces}"
grep -Fq 'export type EntitlementsResponse = {' "${generated_spaces}"
grep -Fq 'useEntitlementsGetEntitlementsV1Query' "${generated_spaces}"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
grep -Fq 'url: `/v1/spaces/${queryArg.spaceId}/entitlements`' "${generated_spaces}"

echo "PASS: required Store endpoints are generated"
Loading