Skip to content

Building and Releasing

Rod Christiansen edited this page Oct 8, 2026 · 2 revisions

Building and Releasing

build.sh is the whole build system. It compiles the Swift package, assembles the app bundle, writes the LaunchDaemon plists and install scripts, and produces a pkg, a zip and a dmg. The Makefile is a thin set of shortcuts over it.

Prerequisites

  • macOS with Xcode. CI builds on macOS 26 with Xcode 26; the packaging step compiles the Icon Composer bundle Resources/ReportMate.icon with actool and fails the build if no Assets.car comes out, so an older SDK that cannot compile it will not produce a pkg.
  • Swift 6 toolchain (swift-tools-version: 6.0).
  • Homebrew bash 5. build.sh starts with #!/opt/homebrew/bin/bash; system bash 3.2 will not run it.
brew install bash

Building

./build.sh

Common variants:

./build.sh --clean --version 2026.10.07.2024
./build.sh --skip-pkg --skip-zip --skip-dmg

Flags

Flag Effect
--version <v> Version to stamp. Defaults to YYYY.MM.DD.HHMM
--debug / --release Build configuration; release is the default
--clean Remove build artifacts first
--skip-build Skip the Swift compile
--skip-pkg, --skip-zip, --skip-dmg Skip that output
--sign Code sign the binaries and app bundle with ReportMate.entitlements
--distribution Implies --sign; signs with ReportMate-Distribution.entitlements and productsigns the pkg
--notarize Implies --distribution; submits the pkg to notarytool, waits, staples and validates
--api-url <url> Bake a default ApiUrl into the postinstall, written only if the key is absent
--install Install the built package locally
--create-tag Create and push a v<version> git tag
--create-release Create a v<version> GitHub release with every ReportMate-<version>.* artifact (needs gh)
--verbose Verbose output
--help Usage

Make targets

Target Runs
make build Debug build, no packaging
make release Release build, no packaging
make test swift test
make package Full build with packaging
make sign Package and sign
make notarize Package, sign and notarize (also the default target)
make install Release build, then copy the bare CLI from .build/release to /usr/local/reportmate. No daemons or app bundle
make clean swift package clean and remove build/ and .build/
make dev-setup Install osquery via Homebrew if missing
make format / make lint swift-format / SwiftLint, if installed

Versioning

Versions are YYYY.MM.DD.HHMM. build.sh generates Sources/Core/AppVersion.swift with the version compiled in, and restores the "YYYY.MM.DD.HHMM" placeholder on exit, so the file is generated, never hand-edited, and the working tree stays clean.

It also writes Sources/Generated/ModuleVersions.swift, giving each module the date of the last commit that touched its processor and model files (falling back to the build time), and restores a placeholder on exit. That is why the build needs full git history (fetch-depth: 0 in CI).

In the app bundle, CFBundleShortVersionString is the date part (2026.10.07) and CFBundleVersion is every digit run together (202610072024), so the build number always increases from one build to the next. Installer compares CFBundleVersion, and an HHMM-only number would go backwards on a later day at an earlier time.

Signing and notarization

Credentials come from a .env beside build.sh, which is not committed. .env.example is a template for all of these except NOTARIZATION_KEYCHAIN_PROFILE, which build.sh also reads:

Variable Purpose
TEAM_ID Apple Developer Team ID
SIGNING_IDENTITY_APP Identity for the binaries and app bundle
SIGNING_IDENTITY_INSTALLER Identity for productsign
SIGNING_TIMESTAMP Add --timestamp; default true
SIGNING_KEYCHAIN Non-default keychain path
NOTARIZATION_KEYCHAIN_PROFILE Stored notarytool profile — preferred
NOTARIZATION_APPLE_ID, NOTARIZATION_PASSWORD Legacy Apple ID + app-specific password

There are no defaults for any of these; the repo is public and ships no signing material.

Prefer the keychain profile. An app-specific password kept in a file is revoked whenever the Apple ID's password changes, and notarytool reports that as a bare HTTP 401 … not allowed for primary authentication with no hint that the credential is simply dead.

xcrun notarytool store-credentials "notarization_credentials" --apple-id you@example.com --team-id XXXXXXXXXX --password xxxx-xxxx-xxxx-xxxx

The bundled macadmins extension is re-signed with --options runtime when signing is on: the upstream binary ships without a hardened runtime, and notarization rejects the package otherwise.

Outputs

Everything lands in release/.

Artifact Notes
ReportMate-<version>.pkg pkgbuild with identifier com.github.reportmate, the preinstall/postinstall scripts, and a component plist that blocks bundle relocation. This is the supported install path
ReportMate-<version>.zip The bare CLI binary
ReportMate-<version>.dmg The bare CLI plus an Install.sh that copies it to /usr/local/reportmate. It installs no daemons, app or helper

The installer scripts are generated by build.sh itself. If a packages/client/Scripts directory exists it is used instead; the repository does not ship one.

CI

.github/workflows/ci.yml runs on pushes and PRs to main that touch Sources/, Tests/, Package.swift, build.sh or the workflow itself. On a macos-26 runner with Xcode 26 it installs Homebrew bash, runs ./build.sh --skip-pkg --skip-zip --skip-dmg, compiles the app icon with actool to catch an SDK that cannot build Assets.car, and runs swift test. It does not package or sign.

.github/workflows/release.yml runs on v* tags, on YYYY.MM.DD.HHMM tags, and on manual dispatch with an optional version. On macos-26 with Xcode 26 it builds with --skip-zip --skip-dmg, checks the pkg name matches ReportMate-YYYY.MM.DD.HHMM.pkg, writes a SHA-256 sidecar, and publishes both as a GitHub release titled ReportMate <version> with generated notes. A manual run creates the tag at the built commit. Releases are tagged as bare YYYY.MM.DD.HHMM versions; the latest is 2026.10.07.2024.

The released pkg is unsigned and unnotarized. No Apple Developer ID material is held in this repository. Sign and notarize the artifact with your own identities before deploying it, exactly as the release notes describe:

codesign --force --options runtime --timestamp --sign "Developer ID Application: <Org> (<TeamID>)" "/Applications/Utilities/Managed Reports Runner.app"
productsign --timestamp --sign "Developer ID Installer: <Org> (<TeamID>)" ReportMate-<version>.pkg ReportMate-<version>-signed.pkg
xcrun notarytool submit ReportMate-<version>-signed.pkg --apple-id <apple-id> --team-id <TeamID> --password <app-specific-password> --wait
xcrun stapler staple ReportMate-<version>-signed.pkg

Clone this wiki locally