Skip to content

ci: ask the AppImage what it bundles, and whether it starts - #584

Merged
PathGao merged 4 commits into
masterfrom
ci/appimage-checks
Aug 11, 2026
Merged

ci: ask the AppImage what it bundles, and whether it starts#584
PathGao merged 4 commits into
masterfrom
ci/appimage-checks

Conversation

@PathGao

@PathGao PathGao commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #583 (the recovery pull request). Merge #583 first and this retargets to master. The diff shown is against #583.

Two questions about the Linux artifact that nothing has been answering, and one comment this makes false.

check-appimage-libraries.sh — is six still the right number?

strip-appimage.sh proves its six libraries are gone after the strip. That is "we removed what we meant to remove". It cannot say whether six is still the right list, and what decides that is not in this repository:

Downloading https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh
                                                                              ^^^^^^

tauri build fetches the plugin that decides which libraries get copied into the AppDir from the tip of someone else's branch, on every run. It can change with no commit here, no pull request and no notification — the same shape as a runner image moving under macos-latest, one repository further away.

The script intersects what the AppImage bundles with the AppImage project's own excludelist and requires the result to equal the strip list exactly, in both directions:

a seventh host-coupled library appears  → red, named, "add it to EXCLUDED"
one of the six stops being bundled      → red, named, "drop it from EXCLUDED"

It reads the list out of strip-appimage.sh rather than restating it. The excludelist is fetched rather than vendored — it growing is the signal, and a copy here would be a second thing to keep current. A failed fetch is a failed check, never a pass.

On the pull request build, deliberately. That AppImage is the un-stripped one, which for this question is the right artifact rather than a worse one: it shows what linuxdeploy actually did before anything was taken back out. It only became comparable at all when #580 moved this job onto the release's Ubuntu.

smoke-appimage.sh — does it start?

strip-appimage.sh said:

A CI smoke test cannot catch this defect at all — the runner's Mesa is the one the libraries came from, so nothing mismatches there

True of the runner, and only of the runner. A container is another distribution's userspace on the same kernel, which is a host that can disagree. The comment now says so.

Two things recorded in #463 and #498 make it testable:

  • the abort does not need a GPU. LIBGL_ALWAYS_SOFTWARE, WEBKIT_DISABLE_DMABUF_RENDERER and WEBKIT_DISABLE_COMPOSITING_MODE were all tried and none helped, so software rendering under Xvfb reproduces it.
  • it prints the same line every time. Could not create default EGL display: EGL_BAD_PARAMETER. Aborting...

Liveness is not the check, and this is the part worth reading. #499: "the window comes up blank while the GTK shell survives". The main process stays up; WebKit's WebProcess is what aborts. A "is it still running" test would have passed through both defects this exists to catch. So it greps for the abort, and keeps liveness as the backstop for a reworded one.

On the release it sits after the re-sign, so it starts the bytes latest.json points at, and before the upload, so a failure leaves the draft without a Linux artifact — and generate-update-feed requires build to have succeeded, so it does not run either. Nothing is published; nothing was ever installable.

Both also run on pull requests, and that is the point

Not thoroughness. Without it:

  • smoke-appimage.sh would reach the release path having never run anywhere — the exact failure mode this series has been about;
  • strip-appimage.sh has still never run outside a release, and it failed in two of the three v2.7.2 release attempts.

The pull request now does what the release does, in the release's order: build → check what is bundled → strip → start it. Smoking before the strip would reproduce #498 rather than test for it, so the order is asserted.

Roughly three minutes on a twelve-minute job. If that stops being worth it, path-filter the two steps rather than deleting them.

Also

One line in dependabot.yml: the github-actions ecosystem covers uses: and not runs-on:. Nothing proposes ubuntu-24.04ubuntu-26.04, and nobody should assume something is watching.

Tests

Three assertions, each checked by mutation, each failing alone:

smoke before strip on the PR              → ✖ the AppImage is started before it can be downloaded
drop the release smoke step               → ✖ (same)
restate the library list in the new script → ✖ the excludelist is checked against what is actually bundled

npm test — 979 pass.

What this pull request cannot tell you

smoke-appimage.sh has never run anywhere. Its first execution is this pull request's own Linux job — which is precisely why it is on pull requests and not only on the release. If it is wrong, it is red here instead of red on a release.

🤖 Generated with Claude Code

@PathGao
PathGao force-pushed the ci/appimage-checks branch from 9946443 to 3295c23 Compare August 11, 2026 12:49
@PathGao
PathGao force-pushed the ci/appimage-checks branch from 3295c23 to d2a5339 Compare August 11, 2026 13:08
Base automatically changed from ci/restore-cache-key-and-package-list to master August 11, 2026 13:33
@PathGao
PathGao force-pushed the ci/appimage-checks branch from d432237 to 75803b6 Compare August 11, 2026 13:33
PathGao and others added 4 commits August 11, 2026 22:10
Stacked on ci/one-linux-dependency-list.

Two questions about the Linux artifact that nothing has been answering, and one
comment that was about to become false.

`check-appimage-libraries.sh`, on every pull request. strip-appimage.sh proves
its six libraries are gone afterwards; that is "we removed what we meant to",
not "six is still the right number". What decides the right number is not in
this repository -- `tauri build` fetches linuxdeploy-plugin-gtk from the tip of
someone else's branch on every run, so the set copied into the AppDir can change
with no commit here, no pull request and no notification. The script intersects
what is bundled with the AppImage project's own excludelist and requires the
result to equal the strip list exactly, in both directions. It reads that list
out of strip-appimage.sh rather than restating it.

It runs on the pull request build because that AppImage is the un-stripped one,
which for this question is the right artifact rather than a worse one. It only
became comparable when #580 moved this job to the release's Ubuntu.

`smoke-appimage.sh`, on the release, after the re-sign and before the upload.
strip-appimage.sh said a CI smoke test cannot catch this defect because the
runner's Mesa is the one the libraries came from -- true of the runner, and only
of the runner. A container is another distribution's userspace on the same
kernel, which is a host that can disagree. The comment now says so.

Liveness is not the check. #499 records that the GTK shell survives while
WebKit's WebProcess aborts behind a blank window, so both defects this exists to
catch would have passed a "is it still running" test. It greps for the abort,
and keeps the liveness check as the backstop for a reworded one.

Both scripts also run on pull requests, after a strip, and that is the point
rather than thoroughness: smoke-appimage.sh would otherwise reach the release
path having never run anywhere, and strip-appimage.sh has never run outside a
release -- it failed in two of the three v2.7.2 release attempts. Roughly three
minutes on a twelve-minute job.

Also, one line in dependabot.yml: the github-actions ecosystem covers `uses:`
and not `runs-on:`, so nothing proposes ubuntu-24.04 -> 26.04 and nobody should
assume something is watching.

Three assertions, each checked by mutation and each failing alone: smoking
before the strip, dropping the release smoke, and restating the library list in
the new script. 979 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ing on it

The check's first run found something real and then drew the wrong conclusion
from it. Run 31493065830, on the pull request build:

  Bundled libraries the excludelist says must come from the host:
    libwayland-client.so.0
  strip-appimage.sh removes a library nothing bundles any more:
    libwayland-cursor.so.0  libwayland-egl.so.1  libwayland-server.so.0
    libxcb-render.so.0      libxcb-shm.so.0

Those five are deliberate. #499 named libwayland-client.so.0 as necessary and
sufficient and added five more that "belong to the host graphics stack for the
same reason and are on the same excludelist" -- prophylaxis against the plugin
starting to bundle them, and `rm -f` costs nothing when they are absent. A build
not bundling them is not a defect, so it is a notice now. A seventh appearing is
still an error, which is the direction that matters.

Worth printing, though, because the two builds disagree and nobody knew. In the
release, `rm -v` reported all six removed in both v2.7.2 and v2.7.3 -- they were
all there. On the pull request's shorter package list, only one is. So the two
jobs have been producing different AppImages, and #581's package prune will
change what a release bundles rather than being the no-op its own message
claimed. The direction is right -- fewer host-coupled libraries is the goal --
but "no behaviour change" was verified against "it builds and produces three
bundles", not against the contents.

Which difference does it, the package list or the linuxdeploy build.yml
pre-downloads, is not isolated. Once both jobs install the same packages, the
next release's `removed` lines say.

The assertion holding the list to one home now exempts comments, the same way
the Ubuntu one does: the new comment names a library while explaining what was
measured, and tripped it. 979 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rom a host

First real run, on the pull request rather than on a release:

  host graphics stack: mesa 1:26.1.6-1
  Markpad: error while loading shared libraries: libfontconfig.so.1
  exit 127

Not the defect this is looking for. An AppImage deliberately does not carry
libfontconfig -- it is on the same excludelist as the six strip-appimage.sh
removes, and for the same reason -- so it expects the host to have it. A bare
archlinux container is not a desktop and does not.

fontconfig and a font go in beside xvfb and mesa. And a missing host library now
says so, instead of being reported as the app quitting early: the two look
identical at exit 127 and mean opposite things.

Worth recording that the container's mesa is 1:26.1.6-1, which is the version
#498 was reported on. The environment is the failing case, not an approximation
of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Naming the host's libraries individually is a round of CI each. Run
31494803899 wanted libfontconfig.so.1; with that added, run 31497012965 wanted
libfribidi.so.0. There is no reason to think that was the last one.

The set is not arbitrary. An AppImage deliberately does not carry these -- they
are on the same excludelist as the six strip-appimage.sh removes, and for the
same reason -- so what it needs from a host is GTK's own dependency closure,
which is exactly what a machine that can run a GTK application has. Installing
gtk3 asks for all of it in one go.
@PathGao
PathGao force-pushed the ci/appimage-checks branch from 28dad09 to 4001ce8 Compare August 11, 2026 14:10
@PathGao
PathGao merged commit 0adb69f into master Aug 11, 2026
4 checks passed
@PathGao
PathGao deleted the ci/appimage-checks branch August 11, 2026 14:27
PathGao added a commit that referenced this pull request Aug 12, 2026
…660)

* docs: bring the release runbook and the syntax reference up to 2.7.4

Four claims in the docs describe behaviour that PRs since 2.7.3 changed, and
two features shipped without reaching the file that documents what Markpad
can do.

RELEASING.md
- snapcraft.yaml no longer builds the app; it unpacks the release's own .deb
  (#579), so the `npm ci` note and the `rust-deps` troubleshooting row both
  described a file that no longer exists in that shape (#577).
- The .deb/.rpm coverage note still said *Check for Updates…* offers an
  update and then fails to install it. #573 asks `self_update_supported`
  first, so it says where updates come from instead.
- Adds the AppImage checks (#584, #658) as their own troubleshooting row, and
  notes that release builds are serialized (#612).

README, README.zh-CN
- The same pre-#573 claim, in both languages. The Chinese one also told
  .deb/.rpm users to update "through their distribution channels", which is
  the sentence #566 removed from the English one — there is no apt or dnf
  repository.

snapcraft.yaml
- The comment pointed at .github/workflows/test_snap.yml, deleted in #601.

samples/markdown-syntax{,.zh-CN}.md
- Lists: Enter continues the marker, Tab changes level, Enter on an empty
  item leaves the list (#636), and the three list chords (#652).
- Tables: cell, row and column keys, why deleting a row has no chord (#645,
  #653).
- Links: Ctrl/Cmd+K (#652).

npm test 904 pass, vitest 365 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(syntax): drop two design arguments that belong in the PRs, not here

The syntax reference carries no issue numbers, no history and no defence of
a decision anywhere in its 500 lines; where it does give a reason -- the
three deliberate incompatibilities -- the reason tells the reader what to
write instead. Two sentences I added argued for the design at the reader:
that four other editors use Mod+K, and why deleting a row has no chord while
deleting a column does. Neither changes anything the reader would type.

What is left of the second is the part that was actually useful: to delete a
row, delete its line.

* docs(readme): drop the only issue number a reader ever sees

#570 was the last issue number in either README, and it was one I added a
few days ago. It is a closed bug report, and after #573 the sentence it was
attached to describes ordinary behaviour -- so a reader asking "how does my
.deb update" now clicks through to a fault that no longer exists.

The half-sentence in front of it went for the same reason: "rather than
offering one it cannot install" describes what the app used to do. What is
left is what the reader needs -- .deb and .rpm are one-time installs, there
is no apt or dnf repository, and Check for Updates says so.

Both READMEs keep the Report a Bug link, which is the only issue link either
of them had before.

* docs(releasing): cut the post-mortems back out of the runbook

This file is what you read on the day you cut a release. Over three days in
August I turned it into a runbook with post-mortems threaded through it:
which run pushed Chocolatey 2.7.2 at 15:37, that the snap served 2.6.11 for
three months and six versions, which two of the three v2.7.2 attempts the
AppImage strip failed in, how the 2.7.2/2.7.3 lock skew was found. All true,
none of it changes what you do next, and it sits between you and step 5
while a build is running.

Test applied to each one: can it change an action taken on release day.

Gone: the "Why package managers publish after the release" section entirely
-- its one operational sentence (workflow_dispatch works against a published
tag, the only way to exercise snapcraft.yaml) moves into step 7, where you
would need it. The failure histories in three troubleshooting rows, keeping
the symptom and the fix. The placeholder-pubkey era. Two design defences.

Kept: reasons that stop you doing the wrong thing -- why not to create
anything at alecdotdev/Markpad, why the Cargo.lock bump is the one that gets
forgotten. A prohibition without its reason gets deleted as superstition by
whoever comes next.

1957 -> 1563 words. The version before I started was 883.

---------

Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant