Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/actions/discover-targets/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: "Discover Targets"
description: "Find valid GOOS targets for a distribution and FIPS configuration"
inputs:
distribution:
description: "The distribution name"
required: true
fips:
description: "Whether this is a FIPS build"
required: true
outputs:
goos:
description: "JSON array of valid GOOS values"
value: ${{ steps.find.outputs.goos }}
runs:
using: "composite"
steps:
- name: Find Valid Build Targets
id: find
shell: bash
run: |
# Extract GOOS values from goreleaser file and extracts to JSON array for use in matrix.
fips_suffix=$([[ "${{ inputs.fips }}" == "true" ]] && echo "-fips" || echo "")
goreleaser_file="distributions/${{ inputs.distribution }}/.goreleaser${fips_suffix}.yaml"

goos=$(yq -r '.builds[].goos[]' "$goreleaser_file" | sort -u | jq -R -s -c 'split("\n")[:-1]')

echo "Found GOOS targets: $goos"
echo "goos=$goos" >> $GITHUB_OUTPUT
146 changes: 102 additions & 44 deletions .github/workflows/ci-base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ on:
outputs:
artifact_name:
description: "Name of the uploaded artifact"
value: ${{ jobs.build.outputs.artifact_name }}
value: ${{ jobs.merge.outputs.artifact_name }}
docker_manifest_sha_name:
description: "Name of the docker manifest SHA file"
value: ${{ jobs.build.outputs.docker_manifest_sha_name }}
value: ${{ jobs.merge.outputs.docker_manifest_sha_name }}

permissions:
contents: read
Expand All @@ -52,11 +52,29 @@ env:
registry: "${{ secrets.aws_account_id }}.dkr.ecr.us-east-1.amazonaws.com"

jobs:
build:
setup:
runs-on: ubuntu-latest
outputs:
artifact_name: ${{ steps.set-outputs.outputs.artifact_name }}
docker_manifest_sha_name: ${{ steps.set-outputs.outputs.docker_manifest_sha_name }}
goos: ${{ steps.targets.outputs.goos }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Discover build targets
id: targets
uses: ./.github/actions/discover-targets
with:
distribution: ${{ inputs.distribution }}
fips: ${{ inputs.fips }}

build:
name: Build (${{ matrix.goos }})
needs: setup
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
goos: ${{ fromJson(needs.setup.outputs.goos) }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Expand All @@ -72,9 +90,6 @@ jobs:
cache: ${{ env.caching_enabled }}
check-latest: true

- name: Tidy go.mod files
run: go mod tidy

- name: Ensure goreleaser up to date
run: make check

Expand All @@ -87,6 +102,9 @@ jobs:
version: "2.15.4"
args: license-verify

- name: Tidy go.mod files
run: go mod tidy

- name: Generate sources cache key
id: sources-cache-key
if: ${{ env.caching_enabled }}
Expand All @@ -97,7 +115,7 @@ jobs:
'Makefile',
'fips/fips.go'
) }}"
echo "sources_key=sources-${{ inputs.distribution }}-${SOURCES_HASH}" >> $GITHUB_OUTPUT
echo "sources_key=sources-${{ inputs.distribution }}-${{ matrix.goos }}-${SOURCES_HASH}" >> $GITHUB_OUTPUT

- name: Cache sources
id: cache-sources
Expand Down Expand Up @@ -149,31 +167,6 @@ jobs:
libc6-dev-arm64-cross \
libc6-dev-amd64-cross

- name: Import GPG key
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
id: import_gpg
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7
with:
gpg_private_key: ${{ secrets.gpg_private_key }}
passphrase: ${{ secrets.gpg_passphrase }}

- name: Write GPG to path in memory for signing rpm/deb
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
id: write_gpg_to_path
run: |
GPG_KEY_PATH="$(mktemp /dev/shm/gpg.XXXXXX)"
echo "$GPG_PRIVATE_KEY" | base64 -d >> "$GPG_KEY_PATH"
echo "gpg_key_path=$GPG_KEY_PATH" >> $GITHUB_OUTPUT
env:
GPG_PRIVATE_KEY: ${{ secrets.gpg_private_key }}

- name: Configure AWS Credentials
if: ${{ inputs.publish }}
uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6
with:
role-to-assume: ${{ secrets.aws_oidc_role_arn }}
aws-region: us-east-1

- name: Build Args
run: |
# Determine base skip flags and snapshot mode based on publish input
Expand All @@ -191,7 +184,7 @@ jobs:

GORELEASER_CONFIG="${{ format('.goreleaser{0}.yaml', inputs.fips && '-fips' || '') }}"

echo "goreleaser_args=$SNAPSHOT_FLAG --clean $BASE_SKIP --timeout 2h --config $GORELEASER_CONFIG" >> $GITHUB_ENV
echo "goreleaser_args=--split $SNAPSHOT_FLAG --clean $BASE_SKIP --timeout 2h --config $GORELEASER_CONFIG" >> $GITHUB_ENV
echo "goreleaser_file=$GORELEASER_CONFIG" >> $GITHUB_ENV

- name: Generate binary cache key
Expand All @@ -203,7 +196,7 @@ jobs:
format('distributions/{0}/_build*/*', inputs.distribution)
) }}"
ARGS_HASH=$(echo "${{ env.goreleaser_args }}" | sha256sum | cut -d' ' -f1)
echo binary_key=goreleaser-build-${{ inputs.distribution }}-${ARGS_HASH}-${BINARY_HASH} >> $GITHUB_OUTPUT
echo binary_key=goreleaser-build-${{ inputs.distribution }}-${{ matrix.goos }}-${ARGS_HASH}-${BINARY_HASH} >> $GITHUB_OUTPUT

- name: Cache goreleaser build
id: cache-goreleaser
Expand Down Expand Up @@ -237,6 +230,7 @@ jobs:
id: goreleaser
uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7
env:
GOOS: ${{ matrix.goos }}
NFPM_PASSPHRASE: ${{ secrets.gpg_passphrase }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
GPG_KEY_PATH: ${{ steps.write_gpg_to_path.outputs.gpg_key_path }}
Expand All @@ -253,12 +247,13 @@ jobs:
run: echo "✅ GoReleaser build skipped - using cached binaries"

- name: Validate GoReleaser build
run: ./scripts/validate-goreleaser-build.sh -d ${{ inputs.distribution }}
run: ./scripts/validate-goreleaser-build.sh -d ${{ inputs.distribution }} -s -g ${{ matrix.goos }}

- name: Extract relevant metadata
if: matrix.goos == 'linux'
run: |
# Always use metadata.json from the dist folder (works for both cached and fresh builds)
VERSION=$(jq -r '.version' distributions/${{ inputs.distribution }}/dist/metadata.json)
# In split mode, metadata.json is in dist/<goos>/metadata.json
VERSION=$(jq -r '.version' distributions/${{ inputs.distribution }}/dist/${{ matrix.goos }}/metadata.json)
echo "Version from metadata.json: $VERSION"
ARCH=$(echo '${{ runner.arch }}' | sed 's/X/amd/g')
ARCH=${ARCH@L}
Expand All @@ -272,7 +267,7 @@ jobs:

- name: Copy GoReleaser binary to Docker context
id: copy-binary
if: steps.cache-goreleaser.outputs.cache-hit == 'true'
if: matrix.goos == 'linux' && steps.cache-goreleaser.outputs.cache-hit == 'true'
run: |
cd distributions/${{ inputs.distribution }}
BINARY_PATH="$(find dist -name "${{ inputs.distribution }}*_linux_${{ env.arch }}*" -type d)/${{ inputs.distribution }}"
Expand Down Expand Up @@ -300,7 +295,7 @@ jobs:

- name: Build and load Docker image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
if: steps.cache-goreleaser.outputs.cache-hit == 'true'
if: matrix.goos == 'linux' && steps.cache-goreleaser.outputs.cache-hit == 'true'
with:
context: ${{ steps.copy-binary.outputs.binary_tmp }}
platforms: linux/${{ env.arch }}
Expand All @@ -310,7 +305,7 @@ jobs:
${{ env.registry }}/${{ inputs.distribution }}:${{ env.image_tag }}

- name: Validate Usage of BoringCrypto
if: inputs.fips
if: matrix.goos == 'linux' && inputs.fips
run: |
ARCH=$(echo '${{ runner.arch }}' | sed 's/X/amd/g')
ARCH=${ARCH@L}
Expand All @@ -335,24 +330,28 @@ jobs:
echo "✅ Binary Go version matches fips/spec.yaml: go${BINARY_GO_VERSION}"

- name: Validate FIPS ciphers
if: inputs.fips
if: matrix.goos == 'linux' && inputs.fips
run: |
./fips/validation/validate.sh "${{ env.registry }}/${{ inputs.distribution }}:${{ env.image_tag }}"

- name: Setup local kind cluster
if: matrix.goos == 'linux'
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
with:
version: v0.21.0
cluster_name: ${{ inputs.test_cluster_name }}
wait: 60s

- name: Load image into kind cluster
if: matrix.goos == 'linux'
run: |
kind load docker-image "${{ env.registry }}/${{ inputs.distribution }}:${{ env.image_tag }}" --name ${{ inputs.test_cluster_name }}

- uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
if: matrix.goos == 'linux'

- name: Derive NR backend URL from region
if: matrix.goos == 'linux'
run: |
case "${{ vars.NR_REGION }}" in
Staging) echo "NR_BACKEND_URL=https://staging-otlp.nr-data.net" >> $GITHUB_ENV ;;
Expand All @@ -361,7 +360,7 @@ jobs:
esac

- name: Run local e2e tests
if: ${{ hashFiles(format('distributions/{0}/test/spec-local.yaml', inputs.distribution)) != '' }}
if: matrix.goos == 'linux' && hashFiles(format('distributions/{0}/test/spec-local.yaml', inputs.distribution)) != ''
uses: newrelic/newrelic-integration-e2e-action@d0c7c2b8f9af227365d71c6b7fddbc024c9e331a # v1
env:
NR_BACKEND_URL: ${{ env.NR_BACKEND_URL }}
Expand All @@ -378,6 +377,7 @@ jobs:
region: ${{ vars.NR_REGION }}

- name: Trivy security check
if: matrix.goos == 'linux'
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: "${{ env.registry }}/${{ inputs.distribution}}:${{ env.image_tag }}"
Expand All @@ -391,6 +391,64 @@ jobs:
TRIVY_SKIP_DB_UPDATE: ${{ !env.ACT }}
TRIVY_SKIP_JAVA_DB_UPDATE: true

- name: Upload dist artifacts
if: false
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: split-dist-${{ inputs.distribution }}${{ inputs.fips && '-fips' || '' }}-${{ matrix.goos }}
path: distributions/${{ inputs.distribution }}/dist
retention-days: 1

merge:
if: false # Skip merging for now until we're ready to iterate on it
needs: build
runs-on: ubuntu-latest
outputs:
artifact_name: ${{ steps.set-outputs.outputs.artifact_name }}
docker_manifest_sha_name: ${{ steps.set-outputs.outputs.docker_manifest_sha_name }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0 # required for tag metadata

- run: ./scripts/free-disk-space.sh

- name: Download all split dist artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: split-dist=${{ inputs.distribution }}${{ inputs.fips && '-fips' || '' }}*
path: distributions/${{ inputs.distribution }}/dist
merge-multiple: true

- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: "~1.26"
cache: ${{ env.caching_enabled }}
check-latest: true

- name: Tidy go.mod files
run: go mod tidy

- name: Import GPG key
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
id: import_gpg
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7
with:
gpg_private_key: ${{ secrets.gpg_private_key }}
passphrase: ${{ secrets.gpg_passphrase }}

- name: Write GPG to path in memory for signing packages
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
id: write_gpg_to_path
run: |
GPG_KEY_PATH="$(mktemp /dev/shm/gpg.XXXXXX)"
echo "$GPG_PRIVATE_KEY" | base64 -d >> "$GPG_KEY_PATH"
echo "gpg_key_path=$GPG_KEY_PATH" >> $GITHUB_OUTPUT
env:
GPG_PRIVATE_KEY: ${{ secrets.gpg_private_key }}

- name: Prepare GoReleaser Artifacts
if: ${{ inputs.publish }}
id: set-outputs
Expand Down
34 changes: 29 additions & 5 deletions scripts/validate-goreleaser-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
# Script to validate a goreleaser distribution's dist file
set -e

while getopts d: flag
split=false
goos=""

while getopts d:sg: flag
do
case "${flag}" in
d) distro=${OPTARG};;
s) split=true;;
g) goos=${OPTARG};;
*) exit 1;;
esac
done
Expand All @@ -18,14 +23,28 @@ if [ -z ${distro} ]; then
exit 1
fi

if [ "${split}" = true ] && [ -z "${goos}" ]; then
echo "Split mode requires GOOS to be provided with -g."
exit 1
fi

cd "distributions/${distro}"
if [ ! -d "dist" ]; then
echo "❌ dist directory not found!"
exit 1
fi

# Set metadata directory based on mode
if [ "${split}" = true ]; then
# In split mode, metadata files are in dist/<goos>/
metadata_dir="dist/${goos}"
else
# In normal/merge mode, metadata files are at dist root
metadata_dir="dist"
fi

echo "📋 Verifying metadata files exist..."
files=("dist/artifacts.json" "dist/metadata.json")
files=("${metadata_dir}/artifacts.json" "${metadata_dir}/metadata.json")
missing_files=()
for file in "${files[@]}"; do
if [ ! -f "$file" ]; then
Expand All @@ -42,7 +61,7 @@ else
fi

echo "📋 Verifying Binaries exist..."
binaries=$( jq -r '.[] | select(.type == "Binary") | .path' dist/artifacts.json )
binaries=$( jq -r '.[] | select(.type == "Binary") | .path' "${metadata_dir}/artifacts.json" )
if [ -z "${binaries}" ]; then
echo "❌ No binaries found in artifacts.json"
exit 1
Expand All @@ -59,7 +78,7 @@ fi
echo "✅ All binaries found!"

echo "📋 Validating Archives and Packages..."
artifacts=$( jq -r '.[] | select(.type == "Archive" or .type == "Linux Package") | .path' dist/artifacts.json )
artifacts=$( jq -r '.[] | select(.type == "Archive" or .type == "Linux Package") | .path' "${metadata_dir}/artifacts.json" )
if [ -z "${artifacts}" ]; then
echo "⚠️ No archives or packages found in artifacts.json"
else
Expand All @@ -72,8 +91,13 @@ else
else
echo " Found artifact: ${artifact}"
fi
# Skip checksum validation in split mode (checksums are generated during merge)
if [ "${split}" = true ]; then
echo " ⏭️ Skipping checksum validation (split mode)"
continue
fi
# Search for the corresponding checksum file and verify it exists
sum_file=$( jq -r ".[] | select(.type == \"Checksum\" and .extra.ChecksumOf == \"${artifact}\") | .path" dist/artifacts.json )
sum_file=$( jq -r ".[] | select(.type == \"Checksum\" and .extra.ChecksumOf == \"${artifact}\") | .path" "${metadata_dir}/artifacts.json" )
if [ -z "${sum_file}" ]; then
echo "❌ Checksum not defined for ${artifact} in artifacts.json"
exit 1
Expand Down
Loading