From e8a1372269c7e981b6c9f123fe0d49431146c4e0 Mon Sep 17 00:00:00 2001 From: Ronni Skansing Date: Thu, 18 Jun 2026 15:00:21 +0200 Subject: [PATCH] fix local build Signed-off-by: Ronni Skansing --- backend/build_scripts/build.sh | 90 +++++++++++++++++++++++++--------- makefile | 4 ++ 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/backend/build_scripts/build.sh b/backend/build_scripts/build.sh index 010a258..0b9059a 100755 --- a/backend/build_scripts/build.sh +++ b/backend/build_scripts/build.sh @@ -1,32 +1,76 @@ #!/bin/sh -echo "### Building frontend" -# remove any old builds -rm -rf phishingclub/frontend/frontend/build -mkdir -p phishingclub/frontend/frontend/build +set -e -sudo docker run --rm \ --v "$(pwd)":/app \ --w /app/phishingclub/frontend \ -node:22-alpine \ -sh -c "npm ci && npm run build-production" +# 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" -# Get current user and group IDs USER_ID=$(id -u) GROUP_ID=$(id -g) - -sudo chown -R $USER_ID:$GROUP_ID phishingclub/frontend/build -sudo mv phishingclub/frontend/build ./phishingclub/frontend/frontend/ - -echo "### Building backend" HASH=$(git rev-parse --short HEAD) -echo "Building with hash: $HASH" +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..." +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 \ --v "$(pwd)":/app \ --w /app/phishingclub/frontend \ -golang:1.25.10-alpine \ -go build -trimpath \ --ldflags="-X github.com/phishingclub/phishingclub/version.hash=ph$HASH" \ --tags production -o ../build/phishingclub main.go + -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" diff --git a/makefile b/makefile index c6719ff..a498171 100644 --- a/makefile +++ b/makefile @@ -6,6 +6,10 @@ down: -sudo docker compose down --remove-orphans up-build: sudo docker compose up --build --force +# build a single, self contained binary with the frontend embedded -> build/phishingclub +# override arch: make build BIN_ARCH=arm64 ; override version: make build VERSION=1.2.3 +build: + BIN_ARCH=$(BIN_ARCH) VERSION=$(VERSION) ./backend/build_scripts/build.sh # same as up but for machines with limited memory, the frontend waits for the backend # build to finish so the two heavy first build steps do not run at the same time up-low-mem: