Skip to content

Commit 4066834

Browse files
committed
chore: refactor devstack versioned image builds with dev suffix
- VERSION-only in devstack (canonical single-source), deprecated cross-repo sync - Image is the configuration reference — lpb.py selects pre-built image - versioned tags: 0.0.x-lpb[-dev] with dev profile support - CI: proper version bump push with git fetch (no --depth=1 race) - Pre-commit: lpb.py unit tests, version format validation - Build: --build-arg from VERSION file, ARG/ENV in Dockerfile - CLI: --tag dev/main aliases, --update versioned image pull - Remove stale LPB_VERSION from lpb.conf.env, orphaned LOCALPIB_VERSION - Fix pi workspace pre-commit: install husky hook, remove stale githooks - Fix Dockerfile: use PI_REF build arg for branch instead of hardcoded lpb-dev
1 parent 0de2a46 commit 4066834

13 files changed

Lines changed: 379 additions & 134 deletions

File tree

.githooks/commit-msg

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,5 @@
11
#!/bin/bash
2-
# Auto-increment patch version on every commit (unless [skip-version])
3-
# Only updates VERSION files + lpb.stack.env LPB_PI_REF
4-
# Does NOT touch package.json versions (kept at original fork versions)
5-
6-
COMMIT_MSG_FILE="$1"
7-
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
8-
if echo "$COMMIT_MSG" | grep -q '\[skip-version\]'; then exit 0; fi
9-
10-
# Find devstack dir by searching upward from the git working tree
11-
DEVSTACK_DIR="$(git rev-parse --show-toplevel 2>/dev/null)"
12-
[ -z "$DEVSTACK_DIR" ] && DEVSTACK_DIR="$PWD"
13-
14-
[ ! -f "$DEVSTACK_DIR/VERSION" ] && exit 0
15-
16-
VERSION=$(cat "$DEVSTACK_DIR/VERSION" 2>/dev/null || echo "0.0.0-lpb")
17-
BASE=$(echo "$VERSION" | sed 's/^[0-9]*\.[0-9]*\.[0-9]*//')
18-
PATCH=$(echo "$VERSION" | sed 's/^[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/')
19-
NEW_PATCH=$((PATCH + 1))
20-
NEW_VERSION="0.0.${NEW_PATCH}${BASE}"
21-
22-
echo "$NEW_VERSION" > "$DEVSTACK_DIR/VERSION"
23-
24-
# Update VERSION in all stack repos
25-
declare -a REPOS=(
26-
"$DEVSTACK_DIR/workspace/pi"
27-
"/home/lpb/.pi/agent/git/github.com/localpibox/lemonade-pi-plugin"
28-
"/home/lpb/workspace/localpibox/config"
29-
"/home/lpb/workspace/localpibox/lpb-memory"
30-
"/home/lpb/.pi/agent/git/github.com/localpibox/pi-subagents"
31-
)
32-
for repo in "${REPOS[@]}"; do
33-
[ -f "$repo/VERSION" ] && echo "$NEW_VERSION" > "$repo/VERSION"
34-
done
35-
36-
# Update lpb.stack.env
37-
[ -f "$DEVSTACK_DIR/lpb.stack.env" ] && sed -i "s/^LPB_PI_REF=.*/LPB_PI_REF=$NEW_VERSION/" "$DEVSTACK_DIR/lpb.stack.env"
38-
2+
# commit-msg hook — No-op
3+
# Version bumping is handled by CI (bump-version job after tests pass)
4+
# Git hooks do NOT touch VERSION files or cross-repo state.
395
exit 0

.github/workflows/build-and-publish.yml

Lines changed: 124 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
# Extensions update at runtime via pi update --extensions
66
#
77
# Triggers:
8+
# - Push to dev (code changes, NOT VERSION bumps)
89
# - Push to main (Dockerfile, support/, lpb.stack.env, lpb.conf.env)
910
# - Weekly cron (Monday 3am UTC) — keep image fresh
1011
# - Manual dispatch
1112
#
12-
# Images built:
13-
# ghcr.io/localpibox/devstack:cli — Base dev environment + Pi CLI
14-
# ghcr.io/localpibox/devstack:web — Extends cli + VSCodium server
13+
# Version model (Option C):
14+
# - Single source: devstack/VERSION
15+
# - CI bumps version after tests pass, sets bumped value as CI output
16+
# - All 6 repos tagged together on each CI run (not pushed as a commit)
17+
# - Docker images tagged: :{version}-cli, :{version}-web
18+
# - Dev branch images: :dev-cli, :dev-web (always latest)
1519
#
1620
# Actions: all latest major versions (Node.js 24 native)
1721
# actions/checkout@v6 · docker/build-push-action@v7
@@ -27,19 +31,17 @@ on:
2731
paths:
2832
- 'Dockerfile'
2933
- 'support/**'
30-
- 'lpb.stack.env'
31-
- 'lpb.conf.env'
32-
- '.github/workflows/*.yml'
3334
- 'scripts/**'
35+
- '.github/workflows/*.yml'
36+
# NOTE: VERSION + lpb.stack.env changes intentionally excluded
37+
# to avoid re-triggering on auto-bump commits
3438
pull_request:
3539
branches: [main]
3640
paths:
3741
- 'Dockerfile'
3842
- 'support/**'
39-
- 'lpb.stack.env'
40-
- 'lpb.conf.env'
41-
- '.github/workflows/*.yml'
4243
- 'scripts/**'
44+
- '.github/workflows/*.yml'
4345
schedule:
4446
- cron: '0 3 * * 1'
4547
workflow_dispatch:
@@ -54,13 +56,16 @@ on:
5456
default: false
5557

5658
permissions:
57-
contents: read
59+
contents: write
5860
packages: write
5961

6062
env:
6163
IMAGE_NAME: ghcr.io/localpibox/devstack
6264

6365
jobs:
66+
# ──────────────────────────────────────────────────────
67+
# Phase 1: Test
68+
# ──────────────────────────────────────────────────────
6469
test-lpb:
6570
name: Run lpb.py unit tests
6671
runs-on: ubuntu-latest
@@ -73,14 +78,108 @@ jobs:
7378
python3 scripts/test_lpb.py
7479
python3 scripts/test_localpibox.py
7580
81+
# ──────────────────────────────────────────────────────
82+
# Phase 2: Bump version + create tags
83+
# ──────────────────────────────────────────────────────
84+
bump-version:
85+
name: Bump version & create tags
86+
runs-on: ubuntu-latest
87+
needs: [test-lpb]
88+
if: ${{ github.event_name != 'pull_request' }}
89+
outputs:
90+
version: ${{ steps.bump.outputs.version }}
91+
steps:
92+
- name: Checkout
93+
uses: actions/checkout@v6
94+
with:
95+
fetch-depth: 0
96+
97+
- name: Set version for build jobs
98+
id: set-version
99+
run: |
100+
VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0-lpb")
101+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
102+
103+
- name: Bump patch version
104+
id: bump
105+
run: |
106+
set -e
107+
# Read current VERSION from repo (set by previous CI bump)
108+
VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0-lpb")
109+
PATCH=$(echo "$VERSION" | sed 's/^[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/')
110+
NEW_PATCH=$((PATCH + 1))
111+
112+
# Add -dev suffix for dev branch, no suffix for main
113+
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
114+
SUFFIX=""
115+
else
116+
SUFFIX="-dev"
117+
fi
118+
NEW_VERSION="0.0.${NEW_PATCH}-lpb${SUFFIX}"
119+
120+
echo "Bumping $VERSION → $NEW_VERSION"
121+
echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
122+
123+
# Persist bumped version back to repo (VERSION/lpb.stack.env excluded
124+
# from CI paths filter, so this won't trigger a new CI run)
125+
echo "$NEW_VERSION" > VERSION
126+
sed -i "s/^LPB_PI_REF=.*/LPB_PI_REF=$NEW_VERSION/" lpb.stack.env
127+
git config user.name "ci-localpibox"
128+
git config user.email "ci@localpibox.dev"
129+
git add VERSION lpb.stack.env
130+
git commit -m "chore: bump VERSION $NEW_VERSION" || echo "Nothing to commit"
131+
git push origin dev --force-with-lease || echo "Push failed (already up-to-date)"
132+
133+
- name: Create tags on all repos
134+
env:
135+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
136+
VERSION: ${{ steps.bump.outputs.version }}
137+
GH_TOKEN: ${{ secrets.LOCALPIBOX_PAT }}
138+
run: |
139+
set -e
140+
# repo -> default branch mapping
141+
declare -A REPO_BRANCHS=(
142+
["localpibox/pi"]="lpb-dev"
143+
["localpibox/pi-subagents"]="lpb-dev"
144+
["localpibox/lemonade-pi-plugin"]="lpb-dev"
145+
["localpibox/config"]="dev"
146+
["localpibox/lpb-memory"]="dev"
147+
)
148+
for repo in "${!REPO_BRANCHS[@]}"; do
149+
branch="${REPO_BRANCHS[$repo]}"
150+
echo "Tagging $repo@$VERSION (from $branch)"
151+
sha=$(git ls-remote "https://github.com/$repo.git" "refs/heads/$branch" | awk '{print $1}')
152+
if [ -n "$sha" ]; then
153+
# POST to /git/refs with {ref, sha} body (NOT /git/refs/tags/{tagname})
154+
status=$(curl -s -o /dev/null -w '%{http_code}' \
155+
-X POST "https://api.github.com/repos/$repo/git/refs" \
156+
-H "Authorization: token $GH_TOKEN" \
157+
-H "Accept: application/vnd.github+json" \
158+
-d "{\"ref\": \"refs/tags/$VERSION\", \"sha\": \"$sha\"}")
159+
if [ "$status" = "201" ] || [ "$status" = "422" ]; then
160+
echo " ✅ $repo@$VERSION"
161+
else
162+
echo " ⚠️ $repo tag creation failed (HTTP $status)"
163+
fi
164+
else
165+
echo " ⚠️ $repo:$branch not found, skipping tag"
166+
fi
167+
done
168+
169+
# ──────────────────────────────────────────────────────
170+
# Phase 3: Build & publish images
171+
# ──────────────────────────────────────────────────────
76172
build-cli:
77173
name: Build & publish cli image
78174
runs-on: ubuntu-latest
79175
timeout-minutes: 45
80-
needs: [test-lpb]
176+
needs: [bump-version]
177+
if: ${{ github.event_name != 'pull_request' }}
81178
steps:
82179
- name: Checkout
83180
uses: actions/checkout@v6
181+
with:
182+
persist-credentials: true
84183

85184
- name: Set up QEMU
86185
uses: docker/setup-qemu-action@v4
@@ -102,7 +201,8 @@ jobs:
102201
source lpb.stack.env
103202
set +a
104203
echo "PI_FORK=$LPB_PI_FORK" >> "$GITHUB_OUTPUT"
105-
echo "PI_REF=$LPB_PI_REF" >> "$GITHUB_OUTPUT"
204+
# Use bumped version from bump-version step (workspace has old value)
205+
echo "PI_REF=${{ needs.bump-version.outputs.version }}" >> "$GITHUB_OUTPUT"
106206
echo "CONFIG_FORK=$LPB_CONFIG_FORK" >> "$GITHUB_OUTPUT"
107207
echo "CONFIG_REF=$LPB_CONFIG_REF" >> "$GITHUB_OUTPUT"
108208
echo "NODE_VERSION=$LPB_NODE_VERSION" >> "$GITHUB_OUTPUT"
@@ -111,11 +211,8 @@ jobs:
111211
MAX_TOKENS=$(grep -E '^LPB_MAX_TOKENS_CONTEXT_RATIO=' lpb.conf.env | cut -d= -f2- | tr -d '"' 2>/dev/null || true)
112212
echo "MAX_TOKENS=${MAX_TOKENS:-0.06}" >> "$GITHUB_OUTPUT"
113213
sha=$(git ls-remote "$LPB_PI_FORK" "refs/heads/$LPB_PI_REF" | awk '{print $1}')
114-
# Read stack version from config repo
115-
cfg_repo=$(echo "$LPB_CONFIG_FORK" | sed -E 's#https://github.com/([^/]+)/([^./]+)\.git#\1/\2#')
116-
stack_ver=$(curl -sf "https://raw.githubusercontent.com/$cfg_repo/$LPB_CONFIG_REF/VERSION" 2>/dev/null || echo "unknown")
117214
echo "sha=${sha:-unknown}" >> "$GITHUB_OUTPUT"
118-
echo "stack_version=${stack_ver}" >> "$GITHUB_OUTPUT"
215+
echo "stack_version=${{ needs.bump-version.outputs.version }}" >> "$GITHUB_OUTPUT"
119216
120217
- name: Build & push cli
121218
uses: docker/build-push-action@v7
@@ -136,16 +233,14 @@ jobs:
136233
PI_HEAD_SHA=${{ steps.config.outputs.sha }}
137234
LPB_VERSION=${{ steps.config.outputs.LPB_VERSION }}
138235
LPB_MAX_TOKENS_CONTEXT_RATIO=${{ steps.config.outputs.MAX_TOKENS }}
236+
secrets: |
237+
GIT_AUTH_TOKEN=${{ secrets.LOCALPIBOX_PAT }}
139238
no-cache: ${{ github.event.inputs.no_cache == 'true' }}
140239
tags: |
141-
${{ env.IMAGE_NAME }}:cli
240+
${{ env.IMAGE_NAME }}:${{ needs.bump-version.outputs.version }}-cli
142241
${{ env.IMAGE_NAME }}:dev-cli
143242
${{ env.IMAGE_NAME }}:main-cli
144243
${{ env.IMAGE_NAME }}:${{ github.sha }}-cli
145-
${{ env.IMAGE_NAME }}:${{ steps.config.outputs.stack_version }}-cli
146-
${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && format('{0}:latest', env.IMAGE_NAME) || '' }}
147-
${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && format('{0}:dev', env.IMAGE_NAME) || '' }}
148-
${{ github.event.inputs.publish_latest && format('{0}:latest', env.IMAGE_NAME) || '' }}
149244
${{ github.event_name == 'schedule' && format('{0}:weekly-cli', env.IMAGE_NAME) || '' }}
150245
provenance: false
151246
platforms: linux/amd64
@@ -154,10 +249,13 @@ jobs:
154249
name: Build & publish web image
155250
runs-on: ubuntu-latest
156251
timeout-minutes: 45
157-
needs: [build-cli]
252+
needs: [build-cli, bump-version]
253+
if: ${{ github.event_name != 'pull_request' }}
158254
steps:
159255
- name: Checkout
160256
uses: actions/checkout@v6
257+
with:
258+
persist-credentials: true
161259

162260
- name: Set up QEMU
163261
uses: docker/setup-qemu-action@v4
@@ -188,11 +286,8 @@ jobs:
188286
MAX_TOKENS=$(grep -E '^LPB_MAX_TOKENS_CONTEXT_RATIO=' lpb.conf.env | cut -d= -f2- | tr -d '"' 2>/dev/null || true)
189287
echo "MAX_TOKENS=${MAX_TOKENS:-0.06}" >> "$GITHUB_OUTPUT"
190288
sha=$(git ls-remote "$LPB_PI_FORK" "refs/heads/$LPB_PI_REF" | awk '{print $1}')
191-
# Read stack version from config repo
192-
cfg_repo=$(echo "$LPB_CONFIG_FORK" | sed -E 's#https://github.com/([^/]+)/([^./]+)\.git#\1/\2#')
193-
stack_ver=$(curl -sf "https://raw.githubusercontent.com/$cfg_repo/$LPB_CONFIG_REF/VERSION" 2>/dev/null || echo "unknown")
194289
echo "sha=${sha:-unknown}" >> "$GITHUB_OUTPUT"
195-
echo "stack_version=${stack_ver}" >> "$GITHUB_OUTPUT"
290+
echo "stack_version=${{ needs.bump-version.outputs.version }}" >> "$GITHUB_OUTPUT"
196291
197292
- name: Build & push web
198293
uses: docker/build-push-action@v7
@@ -213,15 +308,14 @@ jobs:
213308
PI_HEAD_SHA=${{ steps.config.outputs.sha }}
214309
LPB_VERSION=${{ steps.config.outputs.LPB_VERSION }}
215310
LPB_MAX_TOKENS_CONTEXT_RATIO=${{ steps.config.outputs.MAX_TOKENS }}
311+
secrets: |
312+
GIT_AUTH_TOKEN=${{ secrets.LOCALPIBOX_PAT }}
216313
no-cache: ${{ github.event.inputs.no_cache == 'true' }}
217314
tags: |
218-
${{ env.IMAGE_NAME }}:web
315+
${{ env.IMAGE_NAME }}:${{ needs.bump-version.outputs.version }}-web
219316
${{ env.IMAGE_NAME }}:dev-web
220317
${{ env.IMAGE_NAME }}:main-web
221318
${{ env.IMAGE_NAME }}:${{ github.sha }}-web
222-
${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && format('{0}:latest-web', env.IMAGE_NAME) || '' }}
223-
${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && format('{0}:dev-web', env.IMAGE_NAME) || '' }}
224-
${{ github.event.inputs.publish_latest && format('{0}:latest-web', env.IMAGE_NAME) || '' }}
225319
${{ github.event_name == 'schedule' && format('{0}:weekly-web', env.IMAGE_NAME) || '' }}
226320
provenance: false
227321
platforms: linux/amd64

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,11 @@
33
# NOT tracked in git. Recreated by:
44
# ./support/version.sh workspace sync
55
/workspace
6+
67
# ── Documentation archive (moved, not regenerated yet) ─────────────────────
78
doc/archive/
9+
10+
# ── Python bytecode (generated, not tracked) ──────────────────────────────
11+
__pycache__/
12+
*.pyc
13+
*.pyo

.pi/memory-user.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
User Preferences (Memory):
2+
3+
1. **Subprocess Model Routing Verification**: User prefers verifying subprocess model routing by having it perform a visible task (tell a story, do computation) so they can watch NPU/monitoring tools to confirm the right model hardware is active, rather than just checking log output. <!-- created=2026-08-15 -->
4+
5+
2. **Configurable Timeouts & Thresholds**: User explicitly stated timeouts should be configurable, not hardcoded — important for fine-tuning operations. This applies to all timeout/threshold values in subprocess invocations (NPU model calls). <!-- created=2026-08-15 -->
6+
7+
3. **Upstream Fixes Over Downstream Workarounds**: Prefers proper root-cause fixes over workarounds/band-aids. Calls out when a solution masks a deeper design problem (e.g., "this was a workaround not a proper solution"). Expects upstream prevention (fix prompts/architecture) rather than downstream filtering (regex patterns, meta-entry cleanup). <!-- created=2026-08-15 -->
8+
9+
4. **lpb-memory NPU Model**: User wants the lpb-memory extension to use the NPU model (a separate model from the main Qwen3.6-35B-A3B-MTP-GGUF session model) for background memory operations. <!-- created=2026-08-15 -->

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ RUN --mount=type=cache,target=/home/lpb/.npm \
124124
RUN set -eux; \
125125
export PATH="/home/lpb/.npm-global/bin:${PATH}"; \
126126
mkdir -p /opt/pi-src && cd /opt/pi-src; \
127-
git clone --depth=1 --single-branch --branch ${PI_REF} ${PI_FORK} .; \
127+
git clone --branch ${PI_REF} ${PI_FORK} .; \
128+
git fetch origin ${PI_REF}; \
129+
git checkout ${PI_REF}; \
128130
git config user.email "build@localpibox.dev"; \
129131
git config user.name "LocalPibox Build"; \
130132
npm install; \

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.4-lpb
1+
0.0.25-lpb-dev

lpb.conf.env

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ LPB_AGENT_BROWSER_IDLE_TIMEOUT_MS=300000
6161
# ─── Misc ───────────────────────────────────────────────────────────────
6262
# Persist gh CLI auth to ~/.config/gh inside container
6363
LPB_PERSIST_GH_CONFIG=true
64-
# LocalPibox stack version (shown in startup banner, set via lpb.stack.env)
65-
LPB_VERSION=0.2.0-lpb
66-
64+
LPB_PERSIST_GH_CONFIG=true
6765
# ─── Agent Date/Time ──────────────────────────────────────────────────
6866
# Set by start.sh at runtime (via $(date)) so the agent always knows
6967
# the current date/time. Available as PI_DATE and PI_TIME environment

lpb.stack.dev.env

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# lpb.stack.dev.env — Dev profile configuration
2+
#
3+
# Points to development branches for testing.
4+
# Used when running: lpb --tag dev
5+
6+
# ─── Pi Monorepo ────────────────────────────────────────────────────────
7+
LPB_PI_REF=lpb-dev
8+
9+
# ─── Config Preset ───────────────────────────────────────────────────────
10+
LPB_CONFIG_REF=dev

lpb.stack.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# Fork URL — cumulative patches on top of earendil-works/pi
1515
LPB_PI_FORK=https://github.com/localpibox/pi.git
1616
# Branch — which branch to build from (the lpb branch is the stable base)
17-
LPB_PI_REF=0.0.4-lpb
17+
LPB_PI_REF=0.0.25-lpb-dev
1818
# Upstream — original repo (for reference / rebasing)
1919
LPB_PI_UPSTREAM=https://github.com/earendil-works/pi.git
2020

lpb.stack.main.env

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# lpb.stack.main.env — Main profile configuration
2+
#
3+
# Points to stable branches (lpb + main).
4+
# Used when running: lpb --tag main or lpb --tag latest
5+
6+
# ─── Pi Monorepo ────────────────────────────────────────────────────────
7+
# Use 'lpb' branch (stable base) for main pipeline.
8+
LPB_PI_REF=lpb
9+
10+
# ─── Config Preset ───────────────────────────────────────────────────────
11+
LPB_CONFIG_REF=main

0 commit comments

Comments
 (0)