chore: track hooks in .githooks/, update CI to dev, add commit-msg ho… #211
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ═══════════════════════════════════════════════════════════ | ||
| # LocalPibox Devstack — CI/CD Build & Publish | ||
| # ═══════════════════════════════════════════════════════════ | ||
| # Builds on GitHub (fast connection) → publishes to GHCR | ||
| # Extensions update at runtime via pi update --extensions | ||
| # | ||
| # Triggers: | ||
| # - Push to dev (Dockerfile, support/, lpb.stack.env, lpb.conf.env) | ||
| # - Push to main only from LPB version tags (0.*-lpb, 1.*-lpb) | ||
| # - Weekly cron (Monday 3am UTC) — keep image fresh | ||
| # - Manual dispatch | ||
| # | ||
| # Build tags: | ||
| # - dev branch: :dev, :dev-cli, :dev-web, :{sha}-cli, :{sha}-web | ||
| # - tag push: :{version}-cli, :{version}-web | ||
| # - latest: only on manual dispatch or schedule | ||
| # - weekly: :weekly-cli, :weekly-web | ||
| name: Build & Publish Devstack | ||
| on: | ||
| push: | ||
| branches: [dev] | ||
| paths: | ||
| - 'Dockerfile' | ||
| - 'support/**' | ||
| - 'lpb.stack.env' | ||
| - 'lpb.conf.env' | ||
| - '.github/workflows/*.yml' | ||
| - 'scripts/**' | ||
| push: | ||
| tags: | ||
| - '0.*-lpb' | ||
| - '1.*-lpb' | ||
| paths: | ||
| - 'VERSION' | ||
| - 'lpb.stack.env' | ||
| pull_request: | ||
| branches: [dev] | ||
| paths: | ||
| - 'Dockerfile' | ||
| - 'support/**' | ||
| - 'lpb.stack.env' | ||
| - 'lpb.conf.env' | ||
| - '.github/workflows/*.yml' | ||
| - 'scripts/**' | ||
| schedule: | ||
| - cron: '0 3 * * 1' | ||
| workflow_dispatch: | ||
| inputs: | ||
| publish_latest: | ||
| description: 'Publish as :latest tag' | ||
| type: boolean | ||
| default: true | ||
| no_cache: | ||
| description: 'Full rebuild with no cache (pick up fork-branch changes)' | ||
| type: boolean | ||
| default: false | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| env: | ||
| IMAGE_NAME: ghcr.io/localpibox/devstack | ||
| jobs: | ||
| test-lpb: | ||
| name: Run lpb.py unit tests | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
| - name: Run test suite | ||
| run: | | ||
| python3 scripts/test_lpb.py | ||
| python3 scripts/test_localpibox.py | ||
| build-cli: | ||
| name: Build & publish cli image | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 45 | ||
| needs: [test-lpb] | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
| - name: Set up QEMU | ||
| uses: docker/setup-qemu-action@v4 | ||
| - name: Set up buildx | ||
| uses: docker/setup-buildx-action@v4 | ||
| - name: Login to GHCR | ||
| uses: docker/login-action@v4 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Load stack config | ||
| id: config | ||
| run: | | ||
| set -a | ||
| source lpb.stack.env | ||
| set +a | ||
| echo "PI_FORK=$LPB_PI_FORK" >> "$GITHUB_OUTPUT" | ||
| echo "PI_REF=$LPB_PI_REF" >> "$GITHUB_OUTPUT" | ||
| echo "CONFIG_FORK=$LPB_CONFIG_FORK" >> "$GITHUB_OUTPUT" | ||
| echo "CONFIG_REF=$LPB_CONFIG_REF" >> "$GITHUB_OUTPUT" | ||
| echo "NODE_VERSION=$LPB_NODE_VERSION" >> "$GITHUB_OUTPUT" | ||
| echo "VSCODIUM_VERSION=$LPB_VSCODIUM_VERSION" >> "$GITHUB_OUTPUT" | ||
| echo "LPB_VERSION=$(cat VERSION 2>/dev/null || echo unknown)" >> "$GITHUB_OUTPUT" | ||
| MAX_TOKENS=$(grep -E '^LPB_MAX_TOKENS_CONTEXT_RATIO=' lpb.conf.env | cut -d= -f2- | tr -d '"' 2>/dev/null || true) | ||
| echo "MAX_TOKENS=${MAX_TOKENS:-0.06}" >> "$GITHUB_OUTPUT" | ||
| sha=$(git ls-remote "$LPB_PI_FORK" "refs/heads/$LPB_PI_REF" | awk '{print $1}') | ||
| # Read stack version from config repo | ||
| cfg_repo=$(echo "$LPB_CONFIG_FORK" | sed -E 's#https://github.com/([^/]+)/([^./]+)\.git#\1/\2#') | ||
| stack_ver=$(curl -sf "https://raw.githubusercontent.com/$cfg_repo/$LPB_CONFIG_REF/VERSION" 2>/dev/null || echo "unknown") | ||
| echo "sha=${sha:-unknown}" >> "$GITHUB_OUTPUT" | ||
| echo "stack_version=${stack_ver}" >> "$GITHUB_OUTPUT" | ||
| - name: Build & push cli | ||
| uses: docker/build-push-action@v7 | ||
| with: | ||
| context: . | ||
| file: ./Dockerfile | ||
| target: cli | ||
| push: ${{ github.event_name != 'pull_request' }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
| build-args: | | ||
| PI_FORK=${{ steps.config.outputs.PI_FORK }} | ||
| PI_REF=${{ steps.config.outputs.PI_REF }} | ||
| CONFIG_FORK=${{ steps.config.outputs.CONFIG_FORK }} | ||
| CONFIG_REF=${{ steps.config.outputs.CONFIG_REF }} | ||
| NODE_VERSION=${{ steps.config.outputs.NODE_VERSION }} | ||
| VSCODIUM_VERSION=${{ steps.config.outputs.VSCODIUM_VERSION }} | ||
| PI_HEAD_SHA=${{ steps.config.outputs.sha }} | ||
| LPB_VERSION=${{ steps.config.outputs.LPB_VERSION }} | ||
| LPB_MAX_TOKENS_CONTEXT_RATIO=${{ steps.config.outputs.MAX_TOKENS }} | ||
| no-cache: ${{ github.event.inputs.no_cache == 'true' }} | ||
| tags: | | ||
| ${{ env.IMAGE_NAME }}:dev | ||
| ${{ env.IMAGE_NAME }}:dev-cli | ||
| ${{ env.IMAGE_NAME }}:${{ github.sha }}-cli | ||
| ${{ env.IMAGE_NAME }}:${{ steps.config.outputs.stack_version }}-cli | ||
| ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && format('{0}:{1}-cli', env.IMAGE_NAME, github.ref_name) || '' }} | ||
| ${{ github.event.inputs.publish_latest && format('{0}:latest-cli', env.IMAGE_NAME) || '' }} | ||
| ${{ github.event_name == 'schedule' && format('{0}:weekly-cli', env.IMAGE_NAME) || '' }} | ||
| provenance: false | ||
| platforms: linux/amd64 | ||
| build-web: | ||
| name: Build & publish web image | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 45 | ||
| needs: [build-cli] | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
| - name: Set up QEMU | ||
| uses: docker/setup-qemu-action@v4 | ||
| - name: Set up buildx | ||
| uses: docker/setup-buildx-action@v4 | ||
| - name: Login to GHCR | ||
| uses: docker/login-action@v4 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Load stack config | ||
| id: config | ||
| run: | | ||
| set -a | ||
| source lpb.stack.env | ||
| set +a | ||
| echo "PI_FORK=$LPB_PI_FORK" >> "$GITHUB_OUTPUT" | ||
| echo "PI_REF=$LPB_PI_REF" >> "$GITHUB_OUTPUT" | ||
| echo "CONFIG_FORK=$LPB_CONFIG_FORK" >> "$GITHUB_OUTPUT" | ||
| echo "CONFIG_REF=$LPB_CONFIG_REF" >> "$GITHUB_OUTPUT" | ||
| echo "NODE_VERSION=$LPB_NODE_VERSION" >> "$GITHUB_OUTPUT" | ||
| echo "VSCODIUM_VERSION=$LPB_VSCODIUM_VERSION" >> "$GITHUB_OUTPUT" | ||
| echo "LPB_VERSION=$(cat VERSION 2>/dev/null || echo unknown)" >> "$GITHUB_OUTPUT" | ||
| MAX_TOKENS=$(grep -E '^LPB_MAX_TOKENS_CONTEXT_RATIO=' lpb.conf.env | cut -d= -f2- | tr -d '"' 2>/dev/null || true) | ||
| echo "MAX_TOKENS=${MAX_TOKENS:-0.06}" >> "$GITHUB_OUTPUT" | ||
| sha=$(git ls-remote "$LPB_PI_FORK" "refs/heads/$LPB_PI_REF" | awk '{print $1}') | ||
| # Read stack version from config repo | ||
| cfg_repo=$(echo "$LPB_CONFIG_FORK" | sed -E 's#https://github.com/([^/]+)/([^./]+)\.git#\1/\2#') | ||
| stack_ver=$(curl -sf "https://raw.githubusercontent.com/$cfg_repo/$LPB_CONFIG_REF/VERSION" 2>/dev/null || echo "unknown") | ||
| echo "sha=${sha:-unknown}" >> "$GITHUB_OUTPUT" | ||
| echo "stack_version=${stack_ver}" >> "$GITHUB_OUTPUT" | ||
| - name: Build & push web | ||
| uses: docker/build-push-action@v7 | ||
| with: | ||
| context: . | ||
| file: ./Dockerfile | ||
| target: web | ||
| push: ${{ github.event_name != 'pull_request' }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
| build-args: | | ||
| PI_FORK=${{ steps.config.outputs.PI_FORK }} | ||
| PI_REF=${{ steps.config.outputs.PI_REF }} | ||
| CONFIG_FORK=${{ steps.config.outputs.CONFIG_FORK }} | ||
| CONFIG_REF=${{ steps.config.outputs.CONFIG_REF }} | ||
| NODE_VERSION=${{ steps.config.outputs.NODE_VERSION }} | ||
| VSCODIUM_VERSION=${{ steps.config.outputs.VSCODIUM_VERSION }} | ||
| PI_HEAD_SHA=${{ steps.config.outputs.sha }} | ||
| LPB_VERSION=${{ steps.config.outputs.LPB_VERSION }} | ||
| LPB_MAX_TOKENS_CONTEXT_RATIO=${{ steps.config.outputs.MAX_TOKENS }} | ||
| no-cache: ${{ github.event.inputs.no_cache == 'true' }} | ||
| tags: | | ||
| ${{ env.IMAGE_NAME }}:dev | ||
| ${{ env.IMAGE_NAME }}:dev-web | ||
| ${{ env.IMAGE_NAME }}:${{ github.sha }}-web | ||
| ${{ env.IMAGE_NAME }}:${{ steps.config.outputs.stack_version }}-web | ||
| ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && format('{0}:{1}-web', env.IMAGE_NAME, github.ref_name) || '' }} | ||
| ${{ github.event.inputs.publish_latest && format('{0}:latest-web', env.IMAGE_NAME) || '' }} | ||
| ${{ github.event_name == 'schedule' && format('{0}:weekly-web', env.IMAGE_NAME) || '' }} | ||
| provenance: false | ||
| platforms: linux/amd64 | ||
| status: | ||
| name: Build status | ||
| needs: [build-cli, build-web] | ||
| if: always() | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - run: | | ||
| if [ "${{ needs.build-cli.result }}" != "success" ] || [ "${{ needs.build-web.result }}" != "success" ]; then | ||
| echo "Build failed!" | ||
| exit 1 | ||
| fi | ||
| echo "Build complete — both cli and web images pushed" | ||