name: Nightly Build on: schedule: - cron: '0 0 * * *' workflow_dispatch: permissions: contents: read jobs: generate-version: name: Generate Version runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} tag: ${{ steps.version.outputs.tag }} steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Generate version id: version run: | DATE=$(date -u +%Y%m%d) SHA=$(git rev-parse --short=8 HEAD) BASE_VERSION=$(git describe --tags --match "v*" --exclude "*nightly*" --abbrev=0 2>/dev/null || true) if [ -z "$BASE_VERSION" ] || [ "$BASE_VERSION" = "v0.0.0" ]; then VERSION="nightly-${DATE}-${SHA}" else VERSION="${BASE_VERSION}-nightly-${DATE}-${SHA}" fi TAG="nightly-${DATE}-${SHA}" echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" build: name: Build runs-on: ubuntu-latest needs: generate-version steps: - name: Checkout uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Build env: VERSION: ${{ needs.generate-version.outputs.version }} run: make build-all VERSION="$VERSION" - name: Upload artifacts uses: actions/upload-artifact@v6 with: name: picoclaw-binaries path: build build-docker: name: Build Docker runs-on: ubuntu-latest needs: generate-version permissions: contents: read packages: write steps: - name: Checkout uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract Docker metadata id: meta uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository }} tags: | type=raw,value=${{ needs.generate-version.outputs.tag }} type=raw,value=${{ needs.generate-version.outputs.version }} type=raw,value=nightly - name: Build and push uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max platforms: linux/amd64,linux/arm64,linux/riscv64 provenance: false release: name: Release runs-on: ubuntu-latest needs: [generate-version, build, build-docker] permissions: contents: write steps: - name: Checkout uses: actions/checkout@v6 - name: Download artifacts uses: actions/download-artifact@v6 with: name: picoclaw-binaries path: ./build - name: Create release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="${{ needs.generate-version.outputs.tag }}" TITLE="${{ needs.generate-version.outputs.version }}" NOTES=$'Nightly build for **${{ needs.generate-version.outputs.version }}**\n\nThis is an automated build and may be unstable. Use with caution.' if gh release view "$TAG" >/dev/null 2>&1; then echo "Release $TAG already exists, updating metadata and assets..." gh release edit "$TAG" \ --title "$TITLE" \ --notes "$NOTES" \ --prerelease gh release upload "$TAG" build/* --clobber else echo "Creating new release $TAG..." gh release create "$TAG" \ --title "$TITLE" \ --notes "$NOTES" \ --target "${{ github.sha }}" \ --prerelease \ build/* fi echo "Updating rolling 'nightly' release..." gh release delete nightly --cleanup-tag -y >/dev/null 2>&1 || true sleep 2 gh release create nightly \ --title "Nightly Build" \ --notes "$NOTES" \ --target "${{ github.sha }}" \ --prerelease \ build/* echo "Cleaning up old nightly releases (keeping only the most recent)..." gh release list --limit 100 --json tagName -q '.[].tagName | select(startswith("nightly-"))' | tail -n +2 | while read -r old_tag; do if [ -n "$old_tag" ] && [ "$old_tag" != "$TAG" ]; then echo "Deleting old nightly release: $old_tag" gh release delete "$old_tag" --cleanup-tag -y || true fi done