This guide is for both humans and agents wiring Surge into an application repo.
The goal is to make integrations repeatable:
- the app repo should have one clear publishing path
- the app repo should have one clear smoke path
- unreleased Surge fixes should have one clear override path
There are two valid integration shapes. Decide this before editing code.
Use this when the app only needs Surge to:
- pack installers
- publish releases
- manage channels
The app itself does not link surge-core or the FFI at runtime.
Choose this by default if you do not need an in-app update prompt or in-app apply flow.
Use this when the app also needs Surge at runtime to:
- detect whether the app was installed by Surge
- check for updates in-app
- prompt the user or apply updates
In Rust, this usually means linking surge-core. In other stacks, use the C ABI or .NET wrapper.
If you choose this path, the app repo must treat Surge as both:
- a publishing dependency
- a runtime dependency
Do not mix these two shapes accidentally.
Every app repo that integrates Surge should carry these pieces explicitly.
- one committed manifest, usually
.surge/surge.yml - one artifact staging script that maps app build output into Surge's expected layout
- one local filesystem smoke wrapper for macOS/Linux/local Windows
- one Azure Windows smoke wrapper if the app ships a Windows installer
- release docs that say exactly which commands are authoritative
The app repo should not rely on contributors remembering raw surge pack and surge push invocations.
The wrapper scripts should encode these decisions so agents do not need to make them:
- default app id and RID selection
- default Surge version/tag
- artifact, package, and installer output directories
- local smoke versions like
0.2.0-smoke.1and0.2.0-smoke.2 - install root for each platform
- success checks after install and after update
These are the defaults agents should follow unless the app repo says otherwise.
Use the latest released Surge tag by default.
- for Rust runtime integrations, depend on a released
surge-coretag - for publishing/toolchain work in CI, prefer the Surge release bundle or a staged toolchain artifact built from a released tag
Do not point normal app development at an open Surge PR unless you are explicitly validating an unreleased Surge fix.
If the failure is inside Surge itself:
- fix it in the Surge repo first
- merge it
- cut a new prerelease or release
- update the app repo to that released version
Do not carry long-lived app-local patches for Surge behavior that should live upstream.
The app repo should expose copy-paste entrypoints like:
./scripts/run-surge-filesystem-smoke.sh
./scripts/run-surge-azure-smoke.shHumans and agents should use those first. Only fall back to raw surge commands while debugging the wrappers themselves.
If the app links Surge at runtime, keep the app policy explicit and centralized.
The app should decide in one place:
- which channels are eligible for update UX
- which install providers are eligible for update UX
- whether the app prompts, auto-downloads, or auto-applies
Do not spread that policy across multiple UI components.
If the app opens a hosted installer URL, derive that URL from install or release metadata.
Do not hardcode a production repo name into runtime update code if the app ever needs staging or smoke validation against another repo.
Sometimes the app needs to validate an unreleased Surge fix before that fix is merged and tagged.
That path should be explicit and temporary.
Use one of these:
- local checkout override for local smoke
- pinned commit override for CI or Azure smoke
After the fix is released, remove the override and go back to the released tag.
When a Rust app temporarily overrides surge-core, do not patch to the raw crate directory if the crate depends on workspace-inherited settings.
Prefer a local file:// Git source pinned to the exact Surge commit, for example:
[patch."https://github.com/fintermobilityas/surge.git"]
surge-core = { git = "file:///abs/path/to/surge", rev = "<sha>", package = "surge-core" }This keeps Cargo workspace semantics intact during validation.
Avoid:
[patch."https://github.com/fintermobilityas/surge.git"]
surge-core = { path = "../surge/crates/surge-core" }That raw crate-path style can break workspace = true dependency resolution in clean or cross-platform smoke runs.
Windows is usually the strictest smoke path because it surfaces fresh-checkout and installer assumptions early.
If the app ships Windows installers, the app repo should provide a repeatable Azure smoke path.
- keep one warm Azure Windows VM and reuse it during iteration
- install Visual Studio Build Tools once and reuse them
- start the smoke from an interactive scheduled task, not the Startup folder alone
- if the app uses Git LFS assets, run
git lfs pullon the guest after clone
Do not destroy the VM between every iteration unless the point of the test is first-boot provisioning.
The app repo should define success in the same way every time.
At minimum, a green smoke should prove:
- the installer is built
- the installer installs successfully
- the installed app writes its Surge runtime metadata
- a newer build on
betastays hidden from astableinstall - promoting that exact build to
stableexposes the update - the update applies successfully
- the app relaunches successfully after update
If the app only implements prompt-first UX instead of download_and_apply(), the repo should still have one helper that exercises the lower-level apply path during smoke.
If the app ships online or online-gui installers, include one additional clean-install check:
- remove the existing install root
- run the latest stable online installer directly
- verify it can fresh-install the latest stable release even when storage retained an older full archive plus the latest delta
These failures are worth documenting because they usually mean the same root cause.
Symptoms:
- missing
vendor/3rdparty/bzip2/*.c - build failures inside
surge-coreon clean Windows builds
Meaning:
- the crate is not self-contained
- published or tagged consumers are missing required vendored sources
Fix:
- commit the exact vendored sources inside the crate
- do not rely on submodules or symlinks at consumer build time
Symptoms:
- Cargo fails with an error like
dependency.async-trait was not found in workspace.dependencies
Meaning:
- the app patched
surge-coreas an isolated crate path instead of preserving the Surge workspace context
Fix:
- use a Git-based override for the exact Surge checkout or commit
The app repo AGENTS.md should stay short and enforce the defaults.
Recommended rules:
- use released Surge by default
- use app wrapper scripts, not ad hoc
surgecommands - use overrides only for pre-merge Surge validation
- if overriding
surge-core, use a localfile://Git source instead of a raw crate path - if Surge itself is broken, fix Surge upstream first
This document provides the human-readable context. The app repo AGENTS.md should point back to it and restate only the hard rules.