ci: add GitHub and Codeberg release build workflows
Add tag-triggered CI that builds and publishes the linux/amd64, linux/arm64, and windows/amd64 release binaries on both forges. - scripts/ci-build-release.sh: shared, Docker-free build+package for all three targets (Windows cross-compiled via MinGW), reused by both workflows so the build logic lives in one place. - .github/workflows/release.yml: GitHub Actions, publishes via softprops/action-gh-release using the built-in token. - .forgejo/workflows/release.yml: Forgejo Actions for Codeberg, publishes via forgejo-release using a RELEASE_TOKEN secret. - .gitattributes: force LF on workflow YAML so bash run: blocks don't break on Linux runners. - docs/DEVELOPMENT.md: document the tag -> release flow and token setup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
# Forgejo Actions workflow for Codeberg. It mirrors .github/workflows/release.yml
|
||||||
|
# and reuses the same scripts/ci-build-release.sh, so the actual build/package
|
||||||
|
# commands live in exactly one place. Codeberg has no Windows runners, but the
|
||||||
|
# Windows binary is cross-compiled with MinGW-w64 from the Linux job, so a single
|
||||||
|
# golang:1.22-bookworm container produces all three artifacts.
|
||||||
|
#
|
||||||
|
# Publishing needs a token that can write releases. Add a repository secret named
|
||||||
|
# RELEASE_TOKEN (a Codeberg access token with the "write:repository" scope) under
|
||||||
|
# Settings -> Actions -> Secrets. Without it the build still runs; only the
|
||||||
|
# upload step is skipped.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: golang:1.22-bookworm
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: https://code.forgejo.org/actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install cross toolchain
|
||||||
|
# Same package list as the repo Dockerfile / GitHub workflow: native gcc
|
||||||
|
# plus X11/GL headers, the aarch64 cross compiler with arm64 runtime
|
||||||
|
# libs, MinGW-w64 for the Windows GUI binary, and zip for packaging.
|
||||||
|
run: |
|
||||||
|
dpkg --add-architecture arm64
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
gcc \
|
||||||
|
libc6-dev \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
libc6-dev-arm64-cross \
|
||||||
|
linux-libc-dev-arm64-cross \
|
||||||
|
gcc-mingw-w64-x86-64 \
|
||||||
|
binutils-mingw-w64-x86-64 \
|
||||||
|
pkg-config \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
xorg-dev \
|
||||||
|
libgl1-mesa-dev:arm64 \
|
||||||
|
libx11-dev:arm64 \
|
||||||
|
libxcursor-dev:arm64 \
|
||||||
|
libxrandr-dev:arm64 \
|
||||||
|
libxinerama-dev:arm64 \
|
||||||
|
libxi-dev:arm64 \
|
||||||
|
libxxf86vm-dev:arm64 \
|
||||||
|
zip
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
- name: Derive version
|
||||||
|
# On a tag push, strip the leading "v" so artifact names and the injected
|
||||||
|
# app version match the release tag.
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then
|
||||||
|
echo "value=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and package
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.version.outputs.value }}
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/ci-build-release.sh
|
||||||
|
scripts/ci-build-release.sh
|
||||||
|
|
||||||
|
- name: Collect release files
|
||||||
|
# forgejo-release uploads every file in a single directory, so gather the
|
||||||
|
# archives into one flat folder.
|
||||||
|
run: |
|
||||||
|
mkdir -p dist/release
|
||||||
|
cp dist/linux/*.tar.gz dist/windows/*.zip dist/release/
|
||||||
|
|
||||||
|
- name: Publish release
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: https://code.forgejo.org/actions/forgejo-release@v2
|
||||||
|
with:
|
||||||
|
direction: upload
|
||||||
|
url: https://codeberg.org
|
||||||
|
repo: ${{ github.repository }}
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
release-dir: dist/release
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
override: true
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
# Keep shell scripts LF even on Windows checkouts so bash on Linux hosts
|
# Keep shell scripts LF even on Windows checkouts so bash on Linux hosts
|
||||||
# doesn't choke on trailing CRs (e.g. "set: pipefail: invalid parameter name").
|
# doesn't choke on trailing CRs (e.g. "set: pipefail: invalid parameter name").
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
|
|
||||||
|
# CI workflow YAML embeds shell in `run:` blocks that Linux runners execute with
|
||||||
|
# bash, so keep these LF for the same reason as the shell scripts above.
|
||||||
|
.github/workflows/*.yml text eol=lf
|
||||||
|
.forgejo/workflows/*.yml text eol=lf
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
# Build the Linux (amd64/arm64) and Windows (amd64) binaries whenever a version
|
||||||
|
# tag is pushed, then attach the packaged archives to a GitHub Release.
|
||||||
|
#
|
||||||
|
# Everything runs inside golang:1.22-bookworm — the same base image as the
|
||||||
|
# repo Dockerfile — so the CGO/Fyne toolchain matches the local release builds.
|
||||||
|
# The Windows binary is cross-compiled with MinGW-w64 from the same Linux job,
|
||||||
|
# which is why no windows-latest runner is needed.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
# Allow a manual run (from the Actions tab) to smoke-test the build without a
|
||||||
|
# tag. Manual runs build the artifacts but do not publish a release.
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write # required to create the release and upload assets
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: golang:1.22-bookworm
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install cross toolchain
|
||||||
|
# Mirrors the package list in the repo Dockerfile: native gcc + X11/GL
|
||||||
|
# headers for amd64, the aarch64 cross compiler with arm64 runtime libs,
|
||||||
|
# and the MinGW-w64 toolchain for the Windows GUI binary. zip packages
|
||||||
|
# the Windows archive.
|
||||||
|
run: |
|
||||||
|
dpkg --add-architecture arm64
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
gcc \
|
||||||
|
libc6-dev \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
libc6-dev-arm64-cross \
|
||||||
|
linux-libc-dev-arm64-cross \
|
||||||
|
gcc-mingw-w64-x86-64 \
|
||||||
|
binutils-mingw-w64-x86-64 \
|
||||||
|
pkg-config \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
xorg-dev \
|
||||||
|
libgl1-mesa-dev:arm64 \
|
||||||
|
libx11-dev:arm64 \
|
||||||
|
libxcursor-dev:arm64 \
|
||||||
|
libxrandr-dev:arm64 \
|
||||||
|
libxinerama-dev:arm64 \
|
||||||
|
libxi-dev:arm64 \
|
||||||
|
libxxf86vm-dev:arm64 \
|
||||||
|
zip
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
- name: Derive version
|
||||||
|
# For a tag push, use the tag without its leading "v" so the artifact
|
||||||
|
# names and the injected app version match the release. Otherwise fall
|
||||||
|
# back to the version in source (handled by the build script).
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
ref="${GITHUB_REF_NAME:-}"
|
||||||
|
if [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then
|
||||||
|
echo "value=${ref#v}" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and package
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.version.outputs.value }}
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/ci-build-release.sh
|
||||||
|
scripts/ci-build-release.sh
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: gosentry-release
|
||||||
|
path: |
|
||||||
|
dist/linux/*.tar.gz
|
||||||
|
dist/windows/*.zip
|
||||||
|
|
||||||
|
- name: Publish release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
dist/linux/*.tar.gz
|
||||||
|
dist/windows/*.zip
|
||||||
|
generate_release_notes: true
|
||||||
@@ -123,6 +123,43 @@ dist/linux/gosentry-0.9.0-linux-arm64
|
|||||||
dist/windows/gosentry-0.9.0-windows-amd64.exe
|
dist/windows/gosentry-0.9.0-windows-amd64.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Automated release builds (CI)
|
||||||
|
|
||||||
|
Tagged releases are built automatically on both GitHub and Codeberg:
|
||||||
|
|
||||||
|
- `.github/workflows/release.yml` — GitHub Actions.
|
||||||
|
- `.forgejo/workflows/release.yml` — Forgejo Actions (Codeberg).
|
||||||
|
|
||||||
|
Both run inside `golang:1.22-bookworm` (the same base image as the
|
||||||
|
[Dockerfile](../Dockerfile)), install the cross toolchain, and call
|
||||||
|
`scripts/ci-build-release.sh`, which builds and packages all three artifacts:
|
||||||
|
|
||||||
|
```text
|
||||||
|
dist/linux/gosentry-<version>-linux-amd64.tar.gz
|
||||||
|
dist/linux/gosentry-<version>-linux-arm64.tar.gz
|
||||||
|
dist/windows/gosentry-<version>-windows-amd64.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
The Windows binary is cross-compiled with MinGW-w64 from the Linux job, so no
|
||||||
|
Windows runner is required. Each archive contains the executable plus `README.md`
|
||||||
|
and `CHANGELOG.md`, matching the local `package-*` scripts.
|
||||||
|
|
||||||
|
To cut a release, bump `src/app/version.go` and push a matching `v` tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag v0.11.5
|
||||||
|
git push origin v0.11.5 # and to the Codeberg remote
|
||||||
|
```
|
||||||
|
|
||||||
|
The workflow strips the leading `v` from the tag and injects it as the version,
|
||||||
|
so the tag must match `version.go`. Pushing the tag triggers the build and
|
||||||
|
attaches the archives to a release on that forge. `workflow_dispatch` also allows
|
||||||
|
a manual, publish-free build to smoke-test the pipeline.
|
||||||
|
|
||||||
|
Codeberg publishing needs a repository secret named `RELEASE_TOKEN` (a Codeberg
|
||||||
|
access token with the `write:repository` scope) under
|
||||||
|
**Settings → Actions → Secrets**. GitHub uses the built-in `GITHUB_TOKEN`.
|
||||||
|
|
||||||
## Run From Source
|
## Run From Source
|
||||||
|
|
||||||
Windows:
|
Windows:
|
||||||
|
|||||||
Executable
+100
@@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Build and package every release artifact on a Linux host that already has the
|
||||||
|
# cross toolchain installed (native gcc + X11/OpenGL headers, the aarch64 cross
|
||||||
|
# compiler, and the MinGW-w64 toolchain for the Windows GUI binary). This is the
|
||||||
|
# non-Docker counterpart to scripts/build-release-linux.sh: the CI workflows in
|
||||||
|
# .github/ and .forgejo/ install those packages directly on the runner and then
|
||||||
|
# call this script, so the exact build/package commands live in one place and do
|
||||||
|
# not drift between the two forges.
|
||||||
|
#
|
||||||
|
# The build flags mirror the other scripts intentionally: -trimpath strips local
|
||||||
|
# paths, -s -w drops symbol/debug tables to shrink the binaries, -H=windowsgui
|
||||||
|
# suppresses the console window on Windows, and -X injects the version so the
|
||||||
|
# GUI and artifact names agree.
|
||||||
|
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd "${script_dir}/.." && pwd)"
|
||||||
|
cd "$repo_root"
|
||||||
|
|
||||||
|
# VERSION can be provided by CI (for a tagged release the workflow passes the tag
|
||||||
|
# without its leading "v"). Fall back to the source of truth in version.go so the
|
||||||
|
# script also works for a plain local invocation.
|
||||||
|
version="${VERSION:-$(sed -n 's/^var Version = "\(.*\)"/\1/p' src/app/version.go | tr -d '\r')}"
|
||||||
|
version="${version:-0.0.0-dev}"
|
||||||
|
ldflags="-s -w -X gitea.mixdep.ru/mix/gosentry/src/app.Version=${version}"
|
||||||
|
|
||||||
|
echo "Building GoSentry ${version} release artifacts"
|
||||||
|
mkdir -p dist/linux dist/windows
|
||||||
|
|
||||||
|
# --- Linux amd64 -----------------------------------------------------------
|
||||||
|
echo "==> linux/amd64"
|
||||||
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -buildvcs=false -trimpath -ldflags "$ldflags" \
|
||||||
|
-o "dist/linux/gosentry-${version}-linux-amd64" ./cmd/gosentry
|
||||||
|
|
||||||
|
# --- Linux arm64 (cross compiled) ------------------------------------------
|
||||||
|
echo "==> linux/arm64"
|
||||||
|
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 -buildvcs=false -trimpath -ldflags "$ldflags" \
|
||||||
|
-o "dist/linux/gosentry-${version}-linux-arm64" ./cmd/gosentry
|
||||||
|
|
||||||
|
# --- Windows amd64 (cross compiled with MinGW) -----------------------------
|
||||||
|
echo "==> windows/amd64"
|
||||||
|
# windres embeds the .ico into the PE resource so Explorer/taskbar show the icon.
|
||||||
|
# The .syso is suffixed windows_amd64, so Go only links it into the Windows build
|
||||||
|
# and ignores it for the Linux targets above.
|
||||||
|
x86_64-w64-mingw32-windres -O coff \
|
||||||
|
-o cmd/gosentry/rsrc_windows_amd64.syso packaging/windows/gosentry.rc
|
||||||
|
CC=x86_64-w64-mingw32-gcc \
|
||||||
|
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 \
|
||||||
|
go build -buildvcs=false -trimpath -ldflags "-H=windowsgui ${ldflags}" \
|
||||||
|
-o "dist/windows/gosentry-${version}-windows-amd64.exe" ./cmd/gosentry
|
||||||
|
|
||||||
|
# --- Package ---------------------------------------------------------------
|
||||||
|
# Each archive holds the executable plus the top-level README and CHANGELOG,
|
||||||
|
# flattened to the archive root so a user can extract straight into any folder.
|
||||||
|
# This matches the layout produced by package-linux.sh / package-windows.bat.
|
||||||
|
package_linux() {
|
||||||
|
local arch="$1"
|
||||||
|
local binary="dist/linux/gosentry-${version}-linux-${arch}"
|
||||||
|
local tarball="dist/linux/gosentry-${version}-linux-${arch}.tar.gz"
|
||||||
|
local staging="dist/linux/_staging-${arch}"
|
||||||
|
|
||||||
|
rm -rf "$staging"
|
||||||
|
mkdir -p "$staging"
|
||||||
|
cp "$binary" "$staging/gosentry"
|
||||||
|
cp README.md "$staging/README.md"
|
||||||
|
cp docs/CHANGELOG.md "$staging/CHANGELOG.md"
|
||||||
|
tar -czf "$tarball" -C "$staging" .
|
||||||
|
rm -rf "$staging"
|
||||||
|
echo "Packaged $tarball"
|
||||||
|
}
|
||||||
|
|
||||||
|
package_windows() {
|
||||||
|
local binary="dist/windows/gosentry-${version}-windows-amd64.exe"
|
||||||
|
local zipfile="gosentry-${version}-windows-amd64.zip"
|
||||||
|
local staging="dist/windows/_staging-amd64"
|
||||||
|
|
||||||
|
rm -rf "$staging"
|
||||||
|
mkdir -p "$staging"
|
||||||
|
cp "$binary" "$staging/gosentry.exe"
|
||||||
|
cp README.md "$staging/README.md"
|
||||||
|
cp docs/CHANGELOG.md "$staging/CHANGELOG.md"
|
||||||
|
# -j flattens: files land at the zip root with no staging path prefix.
|
||||||
|
( cd "$staging" && zip -j -q "../${zipfile}" ./* )
|
||||||
|
rm -rf "$staging"
|
||||||
|
echo "Packaged dist/windows/${zipfile}"
|
||||||
|
}
|
||||||
|
|
||||||
|
package_linux amd64
|
||||||
|
package_linux arm64
|
||||||
|
package_windows
|
||||||
|
|
||||||
|
echo "Release artifacts:"
|
||||||
|
find dist/linux dist/windows -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.zip' \) -print
|
||||||
Reference in New Issue
Block a user