Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 182 additions & 6 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ on:
pull_request:
push:
branches:
- main
- main
tags:
- 'v*'
schedule:
- cron: '17 14 * * *'

Expand Down Expand Up @@ -46,6 +48,53 @@ jobs:
name: try-dist.tgz
path: try-*.tgz

- name: Package packaging assets
run: |
tar czf packaging.tgz packaging

- name: Upload packaging assets
uses: actions/upload-artifact@v7
with:
name: packaging-assets
path: packaging.tgz

package:
needs: [dist, test-dist]
runs-on: ubuntu-latest

steps:
- name: Download dist tarball
uses: actions/download-artifact@v8
with:
name: try-dist.tgz

- name: Download packaging assets
uses: actions/download-artifact@v8
with:
name: packaging-assets

- name: Unpack tarballs
run: |
tar xzf try-*.tgz --strip-components=1
tar xzf packaging.tgz
rm -f try-*.tgz packaging.tgz

- name: Build packaging image
run: |
docker build -t try-packager packaging/

- name: Build .deb and .rpm
run: |
docker run --rm -v "$PWD:/work" -w /work try-packager packaging/build_packages.sh
Comment on lines +82 to +88

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Sorry, I meant to leave this comment last time but somehow didn't write it down.)

Why bother using docker for this? We already have a clean VM from the CI runner, so we should be able to just invoke the builds script here in CI.... right?


- name: Upload packages
uses: actions/upload-artifact@v7
with:
name: try-packages
path: |
dist-packages/*.deb
dist-packages/*.rpm
Comment on lines +95 to +96

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elsewhere we're careful to name try-*.tgz or whatever, so that we don't glob up other things. Probably good to do that here (and later, when we install things during the smoke tests).


test-dist:
needs: dist
strategy:
Expand All @@ -65,10 +114,11 @@ jobs:

- name: Download dist tarball
uses: actions/download-artifact@v8
with:
name: try-dist.tgz

- name: Unpack tarball; configure and build utilities
run: |
ls -lR
tar xzf try-*.tgz --strip-components=1
rm try-*.tgz
./configure --disable-utils
Expand Down Expand Up @@ -225,6 +275,123 @@ jobs:
run: |
nix-shell --run "sh scripts/run_tests.sh"

tag-version-check:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Check tag against declared versions
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
SCRIPT_VERSION="$(grep 'TRY_VERSION=' try | cut -d'"' -f 2)"
MANPAGE_VERSION="$(grep 'TRY(1)' docs/try.1.md | cut -d' ' -f 4)"
INCLUDE_VERSION="$(grep '#define TRY_VERSION' utils/version.h | cut -d'"' -f 2)"
CONFIGAC_VERSION="$(grep AC_INIT configure.ac | cut -d'[' -f3 | cut -d']' -f1)"

echo " TAG_VERSION = '$TAG_VERSION'"
echo " SCRIPT_VERSION = '$SCRIPT_VERSION'"
echo " MANPAGE_VERSION = '$MANPAGE_VERSION'"
echo " INCLUDE_VERSION = '$INCLUDE_VERSION'"
echo "CONFIGAC_VERSION = '$CONFIGAC_VERSION'"

for v in "$SCRIPT_VERSION" "$MANPAGE_VERSION" "$INCLUDE_VERSION" "$CONFIGAC_VERSION"
do
if [ "$v" != "$TAG_VERSION" ]
then
echo "::error::tag $GITHUB_REF_NAME does not match all declared versions"
exit 1
fi
done
Comment on lines +286 to +307

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Duplicating a response from the prior review, just so everything is in one place.)

We already run scripts/check_version.sh as part of CI---so it's sufficient in tag-version-check to compare against the installed ones. No need to more or less duplicate the logic of the version-checking script in the YAML file.

I think the cleanest thing to do is to have check_version.sh take an optional argument indicating the expected version, and we can run the version check again at that extracted tag.


smoke-test-deb:
needs: package
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')

steps:
- name: Download packages
uses: actions/download-artifact@v8
with:
name: try-packages

- name: Install the .deb
run: |
sudo apt-get update
sudo apt-get install -y ./*.deb

- name: Check installed version matches the tag
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
# try -v writes to stderr
INSTALLED="$(try -v 2>&1 | awk '{print $NF}')"
echo "installed: '$INSTALLED' expected: '$TAG_VERSION'"
[ "$INSTALLED" = "$TAG_VERSION" ]

- name: Check manpage is installed
run: |
man -w try

smoke-test-rpm:
needs: package
runs-on: ubuntu-latest
container: fedora:latest
if: startsWith(github.ref, 'refs/tags/v')

steps:
- name: Download packages
uses: actions/download-artifact@v8
with:
name: try-packages

- name: Install the .rpm
run: |
# fedora's container image sets tsflags=nodocs by default, which
# would silently drop the manpage; override it for this install
dnf install -y --setopt=tsflags= ./*.rpm man-db

- name: Check installed version matches the tag
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
# try -v writes to stderr
INSTALLED="$(try -v 2>&1 | awk '{print $NF}')"
echo "installed: '$INSTALLED' expected: '$TAG_VERSION'"
[ "$INSTALLED" = "$TAG_VERSION" ]

- name: Check manpage is installed
run: |
mandb
man -w try

release:
needs: [tag-version-check, smoke-test-deb, smoke-test-rpm]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write

steps:
- name: Download dist tarball
uses: actions/download-artifact@v8
with:
name: try-dist.tgz

- name: Download packages
uses: actions/download-artifact@v8
with:
name: try-packages

- name: Publish release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$GITHUB_REF_NAME" \
--repo "$GITHUB_REPOSITORY" \
--title "$GITHUB_REF_NAME" \
--generate-notes \
try-*.tgz ./*.deb ./*.rpm

prerelease:
needs:
Expand All @@ -234,17 +401,24 @@ jobs:
- lint
- shellcheck
- trycase-check
- package
runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/main' }}

steps:
- name: Download binaries
uses: actions/download-artifact@v4
- name: Download dist tarball
uses: actions/download-artifact@v8
with:
name: try-dist.tgz

- name: Download packages
uses: actions/download-artifact@v8
with:
name: try-packages

- name: Rename tarball
run: |
mv try-dist.tgz/try-*.tgz try-latest.tgz
rmdir try-dist.tgz
mv try-*.tgz try-latest.tgz

- name: Deploy 'latest' release
uses: "marvinpinto/action-automatic-releases@latest"
Expand All @@ -255,3 +429,5 @@ jobs:
title: "Latest distribution tarball"
files: |
try-latest.tgz
*.deb
*.rpm
12 changes: 12 additions & 0 deletions packaging/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM debian:bookworm-slim

RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
autoconf \
dpkg-dev \
rpm \
ca-certificates && \
rm -rf /var/lib/apt/lists/*

WORKDIR /work
23 changes: 23 additions & 0 deletions packaging/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
This directory holds the container image used to build distribution packages for `try`.

The `Dockerfile` builds a Debian image with a toolchain plus `dpkg-dev` and `rpm`, which
provide the native `dpkg-deb` and `rpmbuild` packaging tools.
The actual packaging logic lives in `build_packages.sh`, which runs inside that image.

# Building

From an unpacked `make dist` tarball:

```
docker build -t try-packager packaging/
docker run --rm -v "$PWD:/work" -w /work try-packager packaging/build_packages.sh
```

Packages land in `dist-packages/`. The bind mount means the results appear on the host directly, with no copy step out of the container.

Both packages are built from a single staged tree.

# Why there is no Homebrew formula
`try` is Linux-only (Linux 5.11 or later).

For distributions outside the deb/rpm families, `../package.nix` and `../shell.nix` provide a Nix package, Arch Linux users can install the [AUR package](https://aur.archlinux.org/packages/try), and building from source with `./configure && make && make install` is supported everywhere.
95 changes: 95 additions & 0 deletions packaging/build_packages.sh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to only build x86_64 debs, since that's what ubuntu-latest is going to run on. I don't know that we need to bother building for aarch64/arm64, but wanted to note this.

Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/bin/sh

# Builds .deb and .rpm packages from an unpacked try dist tree using
# native packaging tools (dpkg-deb, rpmbuild). Intended to run inside
# packaging/Dockerfile, against the tarball produced by `make dist`.

set -e

VERSION=$(grep '#define TRY_VERSION' utils/version.h | cut -d'"' -f2)
ARCH=$(dpkg --print-architecture)
RPMARCH=$(uname -m)
PKGROOT="$PWD/pkgroot"
OUTDIR="$PWD/dist-packages"
RPMTOPDIR="$PWD/rpmbuild"

DESCRIPTION="Lets you run a command and inspect its effects before changing your live system"

for d in "$PKGROOT" "$OUTDIR" "$RPMTOPDIR"; do
if [ -e "$d" ]; then
echo "error: $d already exists; refusing to overwrite" >&2
exit 1
fi
done
mkdir -p "$OUTDIR"

grep -q '^AC_DEFUN(\[TRY_REQUIRE_PROG\], \[\])$' configure.ac ||
sed -i '/^AC_DEFUN(\[TRY_REQUIRE_PROG\]/,/^])$/c\AC_DEFUN([TRY_REQUIRE_PROG], [])' configure.ac
autoconf

./configure
make
make install prefix="$PKGROOT/usr"
Comment thread
mgree marked this conversation as resolved.

# build .deb with dpkg-deb
build_deb() {
output="$1"

mkdir -p "$PKGROOT/DEBIAN"
size=$(du -sk "$PKGROOT/usr" | cut -f1)

cat > "$PKGROOT/DEBIAN/control" <<EOF
Package: try
Version: $VERSION
Section: utils
Priority: optional
Architecture: $ARCH
Installed-Size: $size
Depends: attr
Maintainer: try maintainers <https://github.com/binpash/try>
Homepage: https://github.com/binpash/try
Description: $DESCRIPTION
EOF

dpkg-deb --build --root-owner-group "$PKGROOT" "$output"
}

# build .rpm with rpmbuild
build_rpm() {
output="$1"

mkdir -p "$RPMTOPDIR/BUILD" "$RPMTOPDIR/RPMS" "$RPMTOPDIR/SOURCES" \
"$RPMTOPDIR/SPECS" "$RPMTOPDIR/SRPMS" "$RPMTOPDIR/BUILDROOT"

filelist=$(cd "$PKGROOT" && find usr \( -type f -o -type l \) | sed 's|^|/|')

{
echo "Name: try"
echo "Version: $VERSION"
echo "Release: 1"
echo "Summary: $DESCRIPTION"
echo "License: MIT"
echo "URL: https://github.com/binpash/try"
echo "BuildArch: $RPMARCH"
echo "Requires: attr"
echo "%undefine _enable_debug_packages"
echo "%global debug_package %{nil}"
echo "%global __os_install_post %{nil}"
echo "%global _build_id_links none"
echo ""
echo "%description"
echo "$DESCRIPTION"
echo ""
echo "%install"
echo "cp -a '$PKGROOT'/usr %{buildroot}/usr"
echo ""
echo "%files"
echo "$filelist"
} > "$RPMTOPDIR/SPECS/try.spec"

rpmbuild --define "_topdir $RPMTOPDIR" -bb "$RPMTOPDIR/SPECS/try.spec"
cp "$RPMTOPDIR/RPMS/$RPMARCH"/*.rpm "$output"
}

build_deb "$OUTDIR/try_${VERSION}_$ARCH.deb"
build_rpm "$OUTDIR/try-$VERSION-1.$RPMARCH.rpm"