Build Command
On this page 32
The buddy build command compiles your Stacks application and libraries for production use, optimizing assets for deployment to npm, CDNs, or cloud providers.
Basic Usage
# Interactive build - select what to build
buddy build
# Build specific target
buddy build components
Command Syntax
buddy build [type] [options]
Arguments
| Argument | Description |
|---|---|
type | Optional. Specify build target (components, web-components, functions, views, docs, buddy, cli, stacks, server) |
Options
| Option | Description |
|---|---|
-c, --components | Build your component library |
-w, --web-components | Build your framework-agnostic web component library |
-e, --elements | Alias for --web-components |
-f, --functions | Build your function library |
-p, --views | Build your frontend views |
--pages | Alias for --views |
-d, --docs | Build your documentation site |
-b, --buddy | Build the Buddy binary |
-s, --stacks | Build Stacks framework |
--server | Build the Stacks cloud server (Docker image) |
--project [project] | Target a specific project |
--verbose | Enable verbose output |
Build Targets
Components Library
Build both STX and Web Component libraries:
buddy build:components
# or
buddy prod:components
This creates production-ready component libraries for distribution via npm or CDN.
Web Components
Build framework-agnostic Web Components (Custom Elements):
buddy build:web-components
# or
buddy build:wc
buddy prod:web-components
buddy prod:wc
Functions Library
Build your function library for npm distribution:
buddy build:functions
Frontend Views
Build your frontend for static site generation (SSG):
buddy build:views
Documentation
Build your documentation site:
buddy build:docs
# or
buddy build:documentation
buddy prod:docs
buddy prod:documentation
CLI
Build the Buddy CLI binary:
buddy build:cli
# or
buddy prod:cli
Desktop Application
Build the desktop application with the Craft runtime and compiled Stacks launcher:
buddy build:desktop
# or
buddy prod:desktop
Mobile Applications
Build both native projects, or target one platform while iterating:
buddy build:mobile
buddy build:ios
buddy build:android
# aliases
buddy prod:mobile
buddy prod:ios
buddy prod:android
The iOS toolchain
buddy build:ios generates the Xcode project with
xcodegen, and it runs the copy Pantry
installed, at the version config/deps.ts pins, never whichever one is first
on PATH. Two machines building one commit then generate the same project.
Declare the tools beside androidDependencies:
// config/deps.ts
export const iosDependencies = {
'github.com/yonaskolb/XcodeGen': '2.46.0',
}
Without the export the framework default (^2.46.0) applies. Install them
once per machine:
buddy setup:ios # pantry install --user github.com/yonaskolb/XcodeGen@2.46.0, then checks for Xcode
A build on a machine without them stops before doing anything, naming the
command that installs them. The build looks in the project's pantry/, the
user install (~/.local/share/pantry/global/packages, or under
XDG_DATA_HOME) and /usr/local/packages.
Xcode itself does not come from Pantry: Apple ships it only through the Mac
App Store and developer.apple.com. Install it there and select it with
sudo xcode-select --switch /Applications/Xcode.app. Generating the project
needs only xcodegen; building, archiving and running a simulator need Xcode.
Native screens
The screens a cold start shows can be drawn natively (UIKit) instead of in the
web view. They are ordinary .stx files in resources/native, compiled by
stx's native compiler (@stacksjs/stx/native) during buddy build:ios; every
other path, and every tap out of a native screen
(craft.navigation.open('/path')), stays in the web view.
// config/mobile.ts
ios: {
nativeScreens: { '/m': 'Today' }, // resources/native/Today.stx
// The native tab bar's first frame, before the page describes its own.
tabs: [{ id: '/m', title: 'Today', symbol: 'sun.max' }, { id: '/m/calendar', title: 'Calendar', symbol: 'calendar' }],
// The page's sign-in, for the native screen's own API requests.
shareStorage: { auth_token: 'auth.token' },
}
A native screen reads what the page last saved before its first frame
(craft.snapshots.get(name), written by the page with snapshots.set from
@stacksjs/mobile) and the sign-in with craft.secureStorage.getSync(key).
Shipping the iOS App to TestFlight
buddy release:ios turns the current main into a TestFlight build. It
regenerates the iOS project for production (dropping any MOBILE_URL a dev
shell exported), commits it, tags the commit and pushes main with the tag in
one atomic push:
buddy release:ios # v1.0.0-build.1, v1.0.0-build.2, ... (same version, next build)
buddy release:ios --bump patch # 1.0.0 → 1.0.1, then v1.0.1-build.1
buddy release:ios --bump minor # or major, or an exact version like 2.0.0
buddy release:ios --dry-run # print the tag and the GitHub release it would create
Every release also creates the tag's GitHub release: titled with the version
and build (1.0.0 (build 39)), opening with the TestFlight build the tag
becomes, then the changes since the previous tag, rendered by the same
changelog generator as CHANGELOG.md. The token is the one the push used:
GITHUB_TOKEN or GH_TOKEN, else the GitHub CLI's (gh auth token), else the
git credential for github.com. The tag is pushed first, so a release that
cannot be created (no token, an API error) is a warning, not a failed
release; buddy release:github <tag> finishes it, and leaves a tag that
already has a release alone. --skip-github-release pushes the tag only.
buddy release does the same for an app's own v<version> tags (the
framework's own releases get theirs from CI).
--bump raises the iOS version in config/mobile.ts (the default behind
an env override, such as envVars.IOS_APP_VERSION ?? '1.0.0') and
package.json together. Without it the version stays, which is what you want
until the version is released on the App Store.
The tag is what ships. Commit the generated project
(storage/framework/mobile/ios), then set the Xcode Cloud workflow's start
condition to Tag Changes with tag prefix v and an Archive - iOS
action distributing to App Store Connect. Xcode Cloud numbers the builds
itself, and an internal TestFlight group with access to all builds hands each
one to its testers; with TestFlight's automatic updates on, their phones
install it once Apple has processed it.
Xcode Cloud numbers every run, failed ones too, so a count of tags drifts from
the build number TestFlight shows. Give the release an App Store Connect API
key (Users and Access → Integrations → App Store Connect API, App Manager role)
and it tags v1.0.0-build.<n> with the number Xcode Cloud will give that
build, and expires the builds it supersedes so TestFlight lists only the
newest (--keep-builds <n> keeps more; --dry-run shows which would go):
# .env
APP_STORE_CONNECT_KEY_ID=ABC123DEFG
APP_STORE_CONNECT_ISSUER_ID=00000000-0000-0000-0000-000000000000
APP_STORE_CONNECT_PRIVATE_KEY_PATH=/path/to/AuthKey_ABC123DEFG.p8
What to Test
Every release tells its testers what changed. The notes are written from the
conventional commits since the previous mobile release tag (the newest
v*-build.* before this one), parsed and grouped by logsmith, the generator
behind bumpx and CHANGELOG.md: features, fixes, performance and reverts, one
plain line each, the scope after it, with no hashes or authors. Release
commits, ci, test, docs and chore commits stay out:
Features
- The Calendar a day, a week or a month at a time (mobile)
- Edit your own comments (workouts)
Fixes
- The month keeps its grid when a day is tapped (mobile)
They go to three places:
- TestFlight. The release commit carries
TestFlight/WhatToTest.<locale>.txtbeside the Xcode project (storage/framework/mobile/ios/TestFlight/), which is where Xcode Cloud looks when it uploads the build.en-USalways, plus each language ofios.nativeMessages(de→de-DE). The file is kept under 1000 characters, because Xcode Cloud has been seen to drop larger ones silently; a longer list ends with…and 12 more. - The GitHub release, under "What to Test", above the full changelog.
- Google Play:
buddy release:notes <tag> --playprints them cut to Play's 500 characters, for an Android pipeline to pass on.
To write the notes yourself, pass them in. The first one set wins:
buddy release:ios --notes "Try the new weekly check-ins" # or --notes-file notes.txt
RELEASE_NOTES="..." buddy release:ios # or RELEASE_NOTES_FILE=notes.txt
then release in config/mobile.ts, then the generated notes:
export default {
release: {
notes: undefined, // or notesFile: 'TESTERS.md'
noteTypes: ['feat', 'fix', 'perf', 'revert'],
excludeScopes: ['release', 'ci', 'deps', 'test', 'docs', 'lint'],
locales: ['en-US', 'de-DE'], // default: en-US + the app's languages
},
}
--dry-run prints the notes. With an App Store Connect key, --wait [minutes]
waits (60 minutes by default) for the build to reach App Store Connect, then
sets its What to Test through the API as well, at TestFlight's full 4000
characters. buddy release:notes <tag> --testflight does the same later
without holding up the release. buddy release:notes [tag] prints a tag's
notes (--markdown for a GitHub release, --from <ref> for another range);
notes a person wrote are read back from the tag's release commit.
Server Docker Image
Build the production server Docker image:
buddy build:server
# or
buddy prod:server
buddy build:docker
Stacks Framework
Build the entire Stacks framework (for core developers):
buddy build:stacks
Core Packages
Build core framework packages:
buddy build:core
Production Aliases
Several build:* commands ship a matching prod:* alias (there is no bare prod command):
buddy prod:components # Same as buddy build:components
buddy prod:desktop # Same as buddy build:desktop
buddy prod:mobile # Same as buddy build:mobile
buddy prod:ios # Same as buddy build:ios
buddy prod:android # Same as buddy build:android
buddy prod:web-components # Same as buddy build:web-components
buddy prod:frontend # Same as buddy build:frontend (build:views)
buddy prod:cli # Same as buddy build:cli
buddy prod:server # Same as buddy build:server
buddy prod:docs # Same as buddy build:docs
buddy prod:frontend-static # Same as buddy build:frontend-static
Examples
Build Components with Verbose Output
buddy build:components --verbose
Build Multiple Targets
# Build specific targets sequentially
buddy build:components && buddy build:functions && buddy build:docs
Build for Specific Project
buddy build:views --project my-project
Output Locations
Build outputs are placed in the following locations:
| Target | Output Directory |
|---|---|
| STX Components | dist/stx/ |
| Web Components | dist/web-components/ |
| Functions | dist/functions/ |
| Views | dist/views/ |
| Documentation | dist/docs/ |
| Desktop | dist/desktop/ |
Build Optimization
Stacks automatically applies production optimizations:
- Minification - JavaScript and CSS are minified
- Tree Shaking - Unused code is removed
- Code Splitting - Code is split into optimal chunks
- Asset Optimization - Images and assets are optimized
- Compression - Brotli and gzip compression support
Troubleshooting
Build Fails with Memory Error
For large projects, you may need to increase Node's memory limit:
NODE_OPTIONS="--max-old-space-size=4096" buddy build
TypeScript Errors
If you encounter TypeScript errors during build:
# Run type checking first
buddy test:types
# Fix issues, then rebuild
buddy build
Missing Dependencies
If build fails due to missing dependencies:
buddy install
buddy build
Related Commands
- buddy dev - Development server
- buddy deploy - Deploy to cloud
- buddy test - Run tests