Skip to content

Commit f3acc49

Browse files
authored
Merge pull request #16 from nirrozenbaum/prep
prepare for code migration
2 parents f444b4b + ae6136a commit f3acc49

6 files changed

Lines changed: 300 additions & 18 deletions

File tree

OWNERS

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
# See https://go.k8s.io/owners for format documentation
33

44
approvers:
5-
nirrozenbaum
6-
shaneutt
5+
- nirrozenbaum
6+
- shaneutt
77

88
reviewers:
9-
nirrozenbaum
10-
shaneutt
9+
- nirrozenbaum
10+
- shaneutt

deploy/.gitkeep

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/upstream-versions.md

Lines changed: 0 additions & 12 deletions
This file was deleted.

hack/migrate-gaie-paths.sh

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
#!/usr/bin/env bash
2+
# Migrate one or more directories or files from the source repo into this repo,
3+
# rewriting Go import paths throughout.
4+
#
5+
# Usage:
6+
# ./scripts/migrate-gaie-paths.sh [--since <ref>] <src1> <dest1> [<src2> <dest2> ...]
7+
#
8+
# --since <ref> tag or commit SHA marking the last sync point; when given,
9+
# only commits after <ref> are cherry-picked (incremental sync).
10+
# The ref to pass next time is printed in the PR title.
11+
# Omit for the initial full migration.
12+
#
13+
# Requires: git filter-repo (pip/pipx install git-filter-repo)
14+
#
15+
# NOTE
16+
# Must merge the initial migration PR via "Create a merge commit" to preserve history.
17+
# Incremental migrations can use "merge commit" (cleanest audit trail) or
18+
# "rebase and merge" (for a slightly cleaner linear history).
19+
20+
set -euo pipefail
21+
22+
# Configuration
23+
LOCAL_PATH="/tmp" # override as needed
24+
25+
SOURCE_ORG="kubernetes-sigs"
26+
SOURCE_REPO_NAME="gateway-api-inference-extension"
27+
SOURCE_UPSTREAM_URL="git@github.com:${SOURCE_ORG}/${SOURCE_REPO_NAME}.git"
28+
29+
DEST_ORG="llm-d"
30+
DEST_REPO_NAME="llm-d-inference-payload-processor"
31+
DEST_UPSTREAM_URL="git@github.com:${DEST_ORG}/${DEST_REPO_NAME}.git"
32+
33+
UPSTREAM_REMOTE="upstream"
34+
ORIGIN_REMOTE="origin"
35+
MAIN_BRANCH="main"
36+
37+
SOURCE_DIR="${LOCAL_PATH}/${SOURCE_REPO_NAME}"
38+
DEST_DIR="${LOCAL_PATH}/${DEST_REPO_NAME}"
39+
FILTER_WORK_DIR="${LOCAL_PATH}/${SOURCE_REPO_NAME}-filter-work"
40+
41+
usage() {
42+
cat <<EOF
43+
Usage: $0 [--since <ref>] <src1> <dest1> [<src2> <dest2> ...]
44+
45+
--since <ref> tag or commit SHA of the last migration/sync (from the PR title)
46+
src path relative to source repo root (e.g. pkg/common)
47+
dest path relative to dest repo root (e.g. pkg/common/igw)
48+
49+
Repos are cloned to ${LOCAL_PATH} if not present, otherwise updated to ${UPSTREAM_REMOTE}/${MAIN_BRANCH}.
50+
EOF
51+
exit 1
52+
}
53+
54+
# Parse --since flag
55+
SINCE_REF=""
56+
if [[ "${1:-}" == "--since" ]]; then
57+
[[ $# -ge 2 ]] || usage
58+
SINCE_REF="$2"
59+
shift 2
60+
fi
61+
62+
[[ $# -ge 2 && $(( $# % 2 )) -eq 0 ]] || usage
63+
64+
declare -a SRC_PATHS DEST_PATHS
65+
while [[ $# -gt 0 ]]; do
66+
SRC_PATHS+=("${1%/}")
67+
DEST_PATHS+=("${2%/}")
68+
shift 2
69+
done
70+
71+
# Preflight checks
72+
if ! command -v git-filter-repo &>/dev/null && ! git filter-repo --version &>/dev/null 2>&1; then
73+
echo "error: git filter-repo not installed (pip install git-filter-repo)"
74+
exit 1
75+
fi
76+
77+
GITHUB_USER="${GITHUB_USER:-$(gh api user --jq .login 2>/dev/null || git config github.user 2>/dev/null || echo "")}"
78+
if [[ -z "${GITHUB_USER}" ]]; then
79+
echo "error: cannot determine GitHub username; set GITHUB_USER=<handle>"
80+
exit 1
81+
fi
82+
DEST_ORIGIN_URL="git@github.com:${GITHUB_USER}/${DEST_REPO_NAME}.git"
83+
84+
ensure_repo() {
85+
local dir="$1" upstream_url="$2" origin_url="${3:-}"
86+
if [[ ! -d "${dir}/.git" ]]; then
87+
git clone --origin "${UPSTREAM_REMOTE}" "${upstream_url}" "${dir}"
88+
[[ -n "${origin_url}" ]] && git -C "${dir}" remote add "${ORIGIN_REMOTE}" "${origin_url}"
89+
else
90+
git -C "${dir}" fetch "${UPSTREAM_REMOTE}"
91+
git -C "${dir}" checkout "${MAIN_BRANCH}"
92+
git -C "${dir}" merge --ff-only "${UPSTREAM_REMOTE}/${MAIN_BRANCH}"
93+
fi
94+
}
95+
96+
ensure_repo "${SOURCE_DIR}" "${SOURCE_UPSTREAM_URL}"
97+
ensure_repo "${DEST_DIR}" "${DEST_UPSTREAM_URL}" "${DEST_ORIGIN_URL}"
98+
99+
# Validate --since ref exists in source
100+
if [[ -n "${SINCE_REF}" ]]; then
101+
if ! git -C "${SOURCE_DIR}" rev-parse --verify "${SINCE_REF}^{commit}" &>/dev/null; then
102+
echo "error: '${SINCE_REF}' not found in source repo"
103+
exit 1
104+
fi
105+
fi
106+
107+
SOURCE_SHA=$(git -C "${SOURCE_DIR}" rev-parse --short HEAD)
108+
SOURCE_MODULE=$(grep -m1 '^module ' "${SOURCE_DIR}/go.mod" | awk '{print $2}')
109+
DEST_MODULE=$(grep -m1 '^module ' "${DEST_DIR}/go.mod" | awk '{print $2}')
110+
111+
BRANCH_SLUG=$(IFS='-'; echo "${DEST_PATHS[*]}" | tr '/' '-')
112+
BRANCH_NAME="migrate/${BRANCH_SLUG}"
113+
[[ -n "${SINCE_REF}" ]] && BRANCH_NAME="migrate/since-${SINCE_REF}-${BRANCH_SLUG}"
114+
115+
echo "source: ${SOURCE_DIR} (${SOURCE_MODULE})"
116+
echo "destination: ${DEST_DIR} (${DEST_MODULE})"
117+
[[ -n "${SINCE_REF}" ]] && echo "since: ${SINCE_REF}"
118+
for i in "${!SRC_PATHS[@]}"; do
119+
echo "migrating: ${SRC_PATHS[$i]} -> ${DEST_PATHS[$i]}"
120+
done
121+
echo "branch: ${BRANCH_NAME}"
122+
echo
123+
124+
# Validate all pairs before touching anything
125+
for i in "${!SRC_PATHS[@]}"; do
126+
[[ -e "${SOURCE_DIR}/${SRC_PATHS[$i]}" ]] || { echo "error: '${SRC_PATHS[$i]}' not found in source repo"; exit 1; }
127+
if [[ -z "${SINCE_REF}" ]]; then
128+
while IFS= read -r -d '' src_dir; do
129+
rel="${src_dir#"${SOURCE_DIR}/${SRC_PATHS[$i]}"}"
130+
dest_dir="${DEST_DIR}/${DEST_PATHS[$i]}${rel}"
131+
if [[ -d "${dest_dir}" ]] && \
132+
find "${src_dir}" -maxdepth 1 -name "*.go" -print -quit 2>/dev/null | grep -q . && \
133+
find "${dest_dir}" -maxdepth 1 -name "*.go" -print -quit 2>/dev/null | grep -q .; then
134+
echo "error: Go package conflict at '${DEST_PATHS[$i]}${rel}': both source and destination contain Go files"
135+
exit 1
136+
fi
137+
done < <(find "${SOURCE_DIR}/${SRC_PATHS[$i]}" -type d -print0)
138+
fi
139+
done
140+
141+
if git -C "${DEST_DIR}" rev-parse --verify "${BRANCH_NAME}" &>/dev/null; then
142+
echo "error: branch '${BRANCH_NAME}' already exists; delete it first:"
143+
echo " git -C ${DEST_DIR} branch -D ${BRANCH_NAME}"
144+
exit 1
145+
fi
146+
147+
if ! git -C "${DEST_DIR}" diff --quiet || ! git -C "${DEST_DIR}" diff --cached --quiet; then
148+
echo "error: destination repo has uncommitted changes"
149+
exit 1
150+
fi
151+
152+
# Build filter-repo args for all pairs
153+
FILTER_ARGS=()
154+
for i in "${!SRC_PATHS[@]}"; do
155+
FILTER_ARGS+=(--path "${SRC_PATHS[$i]}")
156+
[[ "${SRC_PATHS[$i]}" != "${DEST_PATHS[$i]}" ]] && FILTER_ARGS+=(--path-rename "${SRC_PATHS[$i]}:${DEST_PATHS[$i]}")
157+
done
158+
159+
# Clone source, filter to target paths, and rewrite bare #NNN issue references to
160+
# SOURCE_ORG/SOURCE_REPO_NAME#NNN so they link to the original repo rather than the
161+
# destination. Matches only #NNN preceded by start-of-line, space, '(' or ',' to
162+
# avoid hex literals, code-block references, and already-qualified org/repo#NNN refs.
163+
MSG_CALLBACK="import re
164+
return re.sub(rb'(?m)(^|[ (,])#(\\d+)', lambda m: m.group(1) + b'${SOURCE_ORG}/${SOURCE_REPO_NAME}#' + m.group(2), message)"
165+
166+
rm -rf "${FILTER_WORK_DIR}"
167+
git clone "file://${SOURCE_DIR}" "${FILTER_WORK_DIR}"
168+
git -C "${FILTER_WORK_DIR}" filter-repo "${FILTER_ARGS[@]}" \
169+
--message-callback "${MSG_CALLBACK}" \
170+
--force
171+
172+
if [[ -n "${SINCE_REF}" ]]; then
173+
# Use filter-repo's commit-map to translate SINCE_REF into the filtered history.
174+
# Find the latest source commit at or before SINCE_REF that touched any of the paths.
175+
SINCE_SRC_SHA=$(git -C "${SOURCE_DIR}" rev-list -1 "${SINCE_REF}" -- "${SRC_PATHS[@]}")
176+
if [[ -z "${SINCE_SRC_SHA}" ]]; then
177+
echo "no commits in source before ${SINCE_REF} touching the specified paths; nothing to sync"
178+
exit 0
179+
fi
180+
COMMIT_MAP="${FILTER_WORK_DIR}/.git/filter-repo/commit-map"
181+
SINCE_FILTERED=$(awk -v sha="${SINCE_SRC_SHA}" 'NR>1 && $1==sha {print $2; exit}' "${COMMIT_MAP}")
182+
if [[ -z "${SINCE_FILTERED}" || "${SINCE_FILTERED}" == "0000000000000000000000000000000000000000" ]]; then
183+
echo "error: could not map ${SINCE_SRC_SHA} to filter-work; the commit may not touch the specified paths"
184+
exit 1
185+
fi
186+
COMMITS=()
187+
while IFS= read -r line; do COMMITS+=("${line}"); done \
188+
< <(git -C "${FILTER_WORK_DIR}" rev-list --reverse "${SINCE_FILTERED}..HEAD" 2>/dev/null || true)
189+
else
190+
COMMITS=()
191+
while IFS= read -r line; do COMMITS+=("${line}"); done \
192+
< <(git -C "${FILTER_WORK_DIR}" rev-list --reverse HEAD 2>/dev/null || true)
193+
fi
194+
195+
if [[ ${#COMMITS[@]} -eq 0 ]]; then
196+
[[ -n "${SINCE_REF}" ]] && { echo "no new commits since ${SINCE_REF} affecting the specified paths"; exit 0; }
197+
echo "error: no commits found for given paths; check the paths"
198+
exit 1
199+
fi
200+
echo "${#COMMITS[@]} commits found"
201+
202+
# Apply filtered commits to a new branch in destination
203+
git -C "${DEST_DIR}" checkout -b "${BRANCH_NAME}"
204+
trap 'git -C "${DEST_DIR}" remote remove _migration 2>/dev/null || true' EXIT
205+
git -C "${DEST_DIR}" remote add _migration "${FILTER_WORK_DIR}"
206+
git -C "${DEST_DIR}" fetch _migration
207+
208+
if [[ -n "${SINCE_REF}" ]]; then
209+
if ! git -C "${DEST_DIR}" -c merge.directoryRenames=false cherry-pick --signoff -S "${COMMITS[@]}"; then
210+
echo
211+
echo "error: cherry-pick stopped due to conflicts"
212+
echo " resolve, then: git -C ${DEST_DIR} cherry-pick --continue"
213+
exit 1
214+
fi
215+
else
216+
MERGE_MSG="migrate: import from ${SOURCE_MODULE}"$'\n'
217+
for i in "${!SRC_PATHS[@]}"; do
218+
MERGE_MSG+=$'\n'" ${SRC_PATHS[$i]} -> ${DEST_PATHS[$i]}"
219+
done
220+
git -C "${DEST_DIR}" merge --allow-unrelated-histories --signoff -S "_migration/${MAIN_BRANCH}" \
221+
--no-edit -m "${MERGE_MSG}"
222+
fi
223+
224+
# Rewrite imports - single pass, all pairs applied per file
225+
declare -a OLD_IMPORTS NEW_IMPORTS
226+
for i in "${!SRC_PATHS[@]}"; do
227+
OLD_IMPORTS+=("${SOURCE_MODULE}/${SRC_PATHS[$i]}")
228+
NEW_IMPORTS+=("${DEST_MODULE}/${DEST_PATHS[$i]}")
229+
echo "rewriting imports: ${SOURCE_MODULE}/${SRC_PATHS[$i]} -> ${DEST_MODULE}/${DEST_PATHS[$i]}"
230+
done
231+
232+
CHANGED=0
233+
while IFS= read -r -d '' f; do
234+
for i in "${!OLD_IMPORTS[@]}"; do
235+
if grep -qF "\"${OLD_IMPORTS[$i]}" "${f}"; then
236+
SED_ARGS=()
237+
for j in "${!OLD_IMPORTS[@]}"; do
238+
SED_ARGS+=(-e "s|\"${OLD_IMPORTS[$j]}/|\"${NEW_IMPORTS[$j]}/|g")
239+
SED_ARGS+=(-e "s|\"${OLD_IMPORTS[$j]}\"|\"${NEW_IMPORTS[$j]}\"|g")
240+
done
241+
sed -i "${SED_ARGS[@]}" "${f}"
242+
echo " ${f#"${DEST_DIR}/"}"
243+
CHANGED=$((CHANGED + 1))
244+
break
245+
fi
246+
done
247+
done < <(find "${DEST_DIR}" -name "*.go" -not -path "*/.git/*" -print0)
248+
echo "${CHANGED} file(s) updated"
249+
250+
cd "${DEST_DIR}"
251+
go mod tidy || echo "warning: go mod tidy has errors (expected for partial migrations with unresolved cross-package deps)"
252+
go build ./... || echo "warning: go build has errors (expected for partial migrations with unresolved cross-package deps)"
253+
254+
if ! git diff --quiet || ! git diff --cached --quiet; then
255+
IMPORT_MSG="chore: rewrite imports from ${SOURCE_MODULE}"$'\n'
256+
for i in "${!SRC_PATHS[@]}"; do
257+
IMPORT_MSG+=$'\n'" ${SOURCE_MODULE}/${SRC_PATHS[$i]} -> ${DEST_MODULE}/${DEST_PATHS[$i]}"
258+
done
259+
git add -A
260+
git commit --signoff -S -m "${IMPORT_MSG}"
261+
fi
262+
263+
# Open PR
264+
if [[ -n "${SINCE_REF}" ]]; then
265+
PR_TITLE="sync: ${SOURCE_MODULE} since ${SINCE_REF} @ ${SOURCE_SHA}"
266+
PR_INTRO="Picks up ${#COMMITS[@]} commit(s) to \`${SOURCE_MODULE}\` since \`${SINCE_REF}\` (now @ ${SOURCE_SHA}):"
267+
else
268+
PR_TITLE="migrate: ${SOURCE_MODULE} @ ${SOURCE_SHA}"
269+
PR_INTRO="Migrates the following paths from \`${SOURCE_MODULE}\` (@ ${SOURCE_SHA}) with full git history:"
270+
fi
271+
272+
PR_BODY="${PR_INTRO}"$'\n'
273+
for i in "${!SRC_PATHS[@]}"; do
274+
PR_BODY+=$'\n'"- \`${SRC_PATHS[$i]}\`\`${DEST_PATHS[$i]}\`"
275+
done
276+
PR_BODY+=$'\n\n'"To pick up future upstream changes: \`migrate-gaie-paths.sh --since ${SOURCE_SHA}"
277+
for i in "${!SRC_PATHS[@]}"; do
278+
PR_BODY+=" ${SRC_PATHS[$i]} ${DEST_PATHS[$i]}"
279+
done
280+
PR_BODY+="\`"
281+
[[ -z "${SINCE_REF}" ]] && PR_BODY+=$'\n\n'"**Merge via \"Create a merge commit\"** — squash or rebase discards the migrated history."
282+
283+
if [[ "${NO_PUSH:-0}" == "1" ]]; then
284+
echo
285+
echo "NO_PUSH=1 — skipping git push and PR creation."
286+
echo "PR title would be: ${PR_TITLE}"
287+
echo "To push manually: git -C ${DEST_DIR} push ${ORIGIN_REMOTE} ${BRANCH_NAME}"
288+
else
289+
git push "${ORIGIN_REMOTE}" "${BRANCH_NAME}"
290+
gh pr create \
291+
--repo "${DEST_ORG}/${DEST_REPO_NAME}" \
292+
--head "${GITHUB_USER}:${BRANCH_NAME}" \
293+
--base "${MAIN_BRANCH}" \
294+
--title "${PR_TITLE}" \
295+
--body "${PR_BODY}"
296+
fi

internal/.gitkeep

Lines changed: 0 additions & 1 deletion
This file was deleted.
File renamed without changes.

0 commit comments

Comments
 (0)