-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Expand file tree
/
Copy pathretract-phantom.sh
More file actions
executable file
·279 lines (258 loc) · 10.9 KB
/
Copy pathretract-phantom.sh
File metadata and controls
executable file
·279 lines (258 loc) · 10.9 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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#!/usr/bin/env bash
# retract-phantom.sh — Publish orphan retraction tags for phantom module paths.
#
# Usage: ./retract-phantom.sh [--dry-run] [--push] [--pilot]
#
# Creates 4 orphan commits (one per module base: v0, v4, v5, v6), each containing
# all go.mod files with retract directives for phantom paths in that base.
# Tags each phantom path on the appropriate orphan commit. Master is never
# touched. The script survives orphan ops by saving itself to /tmp.
#
# --dry-run print what would happen, create nothing
# --pilot process only the first path of each base (validate end-to-end)
# --push push only the new phantom tags to origin (never git push --tags)
set -euo pipefail
DRY_RUN=false
PUSH=false
PILOT=false
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=true ;;
--push) PUSH=true ;;
--pilot) PILOT=true ;;
esac
done
# ── Repo root ───────────────────────────────────────────────────────────────
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
# ── Phantom paths ────────────────────────────────────────────────────────────
# Each line: go-micro.dev/<base>/<relative_path>
# The script creates an orphan commit per path with a go.mod containing:
# module go-micro.dev/<base>/<relative_path>
# retract [v0.0.0, v1.18.2]
PHANTOM_PATHS=(
go-micro.dev/plugins/auth/jwt/v4/token
go-micro.dev/plugins/config/source/grpc/v4/proto
go-micro.dev/plugins/tree/main/v5/registry
go-micro.dev/plugins/tree/main/v5/registry/consul
go-micro.dev/v2client
go-micro.dev/v2client/registry
go-micro.dev/v2client/registry/consul
go-micro.dev/v4/cmd/protoc-gen-micro~
go-micro.dev/v4.0.0/cmd
go-micro.dev/v4.0.0/cmd/protoc-gen-micro
go-micro.dev/v4.10.2/registry
go-micro.dev/v4.6.0/go-plugins/registry/consul
go-micro.dev/v4api
go-micro.dev/v4api/proto
go-micro.dev/v4config
go-micro.dev/v4config/source
go-micro.dev/v4egmentio
go-micro.dev/v4egmentio/ksuid
go-micro.dev/v4eris-io
go-micro.dev/v4eris-io/shortid
go-micro.dev/v4g.org/codec
go-micro.dev/v4g.org/codec/protorpc
go-micro.dev/v4github.com
go-micro.dev/v4github.com/micro
go-micro.dev/v4github.com/micro/go-micro
go-micro.dev/v4github.com/micro/go-micro/client
go-micro.dev/v4github.com/micro/go-micro/metadata
go-micro.dev/v4ithub.io
go-micro.dev/v4ithub.io/maps
go-micro.dev/v4logger/plugins
go-micro.dev/v4logger/plugins/wrapper
go-micro.dev/v4logger/plugins/wrapper/select
go-micro.dev/v4logger/plugins/wrapper/select/version
go-micro.dev/v4registry
go-micro.dev/v4registry/etcd
go-micro.dev/v4registry/etcd/registry
go-micro.dev/v4registry/kubernetes
go-micro.dev/v4rvice
go-micro.dev/v4rvice/client
go-micro.dev/v4rvice/errors
go-micro.dev/v4rvice/logger
go-micro.dev/v4rvice/server
go-micro.dev/v4rvice/store
go-micro.dev/v4~
go-micro.dev/v5-plugins
go-micro.dev/v5-plugins/client
go-micro.dev/v5-plugins/client/grpc
go-micro.dev/v5-plugins/server
go-micro.dev/v5-plugins/server/grpc
go-micro.dev/v5-plugins/v5/client
go-micro.dev/v5-plugins/v5/client/grpc
go-micro.dev/v5-plugins/v5/server
go-micro.dev/v5-plugins/v5/server/grpc
go-micro.dev/v5-plugins/wrapper
go-micro.dev/v5-plugins/wrapper/trace
go-micro.dev/v5-plugins/wrapper/trace/opentelemetry
go-micro.dev/v5service
go-micro.dev/v5service/grpc
go-micro.dev/v5service/logger
)
# ── Parse into per-base arrays ──────────────────────────────────────────────
BASES_V4=() BASES_V5=() BASES_V6=() BASES_V0=()
for p in "${PHANTOM_PATHS[@]}"; do
case "$p" in
go-micro.dev/v4/*) BASES_V4+=("$p") ;;
go-micro.dev/v5/*) BASES_V5+=("$p") ;;
go-micro.dev/v6/*) BASES_V6+=("$p") ;;
go-micro.dev/*) BASES_V0+=("$p") ;;
esac
done
for b in V6 V5 V4 V0; do
declare -n ba="BASES_$b"
mapfile -t ba < <(printf '%s\n' "${ba[@]}" | awk 'NF && !seen[$0]++')
done
# ── Sanity checks ───────────────────────────────────────────────────────────
module_relative_path() {
local path="$1"
if [[ "$path" =~ ^go-micro\.dev/v[0-9]+/(.+)$ ]]; then
printf '%s\n' "${BASH_REMATCH[1]}"
else
printf '%s\n' "${path#go-micro.dev/}"
fi
}
for p in "${PHANTOM_PATHS[@]}"; do
rel="$(module_relative_path "$p")"
[[ -f "$rel/go.mod" ]] && { echo "ERROR: $p has go.mod on master — not a phantom"; exit 1; }
done
# ── Pilot mode ───────────────────────────────────────────────────────────────
# Keep only the first path of each base so the full mechanism can be validated
# on a tiny case before the 10k-path blast.
if $PILOT; then
for b in V4 V5 V6 V0; do
declare -n ba="BASES_$b"
((${#ba[@]})) && ba=("${ba[0]}")
done
fi
# ── Helpers ──────────────────────────────────────────────────────────────────
RETRACT_MAX="v1.18.2"
valid_tag_ref() {
local t="$1"
if [[ "$t" == *..* || "$t" == *"@{"* || "$t" == *"//"* || "$t" == *"/."* || "$t" == *".lock"* || "$t" == .* || "$t" == */ || "$t" == *. ]] || ! [[ "$t" =~ ^[A-Za-z0-9][A-Za-z0-9._/-]*$ ]]; then
git check-ref-format "refs/tags/$t" 2>/dev/null
return $?
fi
return 0
}
make_shared_orphan_commit() {
# make_shared_orphan_commit <label> <tmpdir> <count> <ident>
# Runs ONE `git fast-import` that creates the orphan commit (a parentless
# commit holding a go.mod per new tag) plus its annotated tags, then deletes
# the temporary branch. Prints the commit SHA on stdout.
local label="$1" tmpdir="$2" count="$3" ident="$4"
local branch="refs/heads/phantom-retract-$label"
local msg="retract ${label} phantom paths (${count} modules)"
local stream="$tmpdir/stream"
{
cat "$tmpdir/blobs"
printf 'commit %s\nmark :1000000\nauthor %s\ncommitter %s\ndata %d\n%s\n' \
"$branch" "$ident" "$ident" "$(( ${#msg} + 1 ))" "$msg"
cat "$tmpdir/trees"
printf '\n'
cat "$tmpdir/tags"
} > "$stream"
if ! git fast-import --force < "$stream"; then
echo "ERROR: fast-import failed for $label" >&2
exit 1
fi
git rev-parse "$branch"
git update-ref -d "$branch"
}
# ── Execute ──────────────────────────────────────────────────────────────────
total=$(( ${#BASES_V4[@]} + ${#BASES_V5[@]} + ${#BASES_V6[@]} + ${#BASES_V0[@]} ))
echo "Retracting ${total} phantom paths across 4 orphan commits..."
echo ""
CREATED_TAGS=()
ident="$(git var GIT_COMMITTER_IDENT)"
declare -A EXISTING_TAGS=()
while IFS= read -r t; do
[[ -n "$t" ]] && EXISTING_TAGS["$t"]=1
done < <(git for-each-ref --format='%(refname:short)' refs/tags)
declare -A REMOTE_TAGS=()
while read -r _ ref; do
[[ -n "$ref" ]] && REMOTE_TAGS["${ref#refs/tags/}"]=1
done < <(git ls-remote --tags --refs origin)
for base_info in "v6:${#BASES_V6[@]}" "v5:${#BASES_V5[@]}" "v4:${#BASES_V4[@]}" "v0:${#BASES_V0[@]}"; do
IFS=: read -r label count <<< "$base_info"
if [[ "$count" -eq 0 ]]; then
echo "=== $label: no phantom paths, skipping ==="
continue
fi
echo "=== $label orphan commit ($count paths) ==="
if $DRY_RUN; then
echo "[dry-run] Would create 1 orphan commit with ${count} go.mod files"
echo "[dry-run] Would tag ${count} paths on that commit"
else
declare -n ref="BASES_${label^^}"
tmpdir="$(mktemp -d)"
: > "$tmpdir/blobs"
: > "$tmpdir/trees"
: > "$tmpdir/tags"
n=0
for p in "${ref[@]}"; do
rel="$(module_relative_path "$p")"
tag="${rel}/${RETRACT_MAX}"
if ! valid_tag_ref "$tag"; then
echo " skip (invalid tag name): $tag" >&2
continue
fi
if [[ -n "${REMOTE_TAGS[$tag]+x}" ]]; then
echo " exists on origin: $tag (skip)"
continue
fi
if [[ -n "${EXISTING_TAGS[$tag]+x}" ]]; then
echo " exists locally: $tag (queue for push)"
CREATED_TAGS+=("refs/tags/$tag")
continue
fi
n=$((n+1))
content="module ${p}
go 1.24
// Phantom module path. The Go proxy cached this as a separate module.
// Every version is retracted so 'go install ${p}@latest' errors or
// resolves to the next non-retracted version instead of this path.
retract [v0.0.0, ${RETRACT_MAX}]
"
printf 'blob\nmark :%d\ndata %d\n%s' "$n" "${#content}" "$content" >> "$tmpdir/blobs"
printf 'M 100644 :%d %s/go.mod\n' "$n" "$rel" >> "$tmpdir/trees"
tmsg="retract $tag (phantom)"
printf 'tag %s\nfrom :1000000\ntagger %s\ndata %d\n%s\n\n' \
"$tag" "$ident" "$(( ${#tmsg} + 1 ))" "$tmsg" >> "$tmpdir/tags"
EXISTING_TAGS["$tag"]=1
CREATED_TAGS+=("refs/tags/$tag")
echo " tagged: $tag"
done
orphan_sha="$(make_shared_orphan_commit "$label" "$tmpdir" "$n" "$ident")"
echo " commit: $orphan_sha"
rm -rf "$tmpdir"
fi
echo ""
done
mapfile -t CREATED_TAGS < <(printf '%s\n' "${CREATED_TAGS[@]}" | awk 'NF' | sort -u)
if $PUSH && ! $DRY_RUN; then
echo "Pushing ${#CREATED_TAGS[@]} phantom tags to origin..."
for ((offset = 0; offset < ${#CREATED_TAGS[@]}; offset += 500)); do
declare -A CURRENT_REMOTE_TAGS=()
while read -r _ ref; do
[[ -n "$ref" ]] && CURRENT_REMOTE_TAGS["$ref"]=1
done < <(git ls-remote --tags --refs origin)
batch=()
end=$((offset + 500))
((end > ${#CREATED_TAGS[@]})) && end=${#CREATED_TAGS[@]}
for ((i = offset; i < end; i++)); do
ref="${CREATED_TAGS[i]}"
[[ -z "${CURRENT_REMOTE_TAGS[$ref]+x}" ]] && batch+=("$ref")
done
((${#batch[@]})) && git push origin "${batch[@]}"
done
echo "Done. Verify with: go list -m -u <path>"
elif ! $DRY_RUN; then
echo "All tags created locally. Re-run with --push to publish the phantom tags to origin."
echo ""
echo "After push, verify with:"
echo " go list -m -versions go-micro.dev/v5/api/handler"
echo " go install go-micro.dev/v5/api/handler@latest"
fi