Build flavors
A flavor ships one source tree as more than one app: a paid build beside a free one, a
white-label build per customer, a demo build that goes to fewer platforms. Each flavor is a file
beside Day.toml, and it states only what differs.
# Day-custom.toml — `day build --flavor custom`
# Overlay directories go before the first table: TOML gives a bare key to the table above it.
resources = "resource-custom"
store = "store-custom"
[app]
id = "dev.example.notes.custom"
version = "1.9.0"
title = "Notes Custom"
scheme = "notescustom"
targets = ["macos-appkit", "ios-uikit", "android-mdc", "web-dom"]
[app.android]
id = "dev.example.notes_custom"
[cargo]
features = ["custom-branding"]
[env]
NOTES_EDITION = "Custom"
day build --flavor custom -p ios-uikit
day launch --flavor custom -p macos-appkit
day pack --flavor custom -p android-mdc
--flavor works on every command, so day metadata, day lint, day store stage and
day screenshot all describe the flavor you name. DAY_FLAVOR=custom is the same switch, which
is what a CI job sets for a matrix leg.
What a flavor can change
- Identity.
id,title,artifact,schemeandbuild, including the[app.<platform>]override tables Day.toml already takes. A differentidinstalls beside the base app rather than replacing it. - The version.
versionreplaces the one Cargo.toml carries, whichDay.tomlitself cannot do. A flavor that ships under an app id with a release history behind it needs a version above what that record already published, and the crate has no way to know that number. - Targets.
targetsreplaces the base list, so a flavor can ship on fewer platforms or more. - Code.
[cargo] featuresadds cargo features, on top of the backend feature and the ones the app’s pieces need. - Values.
[env]reaches every build tool the run spawns, sobuild.rsandoption_env!("NOTES_EDITION")see it. - Resources.
resourcesnames aresource/-shaped directory that is merged over the app’s by relative path: ship one icon and one locale file, inherit every other asset. A.ftlfile is replaced whole, so an overlay oflocales/en/app.ftlcarries every key the app reads. - The store listing.
storenames thestore/directoryday store stagereads, because a flavor with a different app id is a different store record. - Release signing.
[signing.<platform>]tables, in the shapeDay.tomltakes, for a flavor that ships under another account: a white-label build the customer signs, or an app continuing a store listing another team owns.
The crate name, the day dependency and the manifest schema stay in Day.toml. Changing those
makes a different project, which is what day new is for.
Reading a flavor from Rust
A cargo feature compiles code in or out; an [env] value is read at compile time.
#[cfg(feature = "custom-branding")]
pub fn edition_badge() -> String {
// `option_env!` keeps a plain `day build` compiling, with no flavor in sight.
match option_env!("NOTES_EDITION") {
Some(edition) => format!("{edition} edition"),
None => "Standard edition".to_string(),
}
}
Text that differs per flavor is better placed in the resource overlay, where it stays translatable.
Where the output goes
A flavored build writes to build/day/flavors/<name>/, so flavors build incrementally against
each other and both apps exist on disk at once. Packaged artifacts take the flavor as a suffix —
notes-custom.dmg — unless app.artifact names something else, so one release directory can
hold them all.
One dayscript, both apps
A flavor changes what the app says, so the dayscript gates match it too:
skip_on: [flavor:custom] and only_on: [flavor:custom], with flavor:none for the base app.
- assert_text: { id: welcome-title, text: "Welcome to Notes", skip_on: [flavor:custom] }
- assert_text: { id: welcome-title, text: "Welcome to the Custom edition", only_on: [flavor:custom] }
Checks
day lint reads every Day-<name>.toml in the project, including the ones this build is not
using, and reports a file that does not parse, a cargo feature Cargo.toml does not declare, and
two flavors that resolve to the same app id — a pair that would install as one app, each
overwriting the other.
day metadata --json reports the active flavor and every declared one, which is how a script
discovers what a project can build.
In CI
The Day app workflow takes a flavors: input. Each name becomes a matrix leg
per target, alongside the base app:
jobs:
app:
uses: daybrite/actions/.github/workflows/dayapp.yml@main
with:
targets: macos-appkit,ios-uikit,android-mdc,web-dom
flavors: custom