Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
⚒️ Release packages.
A monorepo tool for Deno workspaces.
Roka “forge” is crafted for the community for managing Deno packages developed on GitHub. It can compile binaries, calculate versions, and even create GitHub releases. It works best with monorepos of multiple packages published to JSR.
Usage
To get started, just run forge with deno run -A jsr:@roka/forge
and
you’re all set! Or, you can install with deno install -gA jsr:@roka/forge
,
and then run with forge
. No need for any configuration.
Packages
The repository is treated as a list of Deno packages by forge. It
supports simple projects with a single package and monorepos that use
Deno workspaces.
Currently, only the deno.json
file is supported for package configuration.
{ "name": "@roka/example", "version": "1.2.3", "exports": "./example.ts", }
The version
field in this file is how we request new releases. If you are
not ready to release yet, you can set it “0.0.0”, forge will calculate
the first version for you. The package will be skipped if it doesn’t have a
version.
forge list
📦 example @roka/example 1.2.3 📦 testing @roka/testing 0.1.0
By default, every forge command applies to all packages in the repository. However, you can filter packages by specifying a list of package directories or using glob patterns.
forge list "exam*"
Versions
Versioning in forge is only effective when the repository adheres to Conventional Commits. Any bug fix results in a patch version, while a new feature is introduced in a minor version. Commits marked as breaking changes trigger a new major version.
A semantic version is calculated for every package at every commit. The new version begins with the latest release, or “0.0.0” if the package has no releases. It then tracks the commits made since that release.
forge changelog
🏷️ example@2.0.0-pre.3+fedcba9 test(example): forgot unit-testing (#6) style(example): nicer code style (#5) fix(example)!: really fix bug with breaking change (#4) revert(example): revert bug fix, it broke something (#3) fix(example) fix bug (#2) feat(example): introduce new feature (#1)
The current package version will include a pre-release number, which is calculated based on the number of commits that make a version change. Additionally, the version will have a commit hash appended to it, which is derived from the last commit that updated the package.
If you’re working on just one package, you can skip the scopes altogether. In this case, a commit summary like “feat: feature” will also trigger a version change. But if you’re in a workspace with multiple packages, the scope is needed to figure out which packages will update.
You can update multiple packages with a single commit by listing their names with a comma in between. But before you do that, maybe it’s time to think about whether your pull requests can be smaller. 🤓
Releases
Bump the versions
To start a new release, we’ll need to update the package versions in the
configuration files. We’re talking about the version
field in deno.json
.
If you change this version, it means a new release is coming, and forge
can help you with that.
Let’s create a pull request on GitHub to increment our package version. We’ll use the GitHub CLI to get a token and pass it to the tool to identify the pull request creator. When our releases are automated with workflows, authentication will be handled differently. More to that later.
git checkout main GITHUB_TOKEN=$(gh auth token) forge bump example --release --pr
The --release
flag drops the pre-release and build
information from the current version, resulting in a version like 2.0.0.
If we had omitted this flag, the release would have been a pre-release.
The pull request generated by the --pr
flag will include changes to the
configuration and changelog files. Review and merge these changes, and the
release will be ready to roll at the merged commit.
Check out an example pull request
that updates multiple packages. Emojis are optional and enabled with the
--emoji
flag to add some extra flair. 💅
At this point, we are ready to publish the packages to JSR. However, before that, let’s proceed to create a release on GitHub, which is the second step.
Release on GitHub
Great news! The version change is now merged, and GitHub knows the commit hash. Let’s create a release right here!
git pull GITHUB_TOKEN=$(gh auth token) forge release example --draft
The draft release created on GitHub will have the new version, the commit changelog, and a link to the documentation on JSR. This example release was created automatically when the the example pull request above was merged.
At this point, you may want to add further details to your release summary. If everything looks good, go ahead and publish it. This will create a new release tag for the released package, like example@2.0.0.
Publish to JSR
Finally, at this tagged commit, we can publish our packages to JSR.
deno publish
Simply running this command will work, because the deno.json
file is
up-to-date, and all our changes are committed.
The command will guide you to authenticate with JSR and create any new packages if necessary. Ideally, we link our packages to their GitHub repositories and automate the publishing process with GitHub actions. See JSR documentation for publishing for this.
Automate with Actions
We’ve covered the three steps, bump, release, and publish, all of which can be automated using GitHub workflows. In fact, it’s the recommended way, so we are not slowed down by mistakes.
For authentication, the convenient GITHUB_TOKEN
works. However, this token
lacks the ability to initiate CI checks for newly created pull requests.
Additionally, your GitHub account can restrict this token from creating
pull requests, which is a wise practice. A better solution is to create a
personal access token with read and write permissions to contents and
pull requests.
The personal access token is linked to your personal account, and PRs will be created by you. If this suits your needs, you’re all set. However, you won’t be able to approve the bump requests yourself. For teams, it may be preferable to create a bot account and use its personal access token instead.
Check out the workflows in the roka repository to see how we can automate all steps. With forge taking care of most of the work, we can either chill or find more time for coding. 💆♀️
Assets
WARNING: This feature is highly experimental.
The tool supports a non-standard compile
extension in the deno.json
file. Any package with this field will generate release assets during a
release.
{ "name": "@roka/example", "version": "2.0.0", "compile": { "main": "example.ts" } }
A package with the compile
configuration will be compiled into a binary
for every supported Deno target. These
compiled binaries will be bundled and uploaded to the GitHub release as
assets.
forge release example
🚀 Created release example@2.0.0 [https://github.com/withroka/example/releases/tag/example@2.0.0] 🏺 x86_64-unknown-linux-gnu.tar.gz 🏺 aarch64-unknown-linux-gnu.tar.gz 🏺 x86_64-pc-windows-msvc.zip 🏺 x86_64-apple-darwin.tar.gz 🏺 aarch64-apple-darwin.tar.gz 🏺 sha256.txt
Magic! 🔮
Modules
This library also offers programmatic functionality through the following modules:
Add Package
deno add jsr:@roka/forge
Import symbol
import * as forge from "@roka/forge";
Import directly with a jsr specifier
import * as forge from "jsr:@roka/forge";
Add Package
pnpm i jsr:@roka/forge
pnpm dlx jsr add @roka/forge
Import symbol
import * as forge from "@roka/forge";
Add Package
yarn add jsr:@roka/forge
yarn dlx jsr add @roka/forge
Import symbol
import * as forge from "@roka/forge";
Add Package
vlt install jsr:@roka/forge
Import symbol
import * as forge from "@roka/forge";
Add Package
npx jsr add @roka/forge
Import symbol
import * as forge from "@roka/forge";
Add Package
bunx jsr add @roka/forge
Import symbol
import * as forge from "@roka/forge";