Skip to content

viewer: pause writes while text is selected — restores copy #74

viewer: pause writes while text is selected — restores copy

viewer: pause writes while text is selected — restores copy #74

Workflow file for this run

name: Publish apt repo
# Two channels:
# * `stable` — populated when a `v*` tag is pushed. The package
# version is whatever Cargo.toml says (the `release.yml` tag flow
# is expected to bump that to the release version).
# * `nightly` — populated on every push to `main`. The package
# version is rewritten on the fly to
# <base>~nightly<UTC YYYYMMDD>.<UTC HHMMSS>-1
# `~` is the Debian pre-release separator (Debian Versioning
# wiki) so e.g. `0.4.0~nightly20260527.123000` sorts strictly
# BEFORE `0.4.0`. That gives users a "track main" channel that
# auto-downgrades-and-upgrades when a stable lands.
#
# Single output: a Debian "pool" repo on the `gh-pages` branch,
# served from https://deb.tab-atelier.wdes.eu/ (CNAME → GH Pages).
# Signed with the key whose private half lives in the
# `APT_SIGNING_KEY` repo secret.
on:
push:
branches: [main]
tags: ['v*']
permissions:
contents: write
actions: read # download the latest windows-msi artifact for /windows/
env:
RUST_VERSION: "1.95.0"
jobs:
publish:
name: Build .debs + publish to gh-pages
runs-on: ubuntu-latest
# Skip if the commit is a `[skip apt]` chore (e.g. README-only
# tweaks from the release workflow itself).
if: ${{ !contains(github.event.head_commit.message, '[skip apt]') }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Decide channel + version
id: ver
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
CHANNEL=stable
VERSION="${GITHUB_REF#refs/tags/v}"
else
CHANNEL=nightly
BASE=$(grep -m1 '^version = ' Cargo.toml | cut -d'"' -f2 | sed 's/-dev$//')
DATE=$(date -u +%Y%m%d.%H%M%S)
VERSION="${BASE}~nightly${DATE}-1"
fi
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "→ publishing $VERSION to $CHANNEL"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libvulkan-dev libwayland-dev libxkbcommon-dev \
libxkbcommon-x11-dev libx11-dev libxcb1-dev \
libxcb-render0-dev libxcb-shm0-dev libxcb-xkb-dev \
libfontconfig-dev libfreetype-dev \
apt-utils gnupg
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- uses: Swatinem/rust-cache@v2
- name: Build .deb (both variants)
run: |
cargo install cargo-deb --locked
cargo deb -p tab-atelier --locked --deb-version "${{ steps.ver.outputs.version }}"
cargo deb -p tab-atelier --variant headless --locked --deb-version "${{ steps.ver.outputs.version }}"
ls -la target/debian/
# Pull the existing gh-pages tree into site/ so the
# next steps can prune + regenerate against it. First
# run on a fresh repo: the branch doesn't exist; we
# init an empty site/ instead. peaceiris/actions-gh-pages
# at the end handles the orphan-branch case for the
# actual push, so this step just gets the prior state
# in front of us.
- name: Fetch existing gh-pages state
env:
GH_TOKEN_X: ${{ github.token }}
run: |
REMOTE="https://x-access-token:${GH_TOKEN_X}@github.com/${{ github.repository }}.git"
rm -rf site
if git ls-remote --heads "$REMOTE" gh-pages | grep -q '\<gh-pages$'; then
echo "→ gh-pages exists upstream — clone shallow"
git clone --depth=1 -b gh-pages --single-branch "$REMOTE" site
else
echo "→ gh-pages does NOT exist — start with empty site/"
mkdir -p site
fi
- name: Drop .debs into pool/
env:
CHANNEL: ${{ steps.ver.outputs.channel }}
run: |
cd site
mkdir -p pool/$CHANNEL/main/t/tab-atelier
cp ../target/debian/*.deb pool/$CHANNEL/main/t/tab-atelier/
# Cap the pool at the 10 most recent .debs per package
# so the repo doesn't balloon forever. Sort by mtime
# then prune the tail.
for pkg in tab-atelier tab-atelier-headless; do
ls -1t pool/$CHANNEL/main/t/tab-atelier/${pkg}_*.deb 2>/dev/null \
| tail -n +11 | xargs -r rm -f
done
# Windows MSIs are built on a windows runner by
# windows-msi.yml and can't be produced here. Pull the .msi
# from the most recent successful run into site/windows/ and
# regenerate that folder's index. The gh-pages clone above
# already preserved any previously-published MSIs, so if no
# fresh artifact is found the existing set just carries over.
- name: Add Windows MSI builds + index
env:
GH_TOKEN: ${{ github.token }}
run: |
cd site
mkdir -p windows
RUN_ID=$(gh run list --repo "$GITHUB_REPOSITORY" \
--workflow=windows-msi.yml --status=success \
-L 1 --json databaseId --jq '.[0].databaseId // empty')
if [ -n "$RUN_ID" ]; then
echo "→ pulling MSI from windows-msi run $RUN_ID"
rm -rf ../win-tmp && mkdir -p ../win-tmp
if gh run download "$RUN_ID" --repo "$GITHUB_REPOSITORY" -D ../win-tmp; then
find ../win-tmp -name '*.msi' -exec cp -f {} windows/ \;
fi
rm -rf ../win-tmp
else
echo "→ no successful windows-msi run yet; keeping existing MSIs"
fi
# Cap at the 10 most recent MSIs.
ls -1t windows/*.msi 2>/dev/null | tail -n +11 | xargs -r rm -f
# Regenerate the folder index (newest first).
{
echo '<!DOCTYPE html><meta charset=utf-8><title>tab-atelier Windows builds</title>'
echo '<style>body{font-family:system-ui;max-width:42em;margin:3em auto;padding:0 1em}'
echo 'code{background:#f4f4f4;padding:.1em .3em;border-radius:3px}</style>'
echo '<h1>tab-atelier Windows builds</h1>'
echo '<p>Headless daemon MSI installers (<code>tab-atelier-headless</code>), newest first. Each filename carries the commit short SHA.</p>'
echo '<ul>'
for f in $(ls -1t windows/*.msi 2>/dev/null); do
bn=$(basename "$f")
sz=$(du -h "$f" | cut -f1)
echo "<li><a href=\"$bn\">$bn</a> &mdash; $sz</li>"
done
echo '</ul><p><a href="../">&larr; back to the apt repo</a></p>'
} > windows/index.html
- name: Regenerate Packages + Release
env:
CHANNEL: ${{ steps.ver.outputs.channel }}
run: |
cd site
mkdir -p dists/$CHANNEL/main/binary-amd64
apt-ftparchive --arch amd64 packages pool/$CHANNEL/ \
> dists/$CHANNEL/main/binary-amd64/Packages
gzip -9 --force --keep dists/$CHANNEL/main/binary-amd64/Packages
cat > apt-ftparchive-$CHANNEL.conf <<EOF
APT::FTPArchive::Release::Origin "tab-atelier";
APT::FTPArchive::Release::Label "tab-atelier";
APT::FTPArchive::Release::Suite "$CHANNEL";
APT::FTPArchive::Release::Codename "$CHANNEL";
APT::FTPArchive::Release::Architectures "amd64";
APT::FTPArchive::Release::Components "main";
APT::FTPArchive::Release::Description "tab-atelier $CHANNEL channel";
EOF
apt-ftparchive -c apt-ftparchive-$CHANNEL.conf release \
dists/$CHANNEL/ > dists/$CHANNEL/Release
rm apt-ftparchive-$CHANNEL.conf
# Import the apt signing key into the runner's GPG
# keyring AND configure git to use it for commit
# signatures. The same key signs both the apt Release
# file AND the gh-pages commits — anyone with the
# public key (curlable from deb.tab-atelier.wdes.eu)
# can verify both ends with one trust anchor.
- name: Import GPG signing key
id: gpg
uses: crazy-max/ghaction-import-gpg@v7
with:
gpg_private_key: ${{ secrets.APT_SIGNING_KEY }}
fingerprint: ${{ secrets.APT_SIGNING_KEY_ID }}
git_user_signingkey: true
git_commit_gpgsign: true
git_committer_name: 'Wdes Bot'
git_committer_email: 'williamdes+wdes-bot@wdes.fr'
- name: Sign Release
env:
CHANNEL: ${{ steps.ver.outputs.channel }}
GPG_KEY_ID: ${{ steps.gpg.outputs.fingerprint }}
run: |
cd site
gpg --batch --yes --local-user "$GPG_KEY_ID" \
--output dists/$CHANNEL/Release.gpg \
--detach-sign dists/$CHANNEL/Release
gpg --batch --yes --local-user "$GPG_KEY_ID" --clearsign \
--output dists/$CHANNEL/InRelease \
dists/$CHANNEL/Release
# Public key is republished on every run so a
# re-key propagates automatically to users on
# their next `apt update`.
gpg --batch --yes --armor --export "$GPG_KEY_ID" \
> tab-atelier.gpg
- name: Write CNAME + landing page
run: |
cd site
echo "deb.tab-atelier.wdes.eu" > CNAME
cat > index.html <<'HTML'
<!DOCTYPE html><meta charset=utf-8><title>tab-atelier apt repo</title>
<style>body{font-family:system-ui;max-width:42em;margin:3em auto;padding:0 1em}
pre{background:#f4f4f4;padding:1em;border-radius:4px;overflow-x:auto}
code{background:#f4f4f4;padding:.1em .3em;border-radius:3px}</style>
<h1>tab-atelier apt repo</h1>
<p>Debian / Ubuntu install:</p>
<pre>curl -fsSL https://deb.tab-atelier.wdes.eu/tab-atelier.gpg \
| sudo tee /usr/share/keyrings/tab-atelier.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/tab-atelier.gpg] https://deb.tab-atelier.wdes.eu stable main" \
| sudo tee /etc/apt/sources.list.d/tab-atelier.list > /dev/null
sudo apt update
sudo apt install tab-atelier # or tab-atelier-headless</pre>
<p>Replace <code>stable</code> with <code>nightly</code> to track <code>main</code>.</p>
<h2>Windows</h2>
<p>Headless MSI installers for Windows are at <a href="/windows/">/windows/</a>.</p>
<p>Source: <a href="https://github.com/wdes/tab-atelier">github.com/wdes/tab-atelier</a>.</p>
HTML
# peaceiris/actions-gh-pages handles the orphan branch
# case (first run) and the push. The commit it creates
# picks up the global git config crazy-max set above —
# so `git commit` runs with commit.gpgsign=true and
# user.signingkey = the apt key fingerprint. Result:
# every gh-pages commit shows up as Verified on GitHub,
# signed by the same key that signs the apt Release.
# `keep_files: false` means the action REPLACES gh-pages
# with our `site/` tree wholesale — pruning + the prior
# checkout above gave us exactly the desired final
# state, so wipe-and-replace is the right behaviour.
- name: Publish to gh-pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
publish_branch: gh-pages
user_name: 'Wdes Bot'
user_email: 'williamdes+wdes-bot@wdes.fr'
commit_message: 'Publish ${{ steps.ver.outputs.version }} to ${{ steps.ver.outputs.channel }}'
keep_files: false