-
Notifications
You must be signed in to change notification settings - Fork 1
125 lines (117 loc) · 5.84 KB
/
Copy pathci.yml
File metadata and controls
125 lines (117 loc) · 5.84 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
name: CI
permissions:
contents: read # required by actions/checkout in the reusable test workflow
pull-requests: read # list changed files for the doc-only check
concurrency:
group: ci-pr-${{ github.event.pull_request.number }}
cancel-in-progress: true
on:
pull_request:
# We use 'pull_request' (not 'pull_request_target') deliberately.
# 'pull_request_target' runs with write access to the base repo, which is
# a security risk for untrusted fork code. Since this workflow only reads
# from other public repos (no secrets needed), 'pull_request' is correct
# and safe even for fork PRs.
jobs:
resolve:
name: Resolve pgxntool branch
runs-on: ubuntu-latest
outputs:
pgxntool-ref: ${{ steps.pgxntool-ref.outputs.ref }}
pgxntool-owner: ${{ steps.pgxntool-ref.outputs.owner }}
doc-only: ${{ steps.doc-only.outputs.doc-only }}
steps:
# DOC-ONLY BYPASS: skip the Postgres test matrix (the `test` job below)
# when every changed file is pure documentation. Files under .github/
# are never doc-only even if their extension matches (they're workflow
# definitions with real behavioral weight). Everything else — including
# .claude/*.md prompt and command docs, which carry no execution weight
# themselves — counts.
#
# This does NOT affect claude-review: that's a separate workflow gated
# by its own `if:`, unaffected by this job's outputs.
- name: Check doc-only
id: doc-only
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
# Include previous_filename too: a rename like src/foo.sql ->
# docs/foo.md must not read as doc-only just because the new name
# matches — the old path is a real code change.
files=$(gh api "repos/$REPO/pulls/$PR_NUMBER/files" --paginate \
--jq '.[] | .filename, (.previous_filename // empty)')
doc_only=true
[ -z "$files" ] && doc_only=false
while IFS= read -r f; do
case "$f" in
.github/*) doc_only=false ;;
*.md|*.asc|*.adoc|*.asciidoc) ;;
*) doc_only=false ;;
esac
done <<<"$files"
echo "doc-only=$doc_only" >> "$GITHUB_OUTPUT"
echo "doc-only: $doc_only"
echo "changed files:"
echo "$files"
- name: Resolve pgxntool branch
id: pgxntool-ref
# PR-controlled values (head_ref, head owner) are passed via env, never
# interpolated directly into the script, to avoid shell injection.
env:
BRANCH: ${{ github.head_ref }}
# The account this PR's branch lives on: the contributor's fork for
# fork PRs, or Postgres-Extensions for a maintainer branch.
HEAD_OWNER: ${{ github.event.pull_request.head.repo.owner.login }}
# This PR's own pgxntool-test is checked out from the base repo at the
# merge SHA — used only to print an accurate BRANCHES line below.
TEST_OWNER: ${{ github.repository_owner }}
TEST_SHA: ${{ github.sha }}
run: |
# Reject anything that isn't a valid git branch name before using it.
git check-ref-format --branch "$BRANCH" >/dev/null 2>&1 || {
echo "Invalid branch name: $BRANCH" >&2
exit 1
}
# Look for a paired pgxntool branch of the SAME name on the SAME
# account (HEAD_OWNER). A pgxntool change should ship with a matching
# pgxntool-test branch and vice versa; if this test PR has one, use it.
#
# SECURITY — never cross-account: we only ever match a branch on
# HEAD_OWNER. If there's no paired branch (e.g. a pgxntool-test-only
# PR), we fall back to pgxntool master from Postgres-Extensions ONLY,
# never the fork's master (which may be stale or modified) and never a
# same-named branch on any other account. master is the sole ref we
# take cross-account.
#
# --exit-code makes git ls-remote return non-zero when the ref is
# absent, so we can branch on the result cleanly without parsing output.
if git ls-remote --exit-code --heads \
"https://github.com/${HEAD_OWNER}/pgxntool.git" \
"refs/heads/${BRANCH}" > /dev/null 2>&1; then
echo "ref=${BRANCH}" >> "$GITHUB_OUTPUT"
echo "owner=${HEAD_OWNER}" >> "$GITHUB_OUTPUT"
# Same shape/order as run-tests.yml: pgxntool first, owner/ref for each.
echo "=== BRANCHES: pgxntool=${HEAD_OWNER}/${BRANCH} pgxntool-test=${TEST_OWNER}/${TEST_SHA} ==="
else
echo "ref=master" >> "$GITHUB_OUTPUT"
echo "owner=Postgres-Extensions" >> "$GITHUB_OUTPUT"
echo "=== BRANCHES: pgxntool=Postgres-Extensions/master pgxntool-test=${TEST_OWNER}/${TEST_SHA} (no paired pgxntool branch on ${HEAD_OWNER}) ==="
fi
test:
needs: resolve
if: needs.resolve.outputs.doc-only != 'true'
# run-tests.yml is the single source of truth for all test steps.
# See .github/workflows/CLAUDE.md for architecture notes, including
# the cross-repo reusable workflow tradeoffs and merge order constraints.
uses: ./.github/workflows/run-tests.yml
with:
# pgxntool: the paired branch on this PR's account, or Postgres-Extensions
# master when there's no pairing (resolved above).
pgxntool-owner: ${{ needs.resolve.outputs.pgxntool-owner }}
pgxntool-branch: ${{ needs.resolve.outputs.pgxntool-ref }}
# pgxntool-test: this PR's own merged code, which lives on the base repo
# (github.repository_owner) at the merge SHA.
pgxntool-test-owner: ${{ github.repository_owner }}
pgxntool-test-ref: ${{ github.sha }}