Publishing¶
Spectre's library modules (:core, :server, :recording, :recording-macos,
:recording-linux, :recording-windows, :agent, :agent-runtime, :testing) publish to Sonatype Central via the
com.vanniktech.maven.publish plugin.
The sample modules (:sample-desktop, :sample-intellij-plugin) never apply
the plugin — they're deliverables, not libraries.
Tag-driven release flow¶
The pipeline is owned by .github/workflows/release.yml and
triggers on tags matching v*, but the first job rejects anything that is not a
SemVer-shaped release tag: v<major>.<minor>.<patch> with optional SemVer
pre-release/build metadata (for example v0.2.0 or v0.2.0-rc.1). The leading
v is stripped to form the published version, so v0.2.0 publishes coordinates
dev.sebastiano.spectre:spectre-core:0.2.0 and the matching version for every
published library module.
Five jobs run on tag push:
release-gate(Linux runner) — validates the tag shape, runs./gradlew check, installs the docs dependencies, and runsmkdocs build --strict. Nothing else starts until this gate passes.mac-helper(macOS runner, depends onrelease-gate) — builds the arm64+x86_64 universalSpectreScreenCaptureSwift helper, codesigns it with the Developer ID, runsnotarytool submit --wait, and uploads the notarised binary as a GitHub Actions artefact.linux-helpers(Linux runner, depends onrelease-gate) — cross-builds thespectre-wayland-helperRust binary forx86_64andaarch64(same toolchain dance documented inci.yml: dpkg multi-arch + per-arch libdbus sysroot + cross-linker). Uploads the per-arch binaries as a GitHub Actions artefact.windows-helper(Windows runner, depends onrelease-gate) — builds the .NET Windows Graphics Capture helper forx64andarm64, then uploads the per-arch helper directories as a GitHub Actions artefact.publish(Linux runner, depends on the gate and all helper jobs) — downloads the helper artefacts, runs:verifyMavenLocalPublicationto assert the publication shape, then runspublishToMavenCentralagainst the Sonatype Central Portal. Finally creates a draft GitHub release. Maven Central is the canonical artifact host; do not attach a partial jar set to the GitHub release.
Both the Central Portal deployment and the GitHub release stay in manual-
promotion mode (automaticRelease=false in build.gradle.kts, --draft on
gh release create) until the first few tagged releases prove the pipeline. Do
not promote either surface until the release workflow is green and you've
sanity-checked the artefacts side-by-side.
Manual promotion checklist:
- Confirm the tag points at the intended, already-reviewed
mainSHA. - Inspect the Central Portal staging deployment for all nine modules, including POM metadata, sources jars, javadoc jars, and Gradle module metadata.
- Confirm
spectre-recording-<version>.jarcontains nonative/...entries. - Confirm
spectre-recording-macos-<version>.jarcontainsnative/macos/spectre-screencapture. - Confirm
spectre-recording-linux-<version>.jarcontainsnative/linux/x86_64/spectre-wayland-helperandnative/linux/aarch64/spectre-wayland-helper. - Confirm
spectre-recording-windows-<version>.jarcontainsnative/windows/x64/spectre-window-capture.exeandnative/windows/arm64/spectre-window-capture.exe. - Confirm
spectre-agent-runtime-<version>.jarexists and its manifest declaresAgent-Class: dev.sebastiano.spectre.agent.runtime.SpectreAgent. - Run the Central Portal deployment checker:
scripts/central_portal_check.py validate --deployment-id <id> --version <version>. - Promote the Central staging deployment from the Central Portal UI.
- Confirm the GitHub release notes link to Maven Central for artifacts instead of attaching a partial jar set.
- Undraft the GitHub release with
gh release edit <tag> --draft=false.
Required secrets¶
Set these in the repository's Settings → Secrets and variables → Actions:
| Secret | Used by | Purpose |
|---|---|---|
APPLE_DEVELOPER_ID_P12 |
mac-helper |
Base64-encoded Developer ID certificate. |
APPLE_DEVELOPER_ID_P12_PASSWORD |
mac-helper |
P12 password. |
APPLE_SIGNING_KEYCHAIN_PASSWORD |
mac-helper |
Ad-hoc keychain password. |
APPLE_DEVELOPER_IDENTITY |
mac-helper |
Developer ID Application: … codesign identity. |
APPLE_NOTARY_API_KEY |
mac-helper |
Base64-encoded App Store Connect API key. |
APPLE_NOTARY_API_KEY_ID |
mac-helper |
API key ID. |
APPLE_NOTARY_API_ISSUER |
mac-helper |
API issuer UUID. |
MAVEN_CENTRAL_USERNAME |
publish |
Sonatype Central Portal token username. |
MAVEN_CENTRAL_PASSWORD |
publish |
Central Portal token. |
SIGNING_IN_MEMORY_KEY |
publish |
ASCII-armored PGP private key (no header line stripping). |
SIGNING_IN_MEMORY_KEY_PASSWORD |
publish |
PGP key passphrase. |
The Maven Central token comes from Central Portal → View
Account → Generate User Token. The PGP key must be exported
ASCII-armored (gpg --armor --export-secret-key <key-id>) and the public side
must already be uploaded to keys.openpgp.org (or another keyserver pair
Central trusts).
Local verification¶
publishToMavenLocal works without any credentials or signing keys — the
signing convention only fires when ORG_GRADLE_PROJECT_signingInMemoryKey is
set. The :verifyMavenLocalPublication task drives the full shape check:
# Publish all library modules + verify shape. Stub mac helper because Linux cannot build the
# real one; cross-arch Linux helpers come from the real Rust build. Windows helpers are only
# expected when provided explicitly or when running on Windows.
./gradlew verifyMavenLocalPublication \
-PstubMacHelperForTesting \
-PallLinuxArches
It asserts that each module ends up with:
<artifactId>-<version>.jar(main jar)<artifactId>-<version>-sources.jar<artifactId>-<version>-javadoc.jar(empty — see "Open follow-ups" below)<artifactId>-<version>.pomwith the Central-required POM elements (<name>,<description>,<url>,<licenses>,<scm>,<developers>)<artifactId>-<version>.module(Gradle Module Metadata)
It additionally asserts:
- every sources jar is free of generated
native/...helper resources :recordingis API/common-only and contains nonative/...resources:recording-macoscontainsnative/macos/spectre-screencapture:recording-linuxcontainsnative/linux/x86_64/spectre-wayland-helperandnative/linux/aarch64/spectre-wayland-helperwhen built with release helper inputs:recording-windowscontainsnative/windows/x64/spectre-window-capture.exeandnative/windows/arm64/spectre-window-capture.exewhen built with release helper inputs:agent-runtimepublishes the Java Agent runtime jar; it must carry the Java Agent manifest and must not bundle Compose, Skiko, Spectre core, or Kotlin stdlib classes
If you have a real notarised mac helper on disk (e.g. downloaded from a previous release-CI run), point at it instead of the stub:
./gradlew verifyMavenLocalPublication \
-PprebuiltMacHelperPath=/path/to/SpectreScreenCapture \
-PprebuiltLinuxHelpersDir=/path/to/linux-helpers \
-PprebuiltWindowsHelpersDir=/path/to/windows-helpers
The prebuiltLinuxHelpersDir directory must contain
x86_64/spectre-wayland-helper and aarch64/spectre-wayland-helper.
The prebuiltWindowsHelpersDir directory must contain
x64/spectre-window-capture.exe and arm64/spectre-window-capture.exe.
Coordinates¶
| Module | Coordinates |
|---|---|
:core |
dev.sebastiano.spectre:spectre-core:<version> |
:server |
dev.sebastiano.spectre:spectre-server:<version> |
:recording |
dev.sebastiano.spectre:spectre-recording:<version> |
:recording-macos |
dev.sebastiano.spectre:spectre-recording-macos:<version> |
:recording-linux |
dev.sebastiano.spectre:spectre-recording-linux:<version> |
:recording-windows |
dev.sebastiano.spectre:spectre-recording-windows:<version> |
:agent |
dev.sebastiano.spectre:spectre-agent:<version> |
:agent-runtime |
dev.sebastiano.spectre:spectre-agent-runtime:<version> |
:testing |
dev.sebastiano.spectre:spectre-testing:<version> |
The shared metadata (group, license, SCM, developer) lives in
gradle.properties at the repo root; per-module
POM_ARTIFACT_ID / POM_NAME / POM_DESCRIPTION lives in each module's own
gradle.properties. gradle.properties is loaded as Latin-1, so the
descriptions stay ASCII-safe.
Open follow-ups¶
- Dokka HTML javadoc. Currently
JavadocJar.Empty()— satisfies Central's gate but doesn't give consumers offline API docs. WireJavadocJar.Dokka(...)once Dokka 2 is verified against this codebase. Until then the canonical API docs live at https://spectre.sebastiano.dev. - Snapshot publishing. The plumbing supports it (the default version is
0.1.0-SNAPSHOT), but no CI workflow currently publishes snapshots — wire amain-push job if/when there's demand from consumers tracking unreleased changes. - Automatic Central promotion.
automaticRelease=falsekeeps the staging repo in manual-promote mode. Flip totrueonce the first published version is in the wild and we trust the pipeline.