Skip to content

Commit a011c95

Browse files
committed
chore: track hooks in .githooks/, update CI to dev, add commit-msg hook script [skip-version]
1 parent c7428fb commit a011c95

4 files changed

Lines changed: 290 additions & 19 deletions

File tree

.githooks/commit-msg

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/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+
DEVSTACK_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
11+
[ ! -f "$DEVSTACK_DIR/VERSION" ] && exit 0
12+
13+
VERSION=$(cat "$DEVSTACK_DIR/VERSION" 2>/dev/null || echo "0.0.0-lpb")
14+
BASE=$(echo "$VERSION" | sed 's/^[0-9]*\.[0-9]*\.[0-9]*//')
15+
PATCH=$(echo "$VERSION" | sed 's/^[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/')
16+
NEW_PATCH=$((PATCH + 1))
17+
NEW_VERSION="0.0.${NEW_PATCH}${BASE}"
18+
19+
echo "$NEW_VERSION" > "$DEVSTACK_DIR/VERSION"
20+
21+
# Update VERSION in all stack repos
22+
declare -a REPOS=(
23+
"$DEVSTACK_DIR/workspace/pi"
24+
"/home/lpb/.pi/agent/git/github.com/localpibox/lemonade-pi-plugin"
25+
"/home/lpb/workspace/localpibox/config"
26+
"/home/lpb/workspace/localpibox/lpb-memory"
27+
"/home/lpb/.pi/agent/git/github.com/localpibox/pi-subagents"
28+
)
29+
for repo in "${REPOS[@]}"; do
30+
[ -f "$repo/VERSION" ] && echo "$NEW_VERSION" > "$repo/VERSION"
31+
done
32+
33+
# Update lpb.stack.env
34+
[ -f "$DEVSTACK_DIR/lpb.stack.env" ] && sed -i "s/^LPB_PI_REF=.*/LPB_PI_REF=$NEW_VERSION/" "$DEVSTACK_DIR/lpb.stack.env"
35+
36+
exit 0

.githooks/pre-commit

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/bin/bash
2+
# pre-commit hook — Validate & sync all stack repos before commit
3+
#
4+
# Validates:
5+
# 1. All VERSION files in sync
6+
# 2. lpb.stack.env LPB_PI_REF matches VERSION
7+
# 3. settings.json pins match VERSION
8+
# 4. All repos clean (except VERSION/env)
9+
# 5. Auto-add VERSION + env changes to staging
10+
#
11+
# Exit non-zero to abort commit.
12+
13+
# Find devstack dir by searching upward
14+
DEVSTACK_DIR=""
15+
check_dir="$PWD"
16+
while [ "$check_dir" != "/" ]; do
17+
[ -f "$check_dir/VERSION" ] && [ -f "$check_dir/lpb.stack.env" ] && DEVSTACK_DIR="$check_dir" && break
18+
check_dir=$(dirname "$check_dir")
19+
done
20+
[ -z "$DEVSTACK_DIR" ] && DEVSTACK_DIR="$PWD"
21+
22+
ERRORS=0
23+
CURRENT_VERSION=$(cat "$DEVSTACK_DIR/VERSION" 2>/dev/null || echo "NOT FOUND")
24+
25+
# Repo definitions
26+
declare -a REPOS=(
27+
"$DEVSTACK_DIR/workspace/pi"
28+
"/home/lpb/.pi/agent/git/github.com/localpibox/lemonade-pi-plugin"
29+
"/home/lpb/workspace/localpibox/config"
30+
"/home/lpb/workspace/localpibox/lpb-memory"
31+
"/home/lpb/.pi/agent/git/github.com/localpibox/pi-subagents"
32+
)
33+
34+
SETTINGS_JSON="/home/lpb/.pi/agent/settings.json"
35+
36+
info() { echo "$*"; }
37+
warn() { echo " ⚠️ $*"; }
38+
error() { echo "$*"; ERRORS=$((ERRORS + 1)); }
39+
40+
# 1. Validate VERSION files
41+
echo "=== Stack Version Sync ==="
42+
for repo in "${REPOS[@]}"; do
43+
repo_name=$(basename "$repo")
44+
if [ ! -f "$repo/VERSION" ]; then
45+
error "$repo_name: missing VERSION file"
46+
continue
47+
fi
48+
repo_version=$(cat "$repo/VERSION")
49+
if [ "$repo_version" != "$CURRENT_VERSION" ]; then
50+
error "$repo_name: VERSION=$repo_version (expected $CURRENT_VERSION)"
51+
else
52+
info "$repo_name: $repo_version"
53+
fi
54+
done
55+
56+
# 2. Validate lpb.stack.env
57+
echo ""
58+
echo "=== Environment Sync ==="
59+
if [ -f "$DEVSTACK_DIR/lpb.stack.env" ]; then
60+
PI_REF=$(grep '^LPB_PI_REF=' "$DEVSTACK_DIR/lpb.stack.env" | cut -d= -f2)
61+
if [ "$PI_REF" != "$CURRENT_VERSION" ]; then
62+
error "lpb.stack.env: LPB_PI_REF=$PI_REF (expected $CURRENT_VERSION)"
63+
sed -i "s/^LPB_PI_REF=.*/LPB_PI_REF=$CURRENT_VERSION/" "$DEVSTACK_DIR/lpb.stack.env"
64+
echo " 🔧 Fixed LPB_PI_REF to $CURRENT_VERSION"
65+
else
66+
info "lpb.stack.env: LPB_PI_REF=$PI_REF"
67+
fi
68+
fi
69+
70+
# 3. Validate settings.json pins
71+
echo ""
72+
echo "=== Extension Pins ==="
73+
if [ -f "$SETTINGS_JSON" ]; then
74+
for repo in lemonade-pi-plugin lpb-memory pi-subagents; do
75+
if python3 -c "
76+
import json, sys
77+
with open('$SETTINGS_JSON') as f:
78+
d = json.load(f)
79+
pins = d.get('packages', [])
80+
for p in pins:
81+
if isinstance(p, dict) and '$repo' in p.get('extension', ''):
82+
ver = p.get('version', '')
83+
if ver and ver != '$CURRENT_VERSION':
84+
print(f'MISMATCH: {ver}')
85+
sys.exit(1)
86+
elif not ver:
87+
print('NO-PIN')
88+
sys.exit(1)
89+
sys.exit(0)
90+
" 2>/dev/null; then
91+
info "$repo: pinned correctly"
92+
else
93+
warn "$repo: pin verification skipped (format may differ)"
94+
fi
95+
done
96+
else
97+
warn "settings.json not found at $SETTINGS_JSON"
98+
fi
99+
100+
# 4. Check repo cleanliness
101+
echo ""
102+
echo "=== Repo Cleanliness ==="
103+
for repo in "${REPOS[@]}"; do
104+
repo_name=$(basename "$repo")
105+
# Skip if repo doesn't exist
106+
[ -d "$repo" ] || continue
107+
dirty=$(cd "$repo" && git status --porcelain 2>/dev/null | grep -v VERSION | grep -v '.git/hooks' | wc -l)
108+
if [ "$dirty" -gt 0 ]; then
109+
error "$repo_name: $dirty uncommitted change(s) (excluding VERSION)"
110+
else
111+
info "$repo_name: clean"
112+
fi
113+
done
114+
115+
# Check devstack
116+
if [ -d "$DEVSTACK_DIR" ]; then
117+
devstack_dirty=$(cd "$DEVSTACK_DIR" && git status --porcelain 2>/dev/null | grep -vE 'VERSION|lpb\.stack\.env|lpb\.conf\.env|\.githooks/' | wc -l)
118+
if [ "$devstack_dirty" -gt 0 ]; then
119+
error "devstack: $devstack_dirty uncommitted change(s)"
120+
else
121+
info "devstack: clean"
122+
fi
123+
fi
124+
125+
# 5. Auto-add VERSION + env changes
126+
if [ $ERRORS -eq 0 ] && [ -n "$CURRENT_VERSION" ] && [ "$CURRENT_VERSION" != "NOT FOUND" ]; then
127+
echo ""
128+
echo "=== Auto-stage VERSION changes ==="
129+
cd "$DEVSTACK_DIR"
130+
git add VERSION lpb.stack.env 2>/dev/null
131+
for repo in "${REPOS[@]}"; do
132+
if [ -f "$repo/VERSION" ]; then
133+
(cd "$repo" && git add VERSION 2>/dev/null)
134+
fi
135+
done
136+
info "VERSION files + lpb.stack.env staged for commit"
137+
fi
138+
139+
# Result
140+
echo ""
141+
if [ $ERRORS -gt 0 ]; then
142+
echo "❌ Pre-commit FAILED ($ERRORS error(s)) — commit aborted"
143+
echo " Fix issues above and try again, or use --no-verify to skip"
144+
exit 1
145+
else
146+
echo "✅ All checks passed"
147+
exit 0
148+
fi
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/bin/bash
2+
# commit-msg hook — Auto-increment patch version on every commit
3+
#
4+
# Reads VERSION from devstack, bumps patch, updates all 6 repos.
5+
# Skip versioning with: --no-verify or [skip-version] in commit message.
6+
#
7+
# Usage:
8+
# Hook installed automatically via `git config core.hooksPath hooks`
9+
# Place scripts in .git/hooks/ or use git config core.hooksPath
10+
11+
COMMIT_MSG_FILE="$1"
12+
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
13+
14+
# Skip if commit message has [skip-version]
15+
if echo "$COMMIT_MSG" | grep -q '\[skip-version\]'; then
16+
exit 0
17+
fi
18+
19+
# Only run in devstack repo
20+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
21+
DEVSTACK_DIR="$(dirname "$SCRIPT_DIR")"
22+
if [ ! -f "$DEVSTACK_DIR/VERSION" ]; then
23+
exit 0
24+
fi
25+
26+
# Parse version
27+
VERSION=$(cat "$DEVSTACK_DIR/VERSION" 2>/dev/null || echo "0.0.0-lpb")
28+
BASE=$(echo "$VERSION" | sed 's/^[0-9]*\.[0-9]*\.[0-9]*//')
29+
PATCH=$(echo "$VERSION" | sed 's/^[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/')
30+
NEW_PATCH=$((PATCH + 1))
31+
NEW_VERSION="0.0.${NEW_PATCH}${BASE}"
32+
33+
# Only bump if actually a patch version (0.0.x-lpb pattern)
34+
if ! echo "$NEW_VERSION" | grep -qE '^0\.0\.[0-9]+-lpb$'; then
35+
exit 0
36+
fi
37+
38+
# Skip if version hasn't changed
39+
if [ "$NEW_VERSION" = "$VERSION" ]; then
40+
exit 0
41+
fi
42+
43+
# Update VERSION in devstack
44+
echo "$NEW_VERSION" > "$DEVSTACK_DIR/VERSION"
45+
46+
# Update VERSION in all stack repos
47+
declare -a REPOS=(
48+
"$DEVSTACK_DIR/workspace/pi"
49+
"/home/lpb/.pi/agent/git/github.com/localpibox/lemonade-pi-plugin"
50+
"/home/lpb/workspace/localpibox/config"
51+
"/home/lpb/workspace/localpibox/lpb-memory"
52+
"/home/lpb/.pi/agent/git/github.com/localpibox/pi-subagents"
53+
)
54+
55+
for repo in "${REPOS[@]}"; do
56+
if [ -f "$repo/VERSION" ]; then
57+
echo "$NEW_VERSION" > "$repo/VERSION"
58+
fi
59+
done
60+
61+
# Update lpb.stack.env LPB_PI_REF
62+
if [ -f "$DEVSTACK_DIR/lpb.stack.env" ]; then
63+
sed -i "s/^LPB_PI_REF=.*/LPB_PI_REF=$NEW_VERSION/" "$DEVSTACK_DIR/lpb.stack.env"
64+
fi
65+
66+
# Update package.json in repos that have it
67+
for repo in "${REPOS[@]}"; do
68+
if [ -f "$repo/package.json" ]; then
69+
python3 -c "
70+
import json, sys
71+
path = '$repo/package.json'
72+
with open(path) as f:
73+
d = json.load(f)
74+
d['version'] = '$NEW_VERSION'
75+
with open(path, 'w') as f:
76+
json.dump(d, f, indent=2)
77+
f.write('\n')
78+
" 2>/dev/null || true
79+
fi
80+
done
81+
82+
exit 0

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

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,38 @@
55
# Extensions update at runtime via pi update --extensions
66
#
77
# Triggers:
8-
# - Push to main (Dockerfile, support/, lpb.stack.env, lpb.conf.env)
8+
# - Push to dev (Dockerfile, support/, lpb.stack.env, lpb.conf.env)
9+
# - Push to main only from LPB version tags (0.*-lpb, 1.*-lpb)
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
15-
#
16-
# Actions: all latest major versions (Node.js 24 native)
17-
# actions/checkout@v6 · docker/build-push-action@v7
18-
# docker/setup-buildx-action@v4 · docker/setup-qemu-action@v4
19-
# docker/login-action@v4 · docker/metadata-action@v6
20-
# actions/upload-artifact@v7
13+
# Build tags:
14+
# - dev branch: :dev, :dev-cli, :dev-web, :{sha}-cli, :{sha}-web
15+
# - tag push: :{version}-cli, :{version}-web
16+
# - latest: only on manual dispatch or schedule
17+
# - weekly: :weekly-cli, :weekly-web
2118

2219
name: Build & Publish Devstack
2320

2421
on:
2522
push:
26-
branches: [main]
23+
branches: [dev]
2724
paths:
2825
- 'Dockerfile'
2926
- 'support/**'
3027
- 'lpb.stack.env'
3128
- 'lpb.conf.env'
3229
- '.github/workflows/*.yml'
3330
- 'scripts/**'
31+
push:
32+
tags:
33+
- '0.*-lpb'
34+
- '1.*-lpb'
35+
paths:
36+
- 'VERSION'
37+
- 'lpb.stack.env'
3438
pull_request:
35-
branches: [main]
39+
branches: [dev]
3640
paths:
3741
- 'Dockerfile'
3842
- 'support/**'
@@ -138,12 +142,12 @@ jobs:
138142
LPB_MAX_TOKENS_CONTEXT_RATIO=${{ steps.config.outputs.MAX_TOKENS }}
139143
no-cache: ${{ github.event.inputs.no_cache == 'true' }}
140144
tags: |
141-
${{ env.IMAGE_NAME }}:cli
142-
${{ env.IMAGE_NAME }}:main-cli
145+
${{ env.IMAGE_NAME }}:dev
146+
${{ env.IMAGE_NAME }}:dev-cli
143147
${{ env.IMAGE_NAME }}:${{ github.sha }}-cli
144148
${{ env.IMAGE_NAME }}:${{ steps.config.outputs.stack_version }}-cli
145-
${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && format('{0}:latest', env.IMAGE_NAME) || '' }}
146-
${{ github.event.inputs.publish_latest && format('{0}:latest', env.IMAGE_NAME) || '' }}
149+
${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && format('{0}:{1}-cli', env.IMAGE_NAME, github.ref_name) || '' }}
150+
${{ github.event.inputs.publish_latest && format('{0}:latest-cli', env.IMAGE_NAME) || '' }}
147151
${{ github.event_name == 'schedule' && format('{0}:weekly-cli', env.IMAGE_NAME) || '' }}
148152
provenance: false
149153
platforms: linux/amd64
@@ -213,10 +217,11 @@ jobs:
213217
LPB_MAX_TOKENS_CONTEXT_RATIO=${{ steps.config.outputs.MAX_TOKENS }}
214218
no-cache: ${{ github.event.inputs.no_cache == 'true' }}
215219
tags: |
216-
${{ env.IMAGE_NAME }}:web
217-
${{ env.IMAGE_NAME }}:main-web
220+
${{ env.IMAGE_NAME }}:dev
221+
${{ env.IMAGE_NAME }}:dev-web
218222
${{ env.IMAGE_NAME }}:${{ github.sha }}-web
219-
${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && format('{0}:latest-web', env.IMAGE_NAME) || '' }}
223+
${{ env.IMAGE_NAME }}:${{ steps.config.outputs.stack_version }}-web
224+
${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && format('{0}:{1}-web', env.IMAGE_NAME, github.ref_name) || '' }}
220225
${{ github.event.inputs.publish_latest && format('{0}:latest-web', env.IMAGE_NAME) || '' }}
221226
${{ github.event_name == 'schedule' && format('{0}:weekly-web', env.IMAGE_NAME) || '' }}
222227
provenance: false

0 commit comments

Comments
 (0)