diff --git a/.gitignore b/.gitignore index 18457b6..f04e155 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ build/ # uv virtualenv, at the repository root because pyproject.toml is there .venv/ __pycache__/ +result diff --git a/Dockerfile b/Dockerfile index 6f1a785..1b3e9c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,12 @@ FROM golang:1.26-alpine3.24 AS build +# VERSION and COMMIT are baked into the binary at build time via the -ldflags +# below. Defaults mirror flake.nix and CI. The .git directory is excluded from +# the build context, so the real commit has to be supplied by the host, e.g.: +# docker build --build-arg COMMIT=$(git rev-parse HEAD) . +ARG VERSION=0.0.1-alpha +ARG COMMIT=dev + WORKDIR /app ENV CGO_ENABLED=0 @@ -11,7 +18,9 @@ COPY . . RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ - go build -trimpath -ldflags="-s -w" -o /output/pen-bot ./cmd/pen-fun + go build -trimpath \ + -ldflags="-s -w -X github.com/Neon-Genesis-Linux/pen-bot/internal/core.Version=${VERSION} -X github.com/Neon-Genesis-Linux/pen-bot/internal/core.Commit=${COMMIT}" \ + -o /output/pen-bot ./cmd/pen-fun # The database lives on a volume mounted here. The directory is created in # this stage so that it can be given to the unprivileged user the runtime diff --git a/compose.yaml b/compose.yaml index ba01f1a..ebd32e7 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,6 +1,13 @@ services: app: - build: . + build: + context: . + # VERSION and COMMIT are baked into the binary via -ldflags in the + # Dockerfile. Defaults match flake.nix/CI; override from the host, e.g.: + # COMMIT=$(git rev-parse HEAD) docker compose build + args: + VERSION: ${VERSION:-0.0.1-alpha} + COMMIT: ${COMMIT:-dev} pull_policy: build env_file: - .env diff --git a/flake.nix b/flake.nix index f0e6eba..cc42c4e 100644 --- a/flake.nix +++ b/flake.nix @@ -3,13 +3,58 @@ nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; }; - outputs = {nixpkgs, ...}: let + outputs = inputs: let + nixpkgs = inputs.nixpkgs; + lib = nixpkgs.lib; systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin"]; - forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + version = "0.0.1-alpha"; + commit = + if (builtins.hasAttr "self" inputs) && inputs.self != null + && (builtins.hasAttr "rev" inputs.self) + then inputs.self.rev + else "dev"; + # Vendored dependency-tree hash. Obtained via vendorHash = lib.fakeHash; + # then reading the reported value and confirming a full `nix build`. + vendorHash = "sha256-QMHsT94FBrbhFOmfHdu8+EmqG+eMLlYZHGKLxuW9Fmk="; + + # Only ship first-party Go code and its module files to the build. The + # gitignored dev artifacts `custom-gcl` (~55 MB) and `.venv` are excluded so + # the hermetic derivation stays small. `lib.fileset` is used instead of + # `filterSource`: the `builtins.filterSource` primop is unreliable here. + src = + let + repo = ./.; + in + lib.fileset.toSource { + root = repo; + fileset = + lib.fileset.unions [ + (lib.fileset.fromSource (repo + "/go.mod")) + (lib.fileset.fromSource (repo + "/go.sum")) + (lib.fileset.fromSource (repo + "/cmd")) + (lib.fileset.fromSource (repo + "/internal")) + ]; + }; + + buildPen = pkgs: pkgs.buildGoModule { + pname = "pen-fun"; + version = version; + src = src; + subPackages = [ "cmd/pen-fun" ]; + vendorHash = vendorHash; + ldflags = [ + "-s" + "-w" + "-X" + "github.com/Neon-Genesis-Linux/pen-bot/internal/core.Version=${version}" + "-X" + "github.com/Neon-Genesis-Linux/pen-bot/internal/core.Commit=${commit}" + ]; + }; in { - devShells = forAllSystems (pkgs: { - default = pkgs.mkShell { - packages = with pkgs; [ + devShells = lib.genAttrs systems (system: { + default = nixpkgs.legacyPackages.${system}.mkShell { + packages = with nixpkgs.legacyPackages.${system}; [ go_1_26 golangci-lint gofumpt @@ -19,7 +64,19 @@ yamlfmt dockerfmt ]; + # custom-gcl (golangci-lint + NilAway) is built from .custom-gcl.yml at + # activation, mirroring .github/workflows/ci.yml. Network is available in + # a dev shell, and the guard skips rebuilding when it already exists. + shellHook = '' + if [ ! -x ./custom-gcl ]; then + golangci-lint custom + fi + ''; }; }); + + packages = lib.genAttrs systems (system: { + default = buildPen nixpkgs.legacyPackages.${system}; + }); }; } diff --git a/internal/core/start.go b/internal/core/start.go index 1c47e30..472c499 100644 --- a/internal/core/start.go +++ b/internal/core/start.go @@ -17,12 +17,13 @@ import ( "github.com/Neon-Genesis-Linux/pen-bot/internal/db" ) -// Start connects the bot to Discord, syncs the registered commands, and blocks -// until ctx is cancelled or the process is interrupted. It reads the intents -// and command definitions once, at startup, so anything registered afterwards -// has no effect. +var ( + Version = "dev" + Commit = "dev" +) + func Start(ctx context.Context, token string, guildIDs []snowflake.ID) error { - slog.Info("starting pen bot...") + slog.Info("starting pen bot...", "version", Version, "commit", Commit) slog.Info("disgo version", slog.String("version", disgo.Version)) if len(guildIDs) > 0 {