name: Build Ti-Pote SD Image on: workflow_dispatch: push: tags: - 'v*' jobs: build-image: name: Build Raspberry Pi SD Image runs-on: ubuntu-latest timeout-minutes: 180 steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 22 - name: Install pnpm run: npm install -g pnpm@latest - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build robot-client run: pnpm --filter @ti-pote/robot-client build - name: Prepare pi-gen stage files run: | STAGE_FILES="tools/pi-gen-tipote/stage3/01-install-tipote/files" rm -rf "$STAGE_FILES" mkdir -p "$STAGE_FILES" cp -r apps/robot-client/dist "$STAGE_FILES/dist" cp apps/robot-client/package.json "$STAGE_FILES/package.json" cp apps/robot-client/deploy/tipote.service "$STAGE_FILES/tipote.service" cp apps/robot-client/deploy/journald-tipote.conf "$STAGE_FILES/journald-tipote.conf" - name: Register QEMU for ARM emulation run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - name: Build pi-gen Docker image run: docker build -t tipote-builder tools/pi-gen-tipote/ - name: Build SD image in Docker run: | # Clean up any leftover container from a previous failed build docker rm -f tipote-build 2>/dev/null || true # Run pi-gen build inside a privileged container # Use docker cp (not volume mounts) because of DooD path mapping docker run --privileged --name tipote-build tipote-builder # Extract the output image mkdir -p output docker cp tipote-build:/output/. output/ 2>/dev/null || true # Fallback: check pi-gen deploy dir directly if [ -z "$(ls output/*.img* 2>/dev/null)" ]; then docker cp tipote-build:/build/pi-gen/deploy/. output/ 2>/dev/null || true fi docker rm tipote-build echo "=== Build output ===" ls -lh output/ || echo "No output found" - name: Determine version id: version run: | if [[ "$GITHUB_REF" == refs/tags/v* ]]; then VERSION="${GITHUB_REF#refs/tags/v}" else VERSION="dev-$(echo $GITHUB_SHA | cut -c1-7)" fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" - name: Prepare final image run: | VERSION="${{ steps.version.outputs.version }}" IMG_FILE=$(find output -name "*.img.xz" -o -name "*.img" 2>/dev/null | head -1) if [ -z "$IMG_FILE" ]; then echo "ERROR: No image found in output/" ls -laR output/ || true exit 1 fi BASENAME="tipote-${VERSION}.img" if [[ "$IMG_FILE" == *.img.xz ]]; then mv "$IMG_FILE" "output/${BASENAME}.xz" else mv "$IMG_FILE" "output/$BASENAME" xz -T0 -6 "output/$BASENAME" fi ls -lh output/ echo "IMAGE_FILE=${BASENAME}.xz" >> "$GITHUB_ENV" - name: Upload to Gitea release if: startsWith(github.ref, 'refs/tags/v') run: | VERSION="${{ steps.version.outputs.version }}" GITEA_URL="${{ github.server_url }}" REPO="${{ github.repository }}" # Create release curl -sf -X POST "${GITEA_URL}/api/v1/repos/${REPO}/releases" \ -H "Authorization: token ${{ secrets.REGISTRY_PASSWORD }}" \ -H "Content-Type: application/json" \ -d "{\"tag_name\": \"v${VERSION}\", \"name\": \"Ti-Pote v${VERSION}\", \"body\": \"SD image for Raspberry Pi Zero 2W\"}" \ -o /tmp/release.json RELEASE_ID=$(jq -r '.id' /tmp/release.json) echo "Release ID: $RELEASE_ID" # Upload image as attachment curl -sf -X POST "${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${IMAGE_FILE}" \ -H "Authorization: token ${{ secrets.REGISTRY_PASSWORD }}" \ -F "attachment=@output/${IMAGE_FILE}" \ -o /tmp/asset.json echo "Download URL: $(jq -r '.browser_download_url' /tmp/asset.json)" - name: Summary (dev builds) if: ${{ !startsWith(github.ref, 'refs/tags/v') }} run: | echo "=== SD Image built successfully ===" echo "File: output/${IMAGE_FILE}" ls -lh output/ echo "" echo "To create a release, push a tag: git tag v0.1.0 && git push origin v0.1.0"