Packaging & distribution

day pack -p <target> builds the app in release mode, signs it, and produces a standalone installable artifact in build/day/dist/, with a SHA-256 checksum and a signing tier in the result output. One command per platform, the platform’s own signing tools underneath. Day orchestrates codesign/notarytool, xcodebuild -exportArchive, Gradle signing, flatpak-builder, linuxdeploy, makeappx/signtool/makensis, and hap-sign-tool; it never reimplements them.

Artifacts per target

targetartifactnotes
macos-appkit.dmgassembled .app (Info.plist, icons, assets) → inside-out codesign --timestamp -o runtime → UDZO dmg (with an /Applications drop link) → dmg signature → notarytool submit --waitstapler staple
ios-uikit.ipaxcodebuild archive (device, arm64) → -exportArchive with a generated ExportOptions.plist (app-store-connect); without signing config: an unsigned device build, packaged as <name>-unsigned.ipa for sideloading or your own signing
android-mdc.apk + .aabGradle assembleRelease + bundleRelease with a release signingConfig; verified with apksigner and checked for 16 KB page alignment
linux-gtk / linux-qt.flatpaksingle-file bundle; the runtime supplies the toolkit (GTK 4 ⇒ org.gnome.Platform, Qt 6 ⇒ org.kde.Platform) and resolves from Flathub at install time — flatpak install ./MyApp-1.0-gtk-x86_64.flatpak needs no other setup (the toolkit is part of the name, so the gtk and qt bundles coexist). A Qt app that links QtWebEngine also carries the Qt WebEngine BaseApp, since no runtime ships it — that is ~87 MB of Chromium, so day pack adds it only when the binary actually links it
linux-gtk / linux-qt.appimageone executable that carries its own GTK/Qt — chmod +x and run, with no package manager, no runtime download and no root. day pack stages the AppDir and hands the bundling to linuxdeploy plus its gtk/qt plugin, which is what picks up the pieces an ldd closure misses (GdkPixbuf loaders, GIO modules, GSettings schemas, Qt platform plugins). Without the plugin the image still builds and still runs, on a machine that already has the toolkit — day pack says so loudly rather than letting a user discover it
windows-xaml.msix + -setup.exemakeappx + signtool for the MSIX; an NSIS per-user installer (no elevation, ARP entry, silent /S) for classic direct download
harmony-arkui.haphvigor release build, signed with your release material via hap-sign-tool (or the public dev certificate without it)
web-domthere is no pack step: day build -p web-dom writes a self-contained dist/ (host page, shim, stylesheet, .wasm, images, fonts) that you deploy to any static host as-is

--formats narrows the set (day pack -p android-mdc --formats apk); --no-sign and --no-notarize skip stages; --no-wait submits notarization asynchronously (poll with day sign --notarize-status <id>).

Signing configuration

Signing lives in Day.toml under [signing], with every secret referenced as ${ENV_VAR}; values resolve from the environment at pack time and are never stored in the manifest or printed by the tool:

[signing.macos]
identity = "${DAY_SIGN_MACOS_IDENTITY}"   # "Developer ID Application: …"

[signing.macos.notarize]
key-id = "${DAY_NOTARY_KEY_ID}"           # App Store Connect API key
issuer = "${DAY_NOTARY_ISSUER}"
key-path = "${DAY_NOTARY_KEY}"

[signing.ios]
team = "${DAY_APPLE_TEAM}"
key-id = "${DAY_ASC_KEY_ID}"              # ASC key for -allowProvisioningUpdates in CI
issuer = "${DAY_ASC_ISSUER}"
key-path = "${DAY_ASC_KEY}"

[signing.android]
keystore = "${DAY_ANDROID_KEYSTORE}"
key-alias = "${DAY_ANDROID_KEY_ALIAS}"
store-pass = "${DAY_KS_PASS}"
key-pass = "${DAY_KEY_PASS}"

[signing.windows]
provider = "self-signed-dev"              # or signtool-cert-store | azure-artifact-signing

[signing.ohos]
keystore = "${DAY_OHOS_KEYSTORE}"
key-alias = "${DAY_OHOS_KEY_ALIAS}"
store-pass = "${DAY_OHOS_KS_PASS}"
key-pass = "${DAY_OHOS_KEY_PASS}"
cert = "${DAY_OHOS_CERT}"
profile = "${DAY_OHOS_PROFILE}"

day sign --check reports each platform’s readiness (env vars set, key files present) without echoing a single secret value.

Signing tiers

Every artifact carries a tier: release, dev-signed, or unsigned. When a ${VAR} is unset (a laptop without the release keys, a fork PR without repository secrets), day pack warns naming the variable and drops that platform to the dev tier (ad-hoc codesign, a generated dev keystore, a self-signed certificate, the Simulator zip) instead of failing. The result JSON and the console both say so:

     Warning signing.macos.identity: ${DAY_SIGN_MACOS_IDENTITY} is not set — degrading to the dev signing tier
      Packed …/Day Showcase-0.1.0.dmg (dmg, dev-signed) sha256:ec51fa5f02ab…
     Warning Day Showcase-0.1.0.dmg is dev-signed — NOT distributable

A resolved configuration that is broken (a keystore path that doesn’t exist, a rejected notarization) is a hard failure with exit code 6.

Continuous integration

Every CI run packs the showcase on each platform job and uploads the results as dist-<target> artifacts, so the packaging path is exercised on every push, at the dev tier. Adding the DAY_* repository secrets lights up release signing with no workflow changes. Version tags (v*) run the release workflow, which packs every target and attaches the artifacts plus a SHA256SUMS file to a draft GitHub Release.