Bean & Bark: ship Roastery Desk
The Orders API is live and the Pricing SDK is shared, but the physical roastery still runs on spreadsheets. Nadia wants Roastery Desk for batch intake, labels, and stock counts. Her MacBook is on the tasting floor, Tunde has a Windows laptop, and the packing station runs Linux. A publish folder on your machine is not a release any of them can install.
- Declare one versioned desktop product across macOS, Windows, and Linux runtime identifiers
- Create permission-safe first downloads and prove every routed cell without publication authority
- Stage a feed-derived candidate for hands-on testing on all three operating systems
- Promote the approved candidate byte-identically and understand the native-installer graduation path
Name the product version once
Distribution reads the product version from the project or nearest Directory.Build.props. Put it where code review can see it; a candidate will append a feed-derived -rc.N, while the stable release keeps the clean base.
<PropertyGroup> <Version>1.0.0</Version></PropertyGroup> Turn a publish tree into three downloads
--scaffold-archive creates a small checked-in .NET helper and wires it into the manifest. Windows gets ZIP; macOS and Linux get permission-preserving TAR.GZ. The helper writes only declared archives into the output folder, so checksums and uploads do not accidentally include the loose publish tree.
kanject add distributable src/BeanAndBark.RoasteryDesk/BeanAndBark.RoasteryDesk.csproj --id roastery-desk --rid osx-arm64 --rid win-x64 --rid linux-x64 --self-contained --scaffold-archivekanject configure distribution --env prod --distributable roastery-desk The guided distribution command records selector → destination rows in the prod stage. One wildcard can route every RID to the downloads bucket; exact RID rows can override it later without changing the application manifest.
Climb the safety ladder
Before AWS credentials enter the room, ask two progressively stronger questions: is the local plan coherent, and can the real artifact path finish?
kanject doctor --features# Plan every cell without AWS or destination credentialskanject aws distribute --env prod --distributable roastery-desk --dry-run --offline# Produce the real archives and checksums, but upload nothingkanject aws distribute --env prod --distributable roastery-desk --no-publish The offline plan prints all three cells and the source of 1.0.0. The build-only run publishes, archives, verifies, and checksums each cell under .kanject/dist/roastery-desk/..., but has no upload or ledger side effect. You unpack each archive and launch it on the matching machine.
kanject aws distribute --env prod --distributable roastery-desk --rid osx-arm64 --no-publish Confirm the arm64 archive launches and preserves executable permissions. A native DMG later adds .app identity, Developer ID signing, notarization, stapling, and Gatekeeper verification.
kanject aws distribute --env prod --distributable roastery-desk --rid win-x64 --no-publish Confirm the self-contained ZIP launches without a preinstalled runtime. A native MSI later adds upgrade identity, shortcuts, WiX construction, and Authenticode verification.
kanject aws distribute --env prod --distributable roastery-desk --rid linux-x64 --no-publish Confirm the TAR.GZ preserves the executable bit. A native DEB later adds ownership, dependencies, desktop metadata, and package-manager inspection.
Stage one candidate for three people
distribute init creates or adopts the stable downloads bucket and its isolated -rc sibling, then enforces public-access block, encryption, and versioning. Candidate numbering comes from the candidate feed, so the pipeline cannot collide by guessing a suffix.
# Create/adopt stable + candidate buckets and security posturekanject aws distribute init --env prod# Stage the next feed-derived 1.0.0-rc.N releasekanject aws distribute --candidate --env prod --distributable roastery-desk# After the three-machine check, promote the exact candidatekanject aws distribute promote --env prod Nadia, Tunde, and the packing operator download 1.0.0-rc.1 from the candidate channel. The approval records that all three cells passed hands-on checks. promote then performs server-side copies into stable destinations and flips the stable feed to 1.0.0; no compiler, archive helper, or signing script runs again.
When an archive is no longer enough
Saturday's internal pilot can use archives. A customer-facing release needs native identity and OS trust. Scaffold the inert release kit only after the archive path is green:
kanject configure distribution scaffold --distributable roastery-desk The generated README and RID router carry runnable DMG, MSI, and DEB starters plus payload/container signing and independent verification. They are intentionally not activated for you: bundle identifiers, upgrade codes, entitlements, certificates, notarization credentials, install scope, and Linux metadata are product decisions.
- A reviewed
<Version>plus edition × RID manifest defines the product matrix;--scaffold-archivecreates the first usable downloads. - Offline dry-run proves local intent, while
--no-publishexecutes the real artifact path without destination authority. - The candidate feed owns
-rc.N; people test those downloads on matching operating systems before approval. promotecopies the approved bytes to stable without rebuilding; native DMG/MSI/DEB graduation stays explicit and matching-OS.
--no-publish for a three-RID sample, unpack each archive, and compare executable permissions plus SHA256SUMS coverage.Show solution
artifactGlobs or the upload set. Unix executables should retain their executable bits after extraction.