@omnidist/omnidist
v0.1.35
Published
Meta package for omnidist
Readme
omnidist
Package and publish a Go CLI as npm, uv, and RubyGems installable tools with prebuilt cross-platform binaries.
omnidist gives Go CLI maintainers one repeatable release flow:
build -> stage -> verify -> publishThe generated npm packages use platform-specific optional dependencies, so users
can run your CLI with npx without install-time downloader scripts. The uv
distribution stages wheel artifacts so users can run the same CLI from Python
tooling with uvx. The RubyGems distribution stages platform-specific gems so
users can install the same CLI with gem install.
Requirements
- Go 1.25+
- Node.js and npm for npm staging, verification, and publishing
uvfor uv staging, verification, and publishing- Ruby and RubyGems for gem staging, verification, and publishing
gitwhenversion.source: git-tagNPM_PUBLISH_TOKENfor npm token publishing, unless using--dry-runor trusted publishingUV_PUBLISH_TOKENoromnidist uv publish --tokenfor uv publishing, unless using--dry-runGEM_HOST_API_KEY/RUBYGEMS_API_KEYfor gem token publishing, unless using--dry-runor trusted publishing
Install
Run without installing:
npx -y @omnidist/omnidist@latest --help
uvx omnidist --helpInstall with npm:
npm i -g @omnidist/omnidist
omnidist --helpInstall with Go:
go install github.com/metalagman/omnidist/cmd/omnidist@latest
omnidist --helpInstall with RubyGems after publication:
gem install omnidist
omnidist --helpRun from a checkout:
go run ./cmd/omnidist --helpQuick Start
Initialize an existing Go CLI repository:
omnidist initThis creates .omnidist/omnidist.yaml and initial workspace directories under
.omnidist/default/. The generated config uses profiles.default.
Edit the generated config before building:
$EDITOR .omnidist/omnidist.yamlAt minimum, check these fields:
tool.name: the binary name users will run.tool.main: the Go main package, for example./cmd/mytool.distributions.npm.package: the npm package, for example@my-org/mytool.distributions.uv.package: the uv/PyPI package, for examplemytool.distributions.gem.package: the RubyGems package, for examplemytool.version.source: usuallygit-tag,file,env, orfixed.
Then run the local release pipeline:
omnidist build
omnidist stage
omnidist verifyPublish only after verification passes:
omnidist publishGenerate a GitHub Actions release workflow:
omnidist ciThe generated workflow is written to .github/workflows/omnidist-release.yml.
It runs on v* tag pushes, builds once, stages and verifies artifacts, publishes
npm, uv, and gem distributions, then uploads the built binaries and checksums.txt
to the GitHub release.
Configuration
omnidist init writes profiles-mode config by default:
profiles:
default:
tool:
name: mytool
main: ./cmd/mytool
version:
source: git-tag # git-tag | file | env | fixed
file: VERSION # used only when source is file
fixed: 1.2.3 # required only when source is fixed
targets:
- os: darwin
arch: amd64
- os: darwin
arch: arm64
- os: linux
arch: amd64
- os: linux
arch: arm64
- os: windows
arch: amd64
build:
ldflags: -s -w
tags: []
cgo: false
distributions:
npm:
package: "@my-org/mytool"
registry: https://registry.npmjs.org
access: public # public | restricted
publish-auth: token # token | trusted
include-readme: true
uv:
package: mytool
index-url: https://upload.pypi.org/legacy/
linux-tag: manylinux2014 # manylinux2014 | musllinux_1_2
include-readme: true
gem:
package: mytool
registry: https://rubygems.org
publish-auth: token # token | trusted
repository-url: https://github.com/my-org/mytool
include-readme: trueSelect a profile with --profile <name> or OMNIDIST_PROFILE. If profiles
is present and no profile is selected, default is used. Profiles write
artifacts to .omnidist/<profile>/.
Legacy top-level config is still accepted when loading existing config files,
but do not mix top-level runtime fields with a profiles map in the same file.
Targets use Go values: os is GOOS, and arch is GOARCH. Distribution
workflows map them as needed, for example windows/amd64 becomes npm
win32/x64. For RubyGems, windows/amd64 defaults to x64-mingw-ucrt; set
targets[].variant: mingw32 if you need x64-mingw32, or targets[].variant: musl
for Linux musl gem platforms.
Versioning
omnidist build resolves the release version and writes it to
.omnidist/<profile>/dist/VERSION. Stage and publish commands use that build
version so npm, uv, and gem artifacts stay in sync.
Supported version sources:
git-tag:HEADmust be on an exact SemVer tag, eithervX.Y.ZorX.Y.Z.file: read the version fromversion.file, defaulting toVERSION.env: readOMNIDIST_VERSION.fixed: readversion.fixed.
For local prerelease staging, use:
omnidist stage --dev--dev generates prerelease artifact versions from git metadata. For npm
publishing, -dev prerelease versions are automatically published with
--tag dev when no tag is supplied.
Environment and Build Variables
omnidist loads .env at startup when the file exists.
Environment variables:
OMNIDIST_VERSION: used only whenversion.source: env.VERSIONis not used.OMNIDIST_CONFIG: config file path, equivalent to--config.OMNIDIST_PROFILE: config profile name, equivalent to--profile.OMNIDIST_OMNIDIST_ROOT: project root directory, equivalent to--omnidist-root.NPM_PUBLISH_TOKEN: npm token fordistributions.npm.publish-auth: token.UV_PUBLISH_TOKEN: uv publish token when--tokenis not provided.GEM_HOST_API_KEY: RubyGems token fordistributions.gem.publish-auth: token.RUBYGEMS_API_KEY: alternate RubyGems token env name.GEM_HOST_OTP_CODE: RubyGems MFA OTP for token-based publish.
Build ldflags template variables:
OMNIDIST_VERSION: expanded inbuild.ldflagsduringomnidist build.OMNIDIST_GIT_COMMIT: populated byomnidist buildwhen git metadata is available.OMNIDIST_BUILD_DATE: populated byomnidist buildas UTC RFC3339.
Example:
build:
ldflags: >-
-s -w
-X github.com/your-org/mytool/internal/version.version=${OMNIDIST_VERSION}
-X github.com/your-org/mytool/internal/version.gitCommit=${OMNIDIST_GIT_COMMIT}
-X github.com/your-org/mytool/internal/version.buildDate=${OMNIDIST_BUILD_DATE}build.ldflags uses os.ExpandEnv, so both $VAR and ${VAR} are supported.
Unset variables expand to empty strings.
npm Distribution
The npm distribution has two package types:
- Meta package:
distributions.npm.package, with a small Node shim. - Platform packages: one package per target, selected by npm
osandcpuconstraints.
The meta package lists platform packages as optionalDependencies at the same
version. Published packages do not use postinstall scripts or network
downloaders.
Common npm commands:
omnidist npm stage
omnidist npm verify
omnidist npm publish --tag latestToken publishing uses NPM_PUBLISH_TOKEN. omnidist writes a workspace
.omnidist/.npmrc from distributions.npm.registry with npm's token
substitution syntax.
Trusted publishing uses npm OIDC instead of NPM_PUBLISH_TOKEN:
distributions:
npm:
publish-auth: trusted
repository-url: git+https://github.com/your-org/your-repo.gitIn trusted mode:
repository-urlis required and is written to staged package metadata.omnidist npm publishskips token-only auth preflight.- GitHub Actions must grant
id-token: write. - Each npm package, including platform packages, needs a trusted publisher configured on npm.
Print trusted publisher setup commands:
omnidist npm trustApply them directly:
omnidist npm trust --applyUseful trust options:
--workflow-file <name>for a workflow filename other thanomnidist-release.yml.--repo <owner/repo>to overridedistributions.npm.repository-url.--environment <name>for npm trusted publishers restricted to a GitHub Actions environment.--allow-stage-publishto allow npm stage publish.
uv Distribution
The uv distribution stages wheel artifacts under .omnidist/<profile>/uv/dist.
During staging, versions are converted to PEP 440 where needed.
Common uv commands:
omnidist uv stage
omnidist uv verify
omnidist uv publishPublish to a different PyPI-compatible index:
omnidist uv publish --publish-url https://test.pypi.org/legacy/ --token <token>omnidist uv verify rejects versions with local metadata (+...) for
PyPI/TestPyPI publishing, because those indexes reject local versions.
RubyGems Distribution
The RubyGems distribution stages one platform-specific .gem per configured
target under .omnidist/<profile>/gem/pkg. Each gem contains:
- a wrapper executable under
exe/ - the prebuilt CLI binary under
libexec/ - platform metadata so RubyGems installs the matching package
Common gem commands:
omnidist gem stage
omnidist gem verify
omnidist gem publishToken publishing uses GEM_HOST_API_KEY or RUBYGEMS_API_KEY. If your
RubyGems account requires MFA for pushes, also pass --otp or set
GEM_HOST_OTP_CODE.
Trusted publishing uses RubyGems OIDC instead of static secrets:
distributions:
gem:
publish-auth: trusted
registry: https://rubygems.org
repository-url: https://github.com/your-org/your-repoIn trusted mode:
- GitHub Actions must grant
id-token: write. - The repository/workflow must be registered as a trusted publisher on RubyGems.org.
omnidistcan publish a brand-new gem name once the pending trusted publisher is configured on RubyGems.org.
README and License Staging
README source precedence during staging:
distributions.<name>.readme-path -> readme-path -> README.mdIf a configured readme-path is set and cannot be read, staging fails. Set
include-readme: false for a distribution to skip README inclusion.
For npm packages, license can be set explicitly under distributions.npm.
When it is omitted, omnidist includes the project license file when present and
writes package metadata that points to that file.
CI Releases
Generate a release workflow:
omnidist ciOverwrite an existing generated workflow:
omnidist ci --forceThe workflow installs omnidist with npm. When generating this repository's own
release workflow, it uses go run ./cmd/omnidist instead. The workflow also
sets FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true for JavaScript-based GitHub
Actions.
Before the first tag release, configure the required registry credentials:
- npm token mode:
NPM_PUBLISH_TOKENGitHub secret. - npm trusted mode: npm trusted publishers for every staged npm package.
- uv publishing:
UV_PUBLISH_TOKENGitHub secret. - gem token mode:
RUBYGEMS_API_KEYGitHub secret, and optionallyRUBYGEMS_OTP_CODE. - gem trusted mode: RubyGems trusted publisher for the release workflow.
Release by pushing a SemVer tag:
git tag v1.2.3
git push origin v1.2.3Command Reference
Top-level commands:
omnidist init
omnidist quickstart
omnidist build
omnidist stage [--dev] [--only npm|uv|gem|npm,uv,gem]
omnidist verify [--only npm|uv|gem|npm,uv,gem]
omnidist publish [--dry-run] [--only npm|uv|gem|npm,uv,gem]
omnidist ci [--force] [--dry-run]
omnidist versionGlobal flags:
--config <path>
--profile <name>
--omnidist-root <path>npm commands:
omnidist npm stage [--dev]
omnidist npm verify
omnidist npm publish [--dry-run] [--tag <tag>] [--registry <url>] [--otp <code>]
omnidist npm trust [--apply] [--workflow-file <name>] [--repo <owner/repo>] [--environment <name>] [--allow-stage-publish]uv commands:
omnidist uv stage [--dev]
omnidist uv verify
omnidist uv publish [--dry-run] [--publish-url <url>] [--token <token>]gem commands:
omnidist gem stage [--dev]
omnidist gem verify
omnidist gem publish [--dry-run] [--host <url>] [--api-key <token>] [--otp <code>]Top-level stage, verify, and publish run distributions in deterministic
order: npm first, then uv, then gem. Use --only to limit the command to one
or more distributions.
Troubleshooting
omnidist build says no version could be resolved:
Check version.source. For git-tag, HEAD must be exactly on a SemVer tag.
For env, set OMNIDIST_VERSION. For file, create the configured version
file.
omnidist stage says the build version file is missing:
Run omnidist build first. Stage commands use the build version persisted under
.omnidist/<profile>/dist/VERSION.
npm install cannot find a platform package:
Run omnidist npm verify before publishing. It checks package versions,
platform os and cpu fields, required binaries, forbidden postinstall
scripts, repository metadata, and optional dependency parity.
Trusted npm publish fails:
Verify that distributions.npm.repository-url matches the GitHub repository,
the workflow grants id-token: write, and every npm package has a trusted
publisher configured.
uv publish rejects the version:
Restage with a publishable version. PyPI and TestPyPI reject local version
metadata containing +.
Contributing
For repository layout, development workflow, package model details, and migration notes, see CONTRIBUTING.md.
