Skip to content

ci(release): tagged installer releases (Windows + Linux; macOS on demand) - #36

Merged
DeepDiver1975 merged 2 commits into
mainfrom
feat/release-installers
Jul 22, 2026
Merged

ci(release): tagged installer releases (Windows + Linux; macOS on demand)#36
DeepDiver1975 merged 2 commits into
mainfrom
feat/release-installers

Conversation

@DeepDiver1975

@DeepDiver1975 DeepDiver1975 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Closes #35.

Publishes native installers to GitHub Releases. Triggered by a version tag, not by every merge —
see the cost rationale below.

Platform Artifact Built Tooling
Windows lagebuch-<v>-x64.msi every tag WiX v5, fixed UpgradeCode so upgrades replace
Linux lagebuch_<v>_amd64.deb every tag dpkg-deb, .desktop + hicolor icon
macOS (Apple Silicon) lagebuch-<v>-macos-arm64.dmg on request drag-to-Applications, ad-hoc signed

Why tag-triggered, and why macOS on demand

The issue asked for "on every merge to main". Measured cost changed the plan. On a private repo,
Actions minutes bill with multipliers (macOS 10×, Windows 2×, Linux 1×), rounded up per job. One
all-platform run, from a real run:

Job Wall Billable
macOS 44 s 10 min (10×, rounded up)
Windows 135 s 6 min (2×)
Linux 79 s 2 min
version 2 s 1 min
Total 19 min/run

At ~19 merges/month that is ~360 billable min/month for releases alone. So:

  • Release on a version tag (git tag v0.2.0 && git push origin v0.2.0). Deliberate, and the
    version is proper semver from the tag instead of 0.1.<run_number>. A tagged release builds
    Windows + Linux, ~9 billable min.
  • macOS is built on demand — it's the 10× leg. Run the workflow manually (workflow_dispatch)
    with a version and the .dmg is built and attached to that release.

Design

  • Three independent trigger paths. A tag push skips the macOS job; a manual dispatch skips
    windows/linux/release. Skipped ≠ failed.
  • Packaging is committed, reusable scripts/authoring (packaging/{windows,linux,macos}), not inline
    YAML.
  • Unsigned — no Apple Developer ID or Windows cert. README documents the one-time
    Gatekeeper / SmartScreen steps; macOS is ad-hoc signed so it launches once allowed.
  • Placeholder icon — Feuerwehr cross pattée in the app's signal red on its dark tile
    (.svg/.png/.ico), wired into the exe, MSI shortcut, .app bundle and .desktop entry.

Repo-policy cleanup (workflows touched)

  • Every action pinned to a full commit SHA (release.yml + the existing ci.yml, which used @v4).
  • Added .github/dependabot.yml (github-actions + nuget).

Verification — real runners

All three build legs green (earlier all-platform dispatch run): windows (MSI), linux (deb),
macos (dmg). Artifacts type-checked — MSI is a valid OLE/MSI, deb is Debian 2.0 with the full
layout, dmg 55 MB.

Tag-trigger wiring (this revision) validated by a workflow_dispatch run:

version  success
macos    success   ← .dmg built (55 MB artifact)
windows  skipped    linux  skipped    release  skipped   ← correct: dispatch is macOS-only

The attach step correctly detected no v0.0.0-test release and skipped gracefully; no stray
release or tag was created
.

Locally (Linux): the .deb is verified end to end with the same script CI runs
(dpkg-deb -I/-c). MSI and DMG can't be built off their native OS (WiX refuses non-Windows), so
they are validated by the runner builds above.

After merge

The tag path goes live. Pushing v0.2.0 produces a v0.2.0 release with the .msi + .deb; a
manual dispatch adds the .dmg. prerelease: true for now (0.x, under construction) — a one-word
flip at 1.0.

🤖 Generated with Claude Code

DeepDiver1975 and others added 2 commits July 22, 2026 09:54
Closes #35. The repo had no published releases and no way to run Lagebuch
without a .NET SDK. release.yml existed from the first App PR but was never used:
it triggered on v* tags (never pushed), covered only win/linux, and produced
bare executables rather than installers.

Every merge to main now builds and publishes a prerelease with a real installer
per platform:

  Windows  lagebuch-<v>-x64.msi        (WiX v5)
  Linux    lagebuch_<v>_amd64.deb      (dpkg-deb, .desktop + icon)
  macOS    lagebuch-<v>-macos-arm64.dmg (drag-to-Applications, ad-hoc signed)

Version is 0.1.<run_number>, published as a prerelease so nothing is promoted to
"Latest" automatically. workflow_dispatch runs the identical build for testing
but creates no release or tag (release job is gated on event_name == 'push'),
so the pipeline can be exercised from a branch on the real runners before merge.

Packaging lives in packaging/{windows,linux,macos} as committed, reusable
scripts/authoring rather than inline YAML, so each step runs the same locally and
in CI. The .msi uses a fixed UpgradeCode so new versions replace the old.

Packages are unsigned -- no Apple Developer ID or Windows cert -- and the README
documents the one-time Gatekeeper/SmartScreen steps.

A placeholder icon (Feuerwehr cross pattée in the app's signal red on its dark
tile) is generated as .svg/.png/.ico and wired into the exe, the .msi shortcut,
the .app bundle and the .desktop entry; the macOS .icns is derived on the runner.
Clearly a placeholder, easily swapped.

Also, per the repo's action-pinning policy: every workflow action is pinned to a
full commit SHA (release.yml and the existing ci.yml, which used movable @v4),
and a dependabot config is added for the github-actions and nuget ecosystems.

Not locally verifiable: the WiX MSI build and the macOS .app/.dmg only run on
their respective OSes (WiX explicitly refuses non-Windows). The Linux .deb is
verified end to end on Linux -- dpkg-deb -I/-c on the built package. The MSI and
DMG are validated by a workflow_dispatch run on the real runners before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com>
Private-repo Actions minutes are billed with steep multipliers, and one
all-platform run costs ~19 billable minutes: macOS 10 (10x, rounded up from 44s
of work), Windows 6 (2x), Linux 2, version 1. On every merge to main that is
~360 min/month for releases alone.

Release on an intentional version tag instead. Pushing v0.2.0 builds and
publishes the Windows + Linux installers (~9 billable min); the version comes
from the tag, which is proper semver rather than 0.1.<run_number>.

macOS carries the 10x multiplier, so it is not built on every tag. Run the
workflow manually with a version and the .dmg is built and attached to that
release. workflow_dispatch is retained for exactly this.

The three trigger paths are independent: a tag push skips the macOS job, a
dispatch skips windows/linux/release. Packaging steps are unchanged and were
already validated on the real runners.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com>
@DeepDiver1975 DeepDiver1975 changed the title ci(release): native installers for Windows, macOS and Linux on merge to main ci(release): tagged installer releases (Windows + Linux; macOS on demand) Jul 22, 2026
@DeepDiver1975
DeepDiver1975 merged commit 18d61de into main Jul 22, 2026
9 checks passed
@DeepDiver1975
DeepDiver1975 deleted the feat/release-installers branch July 22, 2026 11:22
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.

release: upon each merge to main build installer/packages

1 participant