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
65 changes: 51 additions & 14 deletions .github/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
# build stage
FROM golang:alpine AS build
RUN apk update && apk add git
ADD . /src
WORKDIR /src
ARG GOARCH=amd64
ARG GOARM=
ARG GOMIPS=hardfloat
ARG GO386=

# All build stages explicitly use BUILDPLATFORM so RUN commands execute
# on the host architecture (amd64) even when cross-compiling for other targets.
# This eliminates the need for QEMU entirely — Go's native GOARCH cross-compilation
# handles all target architectures from a single amd64 build host.

FROM --platform=$BUILDPLATFORM docker.io/stagex/pallet-go@sha256:4b7f9fe27d84dd9109fe89c4dab1cefdd66bf2f670817ef95ffd335b84fdf2cb AS deps
ARG CHISEL_VERSION
WORKDIR /app
COPY . .
RUN GO111MODULE=on go mod vendor
RUN test -d vendor && echo "vendor/ created"

FROM --platform=$BUILDPLATFORM docker.io/stagex/pallet-go@sha256:4b7f9fe27d84dd9109fe89c4dab1cefdd66bf2f670817ef95ffd335b84fdf2cb AS check
ARG CHISEL_VERSION
WORKDIR /app
ENV CGO_ENABLED=0
RUN go build \
-ldflags "-X github.com/jpillora/chisel/share.BuildVersion=$(git describe --abbrev=0 --tags)" \
-o /tmp/bin
# run stage
FROM scratch
LABEL maintainer="dev@jpillora.com"
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=deps /app/ .
RUN go vet -mod=vendor ./...

FROM --platform=$BUILDPLATFORM docker.io/stagex/pallet-go@sha256:4b7f9fe27d84dd9109fe89c4dab1cefdd66bf2f670817ef95ffd335b84fdf2cb AS test
ARG CHISEL_VERSION
WORKDIR /app
COPY --from=build /tmp/bin /app/bin
ENTRYPOINT ["/app/bin"]
ENV CGO_ENABLED=0
COPY --from=check /app/ .
RUN go test -mod=vendor -v ./...

FROM --platform=$BUILDPLATFORM docker.io/stagex/pallet-go@sha256:4b7f9fe27d84dd9109fe89c4dab1cefdd66bf2f670817ef95ffd335b84fdf2cb AS build
ARG CHISEL_VERSION
ARG GOARCH=amd64
ARG GOARM=
ARG GOMIPS=hardfloat
ARG GO386=
WORKDIR /app
ENV CGO_ENABLED=0
ENV GOARCH=${GOARCH}
ENV GOARM=${GOARM}
ENV GOMIPS=${GOMIPS}
ENV GO386=${GO386}
COPY --from=test /app/ .
RUN --network=none \
go build -mod=vendor -trimpath \
-ldflags="-s -w -X github.com/jpillora/chisel/share.BuildVersion=${CHISEL_VERSION#v}" \
-o /chisel .

FROM scratch
ARG CHISEL_VERSION
COPY --from=build /chisel /chisel
ENTRYPOINT ["/chisel"]
59 changes: 59 additions & 0 deletions .github/verify-binary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/bin/sh
set -eu

# verify-binary.sh — Verify chisel binary architecture and version
# Usage: verify-binary.sh <GOARCH> [<image:tag>] [<expected-version>]
#
# Checks:
# 1. Binary executes (docker run — catches exec format errors)
# 2. ELF Machine field matches expected GOARCH
# 3. Version string matches (if expected-version provided)

GOARCH="${1:?usage: verify-binary.sh GOARCH [image:tag] [expected-version]}"
IMAGE="${2:-chisel:test}"
EXPECTED_VER="${3:-}"

case "$GOARCH" in
amd64) EXPECTED_MACHINE="Advanced Micro Devices X86-64" ;;
arm64) EXPECTED_MACHINE="AArch64" ;;
386) EXPECTED_MACHINE="Intel 80386" ;;
arm|armv5|armv6|armv7) EXPECTED_MACHINE="ARM" ;;
ppc64le|ppc64) EXPECTED_MACHINE="PowerPC64" ;;
mips|mipsle|mips64|mips64le) EXPECTED_MACHINE="MIPS" ;;
s390x) EXPECTED_MACHINE="IBM S/390" ;;
*) echo "error: unknown GOARCH: $GOARCH" >&2; exit 1 ;;
esac

echo "=== Verify $IMAGE (GOARCH=$GOARCH → $EXPECTED_MACHINE) ==="

# 1. Binary must execute
echo "--- chisel version ---"
docker run --rm "$IMAGE" chisel version

# 2. Extract and check ELF Machine field
echo "--- ELF architecture ---"
CID="$(docker create "$IMAGE")"
docker cp "$CID:/chisel" /tmp/chisel-verify-binary 2>/dev/null
docker rm "$CID" >/dev/null

MACHINE="$(readelf -h /tmp/chisel-verify-binary 2>/dev/null | sed -n 's/^[[:space:]]*Machine:[[:space:]]*//p')"
rm -f /tmp/chisel-verify-binary

if [ "$MACHINE" != "$EXPECTED_MACHINE" ]; then
echo "FAIL: Machine mismatch (expected '$EXPECTED_MACHINE', got '$MACHINE')" >&2
exit 1
fi
echo " Machine: $MACHINE ✓"

# 3. Optional version check
if [ -n "$EXPECTED_VER" ]; then
echo "--- version string ---"
VER="$(docker run --rm "$IMAGE" chisel version 2>/dev/null | sed -n 's/^.*Version: *//p' | awk '{print $1}')"
if [ "$VER" != "$EXPECTED_VER" ]; then
echo "FAIL: Version mismatch (expected '$EXPECTED_VER', got '$VER')" >&2
exit 1
fi
echo " Version: $VER ✓"
fi

echo "PASS: $IMAGE"
100 changes: 67 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ on:
push: {}
permissions: write-all
jobs:
# ================
# BUILD AND TEST JOB
# ================
test:
name: Build & Test
strategy:
Expand All @@ -28,9 +25,6 @@ jobs:
run: go build -v -o /dev/null .
- name: Test
run: go test -v ./...
# ================
# RELEASE BINARIES (on push "v*" tag)
# ================
release_binaries:
name: Release Binaries
needs: test
Expand All @@ -50,9 +44,6 @@ jobs:
GOTOOLCHAIN: auto
with:
args: release --config .github/goreleaser.yml
# ================
# RELEASE DOCKER IMAGES (on push "v*" tag)
# ================
release_docker:
name: Release Docker Images
needs: test
Expand All @@ -61,32 +52,75 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: jpillora
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: jpillora/chisel
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: .github/Dockerfile
platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/386,linux/arm/v7,linux/arm/v6
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push all architectures
env:
CHISEL_VER: ${{ github.ref_name }}
run: |
set -e
IMAGE=jpillora/chisel
VERSION="${CHISEL_VER#v}"

# Architecture matrix: per-platform tag, GOARCH, GOARM
# GOARM is empty for non-ARM architectures (Go uses its default)
ARCHES=(
"amd64 amd64"
"arm64 arm64"
"386 386"
"armv5 arm 5"
"armv6 arm 6"
"armv7 arm 7"
"ppc64le ppc64le"
"ppc64 ppc64"
"mips mips"
"mipsle mipsle"
"mips64 mips64"
"mips64le mips64le"
"s390x s390x"
)

ALL_TAGS=""
for entry in "${ARCHES[@]}"; do
read -r tag goarch goarm <<< "$entry"
build_args="--build-arg GOARCH=$goarch --build-arg CHISEL_VERSION=$VERSION"
[ -n "$goarm" ] && build_args="$build_args --build-arg GOARM=$goarm"
echo "=== Building $tag (GOARCH=$goarch GOARM=$goarm) ==="
docker build $build_args --tag $IMAGE:$tag --file .github/Dockerfile .
.github/verify-binary.sh "$goarch" "$IMAGE:$tag" "$VERSION"
docker push $IMAGE:$tag
ALL_TAGS="$ALL_TAGS $IMAGE:$tag"
done

# Create multi-arch manifest for the version tag
echo "=== Creating manifest $IMAGE:$VERSION ==="
docker manifest create $IMAGE:$VERSION $ALL_TAGS

# Annotate each entry with correct platform metadata
# Without this, all images would be marked linux/amd64 because
# the build ran on an amd64 host regardless of GOARCH.
docker manifest annotate $IMAGE:$VERSION $IMAGE:amd64 --arch amd64
docker manifest annotate $IMAGE:$VERSION $IMAGE:arm64 --arch arm64
docker manifest annotate $IMAGE:$VERSION $IMAGE:386 --arch 386
docker manifest annotate $IMAGE:$VERSION $IMAGE:armv5 --arch arm --variant v5
docker manifest annotate $IMAGE:$VERSION $IMAGE:armv6 --arch arm --variant v6
docker manifest annotate $IMAGE:$VERSION $IMAGE:armv7 --arch arm --variant v7
docker manifest annotate $IMAGE:$VERSION $IMAGE:ppc64le --arch ppc64le
docker manifest annotate $IMAGE:$VERSION $IMAGE:ppc64 --arch ppc64
docker manifest annotate $IMAGE:$VERSION $IMAGE:mips --arch mips
docker manifest annotate $IMAGE:$VERSION $IMAGE:mipsle --arch mipsle
docker manifest annotate $IMAGE:$VERSION $IMAGE:mips64 --arch mips64
docker manifest annotate $IMAGE:$VERSION $IMAGE:mips64le --arch mips64le
docker manifest annotate $IMAGE:$VERSION $IMAGE:s390x --arch s390x

docker manifest push $IMAGE:$VERSION

# Create additional semver tags (major.minor and major)
for tv in "${VERSION%.*}" "${VERSION%%.*}"; do
echo "=== Creating manifest $IMAGE:$tv ==="
docker manifest create $IMAGE:$tv $ALL_TAGS
docker manifest push $IMAGE:$tv
done