name: Test Build on: push: branches: [test-build] workflow_dispatch: inputs: build_arm: description: "Also build arm64 (default builds amd64 only)" type: boolean default: false # BUILD_ARM controls whether arm64 is built alongside amd64. # defaults to false so a normal push to test-build only builds amd64. # trigger the workflow manually and tick "build_arm" to also build arm64. env: BUILD_ARM: ${{ github.event.inputs.build_arm || 'false' }} jobs: test-build: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout code uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 1 - name: Set up QEMU uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 with: platforms: linux/amd64,linux/arm64 - name: Set up Docker Buildx uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to GitHub Container Registry uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract version info id: get_version run: | echo "VERSION=test-$(date +%Y%m%d-%H%M%S)" >> $GITHUB_OUTPUT echo "HASH=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - name: Build frontend files working-directory: frontend run: | sudo docker run --rm \ -v "$(pwd)":/app \ -w /app \ node@sha256:968df39aedcea65eeb078fb336ed7191baf48f972b4479711397108be0966920 `# node:22-alpine` \ sh -c "npm ci && npm run build-production" - name: Move frontend build to backend run: | rm -rf backend/frontend/build mkdir -p backend/frontend/build cp -r frontend/build/* backend/frontend/build/ - name: Cache Go build cache uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: /tmp/go-build-cache key: go-build-${{ runner.os }}-${{ hashFiles('backend/go.sum') }}-${{ github.run_id }} restore-keys: | go-build-${{ runner.os }}-${{ hashFiles('backend/go.sum') }}- go-build-${{ runner.os }}- - name: Build binaries for multiple architectures run: | mkdir -p build/amd64 build/arm64 /tmp/go-build-cache # build amd64 binary — static musl so no glibc floor on target systems sudo docker run --rm \ -v "$(pwd)":/app \ -v /tmp/go-build-cache:/root/.cache/go-build \ -w /app/backend \ golang@sha256:c4ea15b4a7912716eb362a022e2b12317762eca387423760bc59c0f9ae69423c `# golang:1.25.10-alpine linux/amd64` \ sh -c "apk add --no-cache gcc musl-dev && go build -trimpath \ -ldflags='-X github.com/phishingclub/phishingclub/version.hash=ph${{ steps.get_version.outputs.HASH }} -X github.com/phishingclub/phishingclub/version.version=${{ steps.get_version.outputs.VERSION }} -linkmode=external -extldflags=-static' \ -tags production -o ../build/amd64/phishingclub main.go" # build arm64 binary — run natively under QEMU (already set up above) # only when opted in via the BUILD_ARM variable if [ "${BUILD_ARM}" = "true" ]; then sudo docker run --rm \ --platform linux/arm64 \ -v "$(pwd)":/app \ -v /tmp/go-build-cache:/root/.cache/go-build \ -w /app/backend \ golang@sha256:5331adf7f8a0803631d9dc28843e288874789c14b97a3d0b54ed13e59f9e0589 `# golang:1.25.10-alpine linux/arm64` \ sh -c "apk add --no-cache gcc musl-dev && go build -trimpath \ -ldflags='-X github.com/phishingclub/phishingclub/version.hash=ph${{ steps.get_version.outputs.HASH }} -X github.com/phishingclub/phishingclub/version.version=${{ steps.get_version.outputs.VERSION }} -linkmode=external -extldflags=-static' \ -tags production -o ../build/arm64/phishingclub main.go" else echo "BUILD_ARM not enabled - skipping arm64 build" fi - name: Fix build directory permissions run: | sudo chown -R $USER:$USER build/ chmod 755 build/ ls -la build/ - name: Test binary signing (if keys available) run: | if [ -n "${{ secrets.SIGNKEY_1 }}" ]; then echo "Testing binary signing..." # create directory for keys mkdir -p /tmp/keys chmod 700 /tmp/keys # save private key from github secrets echo "${{ secrets.SIGNKEY_1 }}" > /tmp/keys/private1.pem chmod 600 /tmp/keys/private1.pem # sign amd64 binary with primary key openssl pkeyutl -sign -inkey /tmp/keys/private1.pem \ -rawin -in build/amd64/phishingclub \ -out build/amd64/phishingclub.sig # sign arm64 binary with primary key (only when built) if [ -f build/arm64/phishingclub ]; then openssl pkeyutl -sign -inkey /tmp/keys/private1.pem \ -rawin -in build/arm64/phishingclub \ -out build/arm64/phishingclub.sig fi # clean up keys rm -rf /tmp/keys echo "✅ Binary signing test successful" else echo "⚠️ SIGNKEY_1 not available - skipping signing test" fi - name: Test package creation run: | mkdir -p packages # package amd64 binary with signature if [ -f build/amd64/phishingclub.sig ]; then tar -czf packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_amd64.tar.gz \ -C build/amd64 \ phishingclub \ phishingclub.sig echo "✅ AMD64 package created with signature" else tar -czf packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_amd64.tar.gz \ -C build/amd64 \ phishingclub echo "✅ AMD64 package created without signature" fi # test packaging for arm64 (only when built) if [ -f build/arm64/phishingclub ]; then if [ -f build/arm64/phishingclub.sig ]; then tar -czf packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_arm64.tar.gz \ -C build/arm64 \ phishingclub \ phishingclub.sig echo "✅ ARM64 package created with signature" else tar -czf packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_arm64.tar.gz \ -C build/arm64 \ phishingclub echo "✅ ARM64 package created without signature" fi else echo "BUILD_ARM not enabled - skipping arm64 package" fi # - name: Build and push test multi-arch Docker image # uses: docker/build-push-action@v5 # with: # context: . # file: ./Dockerfile.release # push: true # platforms: linux/amd64,linux/arm64 # tags: | # ghcr.io/${{ github.repository }}:test-latest # labels: | # org.opencontainers.image.title=PhishingClub-Test ${{ steps.get_version.outputs.VERSION }} # org.opencontainers.image.description=PhishingClub test build image (linux/amd64, linux/arm64). Not for production deployment. # org.opencontainers.image.url=${{ github.server_url }}/${{ github.repository }} # org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} # org.opencontainers.image.version=${{ steps.get_version.outputs.VERSION }} # org.opencontainers.image.created=${{ github.event.head_commit.timestamp }} # org.opencontainers.image.revision=${{ github.sha }} - name: Verify build artifacts run: | echo "=== Build Summary ===" echo "" echo "AMD64 Binary size: $(du -h build/amd64/phishingclub | cut -f1)" echo "AMD64 Binary info:" file build/amd64/phishingclub if [ -f build/amd64/phishingclub.sig ]; then echo "AMD64 Signature size: $(du -h build/amd64/phishingclub.sig | cut -f1)" fi echo "" echo "AMD64 Package size: $(du -h packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_amd64.tar.gz | cut -f1)" echo "AMD64 Package contents:" tar -tzf packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_amd64.tar.gz if [ -f build/arm64/phishingclub ]; then echo "" echo "ARM64 Binary size: $(du -h build/arm64/phishingclub | cut -f1)" echo "ARM64 Binary info:" file build/arm64/phishingclub if [ -f build/arm64/phishingclub.sig ]; then echo "ARM64 Signature size: $(du -h build/arm64/phishingclub.sig | cut -f1)" fi echo "" echo "ARM64 Package size: $(du -h packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_arm64.tar.gz | cut -f1)" echo "ARM64 Package contents:" tar -tzf packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_arm64.tar.gz else echo "" echo "ARM64 not built (BUILD_ARM not enabled)" fi - name: Upload build artifacts (for review) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: phishingclub-test-build-${{ steps.get_version.outputs.HASH }} # arm64 paths are only present when BUILD_ARM is enabled if-no-files-found: ignore path: | build/amd64/phishingclub build/amd64/phishingclub.sig build/arm64/phishingclub build/arm64/phishingclub.sig packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_amd64.tar.gz packages/phishingclub_${{ steps.get_version.outputs.VERSION }}_linux_arm64.tar.gz retention-days: 2