You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@casbin.apache.org by hs...@apache.org on 2026/08/17 13:37:56 UTC
(casbin-gateway) 01/01: ci: publish nightly binaries as a pre-release
This is an automated email from the ASF dual-hosted git repository.
hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git
commit 912e32e469e5f276d065ab9a365265579772fdf6
Author: Yang Luo <hs...@gmail.com>
AuthorDate: Mon Aug 17 14:24:29 2026 +0800
ci: publish nightly binaries as a pre-release
---
.github/workflows/nightly.yml | 180 ++++++++++++++++++++++++++++++++++++++++++
.gitignore | 3 +
README.md | 22 ++++++
embed.go | 6 ++
scripts/install.ps1 | 121 ++++++++++++++++++++++++++++
scripts/install.sh | 142 +++++++++++++++++++++++++++++++++
6 files changed, 474 insertions(+)
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
new file mode 100644
index 0000000..a3876a8
--- /dev/null
+++ b/.github/workflows/nightly.yml
@@ -0,0 +1,180 @@
+name: Nightly
+
+# Every push to master refreshes the "nightly" pre-release with one archive per
+# platform, built from that commit. These are convenience builds, not releases:
+# the source package cut by release.yml stays the only official artifact, so
+# this workflow never creates a version tag and never publishes a full release.
+
+on:
+ push:
+ branches:
+ - master
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+# Nightly runs are never cancelled halfway: the publish step deletes the old
+# release before creating the new one, and a cancellation in that window would
+# leave no nightly at all.
+concurrency:
+ group: nightly
+ cancel-in-progress: false
+
+env:
+ GO_VERSION: "1.25"
+ BASENAME: casbin-gateway-nightly
+
+jobs:
+ frontend:
+ name: Build web UI
+ runs-on: ubuntu-latest
+ if: ${{ github.repository == 'apache/casbin-gateway' }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'yarn'
+ cache-dependency-path: ./web/yarn.lock
+
+ # Source maps are left out on purpose: everything under web/build ends up
+ # inside every one of the six binaries below.
+ - name: Build
+ run: yarn install && CI=false GENERATE_SOURCEMAP=false yarn run build
+ working-directory: ./web
+
+ - name: Upload web build
+ uses: actions/upload-artifact@v4
+ with:
+ name: web-build-${{ github.run_id }}
+ path: ./web/build
+ retention-days: 1
+
+ nightly:
+ name: Publish nightly
+ runs-on: ubuntu-latest
+ needs: [ frontend ]
+ if: ${{ github.repository == 'apache/casbin-gateway' }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: actions/setup-go@v5
+ with:
+ go-version: ${{ env.GO_VERSION }}
+ cache-dependency-path: ./go.mod
+
+ # The binaries are built with -tags embed, which bakes conf/app.conf, the
+ # web UI and the IP database in, so web/build has to be in place first.
+ - name: Download web build
+ uses: actions/download-artifact@v4
+ with:
+ name: web-build-${{ github.run_id }}
+ path: ./web/build
+
+ - name: Build the platform archives
+ run: |
+ set -euo pipefail
+ mkdir -p dist
+
+ # goos goarch arch-name-used-in-the-file-name
+ targets=(
+ "linux amd64 x86_64"
+ "linux arm64 arm64"
+ "darwin amd64 x86_64"
+ "darwin arm64 arm64"
+ "windows amd64 x86_64"
+ "windows arm64 arm64"
+ )
+
+ for target in "${targets[@]}"; do
+ read -r goos goarch archName <<< "${target}"
+
+ name="${BASENAME}-${goos}-${archName}"
+ stage="dist/${name}"
+ mkdir -p "${stage}"
+
+ binary="${stage}/casbin-gateway"
+ if [[ "${goos}" == "windows" ]]; then
+ binary="${binary}.exe"
+ fi
+
+ echo "Building ${name}"
+ GOOS="${goos}" GOARCH="${goarch}" CGO_ENABLED=0 \
+ go build -tags embed -trimpath -ldflags "-s -w" -o "${binary}" .
+
+ cp LICENSE NOTICE DISCLAIMER README.md "${stage}/"
+
+ if [[ "${goos}" == "windows" ]]; then
+ archive="${name}.zip"
+ (cd dist && zip -qr "${archive}" "${name}")
+ else
+ archive="${name}.tar.gz"
+ tar -czf "dist/${archive}" -C dist "${name}"
+ fi
+
+ (cd dist && sha512sum "${archive}" > "${archive}.sha512")
+ rm -rf "${stage}"
+ done
+
+ ls -l dist
+
+ - name: Write release notes
+ run: |
+ set -euo pipefail
+ cat > release-notes.md <<'NOTES'
+ ## This is not an official release
+
+ These are automated convenience builds of the current `master`. Use them for
+ testing and development only.
+
+ Official releases are source packages, published on the
+ [releases page](https://github.com/apache/casbin-gateway/releases).
+
+ ## Contents
+
+ Each archive holds one self-contained `casbin-gateway` executable, together with
+ `LICENSE`, `NOTICE` and `DISCLAIMER`. The executable is built with `-tags embed`,
+ so it carries its own settings, web UI and IP database and starts with no files
+ next to it. Data is written to `./data`, `./logs` and `./tmp` in the working
+ directory. A `.sha512` file accompanies every archive.
+
+ | Platform | Archive |
+ | --- | --- |
+ | Linux x86_64 | `BASENAME_PLACEHOLDER-linux-x86_64.tar.gz` |
+ | Linux arm64 | `BASENAME_PLACEHOLDER-linux-arm64.tar.gz` |
+ | macOS x86_64 | `BASENAME_PLACEHOLDER-darwin-x86_64.tar.gz` |
+ | macOS arm64 | `BASENAME_PLACEHOLDER-darwin-arm64.tar.gz` |
+ | Windows x86_64 | `BASENAME_PLACEHOLDER-windows-x86_64.zip` |
+ | Windows arm64 | `BASENAME_PLACEHOLDER-windows-arm64.zip` |
+ NOTES
+
+ sed -i "s/BASENAME_PLACEHOLDER/${BASENAME}/g" release-notes.md
+ printf '\nBuilt from commit %s.\n' "${GITHUB_SHA}" >> release-notes.md
+ cat release-notes.md
+
+ # The nightly release is replaced rather than updated, so that its tag
+ # moves to the commit it was actually built from. --cleanup-tag removes
+ # the old tag as well, and --target recreates it at this commit.
+ #
+ # The gh CLI is used instead of a marketplace action: the ASF org only
+ # allows actions owned by GitHub or by the enterprise allow-list.
+ - name: Replace the nightly pre-release
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ set -euo pipefail
+
+ if gh release view nightly >/dev/null 2>&1; then
+ gh release delete nightly --yes --cleanup-tag
+ fi
+
+ gh release create nightly \
+ --prerelease \
+ --target "${GITHUB_SHA}" \
+ --title "Nightly build (not an official release)" \
+ --notes-file release-notes.md \
+ dist/*.tar.gz dist/*.zip dist/*.sha512
diff --git a/.gitignore b/.gitignore
index 4704935..64fb89c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,9 @@
# Built binaries
casbin-gateway
+# Release and nightly archives, staged here by the workflows
+dist/
+
tmp/
tmpFiles/
*.tmp
diff --git a/README.md b/README.md
index 576366d..5a23bde 100644
--- a/README.md
+++ b/README.md
@@ -168,6 +168,28 @@ The startup summary reports which source each one came from:
Being self-contained is about startup, not about staying read-only: a running Gateway still writes `./data` (the SQLite database, deployed apps, agent patch state), `./logs` and `./tmp` relative to its working directory. Start it from the directory where that state belongs.
+#### Nightly builds
+
+Every push to `master` rebuilds the [`nightly`](https://github.com/apache/casbin-gateway/releases/tag/nightly) pre-release, with one such archive per platform — Linux, macOS and Windows, `x86_64` and `arm64` — each carrying `LICENSE`, `NOTICE`, `DISCLAIMER` and a `.sha512` checksum.
+
+**Nightly builds are not official releases.** They are automated builds of whatever is on `master`, and exist so that a change can be tried without a Go and Node toolchain. Anything else should be built from a source release.
+
+On Linux and macOS:
+
+```bash
+curl -fsSL https://raw.githubusercontent.com/apache/casbin-gateway/master/scripts/install.sh | bash
+```
+
+On Windows, in PowerShell:
+
+```powershell
+irm https://raw.githubusercontent.com/apache/casbin-gateway/master/scripts/install.ps1 | iex
+```
+
+Either one downloads the archive for this machine, checks it against its `.sha512`, unpacks it into `~/.local/share/casbin-gateway` (`%LOCALAPPDATA%\casbin-gateway` on Windows) and starts Gateway there. Set `INSTALL_DIR` to install somewhere else, or `NO_START=1` to install without starting.
+
+The `casbin-gateway` command they put on your PATH is a small wrapper rather than the executable itself: Gateway keeps its database, logs and temporary files in the working directory, so the wrapper always starts it in the install directory. Running the executable directly from somewhere else gives you a second, empty installation there.
+
### Necessary configuration
#### Get the code
diff --git a/embed.go b/embed.go
index 8cd8602..83a6629 100644
--- a/embed.go
+++ b/embed.go
@@ -33,6 +33,12 @@ import (
"embed"
"io/fs"
+ // The IANA time zone database, which util.GetCurrentTime() needs. A
+ // self-contained binary cannot count on the host for it: Windows ships no
+ // zoneinfo at all, and -trimpath drops the GOROOT copy Go falls back to,
+ // so without this the first timestamp panics with "unknown time zone".
+ _ "time/tzdata"
+
"github.com/apache/casbin-gateway/embedsupport"
)
diff --git a/scripts/install.ps1 b/scripts/install.ps1
new file mode 100644
index 0000000..8344ada
--- /dev/null
+++ b/scripts/install.ps1
@@ -0,0 +1,121 @@
+# Casbin Gateway one-step install for Windows.
+#
+# Usage (run only if you trust this script source):
+# irm https://raw.githubusercontent.com/apache/casbin-gateway/master/scripts/install.ps1 | iex
+#
+# This downloads the nightly build, which is an automated build of master and
+# not an official release. Use it for testing and development only.
+#
+# Optional environment variables:
+# INSTALL_DIR where the executable and its data live
+# (default: $env:LOCALAPPDATA\casbin-gateway)
+# NO_START set to any value to install without starting Gateway
+
+$ErrorActionPreference = 'Stop'
+
+$Repo = 'apache/casbin-gateway'
+$Tag = 'nightly'
+$BaseName = 'casbin-gateway-nightly'
+
+$InstallDir = if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { "$env:LOCALAPPDATA\casbin-gateway" }
+$BinDir = Join-Path $InstallDir 'bin'
+
+# Windows PowerShell 5.1 still negotiates TLS 1.0 by default, which GitHub
+# refuses. The progress bar makes Invoke-WebRequest many times slower on a
+# download this size.
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$ProgressPreference = 'SilentlyContinue'
+
+function Write-Info { param([string]$Message) Write-Host $Message }
+
+# ── pick the archive for this machine ─────────────────────────────────────────
+$ArchName = switch ($env:PROCESSOR_ARCHITECTURE) {
+ 'AMD64' { 'x86_64' }
+ 'ARM64' { 'arm64' }
+ default { throw "unsupported architecture `"$env:PROCESSOR_ARCHITECTURE`", build from source instead: https://github.com/$Repo" }
+}
+
+$Archive = "$BaseName-windows-$ArchName.zip"
+$Url = "https://github.com/$Repo/releases/download/$Tag/$Archive"
+
+$TmpDir = Join-Path $env:TEMP "casbin-gateway-install-$(Get-Random)"
+New-Item -ItemType Directory -Path $TmpDir | Out-Null
+
+try {
+ # ── download and check ────────────────────────────────────────────────────
+ $ArchivePath = Join-Path $TmpDir $Archive
+ Write-Info "Downloading $Url"
+ Invoke-WebRequest -Uri $Url -OutFile $ArchivePath -UseBasicParsing
+ Invoke-WebRequest -Uri "$Url.sha512" -OutFile "$ArchivePath.sha512" -UseBasicParsing
+
+ Write-Info 'Verifying the checksum'
+ # The .sha512 file is "<hash> <filename>", as written by sha512sum, which
+ # writes lower-case hex where Get-FileHash returns upper-case.
+ $Expected = ((Get-Content "$ArchivePath.sha512" -Raw).Trim() -split '\s+')[0].ToLower()
+ $Actual = (Get-FileHash -Path $ArchivePath -Algorithm SHA512).Hash.ToLower()
+ if ($Actual -ne $Expected) {
+ throw 'the downloaded archive does not match its checksum'
+ }
+
+ Expand-Archive -Path $ArchivePath -DestinationPath $TmpDir -Force
+ $Unpacked = Join-Path $TmpDir "$BaseName-windows-$ArchName"
+
+ # ── install ───────────────────────────────────────────────────────────────
+ Write-Info "Installing to $InstallDir"
+ New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
+
+ $ExePath = Join-Path $InstallDir 'casbin-gateway.exe'
+ try {
+ Copy-Item -Path (Join-Path $Unpacked 'casbin-gateway.exe') -Destination $ExePath -Force
+ }
+ catch {
+ # Windows locks a running executable, so this is what an upgrade over a
+ # started Gateway looks like.
+ throw "cannot replace $ExePath, stop Casbin Gateway if it is running and try again ($_)"
+ }
+
+ foreach ($legalFile in @('LICENSE', 'NOTICE', 'DISCLAIMER')) {
+ Copy-Item -Path (Join-Path $Unpacked $legalFile) -Destination (Join-Path $InstallDir $legalFile) -Force
+ }
+}
+finally {
+ Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue
+}
+
+# ── put a "casbin-gateway" command on PATH ────────────────────────────────────
+# Gateway keeps its database, logs and temporary files in the working
+# directory, so the command is a wrapper that always starts it in $InstallDir.
+# Without it, running "casbin-gateway" from somewhere else would quietly start
+# a second, empty installation. The wrapper lives in its own directory because
+# cmd resolves .exe before .cmd, so a wrapper next to the executable would
+# never be the one that runs.
+New-Item -ItemType Directory -Path $BinDir -Force | Out-Null
+@"
+@echo off
+rem Written by the Casbin Gateway installer. Gateway reads and writes .\data,
+rem .\logs and .\tmp, so it always has to start in its own directory.
+cd /d "%~dp0.." || exit /b 1
+"%~dp0..\casbin-gateway.exe" %*
+"@ | Set-Content -Path (Join-Path $BinDir 'casbin-gateway.cmd') -Encoding ascii
+
+$UserPath = [System.Environment]::GetEnvironmentVariable('PATH', 'User')
+if ($UserPath -notlike "*$BinDir*") {
+ [System.Environment]::SetEnvironmentVariable('PATH', "$UserPath;$BinDir", 'User')
+ $env:PATH = "$env:PATH;$BinDir"
+ Write-Info "Added $BinDir to your PATH, which takes effect in your next terminal"
+}
+
+Write-Info ''
+Write-Info "Casbin Gateway is installed in $InstallDir"
+Write-Info 'Its database, logs and temporary files stay in that directory.'
+Write-Info 'Sign in as "admin" with the password "123", and change it right away.'
+Write-Info ''
+
+if ($env:NO_START) {
+ Write-Info 'Start it with: casbin-gateway'
+ return
+}
+
+Write-Info 'Starting Casbin Gateway, press Ctrl-C to stop it...'
+Set-Location $InstallDir
+& (Join-Path $InstallDir 'casbin-gateway.exe')
diff --git a/scripts/install.sh b/scripts/install.sh
new file mode 100755
index 0000000..fe20305
--- /dev/null
+++ b/scripts/install.sh
@@ -0,0 +1,142 @@
+#!/usr/bin/env bash
+# Casbin Gateway one-step install for Linux and macOS.
+#
+# Usage:
+# curl -fsSL https://raw.githubusercontent.com/apache/casbin-gateway/master/scripts/install.sh | bash
+#
+# This downloads the nightly build, which is an automated build of master and
+# not an official release. Use it for testing and development only.
+#
+# Optional environment variables:
+# INSTALL_DIR where the executable and its data live
+# (default: $HOME/.local/share/casbin-gateway)
+# BIN_DIR where the "casbin-gateway" command is placed
+# (default: $HOME/.local/bin)
+# NO_START set to any value to install without starting Gateway
+
+set -euo pipefail
+
+REPO="apache/casbin-gateway"
+TAG="nightly"
+BASENAME="casbin-gateway-nightly"
+
+INSTALL_DIR="${INSTALL_DIR:-${HOME}/.local/share/casbin-gateway}"
+BIN_DIR="${BIN_DIR:-${HOME}/.local/bin}"
+NO_START="${NO_START:-}"
+
+info() { printf '%s\n' "$*"; }
+die() { printf 'casbin-gateway: %s\n' "$*" >&2; exit 1; }
+
+need_cmd() {
+ command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
+}
+
+need_cmd curl
+need_cmd tar
+
+# ── pick the archive for this machine ─────────────────────────────────────────
+os="$(uname -s)"
+case "${os}" in
+ Linux) osName="linux" ;;
+ Darwin) osName="darwin" ;;
+ *) die "unsupported operating system \"${os}\", build from source instead: https://github.com/${REPO}" ;;
+esac
+
+arch="$(uname -m)"
+case "${arch}" in
+ x86_64|amd64) archName="x86_64" ;;
+ aarch64|arm64) archName="arm64" ;;
+ *) die "unsupported architecture \"${arch}\", build from source instead: https://github.com/${REPO}" ;;
+esac
+
+archive="${BASENAME}-${osName}-${archName}.tar.gz"
+url="https://github.com/${REPO}/releases/download/${TAG}/${archive}"
+
+# ── download and check ────────────────────────────────────────────────────────
+tmpDir="$(mktemp -d)"
+trap 'rm -rf "${tmpDir}"' EXIT
+
+info "Downloading ${url}"
+curl -fsSL -o "${tmpDir}/${archive}" "${url}"
+curl -fsSL -o "${tmpDir}/${archive}.sha512" "${url}.sha512"
+
+info "Verifying the checksum"
+# sha512sum is GNU; macOS ships shasum instead.
+if command -v sha512sum >/dev/null 2>&1; then
+ (cd "${tmpDir}" && sha512sum -c "${archive}.sha512" >/dev/null) || die "the downloaded archive does not match its checksum"
+elif command -v shasum >/dev/null 2>&1; then
+ (cd "${tmpDir}" && shasum -a 512 -c "${archive}.sha512" >/dev/null) || die "the downloaded archive does not match its checksum"
+else
+ info "Neither sha512sum nor shasum is available, skipping the checksum check"
+fi
+
+# ── install ───────────────────────────────────────────────────────────────────
+info "Installing to ${INSTALL_DIR}"
+mkdir -p "${INSTALL_DIR}" || die "cannot create ${INSTALL_DIR}, set INSTALL_DIR to a directory you can write to"
+
+# Unpacking inside INSTALL_DIR keeps the final move on one filesystem, so it is
+# a rename: an already running Gateway holds the old executable open, and
+# writing over it in place would fail with "text file busy".
+stage="${INSTALL_DIR}/.install"
+rm -rf "${stage}"
+mkdir -p "${stage}"
+tar -xzf "${tmpDir}/${archive}" -C "${stage}" --strip-components=1
+
+mv -f "${stage}/casbin-gateway" "${INSTALL_DIR}/casbin-gateway"
+chmod 755 "${INSTALL_DIR}/casbin-gateway"
+for legalFile in LICENSE NOTICE DISCLAIMER; do
+ mv -f "${stage}/${legalFile}" "${INSTALL_DIR}/${legalFile}"
+done
+rm -rf "${stage}"
+
+# ── put a "casbin-gateway" command on PATH ────────────────────────────────────
+# Gateway keeps its database, logs and temporary files in the working
+# directory, so the command is a wrapper that always starts it in INSTALL_DIR.
+# Without it, running "casbin-gateway" from somewhere else would quietly start
+# a second, empty installation.
+if [[ "${BIN_DIR}" == "${INSTALL_DIR}" ]]; then
+ info "BIN_DIR is the install directory, so no wrapper was created"
+else
+ mkdir -p "${BIN_DIR}" || die "cannot create ${BIN_DIR}, set BIN_DIR to a directory you can write to"
+ cat > "${BIN_DIR}/casbin-gateway" <<EOF
+#!/usr/bin/env bash
+# Written by the Casbin Gateway installer. Gateway reads and writes ./data,
+# ./logs and ./tmp, so it always has to start in its own directory.
+cd "${INSTALL_DIR}" || exit 1
+exec "${INSTALL_DIR}/casbin-gateway" "\$@"
+EOF
+ chmod 755 "${BIN_DIR}/casbin-gateway"
+
+ case ":${PATH}:" in
+ *":${BIN_DIR}:"*) ;;
+ *)
+ shellRc=""
+ case "${SHELL:-}" in
+ */zsh) shellRc="${HOME}/.zshrc" ;;
+ */bash) shellRc="${HOME}/.bashrc" ;;
+ esac
+
+ if [[ -n "${shellRc}" ]]; then
+ printf '\nexport PATH="%s:$PATH"\n' "${BIN_DIR}" >> "${shellRc}"
+ info "Added ${BIN_DIR} to PATH in ${shellRc}, which takes effect in your next shell"
+ else
+ info "${BIN_DIR} is not on your PATH, add it to run Gateway by name"
+ fi
+ ;;
+ esac
+fi
+
+info ""
+info "Casbin Gateway is installed in ${INSTALL_DIR}"
+info "Its database, logs and temporary files stay in that directory."
+info "Sign in as \"admin\" with the password \"123\", and change it right away."
+info ""
+
+if [[ -n "${NO_START}" ]]; then
+ info "Start it with: casbin-gateway"
+ exit 0
+fi
+
+info "Starting Casbin Gateway, press Ctrl-C to stop it..."
+cd "${INSTALL_DIR}"
+exec "${INSTALL_DIR}/casbin-gateway"
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@casbin.apache.org
For additional commands, e-mail: commits-help@casbin.apache.org