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
143 changes: 143 additions & 0 deletions .github/workflows/publish-aur.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
name: "publish-aur"

# Publishes each stable release to the AUR as cap-bin. The PKGBUILD is the one
# scripts/build-linux-arch-package.sh builds the release's own pacman package
# from, pointed at the release's CrabNebula deb, so the two stay identical.
#
# Release events run this file from the tagged commit; for a tag that predates
# it, dispatch it from main with that tag. Pushing needs the AUR_SSH_PRIVATE_KEY
# secret of an AUR account that maintains cap-bin. Without it the job still
# builds the package and uploads the PKGBUILD as an artifact.

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "Published stable release tag, e.g. cap-v0.6.1"
required: true
type: string

concurrency:
group: publish-aur
cancel-in-progress: false

permissions:
contents: read

jobs:
aur:
if: ${{ github.event_name == 'workflow_dispatch' || (startsWith(github.event.release.tag_name, 'cap-v') && !github.event.release.prerelease) }}
runs-on: ubuntu-24.04
timeout-minutes: 30
env:
TAG: ${{ inputs.tag || github.event.release.tag_name }}
ARCH_IMAGE: archlinux:base-devel@sha256:68bfc3b0d277b08a99101dc9b94aaa03e5ae70cf1b4fb965c03b2b87b915760d
steps:
- name: Checkout repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false

- name: Resolve release deb
id: release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
VERSION="${TAG#cap-v}"
if [[ "$TAG" != cap-v* || ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::'$TAG' is not a stable cap-v{x.y.z} release tag."
exit 1
fi
RELEASE="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json isDraft,isPrerelease,body)"
if [[ "$(jq -r '.isDraft or .isPrerelease' <<< "$RELEASE")" != false ]]; then
echo "::error::$TAG is not a published stable release."
exit 1
fi
DEB_URL="$(jq -r '.body | capture("<!--\\s*DOWNLOADS_JSON\\s*(?<downloads>\\{[^}]+\\})\\s*-->").downloads | fromjson | .["linux-deb"] // empty' <<< "$RELEASE")"
if [[ ! "$DEB_URL" =~ ^https://cdn\.crabnebula\.app/asset/[0-9A-Za-z]+$ ]]; then
echo "::error::$TAG has no CrabNebula linux-deb link in its DOWNLOADS_JSON."
exit 1
fi
{
echo "version=$VERSION"
echo "deb_url=$DEB_URL"
} >> "$GITHUB_OUTPUT"

- name: Build AUR package
shell: bash
env:
DEB_URL: ${{ steps.release.outputs.deb_url }}
VERSION: ${{ steps.release.outputs.version }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/deb" "$RUNNER_TEMP/aur-package"
curl -fsSL --retry 5 --retry-all-errors -o "$RUNNER_TEMP/deb/Cap.deb" "$DEB_URL"
docker run --rm --network none \
--env CAP_PACKAGE_UID="$(id -u)" \
--volume "$GITHUB_WORKSPACE:/source:ro" \
--volume "$RUNNER_TEMP/deb:/input:ro" \
--volume "$RUNNER_TEMP/aur-package:/output" \
"$ARCH_IMAGE" \
bash -c 'useradd --uid "$CAP_PACKAGE_UID" --create-home cap-package && runuser -u cap-package -- bash /source/scripts/build-linux-arch-package.sh /input/Cap.deb /output "$1"' \
bash "$DEB_URL"
PKGVER="$(awk -F ' = ' '$1 == "\tpkgver" { print $2 }' "$RUNNER_TEMP/aur-package/.SRCINFO")"
if [[ "$PKGVER" != "$VERSION" ]]; then
echo "::error::The linux-deb link holds Cap $PKGVER, not $VERSION."
exit 1
fi

- name: Upload AUR package files
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: aur-cap-bin-${{ steps.release.outputs.version }}
path: |
${{ runner.temp }}/aur-package/PKGBUILD
${{ runner.temp }}/aur-package/.SRCINFO
include-hidden-files: true
overwrite: true
if-no-files-found: error

- name: Push to the AUR
shell: bash
env:
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
VERSION: ${{ steps.release.outputs.version }}
run: |
set -euo pipefail
if [[ -z "$AUR_SSH_PRIVATE_KEY" ]]; then
echo "::warning::AUR_SSH_PRIVATE_KEY is not set, so cap-bin $VERSION was built but not pushed to the AUR."
exit 0
fi
install -d -m 700 "$HOME/.ssh"
printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > "$HOME/.ssh/aur"
chmod 600 "$HOME/.ssh/aur"
echo "aur.archlinux.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN" > "$HOME/.ssh/known_hosts"
export GIT_SSH_COMMAND="ssh -i $HOME/.ssh/aur -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=yes"
PACKAGE="$RUNNER_TEMP/aur-package"
PKGBASE="$(awk -F ' = ' '$1 == "pkgbase" { print $2 }' "$PACKAGE/.SRCINFO")"
AUR="$RUNNER_TEMP/aur"
git -c init.defaultBranch=master clone "ssh://aur@aur.archlinux.org/$PKGBASE.git" "$AUR"
version_of() {
awk -F ' = ' '$1 == "\tepoch" { epoch = $2 ":" } $1 == "\tpkgver" { pkgver = $2 } $1 == "\tpkgrel" { pkgrel = $2 } END { print epoch pkgver "-" pkgrel }' "$1"
}
if [[ -f "$AUR/.SRCINFO" ]]; then
CURRENT="$(version_of "$AUR/.SRCINFO")"
NEW="$(version_of "$PACKAGE/.SRCINFO")"
if (( $(docker run --rm --network none "$ARCH_IMAGE" vercmp "$NEW" "$CURRENT") < 0 )); then
echo "::notice::The AUR already has $PKGBASE $CURRENT, which is newer than $NEW."
exit 0
fi
fi
cp "$PACKAGE/PKGBUILD" "$PACKAGE/.SRCINFO" "$AUR/"
cd "$AUR"
git add PKGBUILD .SRCINFO
if git diff --cached --quiet; then
Comment on lines +135 to +138

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.

P2 Recipe fixes miss installed users

Re-running publish-aur after changing the recipe can push different files with the same package version. The builder always writes pkgrel=1, and this step allows equal versions whenever the files differ. For example, correcting depends and dispatching the same release tag updates the AUR recipe, but installed users see no newer version to upgrade to.

Increment pkgrel for same-release package changes, or reject changed recipes with an unchanged package version.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish-aur.yml
Line: 135-138

Comment:
**Recipe fixes miss installed users**

Re-running `publish-aur` after changing the recipe can push different files with the same package version. The builder always writes `pkgrel=1`, and this step allows equal versions whenever the files differ. For example, correcting `depends` and dispatching the same release tag updates the AUR recipe, but installed users see no newer version to upgrade to.

Increment `pkgrel` for same-release package changes, or reject changed recipes with an unchanged package version.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

echo "The AUR already has $PKGBASE $VERSION."
exit 0
fi
git -c user.name="github-actions[bot]" -c user.email="41898282+github-actions[bot]@users.noreply.github.com" commit -m "Update to $VERSION"
git push origin HEAD:master
18 changes: 18 additions & 0 deletions packaging/linux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ The builder reuses the exact DEB payload, changes the package-format marker, and

On Omarchy, screen capture uses the installed Hyprland desktop portal and PipeWire. Package installation alone does not verify recording, playback, export, or device access; these require tests in the target desktop session.

## AUR

`.github/workflows/publish-aur.yml` publishes each stable release to the AUR as `cap-bin`, installable with `yay -S cap-bin` or `paru -S cap-bin`. When a `cap-v{x.y.z}` GitHub release is published, it downloads the `linux-deb` asset listed in the release's `DOWNLOADS_JSON` and runs the builder with that URL:

```sh
bash scripts/build-linux-arch-package.sh Cap_0.6.1_amd64.deb ./aur https://cdn.crabnebula.app/asset/<asset-id>
```

Given a download URL, the builder also writes the `PKGBUILD` it used and its `.SRCINFO` to the output directory. The `PKGBUILD` downloads the versioned DEB from that URL, so AUR builds produce the same package as the release. The workflow checks that the DEB holds the tagged version, then pushes both files to `ssh://aur@aur.archlinux.org/cap-bin.git`. It skips the push when the AUR already has a newer version or the same files.

One-time setup:

1. Create an AUR account for Cap and add a dedicated SSH public key to it, for example from `ssh-keygen -t ed25519 -f cap-aur -C cap-aur`.
2. Store the private key as the `AUR_SSH_PRIVATE_KEY` repository secret. Without it, the workflow builds the package and uploads the `PKGBUILD` and `.SRCINFO` as an artifact, but does not push.
3. Run `publish-aur` from `main` with the latest release tag, such as `cap-v0.6.1`. The first push creates `cap-bin` with that account as its maintainer.

A release event runs the workflow file from the tagged commit. For a tag that predates the workflow, such as a promoted nightly from before it was added, run it from `main` with that tag.

## CLI camera in Instant mode

On Linux, `cap record start --mode instant --camera DEVICE` composites the camera directly into the recorded screen or window. The CLI uses an unmirrored square at the bottom right, sized to 30% of the shorter screen edge with a 2% margin. Camera images are center-cropped without stretching. If camera frames stop arriving, screen recording continues without a stale camera image.
Expand Down
26 changes: 19 additions & 7 deletions scripts/build-linux-arch-package.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 2 ]]; then
printf 'Usage: %s <Cap.deb> <output-directory>\n' "$0" >&2
if [[ $# -lt 2 || $# -gt 3 ]]; then
printf 'Usage: %s <Cap.deb> <output-directory> [<deb-download-url>]\n' "$0" >&2
exit 1
fi

Expand All @@ -11,6 +11,12 @@ if [[ $(id -u) -eq 0 ]]; then
exit 1
fi

source_url="${3:-}"
if [[ -n "$source_url" && ! "$source_url" =~ ^https://[A-Za-z0-9._~%/:?=+-]+$ ]]; then
printf 'Unsupported download URL: %s\n' "$source_url" >&2
exit 1
fi

deb="$(realpath "$1")"
mkdir -p "$2"
output="$(realpath "$2")"
Expand Down Expand Up @@ -39,9 +45,11 @@ case "$architecture" in
*) printf 'Unsupported package architecture: %s\n' "$architecture" >&2; exit 1 ;;
esac

cp "$deb" "$work/Cap.deb"
checksum="$(sha256sum "$work/Cap.deb" | cut -d ' ' -f 1)"
deb_file="Cap_${version}_${architecture}.deb"
cp "$deb" "$work/$deb_file"
checksum="$(sha256sum "$work/$deb_file" | cut -d ' ' -f 1)"
cat > "$work/PKGBUILD" <<EOF
# Maintainer: Cap Software <hello at cap dot so>
pkgname=cap-bin
pkgver=${version//-/_}
pkgrel=1
Expand All @@ -54,12 +62,12 @@ optdepends=("xdg-desktop-portal-hyprland: screen capture on Hyprland and Omarchy
provides=("cap=\$pkgver")
conflicts=("cap")
options=("!strip" "!debug")
source=("Cap.deb")
noextract=("Cap.deb")
source=("$deb_file${source_url:+::$source_url}")
noextract=("$deb_file")
sha256sums=("$checksum")

package() {
bsdtar -xOf "\$srcdir/Cap.deb" "$data_member" | bsdtar -xf - -C "\$pkgdir"
bsdtar -xOf "\$srcdir/$deb_file" "$data_member" | bsdtar -xf - -C "\$pkgdir"
for binary in Cap cap-cli cap-muxer; do
test -x "\$pkgdir/usr/bin/\$binary"
done
Expand All @@ -68,4 +76,8 @@ package() {
EOF

cd "$work"
if [[ -n "$source_url" ]]; then
cp PKGBUILD "$output/PKGBUILD"
makepkg --printsrcinfo > "$output/.SRCINFO"
fi
PKGDEST="$output" makepkg --nodeps --noconfirm
Loading