Split the packages, publish releases from Gitea Actions, release 0.1.0
release / release (push) Successful in 7m7s
release / release (push) Successful in 7m7s
make deb now builds two packages: jarl-cli (the CLI, its completion and the ACME hooks, no scripts) and jarl (the server), which depends on jarl-cli of the same version. Pushing a v* tag runs .gitea/workflows/release.yml: it tests, builds the tarballs, the CLI for Linux, macOS and Windows (make cli-dist) and the .debs for amd64 and arm64, then scripts/gitea-release.sh creates the Gitea release and uploads the .debs to the Debian package registry, so servers install and upgrade with apt. The stats package's inode lookup is split by OS so the CLI builds for Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
293e6ddd33
commit
4d00be53c3
16 files changed
+450
-84
No files matched your search
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/bin/sh
|
||||
# Publishes a built release (make dist, make cli-dist, make deb for each
|
||||
# architecture) to Gitea:
|
||||
#
|
||||
# * a release for tag v<VERSION> with the CHANGELOG section for VERSION as
|
||||
# its notes, and every file in dist/ (tarballs, CLI binaries, .debs)
|
||||
# plus a SHA256SUMS of them attached;
|
||||
# * the .debs in the owner's Debian package registry, so servers can
|
||||
# 'apt install jarl' and get updates with apt (docs/install.md).
|
||||
#
|
||||
# Run by .gitea/workflows/release.yml when a v* tag is pushed. Running it
|
||||
# again for the same version replaces the attachments and packages.
|
||||
#
|
||||
# Variables:
|
||||
# GITEA_URL e.g. https://gitea.example.com (in Actions:
|
||||
# GITHUB_SERVER_URL)
|
||||
# GITEA_REPO owner/name (in Actions: GITHUB_REPOSITORY)
|
||||
# GITEA_TOKEN a token with write:repository and write:package
|
||||
# VERSION default: the VERSION file
|
||||
# DEB_DISTRIBUTION the apt distribution (default: stable)
|
||||
# DEB_COMPONENT the apt component (default: main)
|
||||
#
|
||||
# Needs curl and jq.
|
||||
set -eu
|
||||
|
||||
: "${GITEA_URL:=${GITHUB_SERVER_URL:-}}" "${GITEA_REPO:=${GITHUB_REPOSITORY:-}}"
|
||||
: "${GITEA_URL:?set GITEA_URL}" "${GITEA_REPO:?set GITEA_REPO}" "${GITEA_TOKEN:?set GITEA_TOKEN}"
|
||||
VERSION=${VERSION:-$(cat VERSION)}
|
||||
DEB_DISTRIBUTION=${DEB_DISTRIBUTION:-stable}
|
||||
DEB_COMPONENT=${DEB_COMPONENT:-main}
|
||||
GITEA_URL=${GITEA_URL%/}
|
||||
owner=${GITEA_REPO%%/*}
|
||||
tag=v$VERSION
|
||||
api=$GITEA_URL/api/v1/repos/$GITEA_REPO
|
||||
|
||||
# curl with the token; fails on HTTP errors except the codes in $ok.
|
||||
call() {
|
||||
ok=$1
|
||||
shift
|
||||
out=$(mktemp)
|
||||
code=$(curl -sS -o "$out" -w '%{http_code}' -H "Authorization: token $GITEA_TOKEN" "$@")
|
||||
case " 200 201 204 $ok " in
|
||||
*" $code "*) cat "$out"; rm -f "$out"; return 0 ;;
|
||||
esac
|
||||
echo "gitea-release: HTTP $code from $*" >&2
|
||||
cat "$out" >&2
|
||||
echo >&2
|
||||
rm -f "$out"
|
||||
return 1
|
||||
}
|
||||
|
||||
files=$(find dist -maxdepth 1 -type f ! -name SHA256SUMS | sort)
|
||||
[ -n "$files" ] || { echo "gitea-release: nothing in dist/" >&2; exit 1; }
|
||||
for f in $files; do
|
||||
case $f in *"$VERSION"*) ;; *) echo "gitea-release: $f is not version $VERSION" >&2; exit 1 ;; esac
|
||||
done
|
||||
# shellcheck disable=SC2046 # file names have no spaces
|
||||
(cd dist && sha256sum $(printf '%s\n' "$files" | sed 's|^dist/||')) > dist/SHA256SUMS
|
||||
files="$files dist/SHA256SUMS"
|
||||
|
||||
# The release notes: the CHANGELOG section for this version.
|
||||
notes=$(awk -v v="$VERSION" '
|
||||
/^## \[/ { if (on) exit; on = index($0, "## [" v "]") == 1; next }
|
||||
on' CHANGELOG.md)
|
||||
[ -n "$notes" ] || notes="See CHANGELOG.md."
|
||||
prerelease=false
|
||||
case $VERSION in *-*) prerelease=true ;; esac
|
||||
|
||||
# The release (created, or found when publishing again).
|
||||
release=$(call 404 "$api/releases/tags/$tag")
|
||||
id=$(printf '%s\n' "$release" | jq -r '.id // empty')
|
||||
if [ -z "$id" ]; then
|
||||
body=$(jq -n --arg tag "$tag" --arg notes "$notes" --argjson pre "$prerelease" \
|
||||
'{tag_name: $tag, name: ("Jarl " + $tag), body: $notes, prerelease: $pre}')
|
||||
release=$(call "" -X POST -H 'Content-Type: application/json' -d "$body" "$api/releases")
|
||||
id=$(printf '%s\n' "$release" | jq -r .id)
|
||||
echo "gitea-release: created release $tag"
|
||||
fi
|
||||
for f in $files; do
|
||||
name=${f#dist/}
|
||||
old=$(printf '%s\n' "$release" | jq -r --arg n "$name" '.assets[]? | select(.name == $n) | .id')
|
||||
[ -z "$old" ] || call "" -X DELETE "$api/releases/$id/assets/$old" >/dev/null
|
||||
call "" -X POST -F "attachment=@$f" "$api/releases/$id/assets?name=$name" >/dev/null
|
||||
echo "gitea-release: attached $name"
|
||||
done
|
||||
|
||||
# The Debian packages. Gitea refuses a package version it already has
|
||||
# (409), so publishing again deletes it first.
|
||||
pool=$GITEA_URL/api/packages/$owner/debian/pool/$DEB_DISTRIBUTION/$DEB_COMPONENT
|
||||
for f in dist/*.deb; do
|
||||
[ -e "$f" ] || continue
|
||||
# shellcheck disable=SC2016,SC2046 # dpkg-deb's fields; split into "$@"
|
||||
set -- $(dpkg-deb --showformat '${Package} ${Version} ${Architecture}' --show "$f")
|
||||
if ! call 409 --upload-file "$f" "$pool/upload" >/dev/null; then
|
||||
echo "gitea-release: upload of $f failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$code" = 409 ]; then
|
||||
call "" -X DELETE "$pool/$1/$2/$3" >/dev/null
|
||||
call "" --upload-file "$f" "$pool/upload" >/dev/null
|
||||
fi
|
||||
echo "gitea-release: uploaded $1 $2 $3 to $DEB_DISTRIBUTION/$DEB_COMPONENT"
|
||||
done
|
||||
+68
-38
@@ -1,19 +1,29 @@
|
||||
#!/bin/sh
|
||||
# Builds a Debian package of Jarl: dist/jarl_<version>_<arch>.deb.
|
||||
# Builds Jarl's two Debian packages:
|
||||
#
|
||||
# dist/jarl-cli_<version>_<arch>.deb the CLI, to install anywhere:
|
||||
# /usr/bin/jarl, its bash completion, and /usr/share/doc/jarl-cli/
|
||||
# (README, CHANGELOG, the CLI, ACME and permissions docs, contrib/ with
|
||||
# the acme.sh and lego hooks). No scripts: installing it changes nothing
|
||||
# else on the machine.
|
||||
# dist/jarl_<version>_<arch>.deb the server, which depends on
|
||||
# jarl-cli of the same version:
|
||||
# /usr/bin/jarld and its bash completion
|
||||
# /usr/lib/systemd/system/{jarl.service,jarl-backup.service,jarl-backup.timer}
|
||||
# /usr/lib/sysusers.d/jarl.conf, /usr/lib/tmpfiles.d/jarl.conf
|
||||
# /usr/share/doc/jarl/ (README, CHANGELOG, docs)
|
||||
# Its scripts (deploy/debian) create the jarl user and directories and
|
||||
# /etc/jarl/jarl.yaml on the first install, turn on the daily backup
|
||||
# timer, restart a running jarld on upgrades, and stop both on removal.
|
||||
# No file in /etc belongs to the package, so an upgrade never asks
|
||||
# about configuration files.
|
||||
#
|
||||
# Until 0.1.0 one package, jarl, held both; jarl-cli takes /usr/bin/jarl
|
||||
# over from it (Replaces: jarl).
|
||||
#
|
||||
# Run through 'make deb' (which sets the variables below); needs only
|
||||
# dpkg-deb, no root and no debhelper.
|
||||
#
|
||||
# The package installs, with Debian's paths:
|
||||
# /usr/bin/jarld, /usr/bin/jarl
|
||||
# /usr/share/bash-completion/completions/{jarl,jarld}
|
||||
# /usr/lib/systemd/system/{jarl.service,jarl-backup.service,jarl-backup.timer}
|
||||
# /usr/lib/sysusers.d/jarl.conf, /usr/lib/tmpfiles.d/jarl.conf
|
||||
# /usr/share/doc/jarl/ (README, CHANGELOG, docs, contrib)
|
||||
# and its scripts (deploy/debian) create the jarl user and directories and
|
||||
# /etc/jarl/jarl.yaml on the first install, turn on the daily backup timer,
|
||||
# restart a running jarld on upgrades, and stop both on removal. No file in /etc belongs to the package,
|
||||
# so an upgrade never asks about configuration files.
|
||||
#
|
||||
# Variables: DEB_ARCH (Debian architecture, e.g. amd64, arm64, armhf),
|
||||
# DEB_VERSION, VERSION, LDFLAGS, GOFLAGS, and DEB_MAINTAINER (default: your
|
||||
# git name and email).
|
||||
@@ -41,26 +51,42 @@ if [ -z "${DEB_MAINTAINER:-}" ]; then
|
||||
DEB_MAINTAINER="${name:-Jarl packager} <${email:-root@localhost}>"
|
||||
fi
|
||||
|
||||
work=dist/deb/jarl_${DEB_VERSION}_${DEB_ARCH}
|
||||
root=$work/root
|
||||
out=dist/jarl_${DEB_VERSION}_${DEB_ARCH}.deb
|
||||
work=dist/deb/${DEB_VERSION}_${DEB_ARCH}
|
||||
stage=$work/stage
|
||||
rm -rf "$work"
|
||||
mkdir -p "$root/DEBIAN"
|
||||
mkdir -p "$stage"
|
||||
|
||||
# Binaries for the package's architecture (bin/ holds the host's, which
|
||||
# also generated the completion scripts).
|
||||
echo "mkdeb: building for linux/$goarch"
|
||||
# go reads GOFLAGS (-trimpath) from the environment.
|
||||
export GOFLAGS
|
||||
GOOS=linux GOARCH=$goarch GOARM=$goarm CGO_ENABLED=0 go build -ldflags "$LDFLAGS" -o "$work/jarld" ./cmd/jarld
|
||||
GOOS=linux GOARCH=$goarch GOARM=$goarm CGO_ENABLED=0 go build -ldflags "$LDFLAGS" -o "$work/jarl" ./cmd/jarl
|
||||
GOOS=linux GOARCH=$goarch GOARM=$goarm CGO_ENABLED=0 go build -ldflags "$LDFLAGS" -o "$work/bin/jarld" ./cmd/jarld
|
||||
GOOS=linux GOARCH=$goarch GOARM=$goarm CGO_ENABLED=0 go build -ldflags "$LDFLAGS" -o "$work/bin/jarl" ./cmd/jarl
|
||||
|
||||
make --no-print-directory install-files DESTDIR="$root" PREFIX=/usr \
|
||||
UNITDIR=/usr/lib/systemd/system JARLD_BIN="$work/jarld" JARL_BIN="$work/jarl" >/dev/null
|
||||
# Everything 'make install' would install, then split in two.
|
||||
make --no-print-directory install-files DESTDIR="$stage" PREFIX=/usr \
|
||||
UNITDIR=/usr/lib/systemd/system JARLD_BIN="$work/bin/jarld" JARL_BIN="$work/bin/jarl" >/dev/null
|
||||
|
||||
cli=$work/jarl-cli
|
||||
srv=$work/jarl
|
||||
mkdir -p "$cli/DEBIAN" "$cli/usr/bin" "$cli/usr/share/bash-completion/completions" "$cli/usr/share/doc/jarl-cli/docs"
|
||||
mv "$stage/usr/bin/jarl" "$cli/usr/bin/"
|
||||
mv "$stage/usr/share/bash-completion/completions/jarl" "$cli/usr/share/bash-completion/completions/"
|
||||
mv "$stage/usr/share/doc/jarl/contrib" "$cli/usr/share/doc/jarl-cli/"
|
||||
cp "$stage/usr/share/doc/jarl/README.md" "$stage/usr/share/doc/jarl/CHANGELOG.md" "$cli/usr/share/doc/jarl-cli/"
|
||||
for d in cli acme permissions; do
|
||||
cp "$stage/usr/share/doc/jarl/docs/$d.md" "$cli/usr/share/doc/jarl-cli/docs/"
|
||||
done
|
||||
mv "$stage" "$srv"
|
||||
mkdir -p "$srv/DEBIAN"
|
||||
for f in postinst prerm postrm; do
|
||||
install -m 0755 "deploy/debian/$f" "$srv/DEBIAN/$f"
|
||||
done
|
||||
|
||||
# Debian's machine-readable copyright file, from LICENSE.
|
||||
indent() { sed -e 's/^$/./' -e 's/^/ /' "$1"; }
|
||||
{
|
||||
copyright() {
|
||||
echo "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/"
|
||||
echo "Upstream-Name: jarl"
|
||||
echo
|
||||
@@ -68,22 +94,26 @@ indent() { sed -e 's/^$/./' -e 's/^/ /' "$1"; }
|
||||
echo "Copyright: $(sed -n 's/^Copyright (c) //p' LICENSE)"
|
||||
echo "License: Expat"
|
||||
indent LICENSE
|
||||
} > "$root/usr/share/doc/jarl/copyright"
|
||||
}
|
||||
|
||||
for f in postinst prerm postrm; do
|
||||
install -m 0755 "deploy/debian/$f" "$root/DEBIAN/$f"
|
||||
done
|
||||
# Directories 0755 and no group/other write, whatever the build's umask.
|
||||
find "$root" -type d -exec chmod 0755 {} +
|
||||
chmod -R go-w "$root"
|
||||
size=$(du -sk --exclude=DEBIAN "$root" | cut -f1)
|
||||
sed -e "s/@VERSION@/$DEB_VERSION/" -e "s/@ARCH@/$DEB_ARCH/" -e "s/@SIZE@/$size/" \
|
||||
-e "s|@MAINTAINER@|$DEB_MAINTAINER|" deploy/debian/control.in > "$root/DEBIAN/control"
|
||||
(cd "$root" && find . -path ./DEBIAN -prune -o -type f -print | sed 's|^\./||' | sort | xargs md5sum) > "$root/DEBIAN/md5sums"
|
||||
chmod 0644 "$root/DEBIAN/control" "$root/DEBIAN/md5sums"
|
||||
|
||||
# Files belong to root in the package, whoever builds it.
|
||||
dpkg-deb --root-owner-group -Zxz --build "$root" "$out" >/dev/null
|
||||
# build PACKAGE ROOT: writes the control file and md5sums and builds the .deb.
|
||||
build() {
|
||||
pkg=$1 root=$2 out=dist/${1}_${DEB_VERSION}_${DEB_ARCH}.deb
|
||||
copyright > "$root/usr/share/doc/$pkg/copyright"
|
||||
rm -f "$root/usr/share/doc/$pkg/LICENSE"
|
||||
# Directories 0755 and no group/other write, whatever the build's umask.
|
||||
find "$root" -type d -exec chmod 0755 {} +
|
||||
chmod -R go-w "$root"
|
||||
size=$(du -sk --exclude=DEBIAN "$root" | cut -f1)
|
||||
sed -e "s/@VERSION@/$DEB_VERSION/g" -e "s/@ARCH@/$DEB_ARCH/" -e "s/@SIZE@/$size/" \
|
||||
-e "s|@MAINTAINER@|$DEB_MAINTAINER|" "deploy/debian/control-$pkg.in" > "$root/DEBIAN/control"
|
||||
(cd "$root" && find . -path ./DEBIAN -prune -o -type f -print | sed 's|^\./||' | sort | xargs md5sum) > "$root/DEBIAN/md5sums"
|
||||
chmod 0644 "$root/DEBIAN/control" "$root/DEBIAN/md5sums"
|
||||
# Files belong to root in the package, whoever builds it.
|
||||
dpkg-deb --root-owner-group -Zxz --build "$root" "$out" >/dev/null
|
||||
echo "mkdeb: wrote $out"
|
||||
dpkg-deb --info "$out" | sed -n 's/^ \(Package\|Version\|Architecture\|Installed-Size\|Depends\):/ \1:/p'
|
||||
}
|
||||
build jarl-cli "$cli"
|
||||
build jarl "$srv"
|
||||
rm -rf dist/deb
|
||||
echo "mkdeb: wrote $out"
|
||||
dpkg-deb --info "$out" | sed -n 's/^ \(Package\|Version\|Architecture\|Installed-Size\):/ \1:/p'
|
||||
Reference in new issue
Block a user