chore(fantasy): add automated sync workflow, vendor versioning, and patch support
GitHub Actions workflow for periodic Fantasy SDK synchronization with upstream. Adds vendor version tracking, patches directory for local modifications, and sync-fantasy.sh orchestration script.
This commit is contained in:
parent
db0ec104cd
commit
621da777c8
5 changed files with 566 additions and 17 deletions
126
.github/workflows/fantasy-sync.yml
vendored
Normal file
126
.github/workflows/fantasy-sync.yml
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
name: fantasy-sdk-sync-check
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Every Monday at 09:00 UTC
|
||||
- cron: "0 9 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-upstream:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Read current vendored version
|
||||
id: current
|
||||
run: |
|
||||
if [[ -f internal/fantasy/.vendor-version ]]; then
|
||||
echo "version=$(cat internal/fantasy/.vendor-version | tr -d '[:space:]')" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "version=unknown" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Query latest upstream version
|
||||
id: latest
|
||||
run: |
|
||||
LATEST=$(go list -m -versions charm.land/fantasy 2>/dev/null \
|
||||
| awk '{$1=""; print}' | tr ' ' '\n' | grep -v '^\s*$' | tail -1)
|
||||
echo "version=${LATEST}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Compare versions
|
||||
id: compare
|
||||
run: |
|
||||
CURRENT="${{ steps.current.outputs.version }}"
|
||||
LATEST="${{ steps.latest.outputs.version }}"
|
||||
echo "current=${CURRENT}"
|
||||
echo "latest=${LATEST}"
|
||||
if [[ "$CURRENT" == "$LATEST" ]]; then
|
||||
echo "behind=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "behind=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Generate diff summary
|
||||
if: steps.compare.outputs.behind == 'true'
|
||||
id: diff
|
||||
run: |
|
||||
TARGET="${{ steps.latest.outputs.version }}"
|
||||
go mod download "charm.land/fantasy@${TARGET}" >/dev/null 2>&1
|
||||
CACHE_DIR="$(go env GOMODCACHE)/charm.land/fantasy@${TARGET}"
|
||||
|
||||
DIFF_STAT=$(git diff --no-index --stat -- internal/fantasy/ "${CACHE_DIR}" 2>/dev/null || true)
|
||||
echo "stat<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "${DIFF_STAT}" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check for existing issue
|
||||
if: steps.compare.outputs.behind == 'true'
|
||||
id: dedup
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TITLE="P2: Fantasy SDK — upstream ${{ steps.latest.outputs.version }} available"
|
||||
EXISTING=$(gh issue list --search "in:title \"${TITLE}\"" --state open --json number -q '.[0].number' 2>/dev/null || true)
|
||||
if [[ -n "$EXISTING" ]]; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Issue #${EXISTING} already exists."
|
||||
else
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Create GitHub issue
|
||||
if: steps.compare.outputs.behind == 'true' && steps.dedup.outputs.exists == 'false'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
CURRENT="${{ steps.current.outputs.version }}"
|
||||
LATEST="${{ steps.latest.outputs.version }}"
|
||||
DIFF_STAT="${{ steps.diff.outputs.stat }}"
|
||||
TITLE="P2: Fantasy SDK — upstream ${LATEST} available (current: ${CURRENT})"
|
||||
|
||||
cat > /tmp/issue-body.md <<ISSUE_EOF
|
||||
## Fantasy SDK upstream update available
|
||||
|
||||
| | Version |
|
||||
|---|---------|
|
||||
| **Vendored** | \`${CURRENT}\` |
|
||||
| **Latest** | \`${LATEST}\` |
|
||||
|
||||
### Diff summary
|
||||
|
||||
\`\`\`
|
||||
${DIFF_STAT}
|
||||
\`\`\`
|
||||
|
||||
### How to sync
|
||||
|
||||
\`\`\`bash
|
||||
make fantasy-diff FANTASY_VERSION=${LATEST} # preview changes
|
||||
make fantasy-sync FANTASY_VERSION=${LATEST} # apply sync + re-apply patches
|
||||
\`\`\`
|
||||
|
||||
After syncing:
|
||||
1. Review the diff: \`git diff\`
|
||||
2. Run full test suite: \`go test ./...\`
|
||||
3. Commit: \`git add -A && git commit -m "build(fantasy): sync vendored SDK to ${LATEST}"\`
|
||||
|
||||
---
|
||||
*Auto-generated by fantasy-sdk-sync-check workflow.*
|
||||
ISSUE_EOF
|
||||
|
||||
gh issue create \
|
||||
--title "${TITLE}" \
|
||||
--body-file /tmp/issue-body.md \
|
||||
--label enhancement \
|
||||
--assignee "@me"
|
||||
1
internal/fantasy/.vendor-version
Normal file
1
internal/fantasy/.vendor-version
Normal file
|
|
@ -0,0 +1 @@
|
|||
v0.8.1
|
||||
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
## What This Is
|
||||
|
||||
This directory contains a vendored copy of `charm.land/fantasy` v0.8.1, the Charmbracelet
|
||||
This directory contains a vendored copy of `charm.land/fantasy`, the Charmbracelet
|
||||
Fantasy LLM agent framework. We vendor it to enable direct modifications for PicoClaw-specific
|
||||
features (progressive disclosure, custom streaming hooks, tool call repair, etc.).
|
||||
|
||||
## How It Works
|
||||
|
||||
PicoClaw's `go.mod` contains a `replace` directive:
|
||||
|
||||
```
|
||||
|
|
@ -17,25 +15,110 @@ replace charm.land/fantasy v0.8.1 => ./internal/fantasy
|
|||
This redirects all `charm.land/fantasy` imports to this local copy. No import paths
|
||||
need to change in either PicoClaw code or the fantasy source itself.
|
||||
|
||||
## Syncing Upstream Updates
|
||||
## Automated Sync System
|
||||
|
||||
To pull in a new upstream version:
|
||||
We maintain a two-layer automation system to keep this vendored copy in sync with upstream.
|
||||
|
||||
1. Check the upstream version: `go list -m -versions charm.land/fantasy`
|
||||
2. Download it: `go mod download charm.land/fantasy@vX.Y.Z`
|
||||
3. Copy to vendor: `cp -r $(go env GOMODCACHE)/charm.land/fantasy@vX.Y.Z/* internal/fantasy/`
|
||||
4. Fix permissions: `chmod -R u+w internal/fantasy/`
|
||||
5. Re-apply local patches (see below)
|
||||
6. Update the replace directive version in `go.mod` if needed
|
||||
7. Run `go mod tidy && go test ./...`
|
||||
### Monitoring (CI)
|
||||
|
||||
## Local Patches
|
||||
A GitHub Actions workflow (`.github/workflows/fantasy-sync.yml`) runs weekly and:
|
||||
|
||||
Document all local modifications here:
|
||||
1. Queries the Go module proxy for the latest `charm.land/fantasy` version
|
||||
2. Compares it against the version in `.vendor-version`
|
||||
3. If a new version exists, generates a diff summary and creates a GitHub issue
|
||||
4. Issues are deduplicated — only one open issue per upstream version
|
||||
|
||||
| Date | File | Description |
|
||||
|------|------|-------------|
|
||||
| (none yet) | — | Initial vendor, no patches applied |
|
||||
### Local Sync (Makefile targets)
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `make fantasy-check` | Print current vs latest version, exit 1 if behind |
|
||||
| `make fantasy-diff` | Show diff against upstream without modifying anything |
|
||||
| `make fantasy-diff FANTASY_VERSION=v0.9.0` | Diff against a specific version |
|
||||
| `make fantasy-sync` | Full sync to latest: download, replace, re-apply patches, test |
|
||||
| `make fantasy-sync FANTASY_VERSION=v0.9.0` | Sync to a specific version |
|
||||
| `make fantasy-patch NAME=my-change` | Save local modifications as a numbered patch file |
|
||||
|
||||
All targets are thin wrappers around `scripts/sync-fantasy.sh`.
|
||||
|
||||
## Sync Workflow
|
||||
|
||||
When the CI creates an issue (or you notice a new version), follow these steps:
|
||||
|
||||
```bash
|
||||
# 1. Preview what changed upstream
|
||||
make fantasy-diff FANTASY_VERSION=vX.Y.Z
|
||||
|
||||
# 2. If the changes look good, run the full sync
|
||||
make fantasy-sync FANTASY_VERSION=vX.Y.Z
|
||||
|
||||
# 3. Review the result
|
||||
git diff
|
||||
|
||||
# 4. Run the full test suite
|
||||
go test ./...
|
||||
|
||||
# 5. Commit
|
||||
git add -A && git commit -m "build(fantasy): sync vendored SDK to vX.Y.Z"
|
||||
```
|
||||
|
||||
The sync script will:
|
||||
- Download the target version from the Go module cache
|
||||
- Replace the vendored copy (preserving the `patches/` directory)
|
||||
- Re-apply all local patches in order
|
||||
- Update `.vendor-version`, `go.mod` replace directive, and this file
|
||||
- Run `go build` and `go test` for validation
|
||||
|
||||
If any patch fails to apply, the script aborts with a clear error message showing
|
||||
which patch conflicted. You'll need to resolve the conflict manually, then re-save
|
||||
the patch with `make fantasy-patch`.
|
||||
|
||||
## Patch Management
|
||||
|
||||
Local modifications to the vendored SDK are tracked as numbered `.patch` files in the
|
||||
`patches/` directory:
|
||||
|
||||
```
|
||||
internal/fantasy/patches/
|
||||
0001-progressive-disclosure-hooks.patch
|
||||
0002-custom-streaming-callbacks.patch
|
||||
...
|
||||
```
|
||||
|
||||
### Creating a patch
|
||||
|
||||
Before making a local modification:
|
||||
|
||||
1. Make your changes to files in `internal/fantasy/`
|
||||
2. Run `make fantasy-patch NAME=descriptive-name`
|
||||
3. The script diffs your working tree against the pristine upstream version
|
||||
4. A numbered patch file is generated in `patches/`
|
||||
|
||||
### How patches are re-applied during sync
|
||||
|
||||
During `make fantasy-sync`, the script:
|
||||
1. Replaces the entire vendored directory with the new upstream version
|
||||
2. Restores the `patches/` directory
|
||||
3. Applies each `.patch` file in sequence using `git apply`
|
||||
4. If a patch fails, the sync halts — fix the conflict and re-save the patch
|
||||
|
||||
### Resolving patch conflicts
|
||||
|
||||
If a patch fails during sync:
|
||||
1. The script prints which patch conflicted
|
||||
2. Manually apply the intent of the patch to the new upstream code
|
||||
3. Delete the old patch: `rm internal/fantasy/patches/NNNN-name.patch`
|
||||
4. Re-save: `make fantasy-patch NAME=name`
|
||||
5. Re-run: `make fantasy-sync FANTASY_VERSION=vX.Y.Z`
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `.vendor-version` | Machine-readable current vendored version (`v0.8.1`) |
|
||||
| `patches/` | Directory of local modification patches |
|
||||
| `patches/.gitkeep` | Ensures the directory is tracked in git |
|
||||
| `VENDORING.md` | This documentation file |
|
||||
|
||||
## Original License
|
||||
|
||||
|
|
|
|||
0
internal/fantasy/patches/.gitkeep
Normal file
0
internal/fantasy/patches/.gitkeep
Normal file
339
scripts/sync-fantasy.sh
Executable file
339
scripts/sync-fantasy.sh
Executable file
|
|
@ -0,0 +1,339 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# sync-fantasy.sh — Monitor and sync the vendored charm.land/fantasy SDK.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/sync-fantasy.sh --check # compare vendored vs latest
|
||||
# ./scripts/sync-fantasy.sh --diff [VERSION] # show diff without modifying
|
||||
# ./scripts/sync-fantasy.sh --sync [VERSION] # full sync + patch re-apply
|
||||
# ./scripts/sync-fantasy.sh --save-patch NAME # capture working-tree diff as patch
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
VENDOR_DIR="${REPO_ROOT}/internal/fantasy"
|
||||
VERSION_FILE="${VENDOR_DIR}/.vendor-version"
|
||||
PATCHES_DIR="${VENDOR_DIR}/patches"
|
||||
VENDORING_MD="${VENDOR_DIR}/VENDORING.md"
|
||||
GO_MOD="${REPO_ROOT}/go.mod"
|
||||
MODULE="charm.land/fantasy"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { printf "${CYAN}[fantasy-sync]${NC} %s\n" "$*"; }
|
||||
warn() { printf "${YELLOW}[fantasy-sync]${NC} %s\n" "$*" >&2; }
|
||||
err() { printf "${RED}[fantasy-sync]${NC} %s\n" "$*" >&2; }
|
||||
ok() { printf "${GREEN}[fantasy-sync]${NC} %s\n" "$*"; }
|
||||
|
||||
current_version() {
|
||||
if [[ -f "$VERSION_FILE" ]]; then
|
||||
cat "$VERSION_FILE" | tr -d '[:space:]'
|
||||
else
|
||||
# Fall back to parsing VENDORING.md header
|
||||
grep -oP 'v[\d]+\.[\d]+\.[\d]+' "$VENDORING_MD" | head -1 || echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
latest_version() {
|
||||
# Query the Go module proxy for the latest version.
|
||||
# go list -m -versions returns all tags; we pick the last one.
|
||||
local versions
|
||||
versions=$(go list -m -versions "${MODULE}" 2>/dev/null | awk '{$1=""; print}' | tr ' ' '\n' | grep -v '^\s*$' | tail -1)
|
||||
if [[ -z "$versions" ]]; then
|
||||
err "Could not query versions for ${MODULE}"
|
||||
exit 1
|
||||
fi
|
||||
echo "$versions"
|
||||
}
|
||||
|
||||
download_version() {
|
||||
local ver="$1"
|
||||
log "Downloading ${MODULE}@${ver} ..." >&2
|
||||
go mod download "${MODULE}@${ver}" >/dev/null 2>&1
|
||||
|
||||
local cache_dir
|
||||
cache_dir="$(go env GOMODCACHE)/${MODULE}@${ver}"
|
||||
if [[ ! -d "$cache_dir" ]]; then
|
||||
err "Downloaded module not found at ${cache_dir}"
|
||||
exit 1
|
||||
fi
|
||||
echo "$cache_dir"
|
||||
}
|
||||
|
||||
# Extract version major component for breaking-change detection.
|
||||
major_of() { echo "$1" | grep -oP '^v\K[0-9]+'; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --check
|
||||
# ---------------------------------------------------------------------------
|
||||
cmd_check() {
|
||||
local cur latest
|
||||
cur="$(current_version)"
|
||||
latest="$(latest_version)"
|
||||
|
||||
printf "${BOLD}Vendored:${NC} %s\n" "$cur"
|
||||
printf "${BOLD}Latest:${NC} %s\n" "$latest"
|
||||
|
||||
if [[ "$cur" == "$latest" ]]; then
|
||||
ok "Up to date."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local cur_major latest_major
|
||||
cur_major="$(major_of "$cur")"
|
||||
latest_major="$(major_of "$latest")"
|
||||
if [[ "$cur_major" != "$latest_major" ]]; then
|
||||
warn "MAJOR version change (${cur_major} -> ${latest_major}) — expect breaking API changes."
|
||||
fi
|
||||
|
||||
warn "Behind upstream by $(echo "$latest" | sed "s/$cur//") — run: make fantasy-sync VERSION=${latest}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --diff [VERSION]
|
||||
# ---------------------------------------------------------------------------
|
||||
cmd_diff() {
|
||||
local target="${1:-$(latest_version)}"
|
||||
local cur
|
||||
cur="$(current_version)"
|
||||
|
||||
log "Diffing vendored (${cur}) against upstream (${target}) ..."
|
||||
|
||||
local upstream_dir
|
||||
upstream_dir="$(download_version "$target")"
|
||||
|
||||
local tmp_upstream
|
||||
tmp_upstream="$(mktemp -d)"
|
||||
trap "rm -rf '${tmp_upstream}'" EXIT
|
||||
|
||||
# Copy upstream to writable temp (module cache is read-only).
|
||||
cp -r "${upstream_dir}/." "${tmp_upstream}/"
|
||||
|
||||
# Exclude non-source noise from the diff.
|
||||
local diff_opts=(
|
||||
--no-index
|
||||
--stat
|
||||
--diff-filter=ACDMRT
|
||||
--
|
||||
"${VENDOR_DIR}"
|
||||
"${tmp_upstream}"
|
||||
)
|
||||
|
||||
echo ""
|
||||
printf "${BOLD}=== Upstream changes (${cur} -> ${target}) ===${NC}\n"
|
||||
git diff --no-index --stat -- "${VENDOR_DIR}" "${tmp_upstream}" 2>/dev/null || true
|
||||
echo ""
|
||||
printf "${BOLD}=== Full diff ===${NC}\n"
|
||||
git diff --no-index --no-color -- "${VENDOR_DIR}" "${tmp_upstream}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --sync [VERSION]
|
||||
# ---------------------------------------------------------------------------
|
||||
cmd_sync() {
|
||||
local target="${1:-$(latest_version)}"
|
||||
local cur
|
||||
cur="$(current_version)"
|
||||
|
||||
if [[ "$cur" == "$target" ]]; then
|
||||
ok "Already at ${target}. Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local cur_major target_major
|
||||
cur_major="$(major_of "$cur")"
|
||||
target_major="$(major_of "$target")"
|
||||
if [[ "$cur_major" != "$target_major" ]]; then
|
||||
warn "MAJOR version change (${cur_major} -> ${target_major}) — breaking API changes likely."
|
||||
read -r -p "Continue? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || { log "Aborted."; exit 0; }
|
||||
fi
|
||||
|
||||
log "Syncing ${MODULE}: ${cur} -> ${target}"
|
||||
|
||||
# 1. Download upstream
|
||||
local upstream_dir
|
||||
upstream_dir="$(download_version "$target")"
|
||||
|
||||
# 2. Show what we're about to change
|
||||
log "Changes from upstream:"
|
||||
git diff --no-index --stat -- "${VENDOR_DIR}" "${upstream_dir}" 2>/dev/null || true
|
||||
echo ""
|
||||
|
||||
# 3. Capture local modifications before overwriting.
|
||||
# Compare current vendored copy against the CACHED version of the OLD upstream
|
||||
# to see what local patches exist that aren't tracked yet.
|
||||
local old_upstream_dir
|
||||
old_upstream_dir="$(go env GOMODCACHE)/${MODULE}@${cur}" 2>/dev/null || true
|
||||
if [[ -d "$old_upstream_dir" ]]; then
|
||||
local untracked_diff
|
||||
untracked_diff="$(git diff --no-index --stat -- "$old_upstream_dir" "${VENDOR_DIR}" 2>/dev/null || true)"
|
||||
if [[ -n "$untracked_diff" ]]; then
|
||||
warn "Detected LOCAL modifications not captured as patches:"
|
||||
echo "$untracked_diff"
|
||||
warn "These will be LOST unless you save them first: make fantasy-patch NAME=<description>"
|
||||
read -r -p "Continue anyway? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || { log "Aborted. Save patches first."; exit 1; }
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. Replace vendored copy
|
||||
log "Replacing vendored copy ..."
|
||||
# Preserve our patches directory and metadata.
|
||||
local tmp_patches
|
||||
tmp_patches="$(mktemp -d)"
|
||||
if [[ -d "$PATCHES_DIR" ]]; then
|
||||
cp -r "${PATCHES_DIR}" "${tmp_patches}/patches"
|
||||
fi
|
||||
local tmp_version=""
|
||||
[[ -f "$VERSION_FILE" ]] && tmp_version="$(cat "$VERSION_FILE")"
|
||||
|
||||
rm -rf "${VENDOR_DIR}"
|
||||
cp -r "${upstream_dir}" "${VENDOR_DIR}"
|
||||
chmod -R u+w "${VENDOR_DIR}"
|
||||
|
||||
# Restore patches directory and metadata.
|
||||
if [[ -d "${tmp_patches}/patches" ]]; then
|
||||
cp -r "${tmp_patches}/patches" "${PATCHES_DIR}"
|
||||
else
|
||||
mkdir -p "${PATCHES_DIR}"
|
||||
fi
|
||||
rm -rf "${tmp_patches}"
|
||||
|
||||
# 5. Re-apply local patches
|
||||
local patch_count=0
|
||||
local patch_failed=0
|
||||
if compgen -G "${PATCHES_DIR}/*.patch" >/dev/null 2>&1; then
|
||||
log "Re-applying local patches ..."
|
||||
for patch_file in "${PATCHES_DIR}"/*.patch; do
|
||||
local pname
|
||||
pname="$(basename "$patch_file")"
|
||||
if git apply --directory="internal/fantasy" --check "$patch_file" 2>/dev/null; then
|
||||
git apply --directory="internal/fantasy" "$patch_file"
|
||||
ok " Applied: ${pname}"
|
||||
patch_count=$((patch_count + 1))
|
||||
else
|
||||
err " FAILED: ${pname}"
|
||||
err " Conflict — resolve manually. Reject file may be in internal/fantasy/"
|
||||
patch_failed=$((patch_failed + 1))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ $patch_failed -gt 0 ]]; then
|
||||
err "${patch_failed} patch(es) failed to apply. Fix conflicts, then run:"
|
||||
err " go build ./... && go test ./internal/fantasy/... ./pkg/fantasy/..."
|
||||
# Still update version file so re-running sync doesn't re-download.
|
||||
echo "${target}" > "${VERSION_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 6. Update metadata
|
||||
echo "${target}" > "${VERSION_FILE}"
|
||||
|
||||
# Update go.mod replace directive
|
||||
sed -i "s|replace ${MODULE} v[^ ]* => ./internal/fantasy|replace ${MODULE} ${target} => ./internal/fantasy|" "${GO_MOD}"
|
||||
|
||||
# Update go.mod require version
|
||||
sed -i "s|${MODULE} v[^ ]*|${MODULE} ${target}|" "${GO_MOD}"
|
||||
|
||||
# Update VENDORING.md header
|
||||
if [[ -f "$VENDORING_MD" ]]; then
|
||||
sed -i "s|${MODULE} v[0-9.]*|${MODULE} ${target}|g" "${VENDORING_MD}"
|
||||
fi
|
||||
|
||||
# 7. Tidy and validate
|
||||
log "Running go mod tidy ..."
|
||||
(cd "${REPO_ROOT}" && go mod tidy)
|
||||
|
||||
log "Building ..."
|
||||
(cd "${REPO_ROOT}" && go build ./...)
|
||||
|
||||
log "Testing fantasy packages ..."
|
||||
(cd "${REPO_ROOT}" && go test ./internal/fantasy/... ./pkg/fantasy/... 2>&1) || {
|
||||
warn "Some tests failed — review output above."
|
||||
}
|
||||
|
||||
echo ""
|
||||
ok "Sync complete: ${cur} -> ${target}"
|
||||
[[ $patch_count -gt 0 ]] && ok "${patch_count} local patch(es) re-applied."
|
||||
ok "Next steps:"
|
||||
ok " 1. Review changes: git diff"
|
||||
ok " 2. Run full tests: go test ./..."
|
||||
ok " 3. Commit: git add -A && git commit -m 'build(fantasy): sync vendored SDK to ${target}'"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --save-patch NAME
|
||||
# ---------------------------------------------------------------------------
|
||||
cmd_save_patch() {
|
||||
local name="${1:?Usage: sync-fantasy.sh --save-patch <name>}"
|
||||
local cur
|
||||
cur="$(current_version)"
|
||||
|
||||
mkdir -p "${PATCHES_DIR}"
|
||||
|
||||
# We diff the module-cache copy of the current version against our vendored copy.
|
||||
local pristine_dir
|
||||
pristine_dir="$(go env GOMODCACHE)/${MODULE}@${cur}"
|
||||
if [[ ! -d "$pristine_dir" ]]; then
|
||||
log "Downloading pristine ${cur} for comparison ..."
|
||||
go mod download "${MODULE}@${cur}" >/dev/null 2>&1
|
||||
pristine_dir="$(go env GOMODCACHE)/${MODULE}@${cur}"
|
||||
fi
|
||||
|
||||
# Count existing patches to determine next sequence number.
|
||||
local next_num
|
||||
next_num=$(printf "%04d" "$(( $(ls -1 "${PATCHES_DIR}"/*.patch 2>/dev/null | wc -l) + 1 ))")
|
||||
|
||||
local patch_file="${PATCHES_DIR}/${next_num}-${name}.patch"
|
||||
|
||||
# Generate the diff, stripping the absolute paths to make it relative.
|
||||
git diff --no-index -- "${pristine_dir}" "${VENDOR_DIR}" 2>/dev/null \
|
||||
| sed "s|a${pristine_dir}/|a/|g; s|b${VENDOR_DIR}/|b/|g" \
|
||||
> "${patch_file}" || true
|
||||
|
||||
if [[ ! -s "$patch_file" ]]; then
|
||||
rm -f "$patch_file"
|
||||
warn "No local modifications detected against pristine ${cur}."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local stat
|
||||
stat="$(diffstat -s "$patch_file" 2>/dev/null || wc -l < "$patch_file")"
|
||||
ok "Saved patch: ${patch_file}"
|
||||
ok "Stats: ${stat}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main dispatch
|
||||
# ---------------------------------------------------------------------------
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $(basename "$0") <command> [args]
|
||||
|
||||
Commands:
|
||||
--check Compare vendored vs latest upstream version
|
||||
--diff [VERSION] Show diff without modifying (default: latest)
|
||||
--sync [VERSION] Full sync: download, replace, re-apply patches, test
|
||||
--save-patch NAME Capture local modifications as a numbered patch file
|
||||
|
||||
Environment:
|
||||
MODULE=${MODULE}
|
||||
VENDOR_DIR=${VENDOR_DIR}
|
||||
USAGE
|
||||
exit 1
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
--check) cmd_check ;;
|
||||
--diff) cmd_diff "${2:-}" ;;
|
||||
--sync) cmd_sync "${2:-}" ;;
|
||||
--save-patch) cmd_save_patch "${2:-}" ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
Loading…
Add table
Reference in a new issue