diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4c7722c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,36 @@ +# Git +.git +.gitignore +.github + +# Build artifacts +dist/ +*.tar.gz +*.zip + +# Documentation +*.md +CLAUDE.md +LICENSE + +# IDE and editors +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Go test cache +*.test +*.out +coverage.txt + +# Dependencies (will be downloaded during build) +vendor/ + +# CI/CD +.github/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c0d7ef8..919c7b0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,10 +12,11 @@ permissions: jobs: build-all-platforms: runs-on: mac-mini-org-heart - + outputs: version: ${{ steps.buildvars.outputs.version }} commit: ${{ steps.buildvars.outputs.commit }} + build_time: ${{ steps.buildvars.outputs.time }} steps: - name: Checkout tag @@ -66,7 +67,7 @@ jobs: anytype-cli-*.tar.gz anytype-cli-*.zip - create-release: + create_release: needs: build-all-platforms runs-on: ubuntu-latest @@ -81,6 +82,7 @@ jobs: run: ls -la artifacts/ - name: Create Release & Upload Assets + id: create_release uses: softprops/action-gh-release@v2 with: name: ${{ github.ref_name }} @@ -88,4 +90,97 @@ jobs: generate_release_notes: true draft: false prerelease: ${{ contains(github.ref, '-rc') }} - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + token: ${{ secrets.GITHUB_TOKEN }} + + push-docker-image: + name: Build Docker image and push to registry + needs: + - build-all-platforms + - create_release + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + ref: ${{ github.ref }} + + - name: Set deploy-platforms + run: | + echo "DEPLOY_PLATFORMS=linux/amd64,linux/arm64" >> $GITHUB_ENV + + - name: Normalization of the release version + id: release-version + shell: bash + run: | + tag="${{ github.ref_name }}" + ver="${tag#v}" + echo "RELEASE_VERSION=${ver}" >> "$GITHUB_OUTPUT" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push to GitHub Container Registry + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + platforms: ${{ env.DEPLOY_PLATFORMS }} + push: true + build-args: | + VERSION=${{ needs.build-all-platforms.outputs.version }} + COMMIT=${{ needs.build-all-platforms.outputs.commit }} + BUILD_TIME=${{ needs.build-all-platforms.outputs.build_time }} + GIT_STATE=clean + tags: | + ghcr.io/${{ github.repository }}:latest + ghcr.io/${{ github.repository }}:${{ github.ref_name }} + ghcr.io/${{ github.repository }}:v${{ steps.release-version.outputs.RELEASE_VERSION }} + labels: | + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.description=Anytype CLI - Command-line interface for Anytype with embedded gRPC server + org.opencontainers.image.licenses=MIT + org.opencontainers.image.version=${{ steps.release-version.outputs.RELEASE_VERSION }} + org.opencontainers.image.revision=${{ github.sha }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push to Docker Hub + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + platforms: ${{ env.DEPLOY_PLATFORMS }} + push: true + build-args: | + VERSION=${{ needs.build-all-platforms.outputs.version }} + COMMIT=${{ needs.build-all-platforms.outputs.commit }} + BUILD_TIME=${{ needs.build-all-platforms.outputs.build_time }} + GIT_STATE=clean + tags: | + docker.io/${{ github.repository }}:latest + docker.io/${{ github.repository }}:${{ github.ref_name }} + docker.io/${{ github.repository }}:v${{ steps.release-version.outputs.RELEASE_VERSION }} + labels: | + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.description=Anytype CLI - Command-line interface for Anytype with embedded gRPC server + org.opencontainers.image.licenses=MIT + org.opencontainers.image.version=${{ steps.release-version.outputs.RELEASE_VERSION }} + org.opencontainers.image.revision=${{ github.sha }} + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9ad2950 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,70 @@ +# syntax=docker/dockerfile:1 + +# ============================================================================= +# Build stage +# ============================================================================= +FROM golang:1.24-alpine AS builder + +WORKDIR /app + +# Install build dependencies +# - build-base: gcc, musl-dev (required for CGO/tantivy linking) +# - curl: for downloading the tantivy library +# - make: to use Makefile build +RUN apk add --no-cache build-base curl make + +# Copy dependency files first for better layer caching +COPY go.mod go.sum ./ +RUN go mod download && go mod verify + +# Copy source code +COPY . . + +# Build arguments for version info (pass via --build-arg) +ARG VERSION=unknown +ARG COMMIT=unknown +ARG BUILD_TIME=unknown +ARG GIT_STATE=unknown +ARG TARGETARCH + +# Build a statically-linked binary via Makefile +RUN CGO_ENABLED=1 \ + GOOS=linux \ + GOARCH="${TARGETARCH}" \ + BUILD_TAGS="noheic" \ + EXTRA_LDFLAGS="-linkmode external -extldflags '-static'" \ + OUTPUT=/app/anytype \ + VERSION="${VERSION}" \ + COMMIT="${COMMIT}" \ + BUILD_TIME="${BUILD_TIME}" \ + GIT_STATE="${GIT_STATE}" \ + make build + +# ============================================================================= +# Production stage +# ============================================================================= +FROM alpine:3.23 AS production + +WORKDIR /app + +# Install ca-certificates for TLS and netcat for health checks +RUN apk add --no-cache ca-certificates netcat-openbsd + +# Copy binary from builder +COPY --from=builder /app/anytype /app/anytype + +# Note: Running as root to avoid volume permission issues in docker-compose + +# gRPC (31010), gRPC-Web (31011), API (31012) +EXPOSE 31010 31011 31012 + +# Persistent data volumes +VOLUME ["/root/.anytype", "/root/.config/anytype"] + +# Health check: verify gRPC port is accepting connections +HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ + CMD nc -z 127.0.0.1 31010 || exit 1 + +# Run the embedded server in foreground +ENTRYPOINT ["/app/anytype"] +CMD ["serve"]