Files
Ronni Skansing e8a1372269 fix local build
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2026-06-18 15:00:21 +02:00

77 lines
3.2 KiB
Bash
Executable File

#!/bin/sh
set -e
# build a single, self contained binary with the frontend embedded
# uses docker only, no host go or node toolchain is needed
# output is build/phishingclub at the repo root
#
# this mirrors .github/workflows/release.yml: same pinned images, same
# musl static cgo build. pinning the images is what keeps the frontend
# build reproducible, a floating node tag pulls a newer node that can
# run out of heap on the production bundle.
#
# defaults to linux amd64, override the arch with:
# BIN_ARCH=arm64 ./backend/build_scripts/build.sh (needs qemu locally)
# override the version string (defaults to git describe) with:
# VERSION=1.2.3 ./backend/build_scripts/build.sh
# pinned images, keep in sync with .github/workflows/release.yml
NODE_IMAGE="node@sha256:968df39aedcea65eeb078fb336ed7191baf48f972b4479711397108be0966920" # node:22-alpine
GO_IMAGE_AMD64="golang@sha256:c4ea15b4a7912716eb362a022e2b12317762eca387423760bc59c0f9ae69423c" # golang:1.25.10-alpine linux/amd64
GO_IMAGE_ARM64="golang@sha256:5331adf7f8a0803631d9dc28843e288874789c14b97a3d0b54ed13e59f9e0589" # golang:1.25.10-alpine linux/arm64
# resolve the repo root from this script location so it works from any cwd
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd)
cd "$REPO_ROOT"
USER_ID=$(id -u)
GROUP_ID=$(id -g)
HASH=$(git rev-parse --short HEAD)
VERSION=${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}
BIN_ARCH=${BIN_ARCH:-amd64}
case "$BIN_ARCH" in
amd64) GO_IMAGE="$GO_IMAGE_AMD64"; PLATFORM="" ;;
arm64) GO_IMAGE="$GO_IMAGE_ARM64"; PLATFORM="--platform linux/arm64" ;;
*) echo "unsupported BIN_ARCH=$BIN_ARCH (use amd64 or arm64)"; exit 1 ;;
esac
echo "### Building frontend"
# remove any old builds so nothing stale gets embedded
rm -rf frontend/build backend/frontend/build
# NODE_OPTIONS only raises the heap ceiling, it does not change the output
# bundle. the production build can peak above node's 2gb default on machines
# with many cores (more parallel transforms in flight), where the small CI
# runner stays under it.
sudo docker run --rm \
-e NODE_OPTIONS=--max-old-space-size=4096 \
-v "$REPO_ROOT/frontend":/app \
-w /app \
"$NODE_IMAGE" \
sh -c "npm ci && npm run build-production"
# place the compiled frontend where the go binary embeds it
# (backend/frontend/frontend.go does //go:embed build/*)
mkdir -p backend/frontend/build
cp -r frontend/build/* backend/frontend/build/
echo "### Building binary for linux/$BIN_ARCH (version $VERSION, hash ph$HASH)"
mkdir -p build
# cgo build statically linked against musl, matches the release binary
sudo docker run --rm $PLATFORM \
-v "$REPO_ROOT":/app \
-w /app/backend \
"$GO_IMAGE" \
sh -c "apk add --no-cache gcc musl-dev && go build -trimpath \
-ldflags='-X github.com/phishingclub/phishingclub/version.hash=ph$HASH -X github.com/phishingclub/phishingclub/version.version=$VERSION -linkmode=external -extldflags=-static' \
-tags production -o /app/build/phishingclub main.go"
# the containers run as root, hand the build outputs back to the caller
sudo chown -R "$USER_ID:$GROUP_ID" build frontend/build frontend/node_modules backend/frontend/build
echo "### Done"
ls -lh "$REPO_ROOT/build/phishingclub"