diff --git a/Dockerfile b/Dockerfile index e2b3366..37acc9a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,29 +30,12 @@ RUN apt-get update && \ WORKDIR /src # Copy module files first so Docker can cache downloaded dependencies while the -# application source changes. This makes repeated local builds much faster. +# application source changes. The release script later mounts the live repository +# over /src, but the module cache remains in the image and keeps repeated builds +# faster. COPY go.mod go.sum ./ RUN go mod download -COPY . . - -# CGO is required by Fyne. This builder produces the release artifacts from -# Linux: Linux amd64, Linux arm64, and a Windows amd64 binary cross-compiled with -# MinGW. The Windows resource is generated inside the container so Explorer still -# sees the application icon. -RUN version="$(sed -n 's/^var Version = "\(.*\)"/\1/p' src/core/version.go)" && \ - version="${version:-0.0.0-dev}" && \ - mkdir -p /out/linux /out/windows && \ - CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \ - go build -trimpath -ldflags "-s -w -X github.com/pysentry/pysentry/src/core.Version=${version}" \ - -o "/out/linux/pysentry-${version}-linux-amd64" ./cmd/pysentry && \ - CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 \ - CGO_CFLAGS="--sysroot=/ -I/usr/include/aarch64-linux-gnu" \ - CGO_LDFLAGS="--sysroot=/ -L/usr/lib/aarch64-linux-gnu" \ - PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig \ - go build -trimpath -ldflags "-s -w -X github.com/pysentry/pysentry/src/core.Version=${version}" \ - -o "/out/linux/pysentry-${version}-linux-arm64" ./cmd/pysentry && \ - x86_64-w64-mingw32-windres -O coff -o cmd/pysentry/rsrc_windows_amd64.syso packaging/windows/pysentry.rc && \ - CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows GOARCH=amd64 \ - go build -trimpath -ldflags "-s -w -H=windowsgui -X github.com/pysentry/pysentry/src/core.Version=${version}" \ - -o "/out/windows/pysentry-${version}-windows-amd64.exe" ./cmd/pysentry +# The image intentionally stops here. Artifact build commands live in +# scripts/build-release-linux.sh so a developer can choose targets interactively +# without rebuilding this environment image for every selection. diff --git a/README.md b/README.md index 89abba5..ca955ee 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ PySentry is being designed and implemented with assistance from OpenAI Codex. ## Features -- Native desktop GUI built with Fyne. +- Native desktop GUI built with [Fyne](https://fyne.io/). - Job storage in one clean YAML file. - App settings in a separate YAML file. - `@every` schedules and standard 5-field cron expressions. @@ -21,16 +21,37 @@ PySentry is being designed and implemented with assistance from OpenAI Codex. Common: -- Go 1.22 or newer. +- [Go](https://go.dev/) 1.22 or newer. Windows: - MSYS2 with UCRT64 GCC in `C:\msys64\ucrt64\bin`. +Install these dependencies on Windows: + +```powershell +# 1. Install Go 1.22 or newer from https://go.dev/dl/. +# The default installer path is C:\Program Files\Go. +go version + +# 2. Install MSYS2 from https://www.msys2.org/. +# Use the default installation path so UCRT64 tools are placed under +# C:\msys64\ucrt64\bin. + +# 3. Open "MSYS2 UCRT64" from the Start menu and install GCC plus windres. +pacman -Syu +pacman -S --needed mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-binutils + +# 4. In PowerShell, check that the compiler is available where the build script +# expects it. build-windows.bat prepends this directory automatically. +Test-Path C:\msys64\ucrt64\bin\gcc.exe +Test-Path C:\msys64\ucrt64\bin\windres.exe +``` + Linux: - A C compiler. -- Fyne native build dependencies, including OpenGL/X11 development packages. +- [Fyne](https://fyne.io/) native build dependencies, including OpenGL/X11 development packages. On Debian/Ubuntu, the Linux dependencies are typically: @@ -97,13 +118,20 @@ dist\linux\pysentry-0.1.0-linux-amd64 Release build from Linux: ```bash -# Builds Linux amd64, Linux arm64, and Windows amd64 artifacts from one -# Linux/Docker workflow. The Dockerfile includes Linux Fyne dependencies plus -# cross-compilers for arm64 Linux and the Windows .exe. +# Interactively choose Linux amd64, Linux arm64, Windows amd64, or all artifacts +# from one Linux/Docker workflow. The Dockerfile contains the builder +# environment; the build commands live in this script. chmod +x ./scripts/build-release-linux.sh ./scripts/build-release-linux.sh ``` +Non-interactive release builds can pass target names: + +```bash +# Build only Linux arm64 and Windows amd64 artifacts. +./scripts/build-release-linux.sh linux-arm64 windows-amd64 +``` + The binaries are copied to: ```text @@ -287,7 +315,7 @@ Build outputs are written to `dist/`. The old local `bin/` directory is not used PySentry keeps the direct dependency list intentionally small: -- `fyne.io/fyne/v2` for the native GUI. +- [`fyne.io/fyne/v2`](https://fyne.io/) for the native GUI. - `github.com/robfig/cron/v3` for cron schedule parsing. - `gopkg.in/yaml.v3` for YAML settings and jobs. diff --git a/scripts/build-linux-docker.sh b/scripts/build-linux-docker.sh index 3aab429..68688a6 100644 --- a/scripts/build-linux-docker.sh +++ b/scripts/build-linux-docker.sh @@ -12,17 +12,14 @@ output="${1:-dist/linux/pysentry-${version}-linux-amd64}" # environment in Docker makes Linux builds repeatable from Windows hosts and CI. docker build -f Dockerfile -t gitea.mixdep.ru/mix/pysentry-builder . -# The image build produces /out/linux and /out/windows. This helper copies only -# the Linux binary for compatibility with the older Linux-only workflow; use -# build-release-linux.sh when both platform artifacts are needed. -container_id="$(docker create gitea.mixdep.ru/mix/pysentry-builder)" -cleanup() { - docker rm "$container_id" >/dev/null -} -trap cleanup EXIT - mkdir -p "$(dirname "$output")" -docker cp "${container_id}:/out/linux/pysentry-${version}-linux-amd64" "$output" +docker run --rm \ + -e "VERSION=${version}" \ + -e "OUTPUT=${output}" \ + -v "$(pwd):/src" \ + -w /src \ + gitea.mixdep.ru/mix/pysentry-builder \ + bash -lc 'CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w -X github.com/pysentry/pysentry/src/core.Version=${VERSION}" -o "${OUTPUT}" ./cmd/pysentry' # Icons are embedded in the Go binary, so there is no assets directory to copy # after extracting the Linux executable. diff --git a/scripts/build-release-linux.sh b/scripts/build-release-linux.sh index 63fb9a2..92969f8 100644 --- a/scripts/build-release-linux.sh +++ b/scripts/build-release-linux.sh @@ -1,22 +1,133 @@ #!/usr/bin/env bash set -euo pipefail -# Build all release artifacts from a Linux host or CI runner. The Docker image -# contains Linux/Fyne dependencies for amd64 and arm64, plus the MinGW -# cross-compiler used for the Windows GUI executable. +# Build selected release artifacts from a Linux host or CI runner. The Docker +# image contains Linux/Fyne dependencies for amd64 and arm64, plus the MinGW +# cross-compiler used for the Windows GUI executable. Actual build commands live +# here rather than in Dockerfile so target selection does not require rebuilding +# the image. tag="gitea.mixdep.ru/mix/pysentry-builder" +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "${script_dir}/.." && pwd)" +cd "$repo_root" + +version="$(sed -n 's/^var Version = "\(.*\)"/\1/p' src/core/version.go)" +version="${version:-0.0.0-dev}" + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac + done +} + +run_in_builder() { + docker run --rm \ + -e "VERSION=${version}" \ + -v "${repo_root}:/src" \ + -w /src \ + "$tag" \ + bash -lc "$1" +} + +build_linux_amd64() { + run_in_builder 'mkdir -p dist/linux && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w -X github.com/pysentry/pysentry/src/core.Version=${VERSION}" -o "dist/linux/pysentry-${VERSION}-linux-amd64" ./cmd/pysentry' +} + +build_linux_arm64() { + run_in_builder 'mkdir -p dist/linux && CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CGO_CFLAGS="--sysroot=/ -I/usr/include/aarch64-linux-gnu" CGO_LDFLAGS="--sysroot=/ -L/usr/lib/aarch64-linux-gnu" PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig go build -trimpath -ldflags "-s -w -X github.com/pysentry/pysentry/src/core.Version=${VERSION}" -o "dist/linux/pysentry-${VERSION}-linux-arm64" ./cmd/pysentry' +} + +build_windows_amd64() { + run_in_builder 'mkdir -p dist/windows && x86_64-w64-mingw32-windres -O coff -o cmd/pysentry/rsrc_windows_amd64.syso packaging/windows/pysentry.rc && CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags "-s -w -H=windowsgui -X github.com/pysentry/pysentry/src/core.Version=${VERSION}" -o "dist/windows/pysentry-${VERSION}-windows-amd64.exe" ./cmd/pysentry' +} + +mapfile -t targets < <(choose_targets "$@" | normalize_targets | awk '!seen[$0]++') +if [ "${#targets[@]}" -eq 0 ]; then + echo "No build targets selected." >&2 + exit 1 +fi + +echo "Building Docker builder image: $tag" docker build -f Dockerfile -t "$tag" . -container_id="$(docker create "$tag")" -cleanup() { - docker rm "$container_id" >/dev/null -} -trap cleanup EXIT - -mkdir -p dist/linux dist/windows -docker cp "${container_id}:/out/linux/." dist/linux -docker cp "${container_id}:/out/windows/." dist/windows +for target in "${targets[@]}"; do + echo "Building $target..." + case "$target" in + linux-amd64) + build_linux_amd64 + ;; + linux-arm64) + build_linux_arm64 + ;; + windows-amd64) + build_windows_amd64 + ;; + esac +done echo "Built release artifacts:" -find dist/linux dist/windows -maxdepth 1 -type f -print +find dist/linux dist/windows -maxdepth 1 -type f -print 2>/dev/null || true