diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index ca4edc2ab..321e35ccd 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -9,67 +9,78 @@ permissions: contents: read jobs: - generate-version: - name: Generate Version + create-tag: + name: Create Git Tag runs-on: ubuntu-latest + permissions: + contents: write outputs: version: ${{ steps.version.outputs.version }} tag: ${{ steps.version.outputs.tag }} + changelog: ${{ steps.version.outputs.changelog }} steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - - name: Generate version + - name: Generate and push tag 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}" + TAG="v0.0.0-nightly.${DATE}.${SHA}" else - VERSION="${BASE_VERSION}-nightly-${DATE}-${SHA}" + TAG="${BASE_VERSION}-nightly.${DATE}.${SHA}" fi - TAG="nightly-${DATE}-${SHA}" + VERSION=$TAG + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then + echo "Tag $TAG already exists, reusing existing tag" + else + git tag -a "$TAG" -m "Nightly build $VERSION" + fi + git push origin "$TAG" + + COMPARE_URL="https://github.com/${{ github.repository }}/commits/${TAG}" + if [ -n "$BASE_VERSION" ] && [ "$BASE_VERSION" != "v0.0.0" ]; then + COMPARE_URL="https://github.com/${{ github.repository }}/compare/${BASE_VERSION}...${TAG}" + fi + echo "changelog=**Full Changelog**: $COMPARE_URL" >> "$GITHUB_OUTPUT" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - build: - name: Build + release: + name: GoReleaser Release + needs: create-tag runs-on: ubuntu-latest - needs: generate-version + permissions: + contents: write + packages: write steps: - - name: Checkout + - name: Checkout tag uses: actions/checkout@v6 + with: + fetch-depth: 0 + ref: ${{ needs.create-tag.outputs.tag }} - - name: Setup Go + - name: Setup Go from go.mod + id: 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 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - name: picoclaw-binaries - path: build + node-version: 22 - 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: Setup pnpm + run: corepack enable && corepack prepare pnpm@latest --activate - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -77,80 +88,79 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Log in to GHCR + - name: Login to GitHub Container Registry 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 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 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 + distribution: goreleaser + version: ~> v2 + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} + DOCKERHUB_IMAGE_NAME: ${{ vars.DOCKERHUB_REPOSITORY }} + GOVERSION: ${{ steps.setup-go.outputs.go-version }} + NIGHTLY_BUILD: "true" + MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }} + MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }} + MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} + MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} + MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }} - - 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 + update-rolling: + name: Update Rolling Nightly + needs: [create-tag, release] runs-on: ubuntu-latest - needs: [generate-version, build, build-docker] permissions: contents: write + packages: 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 + - name: Update nightly release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ needs.create-tag.outputs.tag }} + TITLE: ${{ needs.create-tag.outputs.version }} 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.' - + CHANGELOG='${{ needs.create-tag.outputs.changelog }}' + NOTES=$(cat </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 + echo "Downloading assets from GitHub release for $TAG..." + gh release download "$TAG" --dir build else - echo "Creating new release $TAG..." - gh release create "$TAG" \ - --title "$TITLE" \ - --notes "$NOTES" \ - --target "${{ github.sha }}" \ - --prerelease \ - build/* + echo "GitHub release for $TAG not found; falling back to local dist/ artifacts..." + if [ -d "dist" ]; then + cp -R dist/* build/ + else + echo "Error: no GitHub release for $TAG and no local dist/ directory found." >&2 + exit 1 + fi fi - - echo "Updating rolling 'nightly' release..." - gh release delete nightly --cleanup-tag -y >/dev/null 2>&1 || true - sleep 2 + + # Delete existing nightly release and tag to avoid conflicts + echo "Deleting existing nightly release and tag..." + gh release delete nightly --cleanup-tag -y || true + git push origin :refs/tags/nightly || true + gh release create nightly \ --title "Nightly Build" \ --notes "$NOTES" \ @@ -159,9 +169,36 @@ jobs: 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 + gh release list --limit 100 --json tagName -q '.[].tagName | select(contains("-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 + + echo "Cleaning up old 'vX.X.X-nightly...' Docker images on GHCR..." + OWNER="${{ github.repository_owner }}" + PACKAGE_NAME="${{ github.event.repository.name }}" + + # Check if owner is an organization or user + ORG_TEST=$(gh api -H "Accept: application/vnd.github+json" /orgs/$OWNER 2>/dev/null || true) + if echo "$ORG_TEST" | grep -q '"login"'; then + ACCOUNT_TYPE="orgs" + else + ACCOUNT_TYPE="users" + fi + + PACKAGE_URL="/${ACCOUNT_TYPE}/${OWNER}/packages/container/${PACKAGE_NAME}/versions" + OLD_NIGHTLY_VERSIONS=$(gh api --paginate -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "$PACKAGE_URL" \ + --jq ". | map(select(any(.metadata.container.tags[]; contains(\"-nightly.\") and (. != \"nightly\") and (. != \"$TAG\")))) | .[].id" 2>/dev/null || true) + + for version_id in $OLD_NIGHTLY_VERSIONS; do + if [ -n "$version_id" ]; then + echo "Deleting Docker image version ID: $version_id" + gh api -X DELETE -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/${ACCOUNT_TYPE}/${OWNER}/packages/container/${PACKAGE_NAME}/versions/$version_id" || true + fi + done diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 70ea67323..654ad6ae6 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -116,10 +116,10 @@ dockers_v2: - picoclaw images: - "ghcr.io/{{ .Env.GITHUB_REPOSITORY_OWNER }}/picoclaw" - - "docker.io/{{ .Env.DOCKERHUB_IMAGE_NAME }}" + - '{{ if not (isEnvSet "NIGHTLY_BUILD") }}docker.io/{{ .Env.DOCKERHUB_IMAGE_NAME }}{{ end }}' tags: - "{{ .Tag }}" - - "latest" + - '{{ if isEnvSet "NIGHTLY_BUILD" }}nightly{{ else }}latest{{ end }}' platforms: - linux/amd64 - linux/arm64 @@ -159,7 +159,7 @@ archives: nfpms: - id: picoclaw - builds: + ids: - picoclaw - picoclaw-launcher - picoclaw-launcher-tui