Skip to content

Commit e16eee2

Browse files
Merge remote-tracking branch 'origin/main' into sou-1870-scoped-access-tokens
# Conflicts: # CHANGELOG.md
2 parents c9b3cbd + a07c681 commit e16eee2

40 files changed

Lines changed: 2002 additions & 822 deletions
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
pr_number="${1:-}"
6+
changelog="${CHANGELOG_PATH:-CHANGELOG.md}"
7+
8+
if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then
9+
echo "Expected a pull request number, got: $pr_number" >&2
10+
exit 1
11+
fi
12+
13+
pr_url="https://github.com/sourcebot-dev/sourcebot/pull/$pr_number"
14+
entry="- Updated the bundled Zoekt version. [#$pr_number]($pr_url)"
15+
16+
if grep -Fq "$pr_url" "$changelog"; then
17+
echo "Changelog already links to Sourcebot PR #$pr_number."
18+
exit 0
19+
fi
20+
21+
changelog_directory=$(dirname "$changelog")
22+
changelog_basename=$(basename "$changelog")
23+
temporary_file=$(mktemp "$changelog_directory/.${changelog_basename}.XXXXXX")
24+
trap 'rm -f "$temporary_file"' EXIT
25+
26+
if changelog_mode=$(stat -f '%Lp' "$changelog" 2> /dev/null); then
27+
:
28+
else
29+
changelog_mode=$(stat -c '%a' "$changelog")
30+
fi
31+
32+
awk -v entry="$entry" '
33+
function flush_changed_section( last, i) {
34+
last = changed_line_count
35+
while (last > 0 && changed_lines[last] == "") {
36+
last--
37+
}
38+
for (i = 1; i <= last; i++) {
39+
print changed_lines[i]
40+
}
41+
print entry
42+
print ""
43+
changed_line_count = 0
44+
}
45+
46+
$0 == "## [Unreleased]" {
47+
in_unreleased = 1
48+
last_was_blank = 0
49+
print
50+
next
51+
}
52+
53+
in_unreleased && $0 == "### Changed" {
54+
found_changed = 1
55+
in_changed = 1
56+
print
57+
next
58+
}
59+
60+
in_changed && /^##(#)? / {
61+
flush_changed_section()
62+
in_changed = 0
63+
inserted = 1
64+
if ($0 ~ /^## /) {
65+
in_unreleased = 0
66+
}
67+
print
68+
next
69+
}
70+
71+
in_changed {
72+
changed_lines[++changed_line_count] = $0
73+
next
74+
}
75+
76+
in_unreleased && !found_changed && /^### (Deprecated|Removed|Fixed|Security)$/ {
77+
print "### Changed"
78+
print entry
79+
print ""
80+
found_changed = 1
81+
inserted = 1
82+
print
83+
next
84+
}
85+
86+
in_unreleased && /^## / {
87+
if (!found_changed) {
88+
print "### Changed"
89+
print entry
90+
print ""
91+
found_changed = 1
92+
inserted = 1
93+
}
94+
in_unreleased = 0
95+
print
96+
next
97+
}
98+
99+
{
100+
last_was_blank = ($0 == "")
101+
print
102+
}
103+
104+
END {
105+
if (in_changed) {
106+
flush_changed_section()
107+
inserted = 1
108+
} else if (in_unreleased && !found_changed) {
109+
if (!last_was_blank) {
110+
print ""
111+
}
112+
print "### Changed"
113+
print entry
114+
found_changed = 1
115+
inserted = 1
116+
}
117+
if (!found_changed || !inserted) {
118+
exit 2
119+
}
120+
}
121+
' "$changelog" > "$temporary_file" || {
122+
echo "Unable to add an Unreleased Changed entry to $changelog." >&2
123+
exit 1
124+
}
125+
126+
chmod "$changelog_mode" "$temporary_file"
127+
mv "$temporary_file" "$changelog"
128+
trap - EXIT
129+
echo "Added the changelog entry for Sourcebot PR #$pr_number."

.github/scripts/testZoektSync.sh

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
6+
repository_root=$(cd "$script_dir/../.." && pwd)
7+
update_script="$script_dir/updateZoektSubmodule.sh"
8+
changelog_script="$script_dir/addZoektSyncChangelogEntry.sh"
9+
workflow="$repository_root/.github/workflows/syncZoekt.yml"
10+
11+
fail() {
12+
echo "FAIL: $*" >&2
13+
exit 1
14+
}
15+
16+
assert_contains() {
17+
local file=$1
18+
local expected=$2
19+
local description=$3
20+
21+
if ! grep -Fq -- "$expected" "$file"; then
22+
fail "$description"
23+
fi
24+
}
25+
26+
assert_equals() {
27+
local actual=$1
28+
local expected=$2
29+
local description=$3
30+
31+
if [[ "$actual" != "$expected" ]]; then
32+
fail "$description (expected $expected, got $actual)"
33+
fi
34+
}
35+
36+
test_root=$(mktemp -d)
37+
trap 'rm -rf "$test_root"' EXIT
38+
39+
zoekt_remote="$test_root/zoekt.git"
40+
zoekt_upstream="$test_root/zoekt-upstream"
41+
sourcebot_test="$test_root/sourcebot"
42+
43+
git init --quiet --bare --initial-branch=main "$zoekt_remote"
44+
git init --quiet --initial-branch=main "$zoekt_upstream"
45+
git -C "$zoekt_upstream" config user.name "Zoekt Sync Test"
46+
git -C "$zoekt_upstream" config user.email "zoekt-sync-test@example.com"
47+
git -C "$zoekt_upstream" remote add origin "$zoekt_remote"
48+
49+
printf '%s\n' first > "$zoekt_upstream/version.txt"
50+
git -C "$zoekt_upstream" add version.txt
51+
git -C "$zoekt_upstream" commit --quiet -m "first"
52+
first_sha=$(git -C "$zoekt_upstream" rev-parse HEAD)
53+
git -C "$zoekt_upstream" push --quiet --set-upstream origin main
54+
55+
printf '%s\n' second > "$zoekt_upstream/version.txt"
56+
git -C "$zoekt_upstream" commit --quiet -am "second"
57+
second_sha=$(git -C "$zoekt_upstream" rev-parse HEAD)
58+
git -C "$zoekt_upstream" push --quiet origin main
59+
60+
git init --quiet --initial-branch=main "$sourcebot_test"
61+
git -C "$sourcebot_test" config user.name "Zoekt Sync Test"
62+
git -C "$sourcebot_test" config user.email "zoekt-sync-test@example.com"
63+
git -C "$sourcebot_test" -c protocol.file.allow=always \
64+
submodule add --quiet "$zoekt_remote" vendor/zoekt
65+
git -C "$sourcebot_test/vendor/zoekt" checkout --quiet --detach "$first_sha"
66+
git -C "$sourcebot_test" add vendor/zoekt
67+
git -C "$sourcebot_test" commit --quiet -m "pin first Zoekt commit"
68+
69+
(
70+
cd "$sourcebot_test"
71+
"$update_script" "$second_sha"
72+
)
73+
staged_sha=$(git -C "$sourcebot_test" rev-parse :vendor/zoekt)
74+
assert_equals "$staged_sha" "$second_sha" \
75+
"the updater should stage the requested main-branch commit"
76+
git -C "$sourcebot_test" commit --quiet -m "advance Zoekt"
77+
78+
(
79+
cd "$sourcebot_test"
80+
"$update_script" "$first_sha"
81+
)
82+
current_sha=$(git -C "$sourcebot_test" rev-parse HEAD:vendor/zoekt)
83+
assert_equals "$current_sha" "$second_sha" \
84+
"a stale event must not downgrade the Zoekt gitlink"
85+
git -C "$sourcebot_test" diff --quiet || \
86+
fail "a stale event should leave the worktree unchanged"
87+
git -C "$sourcebot_test" diff --cached --quiet || \
88+
fail "a stale event should leave the index unchanged"
89+
90+
git -C "$zoekt_upstream" switch --quiet --detach "$first_sha"
91+
git -C "$zoekt_upstream" switch --quiet -c divergent
92+
printf '%s\n' divergent > "$zoekt_upstream/version.txt"
93+
git -C "$zoekt_upstream" commit --quiet -am "divergent"
94+
divergent_sha=$(git -C "$zoekt_upstream" rev-parse HEAD)
95+
git -C "$zoekt_upstream" push --quiet origin divergent
96+
git -C "$sourcebot_test/vendor/zoekt" fetch --quiet origin divergent
97+
98+
if (
99+
cd "$sourcebot_test"
100+
"$update_script" "$divergent_sha"
101+
) 2> "$test_root/divergent-error.txt"; then
102+
fail "the updater should reject a commit outside origin/main"
103+
fi
104+
assert_contains "$test_root/divergent-error.txt" \
105+
"is not reachable from origin/main" \
106+
"the non-main error should explain the rejected target"
107+
108+
git -C "$sourcebot_test/vendor/zoekt" checkout --quiet --detach "$divergent_sha"
109+
git -C "$sourcebot_test" add vendor/zoekt
110+
git -C "$sourcebot_test" commit --quiet -m "pin divergent Zoekt commit"
111+
112+
if (
113+
cd "$sourcebot_test"
114+
"$update_script" "$second_sha"
115+
) 2> "$test_root/divergent-history-error.txt"; then
116+
fail "the updater should reject divergent current and target commits"
117+
fi
118+
assert_contains "$test_root/divergent-history-error.txt" \
119+
"Refusing to move Zoekt between divergent histories" \
120+
"the divergent-history error should explain the rejected update"
121+
122+
if (
123+
cd "$sourcebot_test"
124+
"$update_script" deadbeef
125+
) 2> "$test_root/invalid-error.txt"; then
126+
fail "the updater should reject abbreviated commit SHAs"
127+
fi
128+
assert_contains "$test_root/invalid-error.txt" \
129+
"Expected a full lowercase Zoekt commit SHA" \
130+
"the invalid-SHA error should explain the required format"
131+
132+
changelog_fixture="$test_root/CHANGELOG.md"
133+
cat > "$changelog_fixture" <<'EOF'
134+
# Changelog
135+
136+
## [Unreleased]
137+
138+
### Added
139+
- Added something.
140+
141+
### Removed
142+
- Removed something.
143+
144+
### Fixed
145+
- Fixed something.
146+
147+
## [1.0.0]
148+
149+
### Changed
150+
- Changed something in a released version.
151+
152+
## [0.9.0]
153+
154+
### Changed
155+
- Changed something in an older released version.
156+
EOF
157+
158+
chmod 640 "$changelog_fixture"
159+
CHANGELOG_PATH="$changelog_fixture" "$changelog_script" 42
160+
assert_contains "$changelog_fixture" \
161+
"### Changed" \
162+
"the changelog helper should create the Changed section when absent"
163+
assert_contains "$changelog_fixture" \
164+
"- Updated the bundled Zoekt version. [#42](https://github.com/sourcebot-dev/sourcebot/pull/42)" \
165+
"the changelog helper should add the generated PR link"
166+
entry_count=$(grep -Fc "sourcebot/pull/42" "$changelog_fixture")
167+
assert_equals "$entry_count" 1 \
168+
"the changelog helper should add the PR entry only to Unreleased"
169+
CHANGELOG_PATH="$changelog_fixture" "$changelog_script" 42
170+
entry_count=$(grep -Fc "sourcebot/pull/42" "$changelog_fixture")
171+
assert_equals "$entry_count" 1 \
172+
"the changelog helper should not duplicate an existing PR entry"
173+
if changelog_mode=$(stat -f '%Lp' "$changelog_fixture" 2> /dev/null); then
174+
:
175+
else
176+
changelog_mode=$(stat -c '%a' "$changelog_fixture")
177+
fi
178+
assert_equals "$changelog_mode" 640 \
179+
"the changelog helper should preserve the target file mode"
180+
181+
empty_changelog_fixture="$test_root/EMPTY_CHANGELOG.md"
182+
cat > "$empty_changelog_fixture" <<'EOF'
183+
# Changelog
184+
185+
## [Unreleased]
186+
EOF
187+
CHANGELOG_PATH="$empty_changelog_fixture" "$changelog_script" 43
188+
assert_contains "$empty_changelog_fixture" \
189+
"- Updated the bundled Zoekt version. [#43](https://github.com/sourcebot-dev/sourcebot/pull/43)" \
190+
"the changelog helper should handle an empty Unreleased section at EOF"
191+
192+
ruby -e 'require "yaml"; YAML.parse_file(ARGV.fetch(0))' "$workflow"
193+
assert_contains "$workflow" \
194+
"types: [zoekt-pr-merged]" \
195+
"the receiver should handle only Zoekt merge dispatches"
196+
assert_contains "$workflow" \
197+
'uses: actions/create-github-app-token@v2' \
198+
"the receiver should use a GitHub App token"
199+
assert_contains "$workflow" \
200+
'permission-pull-requests: write' \
201+
"the receiver should restrict its app token to required permissions"
202+
assert_contains "$workflow" \
203+
'--force-with-lease=' \
204+
"the receiver should protect updates to its stable automation branch"
205+
assert_contains "$workflow" \
206+
'.github/scripts/updateZoektSubmodule.sh "$ZOEKT_SHA"' \
207+
"the receiver should validate and stage the requested Zoekt commit"
208+
209+
echo "All Zoekt sync tests passed."
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
target_sha="${1:-}"
6+
submodule_path="${ZOEKT_SUBMODULE_PATH:-vendor/zoekt}"
7+
remote="${ZOEKT_REMOTE:-origin}"
8+
branch="${ZOEKT_BRANCH:-main}"
9+
10+
if [[ ! "$target_sha" =~ ^[0-9a-f]{40}$ ]]; then
11+
echo "Expected a full lowercase Zoekt commit SHA, got: $target_sha" >&2
12+
exit 1
13+
fi
14+
15+
repository_root=$(git rev-parse --show-toplevel)
16+
cd "$repository_root"
17+
18+
if ! current_sha=$(git rev-parse "HEAD:$submodule_path"); then
19+
echo "Unable to read the $submodule_path gitlink from HEAD." >&2
20+
exit 1
21+
fi
22+
23+
git -C "$submodule_path" fetch --quiet "$remote" "$branch"
24+
remote_head=$(git -C "$submodule_path" rev-parse FETCH_HEAD)
25+
26+
if ! git -C "$submodule_path" cat-file -e "$target_sha^{commit}"; then
27+
echo "Zoekt commit $target_sha does not exist." >&2
28+
exit 1
29+
fi
30+
31+
if ! git -C "$submodule_path" merge-base --is-ancestor "$target_sha" "$remote_head"; then
32+
echo "Zoekt commit $target_sha is not reachable from $remote/$branch." >&2
33+
exit 1
34+
fi
35+
36+
if git -C "$submodule_path" merge-base --is-ancestor "$target_sha" "$current_sha"; then
37+
echo "Zoekt is already at or ahead of $target_sha."
38+
exit 0
39+
fi
40+
41+
if ! git -C "$submodule_path" merge-base --is-ancestor "$current_sha" "$target_sha"; then
42+
echo "Refusing to move Zoekt between divergent histories: $current_sha -> $target_sha." >&2
43+
exit 1
44+
fi
45+
46+
git -C "$submodule_path" checkout --quiet --detach "$target_sha"
47+
git add -- "$submodule_path"
48+
echo "Staged Zoekt update: $current_sha -> $target_sha"

0 commit comments

Comments
 (0)