diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..70fc93d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,84 @@ +name: ๐Ÿณ Build + +on: + pull_request: + branches: + - main + workflow_call: + inputs: + push: + description: Push images by digest after building + type: boolean + default: false + +env: + DOCKER_REPO: platzio/backend + BASE_IMAGE: platzio/base:v8 + +permissions: + contents: read + +jobs: + image: + name: ๐Ÿณ Build (${{ matrix.platform }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + steps: + - name: ๐Ÿงท Platform pair + run: | + platform="${{ matrix.platform }}" + echo "PLATFORM_PAIR=${platform//\//-}" >> "${GITHUB_ENV}" + + - name: ๐Ÿ›Ž๏ธ Checkout + uses: actions/checkout@v6 + + - name: ๐Ÿ—ฝ Free disk space + uses: ShubhamTatvamasi/free-disk-space-action@master + + - name: ๐Ÿณ Login to DockerHub + if: inputs.push + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: ๐Ÿ› ๏ธ Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + # On push:true we push by digest and the caller assembles the multi-arch + # manifest from the uploaded digests. On PR (push:false) we discard the + # output (type=cacheonly) โ€” the build itself still validates the + # Dockerfile and warms the per-arch GHA cache. + - name: ๐Ÿณ Build + id: build + uses: docker/build-push-action@v7 + with: + context: . + build-args: BASE_IMAGE=${{ env.BASE_IMAGE }} + platforms: ${{ matrix.platform }} + outputs: ${{ inputs.push && format('type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.DOCKER_REPO) || 'type=cacheonly' }} + cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }} + cache-to: type=gha,mode=max,scope=build-${{ env.PLATFORM_PAIR }} + + - name: ๐Ÿ“ค Export digest + if: inputs.push + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: ๐Ÿ“ฆ Upload digest + if: inputs.push + uses: actions/upload-artifact@v7 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ca3778..ed91f7f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,6 @@ name: โŽˆ Release Version on: - pull_request: - branches: - - main push: tags: - v** @@ -15,141 +12,22 @@ permissions: contents: read jobs: - fmt: - name: ๐ŸŽจ Check formatting - runs-on: ubuntu-latest - steps: - - name: ๐Ÿ›Ž๏ธ Checkout - uses: actions/checkout@v5 - - - name: ๐Ÿงฐ Install rustfmt - run: rustup component add rustfmt - - - name: ๐ŸŽจ Cargo fmt - run: cargo fmt --all -- --check - test: - name: ๐Ÿงช Run Tests - runs-on: ubuntu-latest - steps: - - name: ๐Ÿ›Ž๏ธ Checkout - uses: actions/checkout@v5 - - - name: ๐Ÿ“ฆ Cargo version - id: cargo - run: | - echo "rustc=$(rustc --version)" >> "${GITHUB_OUTPUT}" - - - name: ๐Ÿชฃ Cache cargo registry - uses: actions/cache@v4 - with: - key: ${{ steps.cargo.outputs.rustc }}-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ steps.cargo.outputs.rustc }} - path: | - ~/.cargo/registry - ~/.cargo/git - target - - - name: ๐Ÿ“Ž Cargo clippy - run: | - cargo clippy --release -- -D warnings - - - name: ๐Ÿ”จ Cargo test - run: | - cargo test --release - - # One image build per architecture, on a native runner. We push by digest; - # a final `merge` job stitches the two digests into a multi-arch manifest list. - # Native arm64 runners avoid QEMU emulation, which dominated build time. - # - # This job does NOT depend on `test`: the test job compiles for the host gnu - # target while these build for musl, so they share no artifacts and gating - # would only serialize clippy+test ahead of the build for no reuse. Tests run - # concurrently instead; the `merge` job below re-gates on `test` so a tagged - # multi-arch image is never published when linting or tests fail. - image: - name: ๐Ÿณ Build (${{ matrix.platform }}) - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - include: - - platform: linux/amd64 - runner: ubuntu-latest - - platform: linux/arm64 - runner: ubuntu-24.04-arm - steps: - - name: ๐Ÿงท Platform pair - run: | - platform="${{ matrix.platform }}" - echo "PLATFORM_PAIR=${platform//\//-}" >> "${GITHUB_ENV}" - - - name: ๐Ÿ›Ž๏ธ Checkout - uses: actions/checkout@v5 - - - name: ๐Ÿ—ฝ Free disk space - uses: ShubhamTatvamasi/free-disk-space-action@master + name: ๐Ÿงช Test + uses: ./.github/workflows/test.yml - - name: ๐Ÿณ Login to DockerHub - if: github.event_name == 'push' - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: ๐Ÿ› ๏ธ Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # PR validation: build the image but don't push. Cache scoped per arch so - # the two matrix entries don't clobber each other's GHA cache. - - name: ๐Ÿณ Build (PR validation) - if: github.event_name != 'push' - uses: docker/build-push-action@v6 - with: - context: . - build-args: BASE_IMAGE=platzio/base:v8 - platforms: ${{ matrix.platform }} - push: false - cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }} - cache-to: type=gha,mode=max,scope=build-${{ env.PLATFORM_PAIR }} - - # Tag push: build and push by digest. The merge job below assembles the - # final tagged manifest. - - name: ๐Ÿณ Build & push by digest - if: github.event_name == 'push' - id: build - uses: docker/build-push-action@v6 - with: - context: . - build-args: BASE_IMAGE=platzio/base:v8 - platforms: ${{ matrix.platform }} - outputs: type=image,name=${{ env.DOCKER_REPO }},push-by-digest=true,name-canonical=true,push=true - cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }} - cache-to: type=gha,mode=max,scope=build-${{ env.PLATFORM_PAIR }} - - - name: ๐Ÿ“ค Export digest - if: github.event_name == 'push' - run: | - mkdir -p /tmp/digests - digest="${{ steps.build.outputs.digest }}" - touch "/tmp/digests/${digest#sha256:}" - - - name: ๐Ÿ“ฆ Upload digest - if: github.event_name == 'push' - uses: actions/upload-artifact@v4 - with: - name: digests-${{ env.PLATFORM_PAIR }} - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 + build: + name: ๐Ÿณ Build + uses: ./.github/workflows/build.yml + with: + push: true + secrets: inherit merge: name: ๐Ÿงฌ Assemble manifest list - if: github.event_name == 'push' needs: - - image - test + - build runs-on: ubuntu-latest outputs: tag: ${{ steps.tag.outputs.tag }} @@ -160,20 +38,20 @@ jobs: echo "tag=${GITHUB_REF#refs/tags/}" >> "${GITHUB_OUTPUT}" - name: ๐Ÿ“ฅ Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digests-* merge-multiple: true - name: ๐Ÿณ Login to DockerHub - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: ๐Ÿ› ๏ธ Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: ๐Ÿงฌ Create manifest list and push working-directory: /tmp/digests @@ -188,7 +66,6 @@ jobs: release: name: ๐Ÿš€ Create Release - if: github.event_name == 'push' runs-on: ubuntu-latest needs: - merge @@ -203,29 +80,9 @@ jobs: /root/platz-api openapi schema \ > openapi.yaml - - name: โœจ Create release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - draft: true - tag_name: ${{ needs.merge.outputs.tag }} - release_name: ${{ needs.merge.outputs.tag }} - - - name: ๐Ÿ“ฆ Upload OpenAPI schema - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: openapi.yaml - asset_name: openapi.yaml - asset_content_type: application/vnd.oai.openapi - - name: ๐Ÿš€ Publish release - uses: StuYarrow/publish-release@v1.1.2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v3 with: - id: ${{ steps.create_release.outputs.id }} + tag_name: ${{ needs.merge.outputs.tag }} + name: ${{ needs.merge.outputs.tag }} + files: openapi.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e2b6163 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,55 @@ +name: ๐Ÿงช Test + +on: + pull_request: + branches: + - main + workflow_call: + +permissions: + contents: read + +jobs: + fmt: + name: ๐ŸŽจ Check formatting + runs-on: ubuntu-latest + steps: + - name: ๐Ÿ›Ž๏ธ Checkout + uses: actions/checkout@v6 + + - name: ๐Ÿงฐ Install rustfmt + run: rustup component add rustfmt + + - name: ๐ŸŽจ Cargo fmt + run: cargo fmt --all -- --check + + test: + name: ๐Ÿงช Run Tests + runs-on: ubuntu-latest + steps: + - name: ๐Ÿ›Ž๏ธ Checkout + uses: actions/checkout@v6 + + - name: ๐Ÿ“ฆ Cargo version + id: cargo + run: | + echo "rustc=$(rustc --version)" >> "${GITHUB_OUTPUT}" + + - name: ๐Ÿชฃ Cache cargo registry + uses: actions/cache@v5 + with: + key: ${{ steps.cargo.outputs.rustc }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ steps.cargo.outputs.rustc }} + path: | + ~/.cargo/registry + ~/.cargo/git + target + + - name: ๐Ÿ“Ž Cargo clippy + run: | + cargo clippy --release -- -D warnings + + - name: ๐Ÿ”จ Cargo test + run: | + cargo test --release