Files
drover-go/.gitea/workflows/release.yml
T
root 804e32a418
Build / test (push) Successful in 19s
Build / build-windows (push) Successful in 6s
Release / release (push) Failing after 1m3s
workflows: surface GITHUB_TOKEN via env, use it in clone URL
Root cause of the earlier "Failed to authenticate" / "could not read
Username" failures: shell scripts in Gitea Actions don't automatically
inherit secrets — \${GITHUB_TOKEN} expanded to an empty string, so the
URL became "https://forgejo-runner:@..." (empty password) and Gitea's
auth layer rejected it.

Fix: explicit env: block on the Checkout step pulls the token in,
then the URL uses it via x-access-token (canonical token-as-password
username, accepted by Gitea, GitHub, Forgejo alike).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:39:42 +03:00

197 lines
8.7 KiB
YAML

name: Release
on:
push:
tags:
- 'v*'
# We only ever publish a tag once. Don't cancel a running release —
# if the user re-pushed the same tag, that's a manual action and
# should fail loudly via Forgejo's "release exists" 409 from the API.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
release:
runs-on: go
steps:
- name: Checkout
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git clone --no-checkout "https://x-access-token:${GITHUB_TOKEN}@git.okcu.io/${GITHUB_REPOSITORY}.git" /tmp/src
git -C /tmp/src checkout "$GITHUB_SHA"
cp -a /tmp/src/. .
- name: Extract version from tag
id: version
run: |
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#v}"
echo "tag=${TAG}" | tee -a "$GITHUB_OUTPUT"
echo "version=${VERSION}" | tee -a "$GITHUB_OUTPUT"
- name: Cross-compile drover.exe (windows/amd64)
env:
GOOS: windows
GOARCH: amd64
CGO_ENABLED: '0'
run: |
set -e
BUILD_DATE="$(date -u +%Y-%m-%d)"
SHORT_SHA="${GITHUB_SHA:0:7}"
mkdir -p dist
go build -trimpath -ldflags="-s -w \
-X main.Version=${{ steps.version.outputs.version }} \
-X main.Commit=${SHORT_SHA} \
-X main.BuildDate=${BUILD_DATE}" \
-o "dist/drover-${{ steps.version.outputs.tag }}-windows-amd64.exe" \
./cmd/drover
- name: Build portable zip
run: |
set -e
# Stage files for the zip in a clean directory so the archive root
# contains exactly what users see when they unpack.
apt-get update >/dev/null
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends zip >/dev/null
STAGE="$(mktemp -d)"
cp "dist/drover-${{ steps.version.outputs.tag }}-windows-amd64.exe" "$STAGE/drover.exe"
mkdir -p "$STAGE/windivert"
cp third_party/windivert/WinDivert.dll "$STAGE/windivert/"
cp third_party/windivert/WinDivert64.sys "$STAGE/windivert/"
cp third_party/windivert/LICENSE-LGPL "$STAGE/windivert/"
cp third_party/windivert/README.md "$STAGE/windivert/"
cp LICENSE "$STAGE/LICENSE.txt"
cp README.md "$STAGE/README.md"
# zip from inside STAGE so paths are relative
( cd "$STAGE" && zip -r9 - . ) > "dist/drover-${{ steps.version.outputs.tag }}-windows-amd64.zip"
- name: Install Wine + Inno Setup (via innoextract, no Wine GUI install)
run: |
set -e
dpkg --add-architecture i386
apt-get update
# innoextract unpacks Inno Setup .exe installers without running them
# under Wine. We then copy the extracted compiler files into the Wine
# prefix and call ISCC.exe via Wine — much more reliable than running
# the whole interactive installer through Wine + Xvfb (which failed
# silently in the previous iteration).
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
wine wine64 innoextract xvfb curl ca-certificates \
>/dev/null
export WINEPREFIX="$HOME/.wine"
export WINEDLLOVERRIDES="mscoree,mshtml="
export DISPLAY=":99"
Xvfb :99 -screen 0 1024x768x16 &
sleep 1
wineboot --init >/dev/null 2>&1 || true
# Pin Inno Setup version. Must match what innoextract on the
# runner image can unpack — innoextract 1.11 (latest in Debian
# trixie as of 2026-05) supports Inno Setup up to 6.4.x. Newer
# versions (6.5+, 6.7+) fail with "Setup loader checksum mismatch".
# When innoextract 1.12+ lands we can bump to 6.7.x.
INNO_VERSION="6.4.3"
INNO_TAG="is-6_4_3"
curl -fsSL -o /tmp/innosetup.exe \
"https://github.com/jrsoftware/issrc/releases/download/${INNO_TAG}/innosetup-${INNO_VERSION}.exe"
# innoextract unpacks the installer payload to ./app — that's the
# full Inno Setup directory tree (ISCC.exe + support DLLs/scripts).
mkdir -p /tmp/inno-extract
( cd /tmp/inno-extract && innoextract /tmp/innosetup.exe )
# Place it where Windows-style paths can resolve. Wine maps
# C:\InnoSetup to ~/.wine/drive_c/InnoSetup so we copy there.
mkdir -p "$HOME/.wine/drive_c/InnoSetup"
cp -a /tmp/inno-extract/app/. "$HOME/.wine/drive_c/InnoSetup/"
# Sanity check
ls -la "$HOME/.wine/drive_c/InnoSetup/ISCC.exe"
xvfb-run -a wine "C:\\InnoSetup\\ISCC.exe" /? 2>&1 | head -5 || true
- name: Compile Inno Setup installer
env:
DISPLAY: ":99"
WINEDLLOVERRIDES: "mscoree,mshtml="
run: |
set -e
export WINEPREFIX="$HOME/.wine"
# installer.iss references "..\drover.exe" relative to installer/,
# i.e. expects a plain drover.exe at repo root. Stage a copy of the
# versioned binary there before invoking ISCC.
cp "dist/drover-${{ steps.version.outputs.tag }}-windows-amd64.exe" drover.exe
# Inno Setup wants Windows-style /D defines and the script path
# in DOS form. We invoke from repo root; iss script resolves its
# own ..\ paths from installer/.
mkdir -p installer/Output
xvfb-run -a wine "C:\\InnoSetup\\ISCC.exe" \
"/DMyAppVersion=${{ steps.version.outputs.version }}" \
"installer\\installer.iss"
# Inno Setup default OutputDir is "installer/Output/" (relative
# to the .iss). Move the produced exe to dist/ with the canonical
# asset name.
mv "installer/Output/drover-${{ steps.version.outputs.version }}-setup.exe" \
"dist/drover-${{ steps.version.outputs.tag }}-setup.exe"
ls -la dist/
- name: Compute SHA256SUMS.txt
run: |
set -e
( cd dist && sha256sum \
"drover-${{ steps.version.outputs.tag }}-windows-amd64.exe" \
"drover-${{ steps.version.outputs.tag }}-windows-amd64.zip" \
"drover-${{ steps.version.outputs.tag }}-setup.exe" \
) > dist/SHA256SUMS.txt
cat dist/SHA256SUMS.txt
- name: Create Forgejo release and upload assets
env:
FJ_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FJ_HOST: git.okcu.io
FJ_REPO: ${{ github.repository }}
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
run: |
set -e
# jq is used to build the JSON body safely. Install if missing.
command -v jq >/dev/null || {
apt-get update >/dev/null
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends jq >/dev/null
}
# Detect prerelease from tag (anything with a hyphen after the version
# number, e.g. v0.1.0-rc.1, v0.1.0-beta) is a prerelease.
PRERELEASE="false"
case "$VERSION" in
*-*) PRERELEASE="true" ;;
esac
# 1. Create the release. If a release for this tag already exists,
# Forgejo returns 409 and we exit non-zero so the user sees the
# real reason in the log instead of getting silent overwrites.
BODY=$(jq -n \
--arg tag "$TAG" \
--arg name "Drover-Go $TAG" \
--argjson prerelease "$PRERELEASE" \
--arg body "Automated release built from commit $GITHUB_SHA. See [docs](https://git.okcu.io/$FJ_REPO/src/tag/$TAG/docs/) for installation and troubleshooting." \
'{tag_name: $tag, name: $name, prerelease: $prerelease, body: $body, draft: false}')
RELEASE_JSON=$(curl -fsS -X POST \
-H "Authorization: token $FJ_TOKEN" \
-H "Content-Type: application/json" \
-d "$BODY" \
"https://$FJ_HOST/api/v1/repos/$FJ_REPO/releases")
RELEASE_ID=$(echo "$RELEASE_JSON" | jq -r '.id')
echo "Created release id=$RELEASE_ID"
# 2. Upload each asset via multipart/form-data
for asset in dist/*.exe dist/*.zip dist/SHA256SUMS.txt; do
[ -f "$asset" ] || continue
name="$(basename "$asset")"
echo "Uploading $name..."
curl -fsS -X POST \
-H "Authorization: token $FJ_TOKEN" \
-F "attachment=@$asset;filename=$name" \
"https://$FJ_HOST/api/v1/repos/$FJ_REPO/releases/$RELEASE_ID/assets?name=$name" \
>/dev/null
done
echo "Release $TAG published with $(ls dist | wc -l) assets."