BoxLang 🚀 A New JVM Dynamic Language Learn More...

commandbox-release

v3.9.0 CommandBox Modules

commandbox-release

commandbox-release logo

commandbox-release builds and publishes CFML modules and web apps. It adds box release commands to CommandBox.

The commands can change your project's version, add a version and date to its release notes, run TestBox tests, and build a zip file. They publish to GitHub, ForgeBox, or both. Each project keeps its settings in release.json in the project's root folder.

What you need

Install CommandBox and Git. Run the examples below in your terminal.

Your project needs a Git repository with a remote named origin. This is the repository that release commands get commits from and push commits and tags to. For GitHub releases, this remote must point to the project's GitHub repository. If your remote has another name, set remote in release.json. This README says origin for that remote.

You will choose where to publish and whether to run tests during setup:

  • To publish to GitHub, install GitHub CLI and sign in with gh auth login.
  • To publish to ForgeBox, use a ForgeBox account and sign in with box forgebox login.
  • To run tests, provide the path or URL of your project's TestBox runner. Start the test server before releasing, or configure the module to start servers for you under Test more than one CFML engine. A CFML engine, such as Lucee or Adobe ColdFusion, runs your CFML code. If your tests run inside CommandBox without a server, use a test command instead. See Run the tests with a CommandBox command.

Install

box install commandbox-release

Update it later with:

box update commandbox-release --system

Set up a project

Go to the project's root folder and run:

box release init

Answer the questions about publishing and tests. If the project has no box.json, setup offers to create it. That file describes your package and stores its version.

Setup creates release.json and creates CHANGELOG.md if needed. It also updates two files:

  • box.json: adds file patterns to the ignore list to keep development files out of the package.
  • .gitignore: adds .tmp/ and .artifacts/ so Git ignores the generated build files.

Open release.json and check these settings before releasing:

  • branch: your production branch, usually main or master. This is the branch you publish stable versions from. In a Gitflow project, use the production branch rather than develop. To publish alpha or beta versions from develop too, see Publish prereleases from develop.
  • publish.github and publish.forgebox: where to publish. Sign in to each service you enable.
  • runTests, testRunner, and testCommand: whether to run tests, and how. testRunner is the TestBox runner on a web server. Use a path, such as /tests/runner.cfm, for the project's CommandBox server, or a full URL. See Choose the test runner. testCommand is a CommandBox command, such as run-script test, that runs the tests without a server. Use runTests: false when another system already runs your tests.

Modules publish to GitHub and ForgeBox by default. Web apps publish to GitHub by default. Setup treats a project as a module when box.json has a module type, such as modules or commandbox-modules, or the project has ModuleConfig.cfc in its root folder.

Review the box.json ignore list too. It controls which files go into the package. The settings reference below explains the options.

Optional setup flags:

  • --yes accepts the default answers without asking questions.
  • --docs copies the release guide to RELEASE.md in your project.
  • --ci copies a GitHub Actions workflow to .github/workflows/release.yml. That workflow publishes when you push a version tag. Use it instead of publishing from your computer.

You can run setup again. It keeps existing files unless you use --force.

The ColdBox template's postInstall script

ColdBox app templates add this script to box.json:

"postInstall": "pathExists .env || cp .env.example .env && package set ignore=[]"

CommandBox runs the commands in order, so package set ignore=[] runs even when .env exists. Each box install that runs the script empties the ignore list that setup adds. On a server, the script can also copy .env.example to .env, with blank values, next to the real file.

Setup asks whether to remove the script. Answer y to remove it. Then commit box.json. To create .env on a new clone, run cp .env.example .env in box. With --yes, or when nobody can answer, setup keeps the script and prints a note. Setup only removes this exact script. When another install script runs package set ignore, setup and box release check name it, and you change it by hand.

Update an existing project

New versions of commandbox-release may add recommended ignore patterns. These patterns keep files out of the package. To add missing patterns to an existing project, run:

box release init --yes

Setup adds missing patterns to the box.json ignore list. Setup also adds build folders to .gitignore so Git ignores the build files. Your existing patterns, release.json, and changelog stay unchanged. Review the added patterns, then commit box.json and .gitignore.

box release check and box release publish list any recommended patterns that are missing. If your package needs files that a pattern excludes, remove that pattern from box.json. Missing patterns do not stop a release.

Publish your first release

These steps publish directly from your production branch. If your project uses release branches, follow Gitflow releases instead.

  1. Switch to the branch set in release.json. For example, if branch is main:

    git switch main
    
  2. Add notes under ## [Unreleased] in CHANGELOG.md. Describe what changed. For example:

    ## [Unreleased]
    
    ### Added
    
    - A search form on the home page.
    
  3. Commit the setup files and notes before publishing:

    git add box.json release.json CHANGELOG.md .gitignore
    git commit -m "Set up releases and add release notes"
    

    Commit any other changes you want in this release too. A real release stops if you have uncommitted changes.

  4. Start the test server if your settings require one. Then check the release with a dry run:

    box release publish patch --dryRun
    

    This runs enabled tests and builds and checks the zip. It writes build files to .tmp/ and .artifacts/ by default. It does not change the version in box.json, edit your changelog, commit, publish, or push. Read the output and fix any reported problems.

  5. Publish after the dry run passes:

    box release publish patch
    

    patch changes a version such as 1.0.0 to 1.0.1. The command moves your release notes into a dated section and commits the version and changelog changes. It runs enabled tests again and builds the package before publishing to the services you chose.

If you want to keep the version already in box.json for your first release, use none instead of patch in both commands. It dates and commits the release notes without changing the version.

If publishing stops, follow the recovery steps in the output. If the version change was committed but nothing was published or pushed, fix the problem and run box release publish without a level. This uses the current version rather than changing it again.

Ways to release

Change the version and publish in one command

box release publish patch              # 1.0.0 -> 1.0.1 for a bug fix
box release publish minor              # 1.0.0 -> 1.1.0 for a new feature
box release publish major              # 1.0.0 -> 2.0.0 for a breaking change
box release publish minor beta         # 1.0.0 -> 1.1.0-beta.1 for an early version to test

Run these commands on your production branch, such as main. The command first checks Git, the changelog, and your service sign-ins. Then it updates the branch from origin, changes the version, and moves the [Unreleased] notes into a dated section. It commits the changes as Release 1.0.1, runs the tests, and builds and checks the zip. It publishes only after those steps pass. When GitHub publishing is enabled, it creates a version tag, pushes the branch and tag, and creates the GitHub Release. A tag marks the commit used for a version.

Use minor beta to start a prerelease: a version for testing before the normal release. If the command stops, follow its recovery instructions. Do not repeat a command with a level after its version change has been committed.

From a prerelease, patch, minor, and major publish the stable version. For example, patch changes 1.1.0-beta.2 to 1.1.0. The command asks before it does that. See You meant to publish the next prerelease.

Change the version and publish in separate steps

box release bump patch                 # change the version and date the release notes
git add box.json CHANGELOG.md
git commit -m "Release 1.0.1"
box release check                      # optional
box release publish --dryRun           # optional practice
box release publish                    # test, build, and publish the current version

box release publish without a level uses the version already in box.json. It does not change that version.

Gitflow releases

Gitflow uses develop for completed work, temporary branches to prepare releases or hotfixes, and a production branch for published versions. A hotfix is a fix made from production. Set branch in release.json to the production branch.

Merge and publish in one command

On develop, include a version level to create a release branch and publish its changes:

box release gitflow minor --dryRun     # show the steps and build from the current branch
box release gitflow minor              # from 1.0.0: create release/1.1.0, merge, and publish

On an existing release branch, use box release gitflow without a level if the version in box.json is already newer than production. Otherwise, include a level, such as box release gitflow minor.

The command:

  1. Checks Git, release notes, and service sign-ins.
  2. Gets commits from origin and updates local branches when gitSync is enabled. If the current branch changes, it stops so you can run it again to check the updated files.
  3. Creates a release branch if you started on develop.
  4. Updates the version and dates the [Unreleased] notes when a level is used. It commits those files with a message such as Release 1.1.0.
  5. Runs enabled tests on the release branch.
  6. Merges into develop and the production branch. When the changelog is the only file with a merge conflict, the command combines both versions: it keeps the [Unreleased] notes on develop and adds the new version section. Notes that this release published are removed from [Unreleased] on develop, even when develop added notes of its own.
  7. Builds and publishes from production using your settings. It runs tests again if production has different files from the tested release branch.
  8. Pushes both branches, switches to develop, and deletes the release branch locally and on origin. Use --keepBranch to keep the release branch.

Nothing is pushed until the build passes. If the command stops before publishing or creating a tag, follow the reported steps. Fix any code changes on the release branch and commit them. Then run box release gitflow on that branch without a level. Git does not repeat merges that are already complete. If publishing or pushing has started, use the printed recovery steps.

For a hotfix, create a hotfix/* branch from production and commit your fix first. Run box release gitflow on that branch. The command uses the version in box.json if it is newer than production. Otherwise, it makes a patch version. If box.json has a prerelease, such as 1.0.0-beta.2, it makes the next prerelease, 1.0.0-beta.3. To finish the prerelease and publish 1.0.0, run box release gitflow patch. It merges into both develop and production. If a release branch is also open, use box release gitflow --keepBranch to keep the hotfix branch. After publishing, merge the hotfix branch into the release branch yourself. If the hotfix branch was already deleted, merge the production branch into the release branch to include the fix.

A Gitflow dry run builds from your current branch. It does not create or merge branches, change box.json or the changelog, publish, or push. Its zip does not include the results of the planned merges.

Branch names come from your Gitflow settings in Git config. GitKraken and git flow store them there. The defaults are develop, release/, and hotfix/. The production branch comes from release.json and must match your Gitflow settings when those settings specify it.

Gitflow and a Git GUI (like GitKraken)

You can use your Git GUI to merge the release branch and create its version tag. In this example, production is master, and the current version is 1.0.0:

start a release in GitKraken           # develop -> release/1.0.1
(add notes under [Unreleased])
box release bump patch                 # works on any branch; no commit
commit "Release 1.0.1" in GitKraken
finish the release in GitKraken        # merges into master and develop; creates v1.0.1
check out master in GitKraken
box release publish                    # uses v1.0.1 and pushes master and the tag if needed

box release publish does not push develop. Push it from your Git GUI.

For a hotfix, use a hotfix/* branch in the same way. box release publish patch stops on release/* and hotfix/* branches and shows both release options. The release guide also explains the git-flow command and pull requests.

If you finish the branch before you change the version, master gets a tag such as v1.0.1, but box.json still has 1.0.0. Run box release publish on master. It shows the steps that fix this and offers to do them. See You finished a branch before you changed the version.

Publish prereleases from develop

You can publish alpha or beta versions from develop while features keep changing, and keep the production branch for stable versions. List develop in release.json:

{
    "requires": "3.6.0",
    "branch": "master",
    "prereleaseBranches": ["develop"]
}

Then run these commands on develop. Each one commits the version on develop, builds, publishes, tags the commit, and pushes develop and the tag. GitHub marks the release as a prerelease.

box release publish preminor alpha     # 2.0.0 -> 2.1.0-alpha.1 (preid defaults to beta)
box release publish prerelease         # 2.1.0-alpha.1 -> 2.1.0-alpha.2
box release publish                    # publish the prerelease already in box.json
box release publish --dryRun           # show the steps, including git push origin develop

A listed branch publishes prerelease versions only. box release publish minor on develop stops and points to box release gitflow minor. The production branch still publishes every version, so nothing changes there. The list is empty by default. An older commandbox-release ignores the setting and still refuses on develop, so set requires to 3.6.0 or newer.

To release the stable version, run box release gitflow minor on develop as usual. From 2.1.0-alpha.3 it creates release/2.1.0, merges into both branches, and publishes 2.1.0 from production. Each alpha moved its notes into its own changelog section, so write the notes for 2.1.0 under [Unreleased] first. The command stops with a reminder when the section is empty. It also asks before it finishes the prerelease. In CI, add --finishPrerelease. box release gitflow without a level stops on develop while box.json holds an unpublished prerelease, because that prerelease belongs on develop.

A hotfix during a prerelease line changes box.json on both branches, such as 2.0.1 on the hotfix and 2.1.0-alpha.2 on develop. The merge into develop then conflicts on box.json. box release gitflow cancels that merge and shows the steps, including which version to keep.

The GitHub Actions template also runs when you push a prerelease tag, so do not run box release publish on your computer if you use that workflow.

Mistakes box release can fix

box release checks for common mistakes before it changes anything. It fixes some of them by itself. For others, it shows the fix and offers to do it.

You finished a branch before you changed the version

You start hotfix/1.0.1 or release/1.1.0 and add your notes. Then you select Finish in GitKraken, or run git flow hotfix finish, but you did not run box release gitflow or box release bump first. Now master has the tag v1.0.1, but box.json still has 1.0.0.

Run box release publish or box release gitflow on master. The command explains what happened, lists the steps that fix it, and asks:

Do these steps now and publish 1.0.1? [y/n]

Answer y. box release then:

  1. Runs the usual release checks. If a check fails, nothing changes.
  2. Creates hotfix/1.0.1 from master. For a minor or major version, it creates a release branch, such as release/1.1.0.
  3. Deletes the local tag v1.0.1.
  4. Runs box release gitflow on the new branch. It changes the version, dates the notes, runs the tests, merges into develop and master, publishes, and pushes both branches. Then it deletes the branch.

The release tags the new merge commit, the same way it tags any release. master gets a second merge commit for the branch. That is expected.

Answer n, or press Enter, to stop without changes. You can then run the steps yourself:

git tag -d v1.0.1
git switch -c hotfix/1.0.1
box release gitflow patch     # changes the version, merges, publishes, and pushes both branches

In CI or a script, nobody can answer the question. Add --fixTag to do the steps without asking:

box release publish --fixTag
box release gitflow --fixTag --dryRun   # shows the steps and changes nothing

box release does not offer the fix in these cases. The message explains what to do instead.

  • The tag is already on origin. That version may be published. See Common problems.
  • The current branch is not the production branch, or the develop branch is missing.
  • You gave a level that makes a different version, such as box release publish minor for the tag v1.0.1. Leave out the level to publish 1.0.1.
  • GitKraken kept hotfix/1.0.1, and that branch has commits that are not on master.

The fix also stops before it changes anything when origin/master has commits that the tagged commit does not have.

You meant to publish the next prerelease

box.json has 1.0.0-beta.15. You want 1.0.0-beta.16, but you run box release gitflow patch instead of box release gitflow prerelease. From a prerelease, patch, minor, and major all make the stable version 1.0.0. So the command shows what will happen and asks first:

  note  1.0.0-beta.15 -> 1.0.0 finishes the prerelease. 1.0.0 is a stable version.
        To publish the next prerelease instead, run: box release gitflow prerelease
Finish the prerelease and publish stable version 1.0.0? [y/n]

Answer n, or press Enter, to stop without changes. Then run the command from the note. Answer y to publish 1.0.0.

box release publish <level> asks the same question. A practice run (--dryRun) shows the note and does not ask. box release bump <level> shows the note too. It does not ask, because it publishes nothing.

In CI or a script, nobody can answer. The command stops before it changes anything. Add --finishPrerelease to finish the prerelease without the question:

box release gitflow patch --finishPrerelease

The question comes only from a level. box release publish and box release gitflow without a level publish the version already in box.json, so they do not ask. This includes the GitHub Actions template, and the run without a level that continues a release after a failure.

A new major version, such as 1.4.2 to 2.0.0, shows a note but does not ask. You chose major on purpose.

Develop got new notes while a branch was open

You add notes on develop while release/1.1.0 or a hotfix branch is open. Then both branches changed [Unreleased]. box release gitflow keeps the new develop notes under [Unreleased] and removes the notes that the release published. It prints:

Combined CHANGELOG.md: kept the [Unreleased] notes on develop, removed 1 note that [1.1.0] released, and added [1.1.0].

If you reworded a note on develop, the command keeps it under [Unreleased]. Check the changelog on develop after the release.

Other mistakes the checks catch

  • A Git GUI already made the tag. box release publish uses a tag at the current commit, such as one from GitKraken. It pushes the tag and the production branch when origin does not have them. See Gitflow and a Git GUI.
  • The changelog has a merge conflict. When the changelog is the only file with a conflict, box release gitflow combines both versions. See Merge and publish in one command.
  • The new version was already released. box release publish <level> checks the new version's tag before it changes any files.
  • origin has new commits. When git pull brings in new commits, the release stops before it builds. Run the command again to check the updated files.
  • A release stopped after publishing. box release resume creates and pushes the tag and the GitHub Release. Do not run the full release again.

Commands

Run these commands from any folder inside a project. box release help prints the same list.

Command What it does
box release init Creates the release settings, changelog, and package ignore list.
box release publish Publishes the version in box.json. Uses an existing tag if it points to the current commit, and pushes the branch if needed. Runs on the production branch, or on a branch in prereleaseBranches for a prerelease.
box release publish patch Changes the version, commits the change, and publishes. You can also use minor, major, or a prerelease level. A branch in prereleaseBranches accepts prerelease levels only.
box release publish --dryRun Builds the zip without committing, publishing, or pushing.
box release publish --skipTests Publishes without running the tests again.
box release publish --skipHooks Publishes without running the beforePublish command.
box release publish patch --finishPrerelease Finishes a prerelease, such as 1.0.0-beta.4 to 1.0.0, without asking. box release gitflow has the same flag. Use it in CI or a script.
box release publish --fixTag Fixes a Gitflow finish that tagged a version before box.json changed, without asking, and publishes that version. See Mistakes box release can fix.
box release gitflow minor On develop, creates a release branch, merges it, and publishes. On a release or hotfix branch, changes the version, merges that branch, and publishes.
box release gitflow Merges and publishes the current release or hotfix branch. Uses its version if newer than production; otherwise, a hotfix uses patch (or prerelease for a prerelease), and a release requires a level.
box release bump patch Changes the version and dates the release notes. It does not commit.
box release check Finds problems that would stop a release.
box release test Runs the test command, or runs the tests once, or on each engine listed in release.json.
box release package Builds and checks the zip file without publishing.
box release notes Shows the release notes for the current version.
box release resume Finishes a release that stopped after publishing.

Run box help release publish to see all help for one command.

Settings

The setup command creates release.json in the project root. Edit that file to change how release commands work. This example publishes to both GitHub and ForgeBox and runs tests:

{
    "requires": "3.0.0",
    "branch": "main",
    "changelog": "CHANGELOG.md",
    "testRunner": "/tests/runner.cfm",
    "runTests": true,
    "publish": {
        "forgebox": true,
        "github": true
    },
    "engines": []
}

The JSON examples below show individual settings. Update those values in your existing release.json; keep the other settings you need.

Choose the production branch

Set branch to the production branch, usually main or master. This branch holds published stable versions and their tags. In a Gitflow project, do not set it to develop or a temporary release/* branch. Setup uses Gitflow's production branch when it finds one. Check the value in the new file before your first release. To publish prereleases from develop as well, list it in prereleaseBranches. See Publish prereleases from develop.

Publish to GitHub but not ForgeBox

{
    "publish": {
        "forgebox": false,
        "github": true
    }
}

If release.json does not set publish.forgebox, modules publish to ForgeBox and other projects do not. Set it to true or false to choose for yourself.

Choose the test runner

Set testRunner to the path of the TestBox runner on the project's CommandBox server:

{
    "testRunner": "/tests/runner.cfm"
}

The release finds the server's address when it runs the tests, the same way box server open does. So release.json does not repeat the port, and the port can change. The server must have started at least once. With engines, each engine's address comes from its own server JSON file, so the engines can use different ports.

The runner can also be a full URL, such as http://127.0.0.1:60310/tests/runner.cfm. Use a URL when the tests run on a server that CommandBox did not start.

If release.json does not set testRunner, the release uses testbox.runner from box.json. If that is not set either, it uses /tests/runner.cfm.

Do not run tests during the build

Use this setting when another system runs the tests, such as a continuous integration (CI) workflow:

{
    "runTests": false
}

Run the tests with a CommandBox command

Some projects run their tests inside CommandBox, not on a web server. A CommandBox module often does this with box run-script test. Set testCommand to that command:

{
    "runTests": true,
    "testCommand": "run-script test",
    "engines": []
}

The release runs box run-script test in a new process from the project root. You can leave out box. The command's output shows while it runs. If the command fails, the release stops the same way it stops for failing tests. A command fails when it exits with an error, for example because a spec failed. --skipTests skips the command.

With testCommand, the release does not use testRunner and does not need a test server. The command cannot test each engine, because it does not start servers. If release.json sets both testCommand and engines, release commands stop with an error.

The command runs in a new box process, so box must be on your PATH. A test runner for a CommandBox module often unloads that module and loads the working copy. Inside the release's own CommandBox, it could unload commandbox-release in the middle of the release. A new process keeps the release safe from those changes.

box release init suggests run-script test for a CommandBox module when box.json has a test script and no TestBox runner URL.

Run a command before publishing

A release can run a CommandBox command after the tests and the build pass and before anything is published or pushed. Set it in hooks.beforePublish:

{
    "hooks": {
        "beforePublish": "deploybot env push --yes"
    }
}

box release publish and box release gitflow run the command. It runs in a new box process from the project root, the same way as testCommand. You can leave out box. The command's output shows while it runs. If the command fails, the release stops and nothing is published or pushed. A command fails when it exits with an error.

  • A practice run (--dryRun) shows the command but does not run it.
  • --skipHooks skips the command. box release resume never runs it.
  • The command gets no input, so it cannot answer questions. Use the command's option to skip them, such as --yes.
  • The release waits until the command ends. It has no time limit.
  • If a later step fails, the next release runs the command again. Use a command that is safe to run twice.
  • To run more than one command, add a script to box.json and use run-script <name>.

This setting is not the same as the prePublish and postPublish scripts in box.json. CommandBox runs those scripts around its own publish command, which sends the package to ForgeBox.

Example: send a .env file before a deploy

Some deploy tools start a deploy when a branch is pushed. A new version may need settings that the web server does not have yet, such as a new key in .env. Then the deploy can fail. The beforePublish command can send the settings before the release pushes the branch. The example above uses commandbox-deploybot. It sends nothing when the server already has the same settings.

Use this only when the release's push starts the deploy. Sending a .env file usually restarts the site with the new settings. If you deploy later, send the settings right before you deploy instead. Otherwise the old code runs with the new settings until the deploy.

Change settings in a way that the old and new code can both use:

  • Add a new key in one release. Remove an old key in a later release.
  • Do not rename a key or change what a value means in one release.

The GitHub Actions workflow template uses skipHooks=true. The workflow cannot read files from your computer, such as .env.production. Your tag push has also already started any deploy that runs on a push.

Keep files out of the package

The package includes project files unless a pattern in the box.json ignore list leaves them out. A pattern is a rule that matches file paths. ForgeBox uses the same list, so both packages exclude the same files. Setup adds common patterns. You can edit the list at any time.

{
    "ignore": [
        "**/.*",
        "/tests/",
        "/test-harness/",
        "/server*.json",
        "**/*.bak"
    ]
}

Here is how the patterns match paths:

  • /tests/ starts with a slash, so it matches only the tests folder in the project root.
  • temp/ has no leading slash, so it matches a folder named temp at every depth.
  • **/*.bak matches every .bak file at every depth.
  • !/.htaccess keeps a file that an earlier pattern removed. Web apps use this to include .htaccess and .well-known/ while **/.* removes the other hidden files.

The package also leaves out .git, .gitignore, release.json, .tmp, and .artifacts. The build does not use .gitignore to choose package files. For example, .gitignore may exclude build output, such as includes/build/, that the running project needs. The build stops if the box.json ignore list excludes box.json, or ModuleConfig.cfc when the project has one.

The package leaves out coldbox/, modules/, and testbox/ too. box install creates these folders from the dependencies in box.json, so run box install on the server after a deploy. Only box install should write to modules/. Keep your app's own modules in modules_app/, which the package includes. To ship one of these folders, add a pattern that keeps it to the ignore list, such as "!/coldbox/".

The build also stops if the package contains a .env file, such as .env or .env.production. These files can hold passwords and keys. Example files are allowed: .env.example, .env.sample, .env.template, and .env.dist. The **/.* pattern that setup adds leaves out all .env files.

Test more than one CFML engine

During setup, the command finds server.json and server-*.json files in the project root. It adds each server to engines in filename order. Remove servers you do not want to test.

{
    "engines": [
        { "name": "Lucee 5", "configFile": "[email protected]" },
        { "name": "Adobe 2023", "configFile": "[email protected]" }
    ]
}

box release test starts each engine, runs the tests, and stops the engine before starting the next one. When testRunner is a path, the command reads each engine's port after the engine starts, so each server JSON file can use its own port. It tries every engine even if one fails. It reports an error at the end if any engine failed.

When tests fail, the command lists each failed test as bundle > suite > spec -- message. Here, a bundle is a test file, a suite is a group of tests, and a spec is one test. When testing several engines, the final results group failed tests by engine.

Stop the tests at the first failure

The release commands use TestBox's eagerFailure option to ask the runner to stop after the first test file that has a failure. The rest of that file still runs. Later files are skipped.

The standard TestBox runner ignores this option and always runs every test. To use it, copy testbox/system/runners/HTMLRunner.cfm into your tests folder, include the copy from tests/runner.cfm, and change two lines in the copy:

<!--- Add next to the other cfparam tags --->
<cfparam name="url.eagerFailure" default="false" type="boolean">

<!--- Pass the option to TestBox --->
results = testbox.run( reporter=url.reporter, eagerFailure=url.eagerFailure )

A custom runner that calls testbox.run() itself needs only the eagerFailure argument. Other tools that call the runner without eagerFailure=true still run every test.

Other settings

Setting Default What it does
requires the version that created the fileThe oldest commandbox-release version this project can use. Commands stop and show how to update if your installed version is too old.
prereleaseBranches [] Branches that may publish prerelease versions with box release publish, such as ["develop"]. Stable versions still publish from branch. See Publish prereleases from develop.
tagPrefix v The text before the version in tag names.
remote origin The Git remote that release commands get commits from and push to.
gitSync true Gets new commits from the remote before a release, without making a merge commit.
requireCleanTree true Stops a release when there are uncommitted changes.
stagingDir .tmp The temporary build folder.
artifactsDir .artifacts Where the zip and checksum files are written.
coldboxMapping test-harness/coldbox The folder used for the coldbox mapping during the build, if the folder exists.
warmup { "attempts": 60, "delaySeconds": 5 } How often release test checks whether each engine is ready.
hooks { "beforePublish": "" } A CommandBox command to run before publishing. See Run a command before publishing.

Common problems

Some mistakes have an automatic fix. See Mistakes box release can fix.

Message How to fix it
Command "release" cannot be resolved Install the module in this CommandBox with box install commandbox-release.
No box.json file was found Run the command from inside a CommandBox project, or run box release init to create one.
This project requires commandbox-release X or newer Run box update commandbox-release --system.
You have uncommitted changes Commit or stash the changes, and then run the command again.
You are on a Gitflow release branch Run box release gitflow on that branch if its version is newer than production. Otherwise, include a level, such as box release gitflow patch. See Gitflow releases for the GitKraken option.
develop can publish only prerelease versions The branch is in prereleaseBranches, and the version is stable. Use a prerelease level, such as box release publish preminor alpha, or release the stable version with box release gitflow minor.
box release publish <level> must run from production branch Switch to the production branch. To publish a prerelease from this branch instead, add it to prereleaseBranches in release.json.
... could not be merged into develop Switch to develop, merge the release or hotfix branch, fix the conflicts, and commit. Switch back to the release or hotfix branch and run box release gitflow without a level. The command combines a changelog conflict by itself. When it cannot, the message names the part of the changelog that changed on both branches.
The test server ... did not answer Start the project's test server. You can also correct testRunner or set runTests to false when tests run somewhere else. If the tests run without a server, set testCommand.
testRunner is the path ..., but no server has started Start the project's server once with box server start, so CommandBox knows its address. You can also set testRunner to a full URL.
The test command failed: box ... Read the command's output above the error, or run the same command in the project root. Fix the tests, or use --skipTests when the same files already passed.
The test command did not start because box was not found Add the CommandBox folder to PATH, and then open a new terminal.
release.json has both testCommand and engines Remove engines to use the test command. Remove testCommand to test each engine.
The beforePublish command failed: box ... Read the command's output above the error, or run the same command in the project root. Fix the problem, or use --skipHooks to release without it.
release.json hooks has an unknown hook Check the spelling. beforePublish is the only hook.
The package contains .env... Add "**/.env*" to the box.json ignore list. To keep an example file, also add "!/.env.example" after it. If the message names a box.json script, remove that script first. See The ColdBox template's postInstall script.
The box.json ignore list is empty after box install An install script runs package set ignore. box release check names it. Run box release init and answer y to remove the ColdBox template's script. Change any other script by hand. Then run box release init --yes to add the patterns again.
Could not find the GitHub CLI Install gh, open a new terminal, and run gh auth login.
The "## [Unreleased]" section ... is empty Add notes under [Unreleased], and then run the command again.
The package is missing box.json or ModuleConfig.cfc Remove the box.json ignore pattern that excludes the required file.
The package contains node_modules The package includes a node_modules folder with Node.js dependencies. Add /node_modules/ to the box.json ignore list, or run box release init --yes.
The package is ... MB The zip is larger than 50 MB. Add large folders your package does not need to the box.json ignore list. These may include uploads, media, or build output. The package already leaves out coldbox/, modules/, and testbox/. The warning does not stop the build.
Tag v1.2.3 already exists on origin That version was already released. Change the version before trying again.
Tag v1.2.3 points to a different commit on origin The local and remote tags point to different commits. Do not move the published tag. Check the release history or use a new version.
Tag v1.2.3 is on this commit, but box.json still has version 1.2.2 A Gitflow finish tagged the version before box.json changed. Answer y when the command offers to fix it, or add --fixTag. You can also follow the steps in the message. See You finished a branch before you changed the version.
Nobody can answer the question here. To publish X, add --finishPrerelease The level finishes a prerelease. To publish the next prerelease instead, use prerelease. To publish the stable version, add --finishPrerelease.
Version X is committed locally and nothing was published Fix the problem in the message. Then run box release publish without a level.
release.json contains invalid JSON Check for missing quotes, extra commas, or backslashes that must be doubled.

Run box release check when you do not know what is wrong. It reports release problems without changing the project.

Develop commandbox-release

Install the development dependencies and run the tests:

box install
box run-script test          # the full suite
box run-script test:fast     # unit tests only, under a minute

The test runner loads this working copy as the commandbox-release module. It uses a separate CommandBox installation, so a globally installed version does not affect the tests. Unit tests check version rules, changelogs, settings, project lookup, and ignore patterns. Integration tests create temporary projects in .test-work/. They run real commands through tests/support/Invoke.cfc and use a local Git remote. They do not publish to ForgeBox or GitHub. Each integration bundle starts one box and runs all of its commands in it, so the full suite takes minutes instead of a new box start for every command.

To test the working copy as an installed module, run box install <path to this checkout>. Then open a new shell. box release help should list the commands. The module uses its own box release publish command to release itself.

Files in commands/release/ receive command arguments. Files in models/ do the release work. Rules that do not need CommandBox have their own model components.

More information

Changelog

This file lists the important changes to this project.

The format follows Keep a Changelog. Version numbers follow Semantic Versioning.

[Unreleased]

[3.9.0] - 2026-10-30

Added

  • box release init offers to remove the ColdBox template's postInstall script, pathExists .env || cp .env.example .env && package set ignore=[]. Each box install that runs it empties the box.json ignore list. On a server, it can also copy .env.example to .env. With --yes, or when nobody can answer, setup keeps the script and prints a note.
  • box release check, box release publish, and the .env build stop name any install script that runs package set ignore.

Changed

  • The package now leaves out coldbox/, modules/, and testbox/. box install creates these folders, so the server runs box install after a deploy. Web app zips get much smaller.
  • Keep your app's own modules in modules_app/. The package includes that folder.
  • To ship one of these folders, add a pattern that keeps it to the box.json ignore list, such as "!/coldbox/".

[3.8.0] - 2026-10-29

Added

  • box release publish <level> and box release gitflow <level> ask before they finish a prerelease. patch, minor, and major all finish one. For example, patch changes 1.0.0-beta.15 to 1.0.0. The question shows the command that publishes the next prerelease instead. Answer n, or press Enter, to stop without changes.
  • A new --finishPrerelease flag finishes a prerelease without the question. Use it in CI or a script.
  • A practice run shows the same note and does not ask.
  • box release bump <level> shows the note too. It does not ask, because it publishes nothing.
  • A new major version, such as 1.4.2 to 2.0.0, shows a note. It does not ask.

Changed

  • When nobody can answer the question, such as in CI, a level that finishes a prerelease stops before it changes anything. Add --finishPrerelease to the command. A release without a level does not ask, so the GitHub Actions template is not affected.
  • box release publish <level> stops when git pull brings in a different version in box.json. Run the command again to check the updated project.

[3.7.1] - 2026-10-29

  • Update relese.json to enable test runs when publishing

[3.7.0] - 2026-10-29

Added

  • testRunner in release.json can be a path on the project's CommandBox server, such as "/tests/runner.cfm". The release finds the server's address when it runs the tests, so release.json does not repeat the port. A full URL still works.
  • A path in testbox.runner in box.json works too. Before, it stopped every release command with "testRunner must be a full URL".
  • With a runner path, each engine in engines can use its own port. box release test reads each engine's address after it starts the engine.

Changed

  • When neither release.json nor box.json sets a test runner, the release uses /tests/runner.cfm on the project's server. It used http://127.0.0.1:60299/tests/runner.cfm. box release init writes the path into new release.json files. box testbox run uses the same default.

[3.6.0] - 2026-10-26

Added

  • A new testCommand setting in release.json runs the tests with a CommandBox command, such as "testCommand": "run-script test". Use it for a project whose tests run inside CommandBox without a web server, such as a CommandBox module. box release test, box release publish, and box release gitflow run it in a new box process from the project root. If the command fails, the release stops the same way it stops for failing tests. --skipTests skips it.
  • With testCommand, the release does not check testRunner or need a test server. The setting cannot be used with engines. release.json with both stops with an error.
  • box release check shows the test command and checks that it can find box.
  • box release init suggests run-script test for a CommandBox module that has a test script and no TestBox runner URL.
  • A new hooks.beforePublish setting in release.json runs a CommandBox command after the tests and the build pass and before anything is published or pushed. For example, "hooks": { "beforePublish": "deploybot env push --yes" } sends a web app's settings to the server before the push starts a deploy. box release publish and box release gitflow run it. If the command fails, the release stops and nothing is published. A practice run shows the command without running it.
  • box release publish and box release gitflow have a new --skipHooks flag. It skips the beforePublish command.
  • box release check shows the beforePublish command and checks that it can find box.
  • box release publish and box release gitflow can fix a Gitflow finish that tagged a version before box.json changed. This happens when you finish a release or hotfix in GitKraken or git flow before you change the version. The commands show the steps and ask whether to do them. If you answer y, box release creates hotfix/1.0.1 (or release/1.1.0 for a minor or major version) from production and deletes the local tag. Then it changes the version, merges into both branches, publishes, and pushes. A new --fixTag flag does this without asking, such as in CI. The commands never ask in a practice run or when nobody can answer. See "Mistakes box release can fix" in the README.
  • A new prereleaseBranches setting in release.json lists branches that may publish prerelease versions, such as "prereleaseBranches": ["develop"]. On a listed branch, box release publish preminor alpha, box release publish prerelease, and box release publish commit, tag, and push the prerelease on that branch and mark the GitHub Release as a prerelease. Stable versions still publish from the production branch. A stable level on a listed branch stops and points to box release gitflow. The list is empty by default, so existing projects behave as before. See "Publish prereleases from develop" in the README.
  • box release check shows the prerelease branches and accepts a listed branch when box.json holds a prerelease.

Changed

  • box release package on a prerelease branch no longer adds -snapshot to a prerelease version. The package gets the build ID, as it does on the production branch.

  • box release gitflow without a level on develop stops when develop is a prerelease branch and box.json holds a prerelease that is newer than production. Before, it created a release branch for the prerelease and published it from production.

  • When a Gitflow merge into a prerelease branch conflicts on box.json, the manual steps say which version to keep. This happens when a hotfix meets a prerelease on develop.

  • When a prerelease line ends with a stable version and [Unreleased] is empty, the error says that the prerelease notes stay in their own sections and asks for notes for the stable version.

  • The build stops when the package contains a .env file, such as .env.production. These files can hold passwords and keys. Example files such as .env.example are still allowed. Add "**/.env*" to the box.json ignore list to leave them out.

  • The GitHub Actions workflow template passes skipHooks=true. Copy the change into an existing .github/workflows/release.yml if you set hooks.beforePublish.

  • box release gitflow merges the changelog into develop when develop added notes while a release or hotfix branch was open. It keeps the new notes under [Unreleased] and removes the notes that the release published. Before, it stopped with the message "The [Unreleased] notes in CHANGELOG.md changed on both branches" and asked you to merge by hand.

Fixed

  • box release publish <level> checks the new version's tag before it changes any files. Before, it made the "Release x.y.z" commit and then stopped because the tag already existed.
  • A Gitflow finish in GitKraken or git flow can tag a version, such as v1.0.1, before box.json changes. box release publish and box release gitflow now explain how to fix this. When the tag is only on your computer, delete it, create hotfix/1.0.1, and run box release gitflow patch. Before, the message suggested box release bump patch, which stopped on the same tag.
  • When a prerelease version was already released, box release publish and box release check now suggest box release bump prerelease. Before, they suggested box release bump patch, which would have finished the prerelease.

[3.5.3] - 2026-10-22

Fixed

  • box release gitflow no longer warns that the build skipped the tests after they passed on the release or hotfix branch. The build now says which branch passed the tests.

[3.5.2] - 2026-10-18

Changed

  • box release gitflow on a hotfix branch without a level now updates a prerelease. For example, 1.0.0-beta.2 becomes 1.0.0-beta.3. Before, it published 1.0.0. Use box release gitflow patch to finish the prerelease. With preid and no level, the hotfix changes the label, such as 1.0.0-beta.2 to 1.0.0-rc.1.

[3.5.1] - 2026-10-12

Added

  • Added default box.json ignore patterns for common files that should not be in a package.

[3.5.0] - 2026-10-12

Changed

  • box release gitflow now combines the changelog when it is the only file with a merge conflict. It keeps the [Unreleased] notes on develop and adds the new version section. A part of the changelog that changed on both branches still stops the command with the manual merge steps, and so does a conflict in any other file.

[3.4.0] - 2026-10-12

Added

  • box release check and box release publish list recommended box.json ignore patterns that are missing from the project's ignore list. These patterns keep files out of the package. Run box release init --yes to add them. Missing patterns do not stop a release.
  • The build shows the zip size. It warns when the package contains node_modules or is larger than 50 MB.
  • The README explains how to add new recommended ignore patterns to an existing project.

Changed

  • Without --force, box release init skips the setup questions when release.json exists. Setup keeps existing settings and changelog files and adds missing ignore patterns. Use --force to replace those files. Without --yes, setup asks the questions again.

[3.3.0] - 2026-10-09

Changed

  • box release publish with an existing tag now pushes the production branch when origin does not have the tagged commit. The checks report this before anything is published. They stop if origin has commits on that branch that your checkout does not have.

[3.2.0] - 2026-10-09

Added

  • box release gitflow merges a release or hotfix branch and publishes the package. On develop, it creates a release branch first. It changes the version when needed and runs the tests. It merges into develop and the production branch before publishing. Then it pushes both branches and deletes the release or hotfix branch by default.
  • The remote setting in release.json names the Git remote for releases. The default is origin.

Changed

  • box release publish <level> on a Gitflow release or hotfix branch now suggests box release gitflow.
  • Clearer Gitflow comments and command messages. The README now explains each step for a first release and no longer includes instructions for versions before 3.0.0.
  • Release commands now stop with a clear message when the release remote does not exist. box release check reports it too.

Fixed

  • The GitHub Release is now created in the repository of the release remote. Before, GitHub CLI could choose another repository when the checkout had more than one remote.
  • box release check now uses the same tag rule as box release publish. A version tag at the current commit that is already on the remote is reported as ready. A remote that cannot be checked is reported as a problem.
  • box release publish and box release resume now accept an annotated tag that is already on the remote. GitKraken and git flow create annotated tags. Before, the command reported that the tag pointed to a different commit.
  • The package build now reads the branch and commit from Git. This works in linked worktrees and when the project is a folder inside a larger repository.
  • box release gitflow now shows SSH key help when git fetch is refused.

[3.1.1] - 2026-10-08

Fixed

  • NPM dependencies are now shared ignored files between modules and web apps.

[3.1.0] - 2026-10-08

Changed

  • The module is named commandbox-release. Install it with box install commandbox-release and update it with box update commandbox-release --system. Commands still start with release.
  • Project settings are now in release.json instead of build.json. The minimumKitVersion setting is now called requires.
  • box release publish replaces release run. With no level, it publishes the version in box.json. With a level, such as patch, it changes the version, dates the release notes, commits the changes, and publishes.
  • box release publish uses an existing version tag if it points to the current commit. You no longer need --existingTag for tags made by Gitflow or GitKraken.
  • box release publish <level> stops on Gitflow release/* and hotfix/* branches. It shows the steps to finish the release from those branches.
  • box release init asks where to publish the project and whether to run tests. ForgeBox publishing is on by default when box.json or ModuleConfig.cfc shows that the project is a module. Use --yes to accept the default answers. If box.json is missing, setup offers to create it.
  • Packages now use the box.json ignore list to leave files out. ForgeBox uses the same list, so its package and the GitHub zip contain the same files. box release init adds common ignore patterns.
  • The build stops if an ignore pattern leaves out box.json, or ModuleConfig.cfc when the project has one.
  • box release test replaces release engines. It runs tests once if no engines are listed.
  • Test runs list each failed test by bundle, suite, and spec. With engines, the final results list them under each engine.
  • When the TestBox runner errors or times out instead of returning results, the test run shows the runner's error. It no longer reports this as failing tests.
  • Test runs ask the runner to stop after the first spec file with a failure (TestBox eagerFailure). The standard TestBox runner ignores this option. The README shows the two-line runner change that turns it on.
  • box release resume replaces release github.
  • box release bump now shows the steps to commit the change and run box release publish.
  • box release publish stops when git pull brings in new commits. Run it again to check the updated project.
  • stagingDir and artifactsDir must be folders inside the project. The build deletes these folders before each run.
  • When [Unreleased] looks empty because a group heading such as ## Added follows it, box release bump and box release publish <level> name that heading. They say to use ### Added, because ## starts a new version section.

Fixed

  • The GitHub Actions template now signs in to ForgeBox with box config set endpoints.forgebox.APIToken.

Removed

  • release migrate and support for 1.x and 2.x project settings. See "Upgrading from 1.x or 2.x" in the README.
  • The excludes and excludesAdd settings. Release commands explain the problem if these settings remain in release.json.
  • The version= argument for publishing. The publish command reads the version from box.json.

[2.0.1] - 2026-09-13

  • Cleaned up README and code documentation.

[2.0.0] - 2026-09-13

Changed

  • build-template is now a CommandBox module. Install it once on each computer with box install build-template. You no longer need to copy a build folder into each project.
  • Build tasks are now commands in the release namespace. The commands are box release run, release check, release bump <level>, release package, release engines, release init, release notes, release github, and release migrate. Run box release help to list them.
  • Project settings now use build.json in the project root. The kit can still read the old 1.x file at build/build.json. It prints a notice until release migrate moves the file.
  • A project can set minimumKitVersion in build.json. Release commands stop and print the update command when the installed kit is too old.
  • Release commands now find the project from any folder inside it.
  • Use box update build-template --system to update the kit.

Removed

  • Projects no longer need the copied build folder, Update.cfc, build-kit.json, templateVersion, or scripts added to box.json by the installer.
  • release init no longer adds scripts to box.json. release migrate updates old 1.x scripts to use the new commands. Existing box run-script release calls continue to work.

Migration from 1.x

  1. Run box install build-template.
  2. Run box release migrate --dryRun in each project. Then run box release migrate, review the changes, and commit them.
  3. Install the module before the release step in CI. See templates/github-release.yml.

[1.5.0] - 2026-09-13

Added

  • build-kit:update (build/Update.cfc) updates the kit files copied into a project. It can download the latest build-template release or use :source=<folder or zip>. It replaces kit files under build/ but keeps build/build.json. It adds new box.json scripts, records the kit version, and prints changelog entries added since the previous version. Use :version= to choose a release. Use :dryRun=true to list changes without applying them.
  • build/build-kit.json records the kit version and repository. The installer now writes the real kit version to templateVersion instead of always writing 1.0.0.
  • release:check now reports whether the release tag exists on origin.

Changed

  • release:existing-tag now checks origin before publishing. It pushes a local-only tag right before creating the GitHub Release. It stops when origin has the same tag at another commit. Before this change, an unpushed Gitflow tag caused the last release step to fail after the package was already published to ForgeBox.
  • The box.json script list moved to build/lib/PackageScriptService.cfc. The install and update tasks now use the same list.

[1.4.2] - 2028-08-10

Changed

  • bump:beta, bump:alpha, and direct preminor calls now stop before changing an active prerelease to a new target. Use :allowPrereleaseRetarget=true to allow that change.

[1.4.1] - 2028-08-07

  • Updated the logo.

[1.4.0] - 2028-08-07

Added

  • Added TestBox tests for version rules, changelog parsing, project settings, public task APIs, multiple-engine workflows, installation, package builds, and safe release practice runs.

Changed

  • Build task components now use direct names and smaller workflow functions.
  • Developer documentation now uses plain language for people who are new to the project.
  • Version, changelog, and project detection rules now use small internal services under build/lib/.
  • The multiple-engine guide now explains that all configured engines run before the command reports any failures.

[1.3.0] - 2028-08-03

Added

  • Added release:existing-tag. This task publishes a release tag created while finishing a Gitflow release in a tool such as GitKraken.

Changed

  • The Gitflow guide now gives the branch steps in order. Create the release branch first. Change the version and commit on that branch. Then finish the release into production and develop.
  • Git instructions now include the matching review, stage, commit, and push steps for people who use GitKraken or another Git app.
  • The guide now has separate GitKraken instructions for valid, old, and already published tags.

[1.2.0] - 2026-07-31

Added

  • Added a Gitflow release guide for plain Git, pull requests, git-flow, and hotfixes.
  • Added a mode that publishes tags created by Gitflow or tag-based CI jobs.
  • Added release:skip-tests as a clear name for the existing option that skips release tests.

Changed

  • A release practice run can run from a non-production branch and prints a warning. A real release must still run from the configured production branch.
  • Release updates now allow fast-forward changes only. Normal releases now reject tags that already exist locally or on the remote.
  • Setup now uses Gitflow's production branch when it is configured. Git instructions now list separate review, stage, and commit steps for developers who are new to Git.
  • The optional GitHub Actions workflow can publish an existing tag. It requests only the repository content permission needed to create the release.

[1.1.0] - 2026-07-30

Added

  • New projects now get a complete exclusion list in build/build.json. Module packages exclude common ColdBox development files. Applications keep possible deployment files such as modules, .htaccess, and .well-known.
  • Setup now finds server.json and root-level server-*.json files in filename order. It names each engine from app.cfengine, the server name, or the filename.

Changed

  • The included build.json is now marked as a starter file that setup can replace. Setup does not replace an existing unmarked or invalid file unless you use :force=true.

[1.0.0] - 2026-07-29

Added

  • Added the first build kit tasks: Build, Release, Bump, TestEngines, Install, and Doctor. They share one settings file at build/build.json.
  • box run-script release checks the project, updates it from the remote, runs tests, builds the package, publishes to ForgeBox, creates a Git tag, and creates a GitHub Release. It uses the changelog notes and attaches the zip file.
  • release:check reports whether a project is ready to release. It prints a fix for each problem.
  • release:dryrun runs a release practice run without publishing.
  • release:hotfix and :skipTests=true skip the tests and print a warning.
  • test:engines runs the tests on each engine in order. It stops at the first failure and lists the engines that passed before the failure.
  • bump:major, bump:minor, and bump:patch change the version and move [Unreleased] notes into a dated section. Prereleases follow SemVer. For example, bump:patch changes 1.2.0-beta.3 to 1.2.0, not 1.2.1.
  • bump:beta, bump:alpha, and bump:prerelease start or update prereleases. Use :preid for another prerelease label.
  • Install.cfc sets up a project in one command. It reads the test runner from box.json and creates the engine list from root-level server-*.json files.
  • The build checks packages before publishing. It compares the number of staged files with the number of files in the zip and stops when the numbers differ.
  • Packages include the short Git commit hash for the source used to build them.
  • excludesAdd adds entries to the default exclusion list. Excluding one more file requires one new setting.
  • Added an optional GitHub Actions workflow under build/templates/.

$ box install commandbox-release

No collaborators yet.
     
  • {{ getFullDate("2026-09-13T05:19:29-05:00") }}
  • {{ getFullDate("2026-10-30T15:15:13-05:00") }}
  • 409
  • 41