--- name: "Build & Release pipeline" on: pull_request: paths: - '.github/workflows/build-and-release.yml' - '.goreleaser.yml' - 'Dockerfile.multiarches' - 'Dockerfile.singlearch' - 'Makefile' - 'frontend/**' - 'integration-tests/**' - 'proto/**' - 'service/**' - 'var/windows/**' workflow_dispatch: push: tags: - '*' branches: - main - next - beta paths: - '.github/workflows/build-and-release.yml' - '.goreleaser.yml' - 'Dockerfile.multiarches' - 'Dockerfile.singlearch' - 'Makefile' - 'frontend/**' - 'integration-tests/**' - 'proto/**' - 'service/**' - 'var/windows/**' jobs: build: runs-on: ubuntu-latest outputs: new_release_published: ${{ steps.release.outputs.new_release_published }} new_release_git_tag: ${{ steps.release.outputs.new_release_git_tag }} windows_zip_artifact_id: ${{ steps.upload-windows-zip.outputs.artifact-id }} windows_msi_artifact_id: ${{ steps.upload-windows-msi.outputs.artifact-id }} steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up QEMU id: qemu uses: docker/setup-qemu-action@v4 with: image: tonistiigi/binfmt:latest platforms: arm64,arm - name: Setup node (npm cache) if: github.event_name != 'pull_request' uses: actions/setup-node@v6.4.0 with: node-version: '22' cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: Setup node if: github.event_name == 'pull_request' uses: actions/setup-node@v6.4.0 with: node-version: '22' - name: Setup Go uses: actions/setup-go@v6 with: go-version-file: 'service/go.mod' cache: true cache-dependency-path: 'service/go.mod' - name: Print go version run: go version - name: Login to Docker Hub if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_KEY }} - name: Login to ghcr if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.CONTAINER_TOKEN }} - name: get date run: | echo "DATE=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV" - name: make webui run: make -w webui-dist - name: unit tests run: make -w service-unittests - name: build service run: make -w service - name: integration tests run: cd integration-tests && make -w - name: Archive integration tests uses: actions/upload-artifact@v7 if: always() with: name: "OliveTin-integration-tests-${{ env.DATE }}-${{ github.sha }}" path: | integration-tests !integration-tests/node_modules - name: Install wixl and msitools if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false run: sudo apt-get update && sudo apt-get install -y wixl msitools - name: Install goreleaser if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false uses: goreleaser/goreleaser-action@v7 with: install-only: true - name: Set up Docker Buildx if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false uses: docker/setup-buildx-action@v4 - name: release id: release if: github.ref_type != 'tag' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) uses: cycjimmy/semantic-release-action@v5 with: extra_plugins: | @semantic-release/commit-analyzer @semantic-release/exec @semantic-release/git env: GITHUB_TOKEN: ${{ secrets.CONTAINER_TOKEN }} GH_TOKEN: ${{ secrets.CONTAINER_TOKEN }} MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }} MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }} MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }} MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} - name: Upload unsigned Windows zip for SignPath id: upload-windows-zip if: steps.release.outputs.new_release_published == 'true' uses: actions/upload-artifact@v7 with: # Upload as-is so SignPath receives OliveTin-windows-amd64.zip, not a wrapper zip. # With archive: false, the artifact name is the filename (`name` is ignored). path: dist/OliveTin-windows-amd64.zip archive: false if-no-files-found: error - name: Upload unsigned Windows MSI for SignPath id: upload-windows-msi if: steps.release.outputs.new_release_published == 'true' uses: actions/upload-artifact@v7 with: path: dist/OliveTin-windows-amd64.msi archive: false if-no-files-found: error - name: Archive binaries if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false uses: actions/upload-artifact@v7 with: name: "OliveTin-snapshot-${{ env.DATE }}-${{ github.sha }}" path: dist/OliveTin*.* sign-windows: name: Sign Windows artifacts (SignPath) needs: build if: needs.build.outputs.new_release_published == 'true' runs-on: ubuntu-latest permissions: actions: read contents: write steps: - name: Checkout uses: actions/checkout@v6 with: persist-credentials: false - name: Require SignPath configuration env: SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }} SIGNPATH_ORGANIZATION_ID: ${{ vars.SIGNPATH_ORGANIZATION_ID }} SIGNPATH_PROJECT_SLUG: ${{ vars.SIGNPATH_PROJECT_SLUG }} SIGNPATH_SIGNING_POLICY_SLUG: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }} run: | missing=0 for name in SIGNPATH_API_TOKEN SIGNPATH_ORGANIZATION_ID SIGNPATH_PROJECT_SLUG SIGNPATH_SIGNING_POLICY_SLUG; do if [[ -z "${!name}" ]]; then echo "Missing required SignPath setting: ${name}" >&2 missing=1 fi done if [[ "${missing}" -ne 0 ]]; then echo "SignPath secrets/vars are required to upload signed Windows assets. Configure them (see docs/modules/dev/pages/signing.adoc)." >&2 exit 1 fi - name: Sign Windows zip uses: signpath/github-action-submit-signing-request@v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }} project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }} signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }} artifact-configuration-slug: windows-zip github-artifact-id: ${{ needs.build.outputs.windows_zip_artifact_id }} wait-for-completion: true # Preserve the signed .zip/.msi files; default decompress would unpack the zip. skip-decompress: true output-artifact-directory: signed-windows-zip - name: Sign Windows MSI uses: signpath/github-action-submit-signing-request@v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }} project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }} signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }} artifact-configuration-slug: windows-msi github-artifact-id: ${{ needs.build.outputs.windows_msi_artifact_id }} wait-for-completion: true skip-decompress: true output-artifact-directory: signed-windows-msi - name: Upload signed Windows assets env: GH_TOKEN: ${{ secrets.CONTAINER_TOKEN }} GITHUB_TOKEN: ${{ secrets.CONTAINER_TOKEN }} run: | zip_path="$(find signed-windows-zip -type f -name 'OliveTin-windows-amd64.zip' | head -n 1)" msi_path="$(find signed-windows-msi -type f -name 'OliveTin-windows-amd64.msi' | head -n 1)" if [[ -z "${zip_path}" || -z "${msi_path}" ]]; then echo "Signed Windows artifacts not found after SignPath:" >&2 find signed-windows-zip signed-windows-msi -type f >&2 || true exit 1 fi ./var/windows/signpath-publish-signed.sh \ "${{ needs.build.outputs.new_release_git_tag }}" \ "${zip_path}" \ "${msi_path}"