Skip to content
Open
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
170 changes: 170 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
name: build

on:
push:
branches: [main] # pushes TO main
pull_request:
branches: [main] # pull requests AGAINST main
release:
types: [published] # for release build

# cancel CI runs when a new commit is pushed to any branch except main
concurrency:
group: 'build-${{ github.ref }}'
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 5

permissions:
contents: read # for uses: actions/checkout
id-token: write # for actions/attest-build-provenance
attestations: write # for actions/attest-build-provenance

strategy:
fail-fast: false
matrix:
# Static binaries are built with the `stable` version
# If you want to add a target, add it below.
include:
- {goos: 'darwin', goarch: 'amd64'}
- {goos: 'darwin', goarch: 'arm64'}
- {goos: 'linux', goarch: 'amd64'}
- {goos: 'linux', goarch: 'arm', goarm: '6'}
- {goos: 'linux', goarch: 'arm', goarm: '7'}
- {goos: 'linux', goarch: 'arm64'}
- {goos: 'linux', goarch: 'riscv64'}
- {goos: 'windows', goarch: 'amd64'}
- {goos: 'windows', goarch: 'arm64',}

steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Security measures
# See also: https://github.com/actions/checkout/issues/485
persist-credentials: false

- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: stable
# Disabled in multi-platform builds due to complex caching strategies
cache: false

- run: make release
env:
DIST_PATH: dist
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
GOARM: ${{ matrix.goarch == 'arm' && matrix.goarm || '' }}

- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
# Since the necessary permissions cannot be set in the pull_request event, it is limited to push events.
if: ${{ github.event_name != 'pull_request' }}
with:
subject-path: dist/go-httpbin-*

- name: Check artifacts
run: |
file dist/go-httpbin-*
stat dist/go-httpbin-*

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: go-httpbin-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarch == 'arm' && format('v{0}', matrix.goarm) || '' }}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes me vaguely uneasy that this step needs to be able to generate the exact same name that make release will generate, and needs to stay in sync over time.

What if we do something like

  1. update make release to emit the built binary name as the last line on stdout
  2. update the make release step above to capture that last line as an output
  3. update this step to reference ${{ steps.make-release.outputs.release_name }} (or whatever)

Would that make any sense to you?

path: dist/

checksum:
needs: [build]
runs-on: ubuntu-latest
timeout-minutes: 5

permissions:
id-token: write # for uses: aactions/attest-build-provenance
attestations: write # for uses: aactions/attest-build-provenance

steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: dist/
pattern: go-httpbin-*
merge-multiple: true

- name: Check artifacts
run: tree --charset ASCII dist/

- name: Verify artifacts
# Since the necessary permissions cannot be set in the pull_request event, it is limited to push events.
if: ${{ !startsWith(github.event_name, 'pull') }}
run: |
cd dist
for f in go-httpbin-*; do
gh attestation verify "$f" -R "${GITHUB_REPOSITORY}";
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Generate sha256 checksums
run: |
cd dist
for f in $(find . -type f -name 'go-httpbin-*' -printf '%f\n' | sort); do
sha256sum "$f" | tee -a SHA256SUMS;
done

- name: Verify sha256 checksums
run: cd dist && sha256sum -c SHA256SUMS
Comment on lines +121 to +122
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a dumb question: What's the value in separating this step from the previous step?


- name: Generate artifact attestation
# Since the necessary permissions cannot be set in the pull_request event, it is limited to push events.
if: ${{ !startsWith(github.event_name, 'pull') }}
uses: actions/attest-build-provenance@v2
with:
subject-path: dist/SHA256SUMS

- name: Upload sha256 checksum
uses: actions/upload-artifact@v4
with:
name: checksum-sha256sums
path: dist/SHA256SUMS

publish:
needs: [build, checksum]
runs-on: ubuntu-latest
timeout-minutes: 5

permissions:
contents: write # for gh release upload
id-token: write # for actions/attest-build-provenance
attestations: write # for actions/attest-build-provenance

steps:
- name: Download binary artifacts
uses: actions/download-artifact@v4
with:
path: dist/
pattern: go-httpbin-*
merge-multiple: true

- name: Download checksum artifacts
uses: actions/download-artifact@v4
with:
path: dist/
pattern: checksum-*
merge-multiple: true

- name: Check artifacts
run: tree --charset ASCII dist/

- name: Publish artifacts
if: startsWith(github.ref, 'refs/tags/')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we gate this on the release event type to more directly express our intentions here?

run: |-
gh release upload "$(echo "${GITHUB_REF}" | sed -E 's!refs/tags/!!')" dist/* -R "${GITHUB_REPOSITORY}"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just trying to make sure I understand the mechanics here: This will attach the prebuilt & attested binaries and checksums to an existing release, right? And we can be sure the release exists because this step will only be executed when triggered by the release event?

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ STATICCHECK := go run honnef.co/go/tools/cmd/[email protected]
HOST ?= 127.0.0.1
PORT ?= 8080

# Determine the name of the static binary for the release build
GOOS ?= $(shell go env GOOS)
GOARCH ?= $(shell go env GOARCH)
ifeq (x$(GOOS)x,xwindowsx)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is some arcane make-specific nuance, but why the surrounding xs here?

GOENVS ?= GOOS=$(GOOS) GOARCH=$(GOARCH)
BINNAME ?= go-httpbin-$(GOOS)-$(GOARCH).exe
else
ifeq (x$(GOARCH)x,xarmx)
GOENVS ?= GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM)
BINNAME ?= go-httpbin-$(GOOS)-$(GOARCH)v$(GOARM)
else
GOENVS ?= GOOS=$(GOOS) GOARCH=$(GOARCH)
BINNAME ?= go-httpbin-$(GOOS)-$(GOARCH)
endif
endif

# =============================================================================
# build
Expand Down Expand Up @@ -99,3 +114,11 @@ imagepush:
docker buildx build --push --platform linux/amd64,linux/arm64 -t $(DOCKER_TAG) .
docker buildx rm httpbin
.PHONY: imagepush

# =============================================================================
# release build
# =============================================================================
release:
mkdir -p $(DIST_PATH)
CGO_ENABLED=0 $(GOENVS) go build -trimpath -ldflags '-s -w -buildid=' -o $(DIST_PATH)/$(BINNAME) ./cmd/go-httpbin
.PHONY: release