From 17610eee8212c5e43ac842ffdeefd96ad31de84d Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Fri, 27 Feb 2026 14:05:08 +0000 Subject: [PATCH 1/9] Changes required by Gorelase version > 2 --- .github/workflows/release.yml | 1 + .gitignore | 2 + .goreleaser.yml | 67 +++++++++++++++++++------------ npm/README.md | 66 ++++++++++++++++++++++++++++++ npm/cli-darwin-arm64/.npmrc | 2 + npm/cli-darwin-arm64/README.md | 1 + npm/cli-darwin-arm64/package.json | 19 +++++++++ npm/cli-darwin-x64/.npmrc | 2 + npm/cli-darwin-x64/README.md | 1 + npm/cli-darwin-x64/package.json | 19 +++++++++ npm/cli-linux-arm/.npmrc | 2 + npm/cli-linux-arm/README.md | 1 + npm/cli-linux-arm/package.json | 19 +++++++++ npm/cli-linux-arm64/.npmrc | 2 + npm/cli-linux-arm64/README.md | 1 + npm/cli-linux-arm64/package.json | 19 +++++++++ npm/cli-linux-x64/.npmrc | 2 + npm/cli-linux-x64/README.md | 1 + npm/cli-linux-x64/package.json | 19 +++++++++ npm/cli-win32-arm64/.npmrc | 2 + npm/cli-win32-arm64/README.md | 1 + npm/cli-win32-arm64/package.json | 19 +++++++++ npm/cli-win32-x64/.npmrc | 2 + npm/cli-win32-x64/README.md | 1 + npm/cli-win32-x64/package.json | 19 +++++++++ npm/wrapper/.npmrc | 2 + npm/wrapper/install.js | 54 +++++++++++++++++++++++++ npm/wrapper/package.json | 45 +++++++++++++++++++++ scripts/npm-publish.sh | 42 +++++++++++++++++++ 29 files changed, 408 insertions(+), 25 deletions(-) create mode 100644 npm/README.md create mode 100644 npm/cli-darwin-arm64/.npmrc create mode 100644 npm/cli-darwin-arm64/README.md create mode 100644 npm/cli-darwin-arm64/package.json create mode 100644 npm/cli-darwin-x64/.npmrc create mode 100644 npm/cli-darwin-x64/README.md create mode 100644 npm/cli-darwin-x64/package.json create mode 100644 npm/cli-linux-arm/.npmrc create mode 100644 npm/cli-linux-arm/README.md create mode 100644 npm/cli-linux-arm/package.json create mode 100644 npm/cli-linux-arm64/.npmrc create mode 100644 npm/cli-linux-arm64/README.md create mode 100644 npm/cli-linux-arm64/package.json create mode 100644 npm/cli-linux-x64/.npmrc create mode 100644 npm/cli-linux-x64/README.md create mode 100644 npm/cli-linux-x64/package.json create mode 100644 npm/cli-win32-arm64/.npmrc create mode 100644 npm/cli-win32-arm64/README.md create mode 100644 npm/cli-win32-arm64/package.json create mode 100644 npm/cli-win32-x64/.npmrc create mode 100644 npm/cli-win32-x64/README.md create mode 100644 npm/cli-win32-x64/package.json create mode 100644 npm/wrapper/.npmrc create mode 100644 npm/wrapper/install.js create mode 100644 npm/wrapper/package.json create mode 100755 scripts/npm-publish.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1e9b51e44..052bf6bb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -143,6 +143,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FURY_TOKEN: ${{ secrets.FURY_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - uses: actions/upload-artifact@v4 with: diff --git a/.gitignore b/.gitignore index acc7c1a17..eb9181b1f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ docs.kosli.com/resources/_gen/* docs.kosli.com/content/client_reference/kosli* docs.kosli.com/public/ docs.kosli.com/.netlify +npm/cli*/bin/* +npm/wrapper/bin/* *.tar.gz *~ /.idea diff --git a/.goreleaser.yml b/.goreleaser.yml index 8c3429bf3..7f0b24108 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -3,6 +3,7 @@ project_name: kosli before: hooks: - go mod tidy + - rm -rf npm/cli-*/bin builds: - id: kosli binary: kosli @@ -27,6 +28,19 @@ builds: - goos: windows goarch: arm main: ./cmd/kosli/ + hooks: + post: + - cmd: >- + bash -c ' + OS="{{ .Os }}"; + ARCH="{{ .Arch }}"; + [ "$OS" = "windows" ] && OS="win32"; + [ "$ARCH" = "amd64" ] && ARCH="x64"; + EXT=""; + [ "{{ .Os }}" = "windows" ] && EXT=".exe"; + mkdir -p npm/cli-${OS}-${ARCH}/bin && + cp "{{ .Path }}" npm/cli-${OS}-${ARCH}/bin/kosli${EXT} && + chmod +x npm/cli-${OS}-${ARCH}/bin/kosli${EXT}' archives: - @@ -42,39 +56,42 @@ archives: nfpms: - id: kosli - # You can change the file name of the package. - # - # Default:`{{ .PackageName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}` - file_name_template: >- - {{ .ProjectName }}_ - {{- title .Os }}_ - {{- if eq .Arch "amd64" }}x86_64 - {{- else if eq .Arch "386" }}i386 - {{- else }}{{ .Arch }}{{ end }} +# # You can change the file name of the package. +# # +# # Default:`{{ .PackageName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}` + +# # TODO Maybe enable the template again after we have a better understanding of how to use it. It seems to be working without it, but it would be nice to have more control over the file name. + +# # file_name_template: >- +# # {{ .ProjectName }}_ +# # {{- title .Os }}_ +# # {{- if eq .Arch "amd64" }}x86_64 +# # {{- else if eq .Arch "386" }}i386 +# # {{- else }}{{ .Arch }}{{ end }} ids: - kosli - vendor: Kosli Inc. - homepage: https://kosli.com/ - maintainer: Mike Long - description: CLI client for reporting compliance events to https://kosli.com - license: MIT +# vendor: Kosli Inc. +# homepage: https://kosli.com/ +# maintainer: Mike Long +# description: CLI client for reporting compliance events to https://kosli.com +# license: MIT - # Formats to be generated. - formats: - - deb - - rpm +# # Formats to be generated. +# formats: +# - deb +# - rpm - # Template to the path that the binaries should be installed. - # Defaults to `/usr/bin`. - bindir: /usr/bin +# # Template to the path that the binaries should be installed. +# # Defaults to `/usr/bin`. +# bindir: /usr/bin - # Section. - section: misc +# # Section. +# section: misc - # Priority. - priority: optional +# # Priority. +# priority: optional # Contents to add to the package. # GoReleaser will automatically add the binaries. diff --git a/npm/README.md b/npm/README.md new file mode 100644 index 000000000..2bad31b28 --- /dev/null +++ b/npm/README.md @@ -0,0 +1,66 @@ +# NPM Packaging + +This directory contains the npm package structure for distributing the Kosli CLI via npm, following the same pattern used by [esbuild](https://github.com/evanw/esbuild). + +## Structure + +``` +npm/ +├── wrapper/ # @jbrejner/cli — the package users install +│ ├── package.json # declares optionalDependencies for all platforms +│ ├── bin/kosli # JS shim that detects the platform and runs the binary +│ └── install.js # postinstall script that validates the binary +├── cli-linux-x64/ # @jbrejner/cli-linux-x64 +├── cli-linux-arm64/ # @jbrejner/cli-linux-arm64 +├── cli-darwin-x64/ # @jbrejner/cli-darwin-x64 +└── cli-darwin-arm64/ # @jbrejner/cli-darwin-arm64 + ├── package.json # declares os/cpu fields for platform filtering + └── bin/kosli # the native binary — see below +``` + +## How it works + +Users install a single package: + +```sh +npm install @jbrejner/cli +``` + +npm resolves the `optionalDependencies` declared in the wrapper's `package.json` and installs only the platform-specific package that matches the current OS and CPU architecture — all non-matching packages are silently skipped. The wrapper's `bin/kosli` JS shim then locates the binary inside the installed platform package and executes it. + +## The `bin/` directories are populated by goreleaser + +The platform package `bin/` directories are **not committed to git**. They are populated automatically during the release process by a post-build hook in [`.goreleaser.yml`](../.goreleaser.yml): + +```yaml +hooks: + post: + - cmd: bash -c 'ARCH="{{ .Arch }}"; [ "$ARCH" = "amd64" ] && ARCH="x64"; mkdir -p npm/cli-{{ .Os }}-${ARCH}/bin && cp "{{ .Path }}" npm/cli-{{ .Os }}-${ARCH}/bin/kosli && chmod +x npm/cli-{{ .Os }}-${ARCH}/bin/kosli' +``` + +This hook runs once per build target immediately after goreleaser compiles the binary. It maps goreleaser's architecture naming (`amd64` → `x64`, `arm64` → `arm64`) to the npm naming convention and copies the binary into the correct platform package directory. + +## Publishing + +Platform packages must be published before the wrapper, since the wrapper's `optionalDependencies` references them. After a goreleaser build has populated the `bin/` directories: + +```sh +# Publish platform packages first +(cd npm/cli-linux-x64 && npm publish) +(cd npm/cli-linux-arm64 && npm publish) +(cd npm/cli-darwin-x64 && npm publish) +(cd npm/cli-darwin-arm64 && npm publish) + +# Then publish the wrapper +(cd npm/wrapper && npm publish) +``` + +Or as a one-liner that publishes platform packages first, then the wrapper: + +```sh +find npm -name package.json ! -path "npm/wrapper/*" | while read f; do pushd "$(dirname "$f")" && npm publish; popd; done && pushd npm/wrapper && npm publish; popd +``` + +## Versioning + +All packages must share the same version number. When bumping the version, update it in all five `package.json` files — the four platform packages and the wrapper — as well as the `optionalDependencies` versions in `npm/wrapper/package.json`. diff --git a/npm/cli-darwin-arm64/.npmrc b/npm/cli-darwin-arm64/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/cli-darwin-arm64/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-darwin-arm64/README.md b/npm/cli-darwin-arm64/README.md new file mode 100644 index 000000000..1e2832d6f --- /dev/null +++ b/npm/cli-darwin-arm64/README.md @@ -0,0 +1 @@ +This is the macOS ARM 64-bit binary for the Kosli CLI (Apple Silicon). See https://github.com/kosli-dev/cli for details. diff --git a/npm/cli-darwin-arm64/package.json b/npm/cli-darwin-arm64/package.json new file mode 100644 index 000000000..60fef5904 --- /dev/null +++ b/npm/cli-darwin-arm64/package.json @@ -0,0 +1,19 @@ +{ + "name": "@jbrejner/cli-darwin-arm64", + "version": "0.0.0", + "description": "macOS arm64 binary for @jbrejner/cli", + "license": "MIT", + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "files": [ + "bin/" + ], + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} \ No newline at end of file diff --git a/npm/cli-darwin-x64/.npmrc b/npm/cli-darwin-x64/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/cli-darwin-x64/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-darwin-x64/README.md b/npm/cli-darwin-x64/README.md new file mode 100644 index 000000000..50ec71e1b --- /dev/null +++ b/npm/cli-darwin-x64/README.md @@ -0,0 +1 @@ +This is the macOS 64-bit binary for the Kosli CLI. See https://github.com/kosli-dev/cli for details. diff --git a/npm/cli-darwin-x64/package.json b/npm/cli-darwin-x64/package.json new file mode 100644 index 000000000..a33355611 --- /dev/null +++ b/npm/cli-darwin-x64/package.json @@ -0,0 +1,19 @@ +{ + "name": "@jbrejner/cli-darwin-x64", + "version": "0.0.0", + "description": "macOS x64 binary for @jbrejner/cli", + "license": "MIT", + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "files": [ + "bin/" + ], + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} \ No newline at end of file diff --git a/npm/cli-linux-arm/.npmrc b/npm/cli-linux-arm/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/cli-linux-arm/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-linux-arm/README.md b/npm/cli-linux-arm/README.md new file mode 100644 index 000000000..2bcadcc0b --- /dev/null +++ b/npm/cli-linux-arm/README.md @@ -0,0 +1 @@ +This is the Linux ARM 32-bit binary for the Kosli CLI. See https://github.com/kosli-dev/cli for details. diff --git a/npm/cli-linux-arm/package.json b/npm/cli-linux-arm/package.json new file mode 100644 index 000000000..d3e282ec2 --- /dev/null +++ b/npm/cli-linux-arm/package.json @@ -0,0 +1,19 @@ +{ + "name": "@jbrejner/cli-linux-arm", + "version": "0.0.0", + "description": "Linux arm binary for @jbrejner/cli", + "license": "MIT", + "os": [ + "linux" + ], + "cpu": [ + "arm" + ], + "files": [ + "bin/" + ], + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} diff --git a/npm/cli-linux-arm64/.npmrc b/npm/cli-linux-arm64/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/cli-linux-arm64/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-linux-arm64/README.md b/npm/cli-linux-arm64/README.md new file mode 100644 index 000000000..cbe44264c --- /dev/null +++ b/npm/cli-linux-arm64/README.md @@ -0,0 +1 @@ +This is the Linux ARM 64-bit binary for the Kosli CLI. See https://github.com/kosli-dev/cli for details. diff --git a/npm/cli-linux-arm64/package.json b/npm/cli-linux-arm64/package.json new file mode 100644 index 000000000..960e33ebb --- /dev/null +++ b/npm/cli-linux-arm64/package.json @@ -0,0 +1,19 @@ +{ + "name": "@jbrejner/cli-linux-arm64", + "version": "0.0.0", + "description": "Linux arm64 binary for @jbrejner/cli", + "license": "MIT", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "files": [ + "bin/" + ], + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} \ No newline at end of file diff --git a/npm/cli-linux-x64/.npmrc b/npm/cli-linux-x64/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/cli-linux-x64/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-linux-x64/README.md b/npm/cli-linux-x64/README.md new file mode 100644 index 000000000..d9e5df161 --- /dev/null +++ b/npm/cli-linux-x64/README.md @@ -0,0 +1 @@ +This is the Linux 64-bit binary for the Kosli CLI. See https://github.com/kosli-dev/cli for details. diff --git a/npm/cli-linux-x64/package.json b/npm/cli-linux-x64/package.json new file mode 100644 index 000000000..95d051225 --- /dev/null +++ b/npm/cli-linux-x64/package.json @@ -0,0 +1,19 @@ +{ + "name": "@jbrejner/cli-linux-x64", + "version": "0.0.0", + "description": "Linux x64 binary for @jbrejner/cli", + "license": "MIT", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "files": [ + "bin/" + ], + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} \ No newline at end of file diff --git a/npm/cli-win32-arm64/.npmrc b/npm/cli-win32-arm64/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/cli-win32-arm64/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-win32-arm64/README.md b/npm/cli-win32-arm64/README.md new file mode 100644 index 000000000..60b15f5f1 --- /dev/null +++ b/npm/cli-win32-arm64/README.md @@ -0,0 +1 @@ +This is the Windows ARM 64-bit binary for the Kosli CLI. See https://github.com/kosli-dev/cli for details. diff --git a/npm/cli-win32-arm64/package.json b/npm/cli-win32-arm64/package.json new file mode 100644 index 000000000..a6dd4fd7e --- /dev/null +++ b/npm/cli-win32-arm64/package.json @@ -0,0 +1,19 @@ +{ + "name": "@jbrejner/cli-win32-arm64", + "version": "0.0.0", + "description": "Windows arm64 binary for @jbrejner/cli", + "license": "MIT", + "os": [ + "win32" + ], + "cpu": [ + "arm64" + ], + "files": [ + "bin/" + ], + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} \ No newline at end of file diff --git a/npm/cli-win32-x64/.npmrc b/npm/cli-win32-x64/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/cli-win32-x64/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-win32-x64/README.md b/npm/cli-win32-x64/README.md new file mode 100644 index 000000000..c4b7bc5be --- /dev/null +++ b/npm/cli-win32-x64/README.md @@ -0,0 +1 @@ +This is the Windows 64-bit binary for the Kosli CLI. See https://github.com/kosli-dev/cli for details. diff --git a/npm/cli-win32-x64/package.json b/npm/cli-win32-x64/package.json new file mode 100644 index 000000000..87a5aab13 --- /dev/null +++ b/npm/cli-win32-x64/package.json @@ -0,0 +1,19 @@ +{ + "name": "@jbrejner/cli-win32-x64", + "version": "0.0.0", + "description": "Windows x64 binary for @jbrejner/cli", + "license": "MIT", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "files": [ + "bin/" + ], + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} \ No newline at end of file diff --git a/npm/wrapper/.npmrc b/npm/wrapper/.npmrc new file mode 100644 index 000000000..983929573 --- /dev/null +++ b/npm/wrapper/.npmrc @@ -0,0 +1,2 @@ +@jbrejner:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/wrapper/install.js b/npm/wrapper/install.js new file mode 100644 index 000000000..5b257db52 --- /dev/null +++ b/npm/wrapper/install.js @@ -0,0 +1,54 @@ +"use strict"; + +// Postinstall script: validates that the platform binary was installed correctly. +// Runs after `npm install @jbrejner/cli`. + +const { execFileSync } = require("child_process"); +const path = require("path"); +const fs = require("fs"); + +const SUPPORTED = { + linux: { x64: true, arm64: true, arm: true }, + darwin: { x64: true, arm64: true }, + win32: { x64: true, arm64: true }, +}; + +const platform = process.platform; +const arch = process.arch; + +if (!SUPPORTED[platform] || !SUPPORTED[platform][arch]) { + // Not a supported platform — exit cleanly so npm install doesn't fail. + process.exit(0); +} + +const packageName = `@jbrejner/cli-${platform}-${arch}`; + +let binaryPath; +try { + const packageDir = path.dirname( + require.resolve(`${packageName}/package.json`) + ); + const binaryName = platform === "win32" ? "kosli.exe" : "kosli"; + binaryPath = path.join(packageDir, "bin", binaryName); +} catch (e) { + // Optional dependency was skipped (e.g. --no-optional). Warn but don't fail. + process.stderr.write( + `[kosli] Warning: ${packageName} is not installed.\n` + + `[kosli] The kosli binary will not be available.\n` + + `[kosli] Re-run without --no-optional to fix this.\n` + ); + process.exit(0); +} + +if (!fs.existsSync(binaryPath)) { + process.stderr.write(`[kosli] Warning: binary not found at ${binaryPath}\n`); + process.exit(0); +} + +try { + execFileSync(binaryPath, ["version"], { stdio: "ignore" }); +} catch (e) { + process.stderr.write( + `[kosli] Warning: binary validation failed: ${e.message}\n` + ); +} diff --git a/npm/wrapper/package.json b/npm/wrapper/package.json new file mode 100644 index 000000000..aa8c794ef --- /dev/null +++ b/npm/wrapper/package.json @@ -0,0 +1,45 @@ +{ + "name": "@jbrejner/cli", + "version": "0.0.0", + "description": "CLI client for reporting compliance events to https://kosli.com", + "license": "MIT", + "author": "Kosli Inc. ", + "homepage": "https://kosli.com", + "repository": { + "type": "git", + "url": "git+https://github.com/kosli-dev/cli.git" + }, + "bugs": { + "url": "https://github.com/kosli-dev/cli/issues" + }, + "keywords": [ + "cli", + "kosli", + "compliance", + "supply-chain", + "devops" + ], + "bin": { + "kosli": "bin/kosli" + }, + "files": [ + "bin/", + "install.js" + ], + "scripts": { + "postinstall": "node install.js" + }, + "optionalDependencies": { + "@jbrejner/cli-linux-x64": "0.0.0", + "@jbrejner/cli-linux-arm64": "0.0.0", + "@jbrejner/cli-linux-arm": "0.0.0", + "@jbrejner/cli-darwin-x64": "0.0.0", + "@jbrejner/cli-darwin-arm64": "0.0.0", + "@jbrejner/cli-win32-x64": "0.0.0", + "@jbrejner/cli-win32-arm64": "0.0.0" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com", + "access": "public" + } +} \ No newline at end of file diff --git a/scripts/npm-publish.sh b/scripts/npm-publish.sh new file mode 100755 index 000000000..9879bd437 --- /dev/null +++ b/scripts/npm-publish.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -e + +VERSION="$1" +if [ -z "$VERSION" ]; then + echo "Usage: $0 " + exit 1 +fi + +# Regex for stable: X.Y.Z (where X, Y, Z are numbers) +STABLE_REGEX="^[0-9]+\.[0-9]+\.[0-9]+$" + +# Regex for pre-release: X.Y.Z-TAG (where TAG starts with a hyphen) +PRE_REGEX="^[0-9]+\.[0-9]+\.[0-9]+-.*$" + +# Determine npm dist-tag: pre-release versions must not go to "latest" +if [[ $VERSION =~ $STABLE_REGEX ]]; then + echo "✅ '$VERSION' is a STABLE release." + NPM_TAG="latest" +elif [[ $VERSION =~ $PRE_REGEX ]]; then + echo "🧪 '$VERSION' is a PRE-RELEASE version." + NPM_TAG="snapshot" +else + echo "❌ '$VERSION' is not a valid SemVer format." + exit 1 +fi + +# Inject version into all platform package.json files +find npm -name package.json -exec sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" {} \; + +# Also update the optionalDependencies version references in the wrapper +sed -i "s/\(\"@jbrejner\/cli-[^\"]*\": \)\"[^\"]*\"/\1\"${VERSION}\"/g" npm/wrapper/package.json + +# Publish platform packages first (wrapper depends on them) +find npm -name package.json ! -path "npm/wrapper/*" | while read -r f; do + echo "Publishing $(basename "$(dirname "$f")")..." + (cd "$(dirname "$f")" && npm publish --tag "$NPM_TAG") +done + +# Publish wrapper last +echo "Publishing wrapper..." +(cd npm/wrapper && npm publish --tag "$NPM_TAG") From 15cbce65f89f4f1a0b66edf0833d3a46fd4bf4e1 Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Fri, 20 Mar 2026 12:31:34 +0000 Subject: [PATCH 2/9] npm-publish script supports conditional publishing. If gorelease in not in "Release" mode, npm packages are not pushed, so we can avoid multitudes of snapshot packages being pushed to public registry --- scripts/npm-publish.sh | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/scripts/npm-publish.sh b/scripts/npm-publish.sh index 9879bd437..df620e3a6 100755 --- a/scripts/npm-publish.sh +++ b/scripts/npm-publish.sh @@ -7,6 +7,13 @@ if [ -z "$VERSION" ]; then exit 1 fi +# When called from goreleaser, $2 is "true" if snapshot build +DRY_RUN=false +if [ "$2" == "true" ] || [ "$2" == "--dry-run" ]; then + echo "Running in DRY-RUN mode. Packages will be created but not published." + DRY_RUN=true +fi + # Regex for stable: X.Y.Z (where X, Y, Z are numbers) STABLE_REGEX="^[0-9]+\.[0-9]+\.[0-9]+$" @@ -31,12 +38,22 @@ find npm -name package.json -exec sed -i "s/\"version\": \"[^\"]*\"/\"version\": # Also update the optionalDependencies version references in the wrapper sed -i "s/\(\"@jbrejner\/cli-[^\"]*\": \)\"[^\"]*\"/\1\"${VERSION}\"/g" npm/wrapper/package.json -# Publish platform packages first (wrapper depends on them) +# Pack and optionally publish platform packages first (wrapper depends on them) find npm -name package.json ! -path "npm/wrapper/*" | while read -r f; do - echo "Publishing $(basename "$(dirname "$f")")..." - (cd "$(dirname "$f")" && npm publish --tag "$NPM_TAG") + PKG_DIR="$(dirname "$f")" + PKG_NAME="$(basename "$PKG_DIR")" + echo "Packing ${PKG_NAME}..." + (cd "$PKG_DIR" && npm pack) + if [ "$DRY_RUN" = false ]; then + echo "Publishing ${PKG_NAME}..." + (cd "$PKG_DIR" && npm publish --tag "$NPM_TAG") + fi done -# Publish wrapper last -echo "Publishing wrapper..." -(cd npm/wrapper && npm publish --tag "$NPM_TAG") +# Pack and optionally publish wrapper last +echo "Packing wrapper..." +(cd npm/wrapper && npm pack) +if [ "$DRY_RUN" = false ]; then + echo "Publishing wrapper..." + (cd npm/wrapper && npm publish --tag "$NPM_TAG") +fi From 63e353b87cd1759f9e8c1c46d03abf834022aae8 Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Fri, 20 Mar 2026 12:34:36 +0000 Subject: [PATCH 3/9] Create npm packages after goreleaser via a hook script The hook runs after. If goreaser is not in release mode, npm will build in dry-run mode, so the packages are not pushed to registry --- .goreleaser.yml | 64 ++++++++-------- npm/README.md | 120 +++++++++++++++++++++++++++--- npm/cli-darwin-arm64/.npmrc | 3 +- npm/cli-darwin-arm64/package.json | 2 +- npm/cli-darwin-x64/.npmrc | 3 +- npm/cli-darwin-x64/package.json | 2 +- npm/cli-linux-arm/.npmrc | 3 +- npm/cli-linux-arm/package.json | 2 +- npm/cli-linux-arm64/.npmrc | 3 +- npm/cli-linux-arm64/package.json | 2 +- npm/cli-linux-x64/.npmrc | 3 +- npm/cli-linux-x64/package.json | 2 +- npm/cli-win32-arm64/.npmrc | 3 +- npm/cli-win32-arm64/package.json | 2 +- npm/cli-win32-x64/.npmrc | 3 +- npm/cli-win32-x64/package.json | 2 +- npm/wrapper/.npmrc | 3 +- npm/wrapper/package.json | 14 ++-- 18 files changed, 163 insertions(+), 73 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 7f0b24108..60c0ea0ae 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -4,6 +4,7 @@ before: hooks: - go mod tidy - rm -rf npm/cli-*/bin + - find npm -name "*.tgz" -delete builds: - id: kosli binary: kosli @@ -51,55 +52,56 @@ archives: - goos: windows formats: [zip] - # docs for nfpm can be found here: https://goreleaser.com/customization/nfpm/ nfpms: - id: kosli - -# # You can change the file name of the package. -# # -# # Default:`{{ .PackageName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}` - -# # TODO Maybe enable the template again after we have a better understanding of how to use it. It seems to be working without it, but it would be nice to have more control over the file name. - -# # file_name_template: >- -# # {{ .ProjectName }}_ -# # {{- title .Os }}_ -# # {{- if eq .Arch "amd64" }}x86_64 -# # {{- else if eq .Arch "386" }}i386 -# # {{- else }}{{ .Arch }}{{ end }} + # You can change the file name of the package. + # + # Default:`{{ .PackageName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}` + file_name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} ids: - kosli -# vendor: Kosli Inc. -# homepage: https://kosli.com/ -# maintainer: Mike Long -# description: CLI client for reporting compliance events to https://kosli.com -# license: MIT + vendor: Kosli Inc. + homepage: https://kosli.com/ + maintainer: Mike Long + description: CLI client for reporting compliance events to https://kosli.com + license: MIT -# # Formats to be generated. -# formats: -# - deb -# - rpm + # Formats to be generated. + formats: + - deb + - rpm -# # Template to the path that the binaries should be installed. -# # Defaults to `/usr/bin`. -# bindir: /usr/bin + # Template to the path that the binaries should be installed. + # Defaults to `/usr/bin`. + bindir: /usr/bin -# # Section. -# section: misc + # Section. + section: misc -# # Priority. -# priority: optional + # Priority. + priority: optional # Contents to add to the package. # GoReleaser will automatically add the binaries. contents: # The src and dst attributes also supports name templates - - src: dist/{{ .ProjectName }}_{{ .Os }}_{{ if .Amd64 }}{{ .Arch }}_v1{{ else if .Arm }}{{ .Arch }}_6{{ else if eq .Arch "arm64" }}{{ .Arch }}_v8.0{{ else }}{{ .Arch }}{{ end }}/kosli + - src: dist/{{ .ProjectName }}_{{ .Os }}_{{ if .Amd64 }}{{ .Arch }}_v1{{ else if .Arm }}{{ .Arch }}_6{{ else }}{{ .Arch }}{{ end }}/kosli dst: /usr/local/bin/kosli +after: + hooks: + - cmd: bash scripts/npm-publish.sh "{{ .Version }}"{{ if or .IsSnapshot (not .IsRelease) }} --dry-run{{ end }} + # after hooks suppresses output by default. You need to add output: true to the hook to see the script's messages. + output: true + publishers: - name: fury.io # by specifying `packages` id here goreleaser will only use this publisher diff --git a/npm/README.md b/npm/README.md index 2bad31b28..bb6768f71 100644 --- a/npm/README.md +++ b/npm/README.md @@ -11,11 +11,26 @@ npm/ │ ├── bin/kosli # JS shim that detects the platform and runs the binary │ └── install.js # postinstall script that validates the binary ├── cli-linux-x64/ # @jbrejner/cli-linux-x64 +│ ├── package.json # declares os/cpu fields for platform filtering +│ └── bin/kosli # the native binary — see below ├── cli-linux-arm64/ # @jbrejner/cli-linux-arm64 +│ ├── package.json # declares os/cpu fields for platform filtering +│ └── bin/kosli # the native binary — see below +├── cli-linux-arm/ # @jbrejner/cli-linux-arm +│ ├── package.json # declares os/cpu fields for platform filtering +│ └── bin/kosli # the native binary — see below ├── cli-darwin-x64/ # @jbrejner/cli-darwin-x64 -└── cli-darwin-arm64/ # @jbrejner/cli-darwin-arm64 +│ ├── package.json # declares os/cpu fields for platform filtering +│ └── bin/kosli # the native binary — see below +├── cli-darwin-arm64/ # @jbrejner/cli-darwin-arm64 +│ ├── package.json # declares os/cpu fields for platform filtering +│ └── bin/kosli # the native binary — see below +└── cli-win32-x64/ # @jbrejner/cli-win32-x64 +│ ├── package.json # declares os/cpu fields for platform filtering +│ └── bin/kosli[.exe] # the native binary — see below +└── cli-win32-arm64/ # @jbrejner/cli-win32-arm64 ├── package.json # declares os/cpu fields for platform filtering - └── bin/kosli # the native binary — see below + └── bin/kosli[.exe] # the native binary — see below ``` ## How it works @@ -26,6 +41,12 @@ Users install a single package: npm install @jbrejner/cli ``` +or if using in continuous integration you can install globally: + +```sh +npm install -g @jbrejner/cli +``` + npm resolves the `optionalDependencies` declared in the wrapper's `package.json` and installs only the platform-specific package that matches the current OS and CPU architecture — all non-matching packages are silently skipped. The wrapper's `bin/kosli` JS shim then locates the binary inside the installed platform package and executes it. ## The `bin/` directories are populated by goreleaser @@ -35,32 +56,107 @@ The platform package `bin/` directories are **not committed to git**. They are p ```yaml hooks: post: - - cmd: bash -c 'ARCH="{{ .Arch }}"; [ "$ARCH" = "amd64" ] && ARCH="x64"; mkdir -p npm/cli-{{ .Os }}-${ARCH}/bin && cp "{{ .Path }}" npm/cli-{{ .Os }}-${ARCH}/bin/kosli && chmod +x npm/cli-{{ .Os }}-${ARCH}/bin/kosli' + - cmd: >- + bash -c ' + OS="{{ .Os }}"; + ARCH="{{ .Arch }}"; + [ "$OS" = "windows" ] && OS="win32"; + [ "$ARCH" = "amd64" ] && ARCH="x64"; + EXT=""; + [ "{{ .Os }}" = "windows" ] && EXT=".exe"; + mkdir -p npm/cli-${OS}-${ARCH}/bin && + cp "{{ .Path }}" npm/cli-${OS}-${ARCH}/bin/kosli${EXT} && + chmod +x npm/cli-${OS}-${ARCH}/bin/kosli${EXT}' ``` -This hook runs once per build target immediately after goreleaser compiles the binary. It maps goreleaser's architecture naming (`amd64` → `x64`, `arm64` → `arm64`) to the npm naming convention and copies the binary into the correct platform package directory. +This hook runs once per build target immediately after goreleaser compiles the binary. It applies the following naming conventions: + +| goreleaser | npm package dir | +|------------|-----------------| +| `linux` | `linux` | +| `darwin` | `darwin` | +| `windows` | `win32` | +| `amd64` | `x64` | +| `arm64` | `arm64` | +| `arm` | `arm` | + +Windows binaries are copied as `kosli.exe`; all others as `kosli`. The `windows/arm` combination is excluded from builds. + +The `before` hooks in `.goreleaser.yml` clean up stale artifacts before each build run: + +```yaml +before: + hooks: + - rm -rf npm/cli-*/bin + - find npm -name "*.tgz" -delete +``` ## Publishing -Platform packages must be published before the wrapper, since the wrapper's `optionalDependencies` references them. After a goreleaser build has populated the `bin/` directories: +Packages are published to the [npm public registry](https://registry.npmjs.org). Platform packages must be published before the wrapper, since the wrapper's `optionalDependencies` references them by version. After a goreleaser build has populated the `bin/` directories: ```sh # Publish platform packages first -(cd npm/cli-linux-x64 && npm publish) -(cd npm/cli-linux-arm64 && npm publish) -(cd npm/cli-darwin-x64 && npm publish) +(cd npm/cli-linux-x64 && npm publish) +(cd npm/cli-linux-arm64 && npm publish) +(cd npm/cli-linux-arm && npm publish) +(cd npm/cli-darwin-x64 && npm publish) (cd npm/cli-darwin-arm64 && npm publish) +(cd npm/cli-win32-x64 && npm publish) +(cd npm/cli-win32-arm64 && npm publish) # Then publish the wrapper (cd npm/wrapper && npm publish) ``` -Or as a one-liner that publishes platform packages first, then the wrapper: +Each package directory contains an `.npmrc` that sets the auth token: -```sh -find npm -name package.json ! -path "npm/wrapper/*" | while read f; do pushd "$(dirname "$f")" && npm publish; popd; done && pushd npm/wrapper && npm publish; popd ``` +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +``` + +## Automated Publishing with npm-publish.sh + +The `scripts/npm-publish.sh` script automates the npm packaging and publishing process. It injects the version into all `package.json` files, packs each package into a `.tgz`, and optionally publishes them. + +### Usage + +```bash +scripts/npm-publish.sh [--dry-run] +``` + +### Arguments + +- ``: Required. A SemVer string — either `X.Y.Z` (stable) or `X.Y.Z-TAG` (pre-release). +- `--dry-run` (optional second argument): Pack packages but skip publishing. + +### Behavior + +1. Injects `` into the `version` field of all `package.json` files. +2. Updates the `optionalDependencies` version references in `npm/wrapper/package.json` to match. +3. Runs `npm pack` on each platform package, then on the wrapper. +4. Unless `--dry-run` is set, runs `npm publish --tag ` on each package. + +The dist-tag is determined by the version format: + +| Version format | npm dist-tag | +|----------------|--------------| +| `X.Y.Z` | `latest` | +| `X.Y.Z-*` | `snapshot` | + +### Integration with GoReleaser + +GoReleaser calls this script automatically via the `after` hook once all platform binaries have been built and copied into the `bin/` directories: + +```yaml +after: + hooks: + - cmd: bash scripts/npm-publish.sh "{{ .Version }}" ... + output: true +``` + +The script output is surfaced in the goreleaser log (`output: true`). ## Versioning -All packages must share the same version number. When bumping the version, update it in all five `package.json` files — the four platform packages and the wrapper — as well as the `optionalDependencies` versions in `npm/wrapper/package.json`. +All packages share the same version number. When releasing, `npm-publish.sh` updates it automatically in all eight `package.json` files — the seven platform packages and the wrapper — as well as the `optionalDependencies` version pins in `npm/wrapper/package.json`. There is no need to edit these files manually. diff --git a/npm/cli-darwin-arm64/.npmrc b/npm/cli-darwin-arm64/.npmrc index 983929573..2762f8466 100644 --- a/npm/cli-darwin-arm64/.npmrc +++ b/npm/cli-darwin-arm64/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-darwin-arm64/package.json b/npm/cli-darwin-arm64/package.json index 60fef5904..aff827c5f 100644 --- a/npm/cli-darwin-arm64/package.json +++ b/npm/cli-darwin-arm64/package.json @@ -13,7 +13,7 @@ "bin/" ], "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } \ No newline at end of file diff --git a/npm/cli-darwin-x64/.npmrc b/npm/cli-darwin-x64/.npmrc index 983929573..2762f8466 100644 --- a/npm/cli-darwin-x64/.npmrc +++ b/npm/cli-darwin-x64/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-darwin-x64/package.json b/npm/cli-darwin-x64/package.json index a33355611..a651a4a82 100644 --- a/npm/cli-darwin-x64/package.json +++ b/npm/cli-darwin-x64/package.json @@ -13,7 +13,7 @@ "bin/" ], "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } \ No newline at end of file diff --git a/npm/cli-linux-arm/.npmrc b/npm/cli-linux-arm/.npmrc index 983929573..2762f8466 100644 --- a/npm/cli-linux-arm/.npmrc +++ b/npm/cli-linux-arm/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-linux-arm/package.json b/npm/cli-linux-arm/package.json index d3e282ec2..4ee0d3b73 100644 --- a/npm/cli-linux-arm/package.json +++ b/npm/cli-linux-arm/package.json @@ -13,7 +13,7 @@ "bin/" ], "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } diff --git a/npm/cli-linux-arm64/.npmrc b/npm/cli-linux-arm64/.npmrc index 983929573..2762f8466 100644 --- a/npm/cli-linux-arm64/.npmrc +++ b/npm/cli-linux-arm64/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-linux-arm64/package.json b/npm/cli-linux-arm64/package.json index 960e33ebb..784706c3b 100644 --- a/npm/cli-linux-arm64/package.json +++ b/npm/cli-linux-arm64/package.json @@ -13,7 +13,7 @@ "bin/" ], "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } \ No newline at end of file diff --git a/npm/cli-linux-x64/.npmrc b/npm/cli-linux-x64/.npmrc index 983929573..2762f8466 100644 --- a/npm/cli-linux-x64/.npmrc +++ b/npm/cli-linux-x64/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-linux-x64/package.json b/npm/cli-linux-x64/package.json index 95d051225..950a393f8 100644 --- a/npm/cli-linux-x64/package.json +++ b/npm/cli-linux-x64/package.json @@ -13,7 +13,7 @@ "bin/" ], "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } \ No newline at end of file diff --git a/npm/cli-win32-arm64/.npmrc b/npm/cli-win32-arm64/.npmrc index 983929573..2762f8466 100644 --- a/npm/cli-win32-arm64/.npmrc +++ b/npm/cli-win32-arm64/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-win32-arm64/package.json b/npm/cli-win32-arm64/package.json index a6dd4fd7e..4a43dd2c2 100644 --- a/npm/cli-win32-arm64/package.json +++ b/npm/cli-win32-arm64/package.json @@ -13,7 +13,7 @@ "bin/" ], "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } \ No newline at end of file diff --git a/npm/cli-win32-x64/.npmrc b/npm/cli-win32-x64/.npmrc index 983929573..2762f8466 100644 --- a/npm/cli-win32-x64/.npmrc +++ b/npm/cli-win32-x64/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/cli-win32-x64/package.json b/npm/cli-win32-x64/package.json index 87a5aab13..3f6943d4b 100644 --- a/npm/cli-win32-x64/package.json +++ b/npm/cli-win32-x64/package.json @@ -13,7 +13,7 @@ "bin/" ], "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } \ No newline at end of file diff --git a/npm/wrapper/.npmrc b/npm/wrapper/.npmrc index 983929573..2762f8466 100644 --- a/npm/wrapper/.npmrc +++ b/npm/wrapper/.npmrc @@ -1,2 +1 @@ -@jbrejner:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} diff --git a/npm/wrapper/package.json b/npm/wrapper/package.json index aa8c794ef..c62fbe913 100644 --- a/npm/wrapper/package.json +++ b/npm/wrapper/package.json @@ -30,16 +30,16 @@ "postinstall": "node install.js" }, "optionalDependencies": { - "@jbrejner/cli-linux-x64": "0.0.0", - "@jbrejner/cli-linux-arm64": "0.0.0", - "@jbrejner/cli-linux-arm": "0.0.0", - "@jbrejner/cli-darwin-x64": "0.0.0", "@jbrejner/cli-darwin-arm64": "0.0.0", - "@jbrejner/cli-win32-x64": "0.0.0", - "@jbrejner/cli-win32-arm64": "0.0.0" + "@jbrejner/cli-darwin-x64": "0.0.0", + "@jbrejner/cli-linux-arm": "0.0.0", + "@jbrejner/cli-linux-arm64": "0.0.0", + "@jbrejner/cli-linux-x64": "0.0.0", + "@jbrejner/cli-win32-arm64": "0.0.0", + "@jbrejner/cli-win32-x64": "0.0.0" }, "publishConfig": { - "registry": "https://npm.pkg.github.com", + "registry": "https://registry.npmjs.org", "access": "public" } } \ No newline at end of file From aef91bcafbd63e72ab13bbcc3defa1f12741b033 Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Fri, 20 Mar 2026 14:57:01 +0000 Subject: [PATCH 4/9] Update documentation with NPM installation instructions --- docs.kosli.com/content/getting_started/install.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs.kosli.com/content/getting_started/install.md b/docs.kosli.com/content/getting_started/install.md index d07e53400..504b06070 100644 --- a/docs.kosli.com/content/getting_started/install.md +++ b/docs.kosli.com/content/getting_started/install.md @@ -1,4 +1,4 @@ ---- + --- title: "Part 2: Install Kosli CLI" bookCollapseSection: false weight: 220 @@ -89,6 +89,15 @@ sudo mv kosli /usr/local/bin/kosli {{< /tab >}} +{{< tab "NPM" >}} +You can install Kosli CLI system-wide with `npm` from the default registry + +```shell {.command} +npm install -g @kosli/cli +``` + +{{< /tab >}} + {{< tab "From source" >}} You can build Kosli CLI from source by running: ```shell {.command} @@ -100,7 +109,6 @@ make build {{< /tabs >}} - ## Verifying the installation worked Run this command: From b45c7e6257b17d289e6ed1984f2f8066f66b6e2e Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Mon, 23 Mar 2026 12:56:17 +0000 Subject: [PATCH 5/9] Switch npm package scope to @kosli --- npm/README.md | 20 ++++++++++---------- npm/cli-darwin-arm64/package.json | 4 ++-- npm/cli-darwin-x64/package.json | 4 ++-- npm/cli-linux-arm/package.json | 4 ++-- npm/cli-linux-arm64/package.json | 4 ++-- npm/cli-linux-x64/package.json | 4 ++-- npm/cli-win32-arm64/package.json | 4 ++-- npm/cli-win32-x64/package.json | 4 ++-- npm/wrapper/install.js | 4 ++-- npm/wrapper/package.json | 16 ++++++++-------- scripts/npm-publish.sh | 2 +- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/npm/README.md b/npm/README.md index bb6768f71..016155733 100644 --- a/npm/README.md +++ b/npm/README.md @@ -6,29 +6,29 @@ This directory contains the npm package structure for distributing the Kosli CLI ``` npm/ -├── wrapper/ # @jbrejner/cli — the package users install +├── wrapper/ # @kosli/cli — the package users install │ ├── package.json # declares optionalDependencies for all platforms │ ├── bin/kosli # JS shim that detects the platform and runs the binary │ └── install.js # postinstall script that validates the binary -├── cli-linux-x64/ # @jbrejner/cli-linux-x64 +├── cli-linux-x64/ # @kosli/cli-linux-x64 │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli # the native binary — see below -├── cli-linux-arm64/ # @jbrejner/cli-linux-arm64 +├── cli-linux-arm64/ # @kosli/cli-linux-arm64 │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli # the native binary — see below -├── cli-linux-arm/ # @jbrejner/cli-linux-arm +├── cli-linux-arm/ # @kosli/cli-linux-arm │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli # the native binary — see below -├── cli-darwin-x64/ # @jbrejner/cli-darwin-x64 +├── cli-darwin-x64/ # @kosli/cli-darwin-x64 │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli # the native binary — see below -├── cli-darwin-arm64/ # @jbrejner/cli-darwin-arm64 +├── cli-darwin-arm64/ # @kosli/cli-darwin-arm64 │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli # the native binary — see below -└── cli-win32-x64/ # @jbrejner/cli-win32-x64 +└── cli-win32-x64/ # @kosli/cli-win32-x64 │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli[.exe] # the native binary — see below -└── cli-win32-arm64/ # @jbrejner/cli-win32-arm64 +└── cli-win32-arm64/ # @kosli/cli-win32-arm64 ├── package.json # declares os/cpu fields for platform filtering └── bin/kosli[.exe] # the native binary — see below ``` @@ -38,13 +38,13 @@ npm/ Users install a single package: ```sh -npm install @jbrejner/cli +npm install @kosli/cli ``` or if using in continuous integration you can install globally: ```sh -npm install -g @jbrejner/cli +npm install -g @kosli/cli ``` npm resolves the `optionalDependencies` declared in the wrapper's `package.json` and installs only the platform-specific package that matches the current OS and CPU architecture — all non-matching packages are silently skipped. The wrapper's `bin/kosli` JS shim then locates the binary inside the installed platform package and executes it. diff --git a/npm/cli-darwin-arm64/package.json b/npm/cli-darwin-arm64/package.json index aff827c5f..cc8708895 100644 --- a/npm/cli-darwin-arm64/package.json +++ b/npm/cli-darwin-arm64/package.json @@ -1,7 +1,7 @@ { - "name": "@jbrejner/cli-darwin-arm64", + "name": "@kosli/cli-darwin-arm64", "version": "0.0.0", - "description": "macOS arm64 binary for @jbrejner/cli", + "description": "macOS arm64 binary for @kosli/cli", "license": "MIT", "os": [ "darwin" diff --git a/npm/cli-darwin-x64/package.json b/npm/cli-darwin-x64/package.json index a651a4a82..d1c81a221 100644 --- a/npm/cli-darwin-x64/package.json +++ b/npm/cli-darwin-x64/package.json @@ -1,7 +1,7 @@ { - "name": "@jbrejner/cli-darwin-x64", + "name": "@kosli/cli-darwin-x64", "version": "0.0.0", - "description": "macOS x64 binary for @jbrejner/cli", + "description": "macOS x64 binary for @kosli/cli", "license": "MIT", "os": [ "darwin" diff --git a/npm/cli-linux-arm/package.json b/npm/cli-linux-arm/package.json index 4ee0d3b73..a62339dfc 100644 --- a/npm/cli-linux-arm/package.json +++ b/npm/cli-linux-arm/package.json @@ -1,7 +1,7 @@ { - "name": "@jbrejner/cli-linux-arm", + "name": "@kosli/cli-linux-arm", "version": "0.0.0", - "description": "Linux arm binary for @jbrejner/cli", + "description": "Linux arm binary for @kosli/cli", "license": "MIT", "os": [ "linux" diff --git a/npm/cli-linux-arm64/package.json b/npm/cli-linux-arm64/package.json index 784706c3b..f70151fb1 100644 --- a/npm/cli-linux-arm64/package.json +++ b/npm/cli-linux-arm64/package.json @@ -1,7 +1,7 @@ { - "name": "@jbrejner/cli-linux-arm64", + "name": "@kosli/cli-linux-arm64", "version": "0.0.0", - "description": "Linux arm64 binary for @jbrejner/cli", + "description": "Linux arm64 binary for @kosli/cli", "license": "MIT", "os": [ "linux" diff --git a/npm/cli-linux-x64/package.json b/npm/cli-linux-x64/package.json index 950a393f8..eb440270e 100644 --- a/npm/cli-linux-x64/package.json +++ b/npm/cli-linux-x64/package.json @@ -1,7 +1,7 @@ { - "name": "@jbrejner/cli-linux-x64", + "name": "@kosli/cli-linux-x64", "version": "0.0.0", - "description": "Linux x64 binary for @jbrejner/cli", + "description": "Linux x64 binary for @kosli/cli", "license": "MIT", "os": [ "linux" diff --git a/npm/cli-win32-arm64/package.json b/npm/cli-win32-arm64/package.json index 4a43dd2c2..6f65ff022 100644 --- a/npm/cli-win32-arm64/package.json +++ b/npm/cli-win32-arm64/package.json @@ -1,7 +1,7 @@ { - "name": "@jbrejner/cli-win32-arm64", + "name": "@kosli/cli-win32-arm64", "version": "0.0.0", - "description": "Windows arm64 binary for @jbrejner/cli", + "description": "Windows arm64 binary for @kosli/cli", "license": "MIT", "os": [ "win32" diff --git a/npm/cli-win32-x64/package.json b/npm/cli-win32-x64/package.json index 3f6943d4b..f1fb6feb4 100644 --- a/npm/cli-win32-x64/package.json +++ b/npm/cli-win32-x64/package.json @@ -1,7 +1,7 @@ { - "name": "@jbrejner/cli-win32-x64", + "name": "@kosli/cli-win32-x64", "version": "0.0.0", - "description": "Windows x64 binary for @jbrejner/cli", + "description": "Windows x64 binary for @kosli/cli", "license": "MIT", "os": [ "win32" diff --git a/npm/wrapper/install.js b/npm/wrapper/install.js index 5b257db52..807058d98 100644 --- a/npm/wrapper/install.js +++ b/npm/wrapper/install.js @@ -1,7 +1,7 @@ "use strict"; // Postinstall script: validates that the platform binary was installed correctly. -// Runs after `npm install @jbrejner/cli`. +// Runs after `npm install @kosli/cli`. const { execFileSync } = require("child_process"); const path = require("path"); @@ -21,7 +21,7 @@ if (!SUPPORTED[platform] || !SUPPORTED[platform][arch]) { process.exit(0); } -const packageName = `@jbrejner/cli-${platform}-${arch}`; +const packageName = `@kosli/cli-${platform}-${arch}`; let binaryPath; try { diff --git a/npm/wrapper/package.json b/npm/wrapper/package.json index c62fbe913..b729ff208 100644 --- a/npm/wrapper/package.json +++ b/npm/wrapper/package.json @@ -1,5 +1,5 @@ { - "name": "@jbrejner/cli", + "name": "@kosli/cli", "version": "0.0.0", "description": "CLI client for reporting compliance events to https://kosli.com", "license": "MIT", @@ -30,13 +30,13 @@ "postinstall": "node install.js" }, "optionalDependencies": { - "@jbrejner/cli-darwin-arm64": "0.0.0", - "@jbrejner/cli-darwin-x64": "0.0.0", - "@jbrejner/cli-linux-arm": "0.0.0", - "@jbrejner/cli-linux-arm64": "0.0.0", - "@jbrejner/cli-linux-x64": "0.0.0", - "@jbrejner/cli-win32-arm64": "0.0.0", - "@jbrejner/cli-win32-x64": "0.0.0" + "@kosli/cli-darwin-arm64": "0.0.0", + "@kosli/cli-darwin-x64": "0.0.0", + "@kosli/cli-linux-arm": "0.0.0", + "@kosli/cli-linux-arm64": "0.0.0", + "@kosli/cli-linux-x64": "0.0.0", + "@kosli/cli-win32-arm64": "0.0.0", + "@kosli/cli-win32-x64": "0.0.0" }, "publishConfig": { "registry": "https://registry.npmjs.org", diff --git a/scripts/npm-publish.sh b/scripts/npm-publish.sh index df620e3a6..d65f0f469 100755 --- a/scripts/npm-publish.sh +++ b/scripts/npm-publish.sh @@ -36,7 +36,7 @@ fi find npm -name package.json -exec sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" {} \; # Also update the optionalDependencies version references in the wrapper -sed -i "s/\(\"@jbrejner\/cli-[^\"]*\": \)\"[^\"]*\"/\1\"${VERSION}\"/g" npm/wrapper/package.json +sed -i "s/\(\"@kosli\/cli-[^\"]*\": \)\"[^\"]*\"/\1\"${VERSION}\"/g" npm/wrapper/package.json # Pack and optionally publish platform packages first (wrapper depends on them) find npm -name package.json ! -path "npm/wrapper/*" | while read -r f; do From ee516b121e4efb4478dc432eba36b7576f4a9975 Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Wed, 25 Mar 2026 18:25:42 +0000 Subject: [PATCH 6/9] Fix: Add the missing bin/kosli JS Shim --- .gitignore | 2 +- npm/README.md | 2 +- npm/cli-darwin-arm64/package.json | 1 + npm/cli-darwin-x64/package.json | 1 + npm/cli-linux-arm/package.json | 3 ++- npm/cli-linux-arm64/package.json | 1 + npm/cli-linux-x64/package.json | 1 + npm/cli-win32-arm64/package.json | 1 + npm/cli-win32-x64/package.json | 1 + npm/wrapper/bin/kosli | 32 +++++++++++++++++++++++++++++++ npm/wrapper/package.json | 2 +- 11 files changed, 43 insertions(+), 4 deletions(-) create mode 100755 npm/wrapper/bin/kosli diff --git a/.gitignore b/.gitignore index eb9181b1f..3bcee113c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ docs.kosli.com/content/client_reference/kosli* docs.kosli.com/public/ docs.kosli.com/.netlify npm/cli*/bin/* -npm/wrapper/bin/* +npm/*/kosli*.tgz *.tar.gz *~ /.idea diff --git a/npm/README.md b/npm/README.md index 016155733..8793c64a6 100644 --- a/npm/README.md +++ b/npm/README.md @@ -111,7 +111,7 @@ Packages are published to the [npm public registry](https://registry.npmjs.org). Each package directory contains an `.npmrc` that sets the auth token: -``` +```text //registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} ``` diff --git a/npm/cli-darwin-arm64/package.json b/npm/cli-darwin-arm64/package.json index cc8708895..d48cbf394 100644 --- a/npm/cli-darwin-arm64/package.json +++ b/npm/cli-darwin-arm64/package.json @@ -9,6 +9,7 @@ "cpu": [ "arm64" ], + "bin": {"kosli": "bin/kosli"}, "files": [ "bin/" ], diff --git a/npm/cli-darwin-x64/package.json b/npm/cli-darwin-x64/package.json index d1c81a221..a8b56200c 100644 --- a/npm/cli-darwin-x64/package.json +++ b/npm/cli-darwin-x64/package.json @@ -9,6 +9,7 @@ "cpu": [ "x64" ], + "bin": {"kosli": "bin/kosli"}, "files": [ "bin/" ], diff --git a/npm/cli-linux-arm/package.json b/npm/cli-linux-arm/package.json index a62339dfc..6c6b0d61a 100644 --- a/npm/cli-linux-arm/package.json +++ b/npm/cli-linux-arm/package.json @@ -9,6 +9,7 @@ "cpu": [ "arm" ], + "bin": {"kosli": "bin/kosli"}, "files": [ "bin/" ], @@ -16,4 +17,4 @@ "registry": "https://registry.npmjs.org", "access": "public" } -} +} \ No newline at end of file diff --git a/npm/cli-linux-arm64/package.json b/npm/cli-linux-arm64/package.json index f70151fb1..3d2b04516 100644 --- a/npm/cli-linux-arm64/package.json +++ b/npm/cli-linux-arm64/package.json @@ -9,6 +9,7 @@ "cpu": [ "arm64" ], + "bin": {"kosli": "bin/kosli"}, "files": [ "bin/" ], diff --git a/npm/cli-linux-x64/package.json b/npm/cli-linux-x64/package.json index eb440270e..29e8f51c5 100644 --- a/npm/cli-linux-x64/package.json +++ b/npm/cli-linux-x64/package.json @@ -9,6 +9,7 @@ "cpu": [ "x64" ], + "bin": {"kosli": "bin/kosli"}, "files": [ "bin/" ], diff --git a/npm/cli-win32-arm64/package.json b/npm/cli-win32-arm64/package.json index 6f65ff022..ad2ae567a 100644 --- a/npm/cli-win32-arm64/package.json +++ b/npm/cli-win32-arm64/package.json @@ -9,6 +9,7 @@ "cpu": [ "arm64" ], + "bin": {"kosli": "bin/kosli.exe"}, "files": [ "bin/" ], diff --git a/npm/cli-win32-x64/package.json b/npm/cli-win32-x64/package.json index f1fb6feb4..da12b0b5c 100644 --- a/npm/cli-win32-x64/package.json +++ b/npm/cli-win32-x64/package.json @@ -9,6 +9,7 @@ "cpu": [ "x64" ], + "bin": {"kosli": "bin/kosli.exe"}, "files": [ "bin/" ], diff --git a/npm/wrapper/bin/kosli b/npm/wrapper/bin/kosli new file mode 100755 index 000000000..c9d405e4c --- /dev/null +++ b/npm/wrapper/bin/kosli @@ -0,0 +1,32 @@ +#!/usr/bin/env node +"use strict"; + +const { spawnSync } = require("child_process"); +const path = require("path"); + +const platform = process.platform; +const arch = process.arch; + +const packageName = `@kosli/cli-${platform}-${arch}`; + +let binaryPath; +try { + const packageDir = path.dirname( + require.resolve(`${packageName}/package.json`) + ); + const binaryName = platform === "win32" ? "kosli.exe" : "kosli"; + binaryPath = path.join(packageDir, "bin", binaryName); +} catch (e) { + process.stderr.write( + `[kosli] Error: platform package ${packageName} is not installed.\n` + + `[kosli] Try reinstalling: npm install -g @kosli/cli\n` + ); + process.exit(1); +} + +const result = spawnSync(binaryPath, process.argv.slice(2), { stdio: "inherit" }); +if (result.error) { + process.stderr.write(`[kosli] Error: failed to run binary: ${result.error.message}\n`); + process.exit(1); +} +process.exit(result.status); diff --git a/npm/wrapper/package.json b/npm/wrapper/package.json index b729ff208..897621837 100644 --- a/npm/wrapper/package.json +++ b/npm/wrapper/package.json @@ -42,4 +42,4 @@ "registry": "https://registry.npmjs.org", "access": "public" } -} \ No newline at end of file +} From 2291d409102333f6a6883b30c0db5c1efbbc23ef Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Wed, 25 Mar 2026 18:30:36 +0000 Subject: [PATCH 7/9] Fix: Token Variable Mismatch --- npm/README.md | 2 +- npm/cli-darwin-arm64/.npmrc | 2 +- npm/cli-darwin-x64/.npmrc | 2 +- npm/cli-linux-arm/.npmrc | 2 +- npm/cli-linux-arm64/.npmrc | 2 +- npm/cli-linux-x64/.npmrc | 2 +- npm/cli-win32-arm64/.npmrc | 2 +- npm/cli-win32-x64/.npmrc | 2 +- npm/wrapper/.npmrc | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/npm/README.md b/npm/README.md index 8793c64a6..ee283bab8 100644 --- a/npm/README.md +++ b/npm/README.md @@ -112,7 +112,7 @@ Packages are published to the [npm public registry](https://registry.npmjs.org). Each package directory contains an `.npmrc` that sets the auth token: ```text -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} ``` ## Automated Publishing with npm-publish.sh diff --git a/npm/cli-darwin-arm64/.npmrc b/npm/cli-darwin-arm64/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/cli-darwin-arm64/.npmrc +++ b/npm/cli-darwin-arm64/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} diff --git a/npm/cli-darwin-x64/.npmrc b/npm/cli-darwin-x64/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/cli-darwin-x64/.npmrc +++ b/npm/cli-darwin-x64/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} diff --git a/npm/cli-linux-arm/.npmrc b/npm/cli-linux-arm/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/cli-linux-arm/.npmrc +++ b/npm/cli-linux-arm/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} diff --git a/npm/cli-linux-arm64/.npmrc b/npm/cli-linux-arm64/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/cli-linux-arm64/.npmrc +++ b/npm/cli-linux-arm64/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} diff --git a/npm/cli-linux-x64/.npmrc b/npm/cli-linux-x64/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/cli-linux-x64/.npmrc +++ b/npm/cli-linux-x64/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} diff --git a/npm/cli-win32-arm64/.npmrc b/npm/cli-win32-arm64/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/cli-win32-arm64/.npmrc +++ b/npm/cli-win32-arm64/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} diff --git a/npm/cli-win32-x64/.npmrc b/npm/cli-win32-x64/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/cli-win32-x64/.npmrc +++ b/npm/cli-win32-x64/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} diff --git a/npm/wrapper/.npmrc b/npm/wrapper/.npmrc index 2762f8466..ae643592e 100644 --- a/npm/wrapper/.npmrc +++ b/npm/wrapper/.npmrc @@ -1 +1 @@ -//registry.npmjs.org/:_authToken=${MY_LOCAL_NPM_TOKEN} +//registry.npmjs.org/:_authToken=${NPM_TOKEN} From 899f001e1ba1ecdf9057c8c14fafa6b7366d5f91 Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Wed, 25 Mar 2026 18:40:32 +0000 Subject: [PATCH 8/9] Fix: Silent Postinstall Failures --- npm/wrapper/bin/kosli | 16 +++++++++++++++- npm/wrapper/install.js | 16 ++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/npm/wrapper/bin/kosli b/npm/wrapper/bin/kosli index c9d405e4c..fb2ca85de 100755 --- a/npm/wrapper/bin/kosli +++ b/npm/wrapper/bin/kosli @@ -7,6 +7,20 @@ const path = require("path"); const platform = process.platform; const arch = process.arch; +const SUPPORTED = { + linux: { x64: true, arm64: true, arm: true }, + darwin: { x64: true, arm64: true }, + win32: { x64: true, arm64: true }, +}; + +if (!SUPPORTED[platform] || !SUPPORTED[platform][arch]) { + process.stderr.write( + `[kosli] Error: ${platform}/${arch} is not a supported platform.\n` + + `[kosli] See https://github.com/kosli-dev/cli for supported platforms.\n` + ); + process.exit(1); +} + const packageName = `@kosli/cli-${platform}-${arch}`; let binaryPath; @@ -29,4 +43,4 @@ if (result.error) { process.stderr.write(`[kosli] Error: failed to run binary: ${result.error.message}\n`); process.exit(1); } -process.exit(result.status); +process.exit(result.status ?? 1); diff --git a/npm/wrapper/install.js b/npm/wrapper/install.js index 807058d98..d364f69c2 100644 --- a/npm/wrapper/install.js +++ b/npm/wrapper/install.js @@ -17,7 +17,10 @@ const platform = process.platform; const arch = process.arch; if (!SUPPORTED[platform] || !SUPPORTED[platform][arch]) { - // Not a supported platform — exit cleanly so npm install doesn't fail. + process.stderr.write( + `[kosli] Note: ${platform}/${arch} is not a supported platform.\n` + + `[kosli] The kosli binary will not be available on this system.\n` + ); process.exit(0); } @@ -41,14 +44,19 @@ try { } if (!fs.existsSync(binaryPath)) { - process.stderr.write(`[kosli] Warning: binary not found at ${binaryPath}\n`); - process.exit(0); + process.stderr.write( + `[kosli] Error: binary not found at ${binaryPath}\n` + + `[kosli] Try reinstalling: npm install -g @kosli/cli\n` + ); + process.exit(1); } try { execFileSync(binaryPath, ["version"], { stdio: "ignore" }); } catch (e) { process.stderr.write( - `[kosli] Warning: binary validation failed: ${e.message}\n` + `[kosli] Error: binary validation failed: ${e.message}\n` + + `[kosli] Try reinstalling: npm install -g @kosli/cli\n` ); + process.exit(1); } From 796c478c822af31bfb9c2c00be90ec69c34136a5 Mon Sep 17 00:00:00 2001 From: Jens Brejner Date: Thu, 26 Mar 2026 09:55:03 +0100 Subject: [PATCH 9/9] Update npm/README.md Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- npm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/README.md b/npm/README.md index ee283bab8..b918d1ff4 100644 --- a/npm/README.md +++ b/npm/README.md @@ -25,7 +25,7 @@ npm/ ├── cli-darwin-arm64/ # @kosli/cli-darwin-arm64 │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli # the native binary — see below -└── cli-win32-x64/ # @kosli/cli-win32-x64 +├── cli-win32-x64/ # @kosli/cli-win32-x64 │ ├── package.json # declares os/cpu fields for platform filtering │ └── bin/kosli[.exe] # the native binary — see below └── cli-win32-arm64/ # @kosli/cli-win32-arm64