diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index a4a83c9..0000000 --- a/.dockerignore +++ /dev/null @@ -1,58 +0,0 @@ -# Docker build context trimming. The image build only needs the Go source, the -# embedded web assets (web/embed.go go:embed list), go.mod/go.sum, and -# scripts/xbuild-tile57.sh — everything else just bloats the context. The tile57 -# engine is git-cloned inside the builder, not copied from here. - -# VCS + agent/editor state -.git/ -.github/ -.claude/ -.idea/ -.vscode/ -.DS_Store - -# The tile57 engine submodule: cloned fresh in the builder (shallow + floating), -# never copied from the context — so the local checkout doesn't leak in or pin it. -tile57/ - -# Build output / release staging -bin/ -dist/ -release-assets/ -*.test -*.out -coverage.* - -# Local Go workspace override (must never reach the builder — it would repoint the -# engine replace at a path that doesn't exist in the image). -go.work -go.work.sum - -# Baked tile archives + generated (non-embedded) client assets -*.pmtiles -web/*.pmtiles -web/colortables.json -web/linestyles.json -web/patterns.json -web/patterns.png -web/sprite.json -web/sprite.png -web/chartplotter.wasm -web/charts-index.json -web/charts-user.* - -# Large test/data material + local artifacts (not needed to build the binary) -testdata/ -examples/ -*.cpx -/chartplotter - -# The docs site (Docusaurus) is not part of the binary build -docs/node_modules/ -docs/build/ -docs/.docusaurus/ - -# Docker + compose files themselves -Dockerfile -.dockerignore -compose.yaml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index bfb6793..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Docker - -# Build + publish the multi-arch container image to GHCR. On a v* tag we push the -# version + `latest`; on main we push `edge` (a rolling pre-release image). The -# image is a FROM scratch wrapper around the fully-static musl binary (the -# "go-dims" pattern) — see Dockerfile. -# -# Multi-arch is done by zig-cc CROSS-COMPILE inside the builder stage, NOT QEMU: -# the builder is pinned to the native BUILDPLATFORM and links each TARGETARCH with -# `zig cc`, so buildx emits linux/amd64 + linux/arm64 from one fast amd64 runner. -# The engine (public github.com/beetlebugorg/tile57, whose submodules hold the IHO -# S-101 catalogues) is git-cloned inside the Dockerfile, so no sibling checkout is -# needed here. Same IHO-embed caveat as the binary releases (accepted — the image -# embeds the catalogue via libtile57). -# -# NOTE: correct-by-construction but UNVALIDATED until a real push runs on GitHub -# (buildx needs GHCR auth + the runner, neither available locally). - -on: - push: - branches: [main] - tags: ["v*"] - -permissions: - contents: read - packages: write - -env: - IMAGE: ghcr.io/beetlebugorg/chartplotter - -jobs: - image: - runs-on: ubuntu-latest - steps: - - name: Checkout chartplotter - uses: actions/checkout@v7 - - - name: Set up QEMU - # Only needed so buildx can DECLARE the arm64 platform; the actual build is - # a native cross-compile (the builder stage pins --platform=$BUILDPLATFORM), - # so no guest code is emulated. - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Derive image tags - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE }} - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} - type=raw,value=edge,enable=${{ github.ref == 'refs/heads/main' }} - - - name: Build + push (linux/amd64, linux/arm64) - uses: docker/build-push-action@v6 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - VERSION=${{ github.ref_type == 'tag' && github.ref_name || format('edge-{0}', github.sha) }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b8b66b7..0000000 --- a/Dockerfile +++ /dev/null @@ -1,114 +0,0 @@ -# syntax=docker/dockerfile:1 - -# ============================================================================= -# chartplotter — a tiny multi-arch image built from a FULLY STATIC musl binary -# (the "go-dims" pattern). The Go binary is already self-contained: the web -# assets, the S-101 catalogue, and the native libtile57 engine are all embedded -# / statically linked, so a static musl build dropped into `FROM scratch` yields -# an image that is basically just the ~25 MB binary (+ a CA bundle). -# -# Multi-arch WITHOUT QEMU emulation: the builder runs on the native BUILDPLATFORM -# and cross-links each TARGETARCH with `zig cc` (the same recipe as `make musl`), -# so one fast builder produces both linux/amd64 and linux/arm64 by target swap. -# docker buildx build --platform linux/amd64,linux/arm64 -t chartplotter . -# A plain `docker build` builds only the host arch. -# ============================================================================= - -# ---- builder: Go 1.26 + Zig 0.16, cross-links the static musl binary --------- -# Pinned to BUILDPLATFORM so it stays native (no emulation) even when the target -# is a different arch — Go + zig do the cross-compile. -FROM --platform=$BUILDPLATFORM golang:1.26-bookworm AS builder - -# Zig 0.16 is the C toolchain for the CGO cross-link (see scripts/xbuild-tile57.sh). -ARG ZIG_VERSION=0.16.0 -# Predefined by buildx: BUILDARCH = the builder's own arch (fetch the matching -# host Zig); TARGETARCH = the arch we cross-compile FOR. -ARG BUILDARCH -ARG TARGETARCH -# Stamped into the binary (main.version). Pass --build-arg VERSION=v1.2.3 in CI. -ARG VERSION=docker -# Optional: pin the engine to a branch/tag for reproducible builds (default: the -# repo's default branch). The engine ABI lives in include/ + bindings/go. -ARG TILE57_REF= - -# Install Zig 0.16 (host arch) — the C cross-compiler `zig cc` links libtile57 + -# the cgo objects against musl for any target triple from this one host. xz-utils -# is needed to unpack Zig's .tar.xz (not in the golang image by default). -RUN set -eux; \ - apt-get update; \ - apt-get install -y --no-install-recommends xz-utils; \ - rm -rf /var/lib/apt/lists/*; \ - case "$BUILDARCH" in \ - amd64) zarch=x86_64 ;; \ - arm64) zarch=aarch64 ;; \ - *) echo "unsupported build arch: $BUILDARCH" >&2; exit 1 ;; \ - esac; \ - url="https://ziglang.org/download/${ZIG_VERSION}/zig-${zarch}-linux-${ZIG_VERSION}.tar.xz"; \ - curl -fsSL "$url" -o /tmp/zig.tar.xz; \ - mkdir -p /opt/zig; \ - tar -xJf /tmp/zig.tar.xz -C /opt/zig --strip-components=1; \ - rm /tmp/zig.tar.xz; \ - ln -s /opt/zig/zig /usr/local/bin/zig; \ - zig version - -# chartplotter source. The engine is the ./tile57 submodule, but the build context -# doesn't carry submodule contents (.dockerignore excludes tile57/), so clone it -# fresh IN-TREE below — a shallow, floating clone of the PUBLIC -# github.com/beetlebugorg/tile57 (its own submodules carry the IHO S-101 -# catalogues). go.mod's `replace … => ./tile57/bindings/go` and the binding's cgo -# LDFLAGS resolve against ./tile57. -COPY . /src/chartplotter -WORKDIR /src/chartplotter -RUN set -eux; \ - rm -rf tile57; \ - if [ -n "$TILE57_REF" ]; then br="--branch $TILE57_REF"; else br=""; fi; \ - git clone --depth 1 $br --recurse-submodules --shallow-submodules \ - https://github.com/beetlebugorg/tile57 tile57; \ - git -C tile57 rev-parse --short=9 HEAD - -# Cross-link the fully-static musl binary for TARGETARCH. LIBC=musl makes zig's -# lld emit a static ELF (`ldd` → "not a dynamic executable"); SKIP_RESTORE=1 skips -# the throwaway host-lib rebuild (the tree is discarded after this stage). The Go -# build cache + module cache are mounted so rebuilds are fast. -RUN --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/go/pkg/mod \ - set -eux; \ - LIBC=musl SKIP_RESTORE=1 \ - PLATFORMS="linux/${TARGETARCH}" \ - OUT=/out \ - VERSION="${VERSION}" \ - TILE57=tile57 \ - ENGINE_COMMIT="$(git -C tile57 rev-parse --short=9 HEAD)" \ - scripts/xbuild-tile57.sh; \ - cp "/out/chartplotter_linux_${TARGETARCH}_musl" /chartplotter; \ - # Smoke-test only on a native build — a cross-built (e.g. arm64-on-amd64) - # binary can't be executed by the amd64 builder (no emulation here). - if [ "$TARGETARCH" = "$BUILDARCH" ]; then /chartplotter version; fi - -# An empty, world-writable /tmp to seed into the scratch image: `serve` uses -# os.MkdirTemp for the regenerated S-101 client assets, and scratch has no /tmp. -RUN mkdir -m 1777 -p /image-tmp - -# ---- final: FROM scratch — just the binary + a CA bundle -------------------- -FROM scratch - -# A static musl binary has NO system trust store, and chartplotter makes outbound -# HTTPS calls (charts.noaa.gov / ienccloud.us chart downloads). VERIFIED: without -# a CA bundle those fetches fail with `x509: certificate signed by unknown -# authority`. Bundle the roots from the builder and point Go's crypto/x509 at them. -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt - -COPY --from=builder /chartplotter /chartplotter - -# scratch ships no /tmp; `serve` needs one for the regenerated S-101 assets. -COPY --from=builder /image-tmp /tmp - -# Persistent chart state (source ENC + baked tiles) — back it with a named volume. -VOLUME ["/data"] -EXPOSE 8080 - -# `serve` defaults to --host 127.0.0.1, which is unreachable from outside the -# container, so bind 0.0.0.0. Source ENC + baked tiles land under the volume. -ENTRYPOINT ["/chartplotter"] -CMD ["serve", "--host", "0.0.0.0", "--data", "/data", "--cache", "/data/cache"] diff --git a/Makefile b/Makefile index 17129fc..6ea229f 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ CACHE ?= $(if $(XDG_CACHE_HOME),$(XDG_CACHE_HOME),$(HOME)/.cache)/chartplotter S101_PC ?= $(HOME)/Projects/s101-portrayal-catalogue/PortrayalCatalog S101_FC ?= $(HOME)/Projects/s101-feature-catalogue/S-101FC/FeatureCatalogue.xml -.PHONY: build build-tile57 tile57-lib vendor-style-engine xbuild xbuild-tile57 musl test vet fmt fmt-check tidy clean clear-cache serve docs docs-shots bake-ienc bake-noaa serve-widget demo demo-chart1 serve-demo preslib-chart1 s64-pages +.PHONY: build build-tile57 tile57-lib vendor-style-engine xbuild xbuild-tile57 test vet fmt fmt-check tidy clean clear-cache serve docs docs-shots bake-ienc bake-noaa serve-widget demo demo-chart1 serve-demo preslib-chart1 s64-pages # Prebaked prod test set (US Inland ENC bundle + the NOAA world archive). # NB: keep these as bare values with NO inline `#` comments — Make folds any @@ -137,20 +137,6 @@ build-tile57: build ## Alias for `build` (libtile57 is the sole engine now) xbuild xbuild-tile57: $(TILE57)/include/tile57.h ## Cross-compile CGO+libtile57 binaries with zig cc (linux+windows; darwin builds on a Mac runner) VERSION="$(VERSION)" TILE57="$(TILE57)" ENGINE_COMMIT="$(ENGINE_COMMIT)" scripts/xbuild-tile57.sh -# Fully-static musl binaries for the tiny FROM scratch Docker image (the "go-dims" -# pattern): CGO+libtile57 linked against musl STATICALLY via zig cc, so `ldd` reports -# "not a dynamic executable" and the binary drops straight into `FROM scratch`. Builds -# linux/amd64 + linux/arm64 into dist/chartplotter_linux__musl (same zig-cc -# cross-build as xbuild — no QEMU; one host cross-links both arches by target swap). -# The Dockerfile calls scripts/xbuild-tile57.sh with LIBC=musl directly; this target -# is the local/CI equivalent. musl-static is strictly more portable than the gnu -# xbuild (no glibc floor), so it's the recommended linux artifact — the gnu xbuild -# path is kept intact for anyone who needs a glibc-dynamic build. -MUSL_PLATFORMS ?= linux/amd64 linux/arm64 -musl: $(TILE57)/include/tile57.h ## Build fully-static musl binaries (linux amd64+arm64) into dist/ for the scratch Docker image - VERSION="$(VERSION)" TILE57="$(TILE57)" ENGINE_COMMIT="$(ENGINE_COMMIT)" \ - LIBC=musl PLATFORMS="$(MUSL_PLATFORMS)" scripts/xbuild-tile57.sh - serve: build ## Serve the web frontend + provisioning API on :8080 (HOST/PORT/ASSETS overridable) $(BIN) serve --host $(HOST) --port $(PORT) --assets $(ASSETS) diff --git a/README.md b/README.md index 92de473..11cd6dc 100644 --- a/README.md +++ b/README.md @@ -117,35 +117,6 @@ of the chart — **instrument gauges**, custom overlays, routes, and more — wi forking the core. NMEA 0183 own-ship and AIS are the first slice of that; expect the surface to grow and change. -## 🐳 Run with Docker - -The simplest way to run chartplotter — and the primary path for the -**server-hub-on-a-boat** model (a Raspberry Pi, laptop, or mini PC that holds all -chart state while every screen just points a browser at it) — is the published -container image: - -```sh -docker run -p 8080:8080 -v chartplotter-data:/data \ - ghcr.io/beetlebugorg/chartplotter -# open http://localhost:8080 -``` - -Or with Docker Compose ([`compose.yaml`](compose.yaml)): - -```sh -docker compose up -d -``` - -The image is **multi-arch** (`linux/amd64` + `linux/arm64`), so the same command -runs on a Raspberry Pi and on an amd64 box. It's built `FROM scratch` around a -**fully-static musl binary**, so it's tiny — essentially just the ~26 MB binary -plus a CA bundle. The named `/data` volume holds the ENC source, baked tiles, and -settings, and survives image upgrades. **macOS / Windows** users run the same -image via **Docker Desktop** — no native Mac/Windows binary needed. - -Native binaries (below) remain available as a secondary option for bare-metal -installs. - ## 📦 Install & build **Download a binary.** Every tagged release publishes a self-contained @@ -154,8 +125,7 @@ installs. the archive for your platform and run it — the S-101 catalogue and web frontend are baked in, so you supply only the ENC cells. **macOS** is not shipped as a prebuilt binary (the engine links Apple frameworks Zig can't cross-compile) — Mac -users run the [Docker image](#-run-with-docker) via Docker Desktop, or build from -source below. +users build from source below. Those published binaries embed the **IHO S-101 Portrayal and Feature Catalogues** (compiled into libtile57 from the IHO's own GitHub repositories, which declare no @@ -278,16 +248,8 @@ make vet # go vet ./... make fmt # gofmt -w . make serve # build + serve web/ on :8080 make xbuild # cross-compile with `zig cc` (linux + windows, amd64/arm64) -make musl # fully-static musl binaries (linux amd64+arm64) for the Docker image ``` -The container image is built `FROM scratch` around the static musl binary (`make -musl` / the [`Dockerfile`](Dockerfile)); the engine is git-cloned inside the -builder, so no sibling checkout is needed to `docker build`. `zig cc` -cross-compiles both arches from one native builder — no QEMU — and -[`.github/workflows/docker.yml`](.github/workflows/docker.yml) pushes the -multi-arch image to GHCR on each `v*` tag. - CGO is required — libtile57 is the sole tile/portrayal engine, so `CGO_ENABLED=0` does not build. Cross-compilation still works with **Zig as the C toolchain** (`make xbuild` covers linux and windows; darwin must be built diff --git a/compose.yaml b/compose.yaml deleted file mode 100644 index c9895ed..0000000 --- a/compose.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# chartplotter — the "server-hub on a boat" deployment (Pi / laptop / mini PC). -# One long-running server holds all chart state; every screen (helm, tablet, -# phone) points its browser at http://:8080/ and they stay in sync. -# -# docker compose up -d # pull ghcr.io image + run -# docker compose up -d --build # build the image locally from ./Dockerfile -# -# The image is multi-arch (linux/amd64 + linux/arm64), so the SAME compose file -# runs on a Raspberry Pi and on an amd64 box. - -name: chartplotter - -services: - chartplotter: - image: ghcr.io/beetlebugorg/chartplotter:latest - # `docker compose build` / `--build` builds this instead of pulling. - build: - context: . - args: - VERSION: dev - ports: - - "8080:8080" - volumes: - # Source ENC (downloaded district zips, raw cells) + baked tiles + client - # settings. A named volume survives image upgrades and `compose down`. - - chartplotter-data:/data - restart: unless-stopped - # NOTE: no Docker HEALTHCHECK — the FROM scratch image has no shell/curl for a - # CMD probe. Check health from the host: `curl -fsS http://localhost:8080/`. - -volumes: - chartplotter-data: diff --git a/docs/docs/installation.md b/docs/docs/installation.md index 8328559..cfb55fc 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -12,9 +12,8 @@ self-contained `chartplotter` for **linux and windows** (amd64 + arm64) on the the archive for your platform and run it — the web frontend and the S-101 catalogue are compiled in, so you supply only the ENC cells. **macOS** is not shipped as a prebuilt binary (the engine links Apple frameworks Zig can't -cross-compile); Mac users run the [Docker image](#run-with-docker-recommended) -or [build from source](#build-from-source). To build it yourself on any platform, -follow [Build from source](#build-from-source) below. `go install …@latest` does +cross-compile). To build it yourself on any platform — including macOS — follow +[Build from source](#build-from-source) below. `go install …@latest` does **not** work — the build statically links a native library and uses a local `replace` directive. @@ -30,43 +29,6 @@ material. The project distributes those binaries as an accepted position; see ::: -## Run with Docker (recommended) - -The simplest way to run chartplotter — and the primary path for the -**server-hub-on-a-boat** deployment (a Raspberry Pi, laptop, or mini PC that -holds all chart state while every screen just points a browser at it) — is the -published container image: - -```sh -docker run -p 8080:8080 -v chartplotter-data:/data \ - ghcr.io/beetlebugorg/chartplotter -# open http://localhost:8080 -``` - -Or with Docker Compose, using the [`compose.yaml`](https://github.com/beetlebugorg/chartplotter/blob/main/compose.yaml) -in the repo: - -```sh -docker compose up -d -``` - -The image is **multi-arch** (`linux/amd64` + `linux/arm64`), so the same command -runs on a Raspberry Pi (arm64) and on an amd64 box. It is built `FROM scratch` -around a **fully-static musl binary**, so it is tiny — essentially just the -~26 MB binary plus a CA-certificate bundle. The named `/data` volume holds the -downloaded ENC source, the baked tiles, and your settings, and survives image -upgrades (`docker compose pull && docker compose up -d`). - -The container binds `0.0.0.0:8080` inside, so map it with `-p 8080:8080` (or any -host port). It writes source ENC to `/data` and regenerable baked tiles to -`/data/cache`. - -:::tip macOS and Windows -Run the same image via **Docker Desktop** — no native Mac or Windows binary is -needed. The native binaries below remain available as a secondary option for -bare-metal installs. -::: - ## Requirements - **Go 1.26 or newer.** @@ -131,7 +93,6 @@ targets you'll use most: | `make fmt` | `gofmt -w .`. | | `make serve` | Build, then serve the web frontend on `:8080` (`HOST`/`PORT`/`ASSETS` overridable). | | `make xbuild` | Cross-compile release binaries with `zig cc` (linux + windows, amd64/arm64). | -| `make musl` | Fully-static musl binaries (linux amd64 + arm64) for the `FROM scratch` Docker image. | Run `make fmt vet test` before you commit. `make xbuild` deliberately skips macOS — Go's `crypto/x509` links Apple frameworks Zig can't cross-compile, so a