BoxLang 🚀 A New JVM Dynamic Language Learn More...
build-template is a CommandBox
module that releases CFML projects. Install it once on each machine
and it gives you a release namespace that can:
Each project keeps only a small build.json. You never
copy the kit into a repository, and one box update brings
every project on the machine up to date.
The normal release process has four parts:
[Unreleased] in CHANGELOG.md.box release bump to update the version and date
those notes.The release stops if it finds a problem, such as uncommitted changes, a missing changelog entry, failed tests, or a version that has already been released.
Every machine needs:
Depending on how a project is configured, you may also need:
runTests is true.Sign in to the services you use:
gh auth login
box forgebox login
box install build-template
That installs the module into CommandBox itself, so the commands are available in every project. Then, in a project's root folder:
box release init
The installer:
build.json with settings detected from your
project; andCHANGELOG.md if the project does not have one.Add --docs to copy the detailed RELEASE.md
guide into the project, and --ci to copy a GitHub Actions
workflow to .github/workflows/release.yml. Rerunning it
is safe: existing files are kept unless you pass --force.
Afterwards, review build.json and correct anything the
installer could not detect, especially the test runner URL and release branch.
box update build-template --system
Every project on the machine uses the updated kit at once. A project
can insist on a newer kit with minimumKitVersion in
build.json; a machine with an older kit is told to update
instead of releasing with the wrong behaviour.
Projects that copied the 1.x build folder keep working
until you migrate them: the old build/build.json still
loads, with a notice. To move a project over:
box release migrate --dryRun
box release migrate
This moves build/build.json to build.json,
deletes the kit's own files from build/ (anything else in
there is kept), and rewrites the 1.x box run-script
entries in box.json to the new commands. Review with
git diff, then commit. Add --removeScripts
to delete the old scripts instead of rewriting them.
This example assumes the current version is 1.0.0 and
you are releasing 1.0.1.
Add a useful line under ## [Unreleased] in CHANGELOG.md:
## [Unreleased]
### Fixed
- Fixed the login form validation.
These notes become the body of the GitHub Release.
For a bug fix, run:
box release bump patch
This changes the version in box.json from
1.0.0 to 1.0.1 and moves the notes into a
dated 1.0.1 section.
Use a different level when needed:
box release bump minor # 1.0.0 -> 1.1.0 for a new feature
box release bump major # 1.0.0 -> 2.0.0 for a breaking change
git status
git diff
git add box.json CHANGELOG.md
git diff --staged
git commit -m "Release 1.0.1"
If your changelog has a different filename, use that filename in the
git add command. The commit is only local until you
send it to the remote with git push.
Using GitKraken or another Git GUI? Review the
changed box.json and changelog, stage only those release
files, review the staged changes, commit them as Release
1.0.1, and then push the current branch. Those are the GUI
equivalents of the commands above.
Start the project's test server if tests are enabled, then run:
box release check
This command changes nothing. It checks the installed kit, the settings, the Git repository, the changelog, the required tools, the service logins, and the test server. If something is wrong, it prints what to fix.
box release run --dryRun
The dry run executes the checks, tests, and package build, but does not publish, tag, or push anything.
box release run
The release:
The finished zip and checksum are saved under .artifacts/.
Run these from anywhere inside a project. box release
help prints the same list.
| Command | Use it to |
|---|---|
box release check
| Find anything that would stop a release. |
box release run --dryRun
| Rehearse a release without publishing. |
box release run
| Build and publish the current version. |
box release run --existingTag
| Publish a tag already created at the checked-out commit by Gitflow or GitKraken. Pushes the tag first if origin does not have it yet. |
box release run --skipTests
| Publish without rerunning tests that were already completed. |
box release bump patch
| Release a backward-compatible bug fix. |
box release bump minor
| Release a backward-compatible feature. |
box release bump major
| Release a breaking change. |
box release bump preminor beta
| Start a prerelease, for example 1.1.0-beta.1. |
box release package
| Build and check the zip without publishing it. |
box release engines
| Run the test suite on each configured CFML engine. |
box release notes
| Show the release notes for the current version. |
box release github
| Finish a release that stopped after publishing. |
box release init
| Set a project up: build.json and CHANGELOG.md. |
box release migrate
| Move a project off the 1.x copied build folder. |
Full help for any command: box help release run.
Edit build.json in the project root to change how the
commands work. The installer creates this file with values detected
from your project and a complete package exclusion list.
branch is the branch that receives release tags and
published versions, normally main or master.
In a Gitflow repository it is the production branch, never
develop or a temporary release/* branch. The
installer reads Gitflow's configured production branch when available,
but you should still verify the generated value.
{
"branch": "main"
}
{
"minimumKitVersion": "2.0.0"
}
The installer writes the version it was run with. Raise it when the project starts depending on a newer kit feature.
{
"publish": {
"forgebox": false,
"github": true
}
}
An application still gets a versioned zip and can still get a GitHub Release:
{
"projectType": "app",
"publish": {
"forgebox": false,
"github": true
}
}
Use this when another system, such as CI, is responsible for running the tests:
{
"runTests": false
}
excludes is the complete list of regular expressions
matched against top-level files and folders. A module starts with
broad packaging defaults: build and test tooling, downloaded
dependencies, server definitions, editor workspaces, agent notes,
archives, and hidden files stay out. An application gets a narrower
list: possible deployment content such as modules,
resources, package manifests, .htaccess, and
.well-known remains available.
Edit excludes when you need to change that baseline. Use
excludesAdd for project-specific additions that should
sit on top of it. For example, this keeps the top-level
docs folder out of the package:
{
"excludesAdd": [
"^docs$"
]
}
Use double backslashes when a regular expression needs a backslash because the value is JSON.
During release init, server.json and every
server-*.json file in the project root are added here in
filename order. Nested server files are deliberately ignored. A
readable name comes from app.cfengine, then the server's
name, then its filename; review the generated list and
remove any server that is not part of your compatibility suite.
Each configFile remains a CommandBox server JSON file in
the project root:
{
"engines": [
{
"name": "Lucee 5",
"configFile": "[email protected]"
},
{
"name": "Adobe 2023",
"configFile": "[email protected]"
}
]
}
Run the configured list with:
box release engines
The engines run one at a time. Every engine still runs after a failure. The final report lists all results, and the command returns an error when any engine failed.
| Message | What to do |
|---|---|
Command "release" cannot be resolved
| The module is not installed in this CommandBox. Run
box install build-template. |
No box.json found
| Run the command from inside a CommandBox project. |
This project needs build-template X or newer
| Run box update build-template --system. |
You have uncommitted changes
| Commit or stash the changes, then run the command again. |
No answer from the test server
| Start the project's test server, check
testRunner, or turn off runTests if
tests run elsewhere. |
Could not find the GitHub CLI
| Install gh, open a new terminal, and run
gh auth login. |
has no "## [version]" section
| Add notes under [Unreleased], then run
box release bump. |
Tag v1.2.3 already exists
| That version has already been released. Bump the version before trying again. |
Tag v1.2.3 is on origin at a different commit
| Your local tag and the published tag disagree. Do not move the published tag; check the release history or choose a new version. |
build.json is not valid JSON
| Check for missing quotes, trailing commas, or backslashes that need to be doubled. |
Start with box release check when you are unsure. It
reports all readiness problems without changing the project.
Install the development dependencies and run the tests:
box install
box run-script test
The test runner loads this checkout as the
build-template module inside its own CommandBox, so the
specs always exercise the working copy, even when a released copy is
installed globally. Unit tests cover the version, changelog,
configuration, project-detection, and migration rules. Integration
tests create throwaway projects under the ignored
.test-work/ folder and run the real commands in them
through tests/support/Invoke.cfc, with a local Git
remote. The tests never publish to ForgeBox or GitHub.
To try the working copy as a real install, run box install
<path to this checkout> and open a new shell; box
release help should list the commands. The kit releases
itself with its own box release run.
The commands in commands/release/ stay thin. The work
happens in models/, and pure rules that do not need
CommandBox live in their own components there so they are easy to test.
All notable changes to this project are written down here.
The format follows Keep a Changelog, and the version numbers follow Semantic Versioning.
box install build-template, instead of a build folder copied into every project. The
tasks became commands in the release namespace: box release run, release check,
release bump <level>, release package, release engines, release init,
release notes, release github, and release migrate. box release help lists them.build.json in the project root. The 1.x build/build.json still
loads, with a notice, until release migrate moves it.minimumKitVersion in build.json makes every machine refuse to release with an older kit
and prints the update command, so a project behaves the same wherever it is released.box update build-template --system.build folder, Update.cfc, build-kit.json, templateVersion, and the
installer-written box.json scripts. release init no longer writes scripts; release migrate rewrites the 1.x ones to the new commands so box run-script release keeps working.box install build-templatebox release migrate --dryRun, then box release migrate, review the
changes, commit.templates/github-release.yml.build-kit:update (build/Update.cfc) brings a project's copy of the kit up to date. It
downloads the latest build-template release, or takes :source=<folder or zip>, replaces the
kit files under build/ without touching build/build.json, adds new box.json scripts,
records the kit version, and prints the template changelog since the version the project
was on. :version= picks a release and :dryRun=true only lists the changes.build/build-kit.json records the kit version and repository. The installer now stamps the
real kit version into templateVersion instead of 1.0.0.release:check reports whether the release tag is already on origin.release:existing-tag checks origin during preflight, pushes a tag that exists only locally
right before creating the GitHub Release, and refuses when origin holds the tag at a
different commit. A tag left unpushed after a Gitflow finish used to fail the release at its
last step, after the ForgeBox publish.build/lib/PackageScriptService.cfc, shared by the
install and update tasks.bump:beta, bump:alpha, and direct preminor calls now refuse to retarget an active
prerelease unless :allowPrereleaseRetarget=true is supplied explicitly.build/lib/.release:existing-tag for publishing a release tag created while finishing a Gitflow
release in tools such as GitKraken.develop.git-flow, and hotfix paths.release:skip-tests as the clear name for the existing skip-tests release behavior.build/build.json. Module packages get the broad ColdBox-style development exclusions;
applications preserve deployment content such as modules, .htaccess, and .well-known.server.json and root-level server-*.json files in stable
filename order, naming them from app.cfengine, the server name, or the filename.build.json is now a marked starter that a fresh install replaces
automatically. Existing unmarked and malformed configurations remain untouched unless
installation is run with :force=true.Build, Release, Bump, TestEngines, Install,
and Doctor, sharing one settings file at build/build.json.box run-script release runs the whole release: checks, remote sync, tests, build,
ForgeBox publish, git tag, and a GitHub Release with the changelog notes and the zip
attached.release:check reports whether a project is ready to release and prints the fix for
anything that is not.release:dryrun rehearses a release and publishes nothing.release:hotfix and :skipTests=true skip the test suite, with a warning.test:engines runs the suite on every engine in turn, stopping at the first failure and
naming the engines that already passed.bump:major, bump:minor, and bump:patch raise the version and move [Unreleased]
notes into a dated section. Prereleases follow SemVer, so finishing 1.2.0-beta.3 with
bump:patch gives 1.2.0 rather than skipping to 1.2.1.bump:beta, bump:alpha, and bump:prerelease start and step prereleases, with a
:preid argument for other labels.Install.cfc sets a project up in one command, working out the test runner from
box.json and the engine list from the server-*.json files in the project root.excludesAdd appends to the default exclude list, so keeping one extra file out of a
package is a one line change.build/templates/.
$
box install build-template