name: Build Pipeline on: workflow_dispatch: push: branches: - main paths-ignore: - 'docs/**' - 'README.md' - 'CHANGELOG.md' - '.forgejo/ISSUE_TEMPLATE/**' - '**/*.md' concurrency: group: build-${{ gitea.workflow }}-${{ gitea.ref }} cancel-in-progress: true permissions: contents: read actions: read packages: write env: GO_VERSION: '1.25.0' ZIG_VERSION: '0.15.2' RSYNC_VERSION: '3.3.0' REGISTRY: ghcr.io IMAGE_NAME: fetchml-worker jobs: build-binaries: name: Build Binaries runs-on: self-hosted timeout-minutes: 30 strategy: matrix: build_config: - name: "native" tags: "native_libs" cgo_enabled: "1" build_native: "true" - name: "cgo-only" tags: "" cgo_enabled: "1" build_native: "false" - name: "no-cgo" tags: "" cgo_enabled: "0" build_native: "false" fail-fast: false steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 1 - name: Set up Go run: | REQUIRED_GO="1.25.0" if command -v go &> /dev/null && go version | grep -q "go${REQUIRED_GO}"; then echo "Go ${REQUIRED_GO} already installed - skipping download" else echo "Installing Go ${REQUIRED_GO}..." curl -sL "https://go.dev/dl/go${REQUIRED_GO}.linux-amd64.tar.gz" | sudo tar -C /usr/local -xzf - export PATH="/usr/local/go/bin:$PATH" echo "/usr/local/go/bin" >> $GITHUB_PATH echo "Go ${REQUIRED_GO} installed" fi go version - name: Set up Zig run: | ZIG_VERSION="${{ env.ZIG_VERSION }}" if command -v zig &> /dev/null && zig version | grep -q "${ZIG_VERSION}"; then echo "Zig ${ZIG_VERSION} already installed - skipping download" else echo "Installing Zig ${ZIG_VERSION}..." ZIG_DIR="/usr/local/zig-${ZIG_VERSION}" if [[ "$OSTYPE" == "linux-gnu"* ]]; then curl -fsSL --retry 3 "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" -o /tmp/zig.tar.xz sudo mkdir -p "${ZIG_DIR}" sudo tar -C "${ZIG_DIR}" --strip-components=1 -xJf /tmp/zig.tar.xz sudo ln -sf "${ZIG_DIR}/zig" /usr/local/bin/zig elif [[ "$OSTYPE" == "darwin"* ]]; then curl -fsSL --retry 3 "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-macos-${ZIG_VERSION}.tar.xz" -o /tmp/zig.tar.xz sudo mkdir -p "${ZIG_DIR}" sudo tar -C "${ZIG_DIR}" --strip-components=1 -xJf /tmp/zig.tar.xz sudo ln -sf "${ZIG_DIR}/zig" /usr/local/bin/zig fi rm -f /tmp/zig.tar.xz echo "Zig ${ZIG_VERSION} installed" fi zig version - name: Install build dependencies run: | sudo apt-get update sudo apt-get install -y podman build-essential autoconf automake libtool pkg-config musl-tools cmake zlib1g-dev - name: Build pinned rsync from official source run: | make -C cli build-rsync RSYNC_VERSION=${{ env.RSYNC_VERSION }} - name: Build SQLite for CLI run: | make -C cli build-sqlite - name: Build CLI binary run: | cd cli && make tiny - name: Build Native Libraries if: matrix.build_config.build_native == 'true' run: | echo "Building native C++ libraries..." make native-build 2>&1 || { echo "Native build failed!" exit 1 } echo "Native libraries built successfully" - name: Build Go binaries (${{ matrix.build_config.name }}) run: | echo "Building Go binaries with CGO_ENABLED=${{ matrix.build_config.cgo_enabled }}, tags=${{ matrix.build_config.tags }}" CGO_ENABLED=${{ matrix.build_config.cgo_enabled }} make build # Tag the binaries with the build config name mkdir -p "bin/${{ matrix.build_config.name }}" cp bin/* "bin/${{ matrix.build_config.name }}/" 2>/dev/null || true - name: Test binaries run: | ./bin/worker --help || true ./cli/zig-out/bin/ml --help || true ls -lh ./cli/zig-out/bin/ml - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: fetch_ml_binaries_${{ matrix.build_config.name }} path: | bin/ cli/zig-out/ retention-days: 30 build-docker: name: Build Docker Images runs-on: self-hosted needs: build-binaries timeout-minutes: 45 steps: - name: Checkout code uses: actions/checkout@v4 - name: Download build artifacts uses: actions/download-artifact@v4 with: name: fetch_ml_binaries_native path: bin/ - name: Set up Docker run: | # Check Docker is available docker --version || { echo "Docker not available, using Podman" sudo apt-get install -y podman } - name: Build Docker image run: | # Build the Docker image docker build -f build/docker/simple.Dockerfile -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }} . - name: Generate image digest run: | docker inspect --format='{{index .RepoDigests 0}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }} > image-digest.txt cat image-digest.txt - name: Tag images run: | # Tag with commit SHA docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # If this is a version tag, tag with version if [[ "${{ gitea.ref }}" == refs/tags/v* ]]; then VERSION=$(echo "${{ gitea.ref }}" | sed 's/refs\/tags\///') docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION} fi - name: Container image scan (trivy) run: | # Scan the built image for vulnerabilities trivy image --exit-code 1 --severity CRITICAL ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }} || { echo "CRITICAL vulnerabilities found in container image" exit 1 } - name: Save image digest artifact uses: actions/upload-artifact@v4 with: name: image-digest path: image-digest.txt retention-days: 30 # Note: In Forgejo, you may need to configure a local registry or use external push # This section is a placeholder for registry push - name: Push to registry (optional) run: | echo "Image built: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }}" echo "Note: Registry push requires proper authentication setup in Forgejo" # docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }} # docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest sign-hipaa-config: name: Sign HIPAA Config runs-on: self-hosted needs: build-binaries timeout-minutes: 10 steps: - name: Checkout code uses: actions/checkout@v4 - name: Install cosign (if available) run: | # Try to install cosign for signing if command -v cosign &> /dev/null; then echo "cosign already installed" else echo "Installing cosign..." curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 | sudo tee /usr/local/bin/cosign > /dev/null sudo chmod +x /usr/local/bin/cosign || { echo "cosign installation failed - signing will be skipped" } fi cosign version || echo "cosign not available" - name: Sign HIPAA config (placeholder) run: | echo "HIPAA config signing placeholder" echo "To enable signing, configure COSIGN_KEY secret" # Check if signing key is available if [ -n "${{ secrets.COSIGN_KEY }}" ]; then echo "Signing HIPAA config..." # cosign sign-blob \ # --key ${{ secrets.COSIGN_KEY }} \ # deployments/configs/worker/docker-hipaa.yaml \ # > deployments/configs/worker/docker-hipaa.yaml.sig echo "Signing would happen here with real cosign key" else echo "COSIGN_KEY not set - skipping HIPAA config signing" # Create a placeholder signature file for now echo "UNSIGNED_PLACEHOLDER" > deployments/configs/worker/docker-hipaa.yaml.sig fi - name: Upload HIPAA config signature uses: actions/upload-artifact@v4 with: name: hipaa-config-signature path: deployments/configs/worker/docker-hipaa.yaml.sig retention-days: 30 provenance: name: Generate SLSA Provenance runs-on: self-hosted needs: [build-binaries, build-docker] timeout-minutes: 15 steps: - name: Checkout code uses: actions/checkout@v4 - name: Download build artifacts uses: actions/download-artifact@v4 with: path: artifacts/ - name: Generate provenance run: | echo "Generating SLSA provenance..." # Create a basic SLSA provenance file cat > provenance.json << 'EOF' { "_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}", "digest": { "sha256": "$(cat artifacts/image-digest/image-digest.txt | cut -d':' -f2 || echo 'unknown')" } } ], "predicate": { "builder": { "id": "https://forgejo.example.com/jfraeysd/fetch_ml/.forgejo/workflows/build.yml" }, "buildType": "https://forgejo.example.com/buildType/docker", "invocation": { "configSource": { "uri": "https://forgejo.example.com/jfraeysd/fetch_ml", "digest": { "sha1": "${{ gitea.sha }}" }, "entryPoint": ".forgejo/workflows/build.yml" }, "parameters": {}, "environment": { "gitea_actor": "${{ gitea.actor }}", "gitea_ref": "${{ gitea.ref }}" } }, "metadata": { "buildInvocationId": "${{ gitea.run_id }}", "buildStartedOn": "$(date -Iseconds)", "completeness": { "parameters": false, "environment": false, "materials": false } }, "materials": [ { "uri": "https://forgejo.example.com/jfraeysd/fetch_ml", "digest": { "sha1": "${{ gitea.sha }}" } } ] } } EOF cat provenance.json - name: Upload provenance uses: actions/upload-artifact@v4 with: name: slsa-provenance path: provenance.json retention-days: 30