|
| 1 | +--- |
| 2 | +title: How to distribute Go binaries with NPM |
| 3 | +publishing_date: 2025-09-27 |
| 4 | +author: Clelia Astra Bertelli |
| 5 | +--- |
| 6 | + |
| 7 | +[NPM](https://npmjs.org) is a foundational piece of modern software development, since it's the registry (and package manager) that stores all the packages for JavaScript and Typescript world. |
| 8 | + |
| 9 | +Despite clearly being oriented toward the JS/TS world, NPM can also be used to distribute binaries from other languages, like Go, Rust or C/C++. |
| 10 | + |
| 11 | +In this article, I would like to walk you through how you can easily release a Go package and distribute it through npm, thanks to an extremely useful utility called [`go-npm`](https://github.com/Nelwhix/go-npm). |
| 12 | + |
| 13 | +To do so, we'll follow these steps: |
| 14 | + |
| 15 | +- create a very simple ‘hello world’ program in Go |
| 16 | +- create a `.goreleaser.yaml` file to configure [GoReleaser](https://goreleaser.com) so that we can easily carry out cross-platform builds |
| 17 | +- create a `package.json` to package and distribute the binaries |
| 18 | +- create a `.github/workflows/release.yaml` file to automate the release whenever we want to publish the package |
| 19 | + |
| 20 | +Without further ado, let's dive in! |
| 21 | + |
| 22 | +## 1. Create a Hello World in Go |
| 23 | + |
| 24 | +If you're interested in this tutorial, you probably already know Go, but, assuming you don't, let's create a very simple ‘hello world’ script that we will save under `main.go`: |
| 25 | + |
| 26 | +```go |
| 27 | +package main |
| 28 | + |
| 29 | +import "fmt" |
| 30 | + |
| 31 | +func main() { |
| 32 | + fmt.Println("Hello world!") |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +In order to build and run this Go script, you simply need to run: |
| 37 | + |
| 38 | +```bash |
| 39 | +go build . -o hello-world |
| 40 | +./hello-world |
| 41 | +``` |
| 42 | + |
| 43 | +And you will get a nice `Hello world` printed out on the console. |
| 44 | + |
| 45 | +## 2. Create a .goreleaser.yaml |
| 46 | + |
| 47 | +GoReleaser is an extremely useful utility to publish Go binaries on platforms such as GitHub. |
| 48 | + |
| 49 | +In order to run it, you simply need to add a `.goreleaser.yaml` file in the root directory of the Go program you want to build. |
| 50 | + |
| 51 | +Here is an example for our hello world: |
| 52 | + |
| 53 | +```yaml |
| 54 | +builds: |
| 55 | + - binary: hello-world |
| 56 | + goos: |
| 57 | + - windows |
| 58 | + - darwin |
| 59 | + - linux |
| 60 | + goarch: |
| 61 | + - amd64 |
| 62 | + - arm64 |
| 63 | +``` |
| 64 | +
|
| 65 | +As you can see, we specify: |
| 66 | +
|
| 67 | +- The name of the binary (`binary`) |
| 68 | +- The Operative Systems for which the binary will be built (`goos` , which in our case is an array of the most common OS, Windows, Dawin/macOS and Linux) |
| 69 | +- The processor architecture for the target operating system (`goarch`, which in our case is an array with the two major available architectures, AMD64 and ARM64) |
| 70 | + |
| 71 | +Once this is configured, you will be able to automate the builds of your binary effortlessly. |
| 72 | + |
| 73 | +## 3. Create a package.json |
| 74 | + |
| 75 | +`package.json` is the file that is used to specify package information for NPM packages. Specifically, in our `package.json` we will specify: |
| 76 | + |
| 77 | +- Name of the package and several other metadata useful for discovery on the NPM registry (like the source repository, the name of the author, keywords, where to submit issues…) |
| 78 | +- Dependencies, among which we will specify `go-npm`, which is crucial for building our package locally, once it has been pulled from NPM registry |
| 79 | +- A post-install script, which will install the Go binary on our system |
| 80 | +- The target Go binary to install from our GitHub repository (binary that we built and published with GoReleaser) |
| 81 | + |
| 82 | +Here is an example of the `package.json` file for our hello world project: |
| 83 | + |
| 84 | +```json |
| 85 | +{ |
| 86 | + "name": "@namespace/hello-world", // change @namespace to your actual namespace! |
| 87 | + "version": "0.1.0", |
| 88 | + "description": "My first Go project published on NPM!", |
| 89 | + "main": "index.js", |
| 90 | + "scripts": { |
| 91 | + "postinstall": "golang-npm install", |
| 92 | + "test": "echo \"Error: no test specified\" && exit 1" |
| 93 | + }, |
| 94 | + "repository": { |
| 95 | + "type": "git", |
| 96 | + "url": "git+https://github.com/your-name/hello-world.git" // change your-name to your actual GitHub username! |
| 97 | + }, |
| 98 | + "keywords": [ |
| 99 | + "hello world", |
| 100 | + "go", |
| 101 | + "npm", |
| 102 | + "tutorial" |
| 103 | + ], |
| 104 | + "author": "Your Name", |
| 105 | + "license": "MIT", |
| 106 | + "bugs": { |
| 107 | + "url": "https://github.com/your-name/hello-world/issues" |
| 108 | + }, |
| 109 | + "homepage": "https://github.com/Ayour-name/hello-world#readme", |
| 110 | + "dependencies": { |
| 111 | + "golang-npm": "^0.0.6" |
| 112 | + }, |
| 113 | + "goBinary": { |
| 114 | + "name": "hello-world", |
| 115 | + "path": "./bin", |
| 116 | + "url": "https://github.com/your-name/hello-world/releases/download/v{{version}}/git-push-blog_{{version}}_{{platform}}_{{arch}}.tar.gz" |
| 117 | + } |
| 118 | +} |
| 119 | +
|
| 120 | +``` |
| 121 | + |
| 122 | +## 4. Automate with a GitHub Action |
| 123 | + |
| 124 | +Once we have everything in place, we can automate the publishing with a GitHub Action that runs every time we push a tag to it. |
| 125 | + |
| 126 | +Here is the code, that you should save under `.github/workflows/release.yml`: |
| 127 | + |
| 128 | +```yaml |
| 129 | +name: Release |
| 130 | +on: |
| 131 | + push: |
| 132 | + tags: |
| 133 | + - "v*.*.*" |
| 134 | +
|
| 135 | +permissions: |
| 136 | + contents: read # for checkout |
| 137 | +
|
| 138 | +jobs: |
| 139 | + release: |
| 140 | + name: Release |
| 141 | + runs-on: ubuntu-latest |
| 142 | + permissions: |
| 143 | + contents: write # to be able to publish a GitHub release |
| 144 | + issues: write # to be able to comment on released issues |
| 145 | + pull-requests: write # to be able to comment on released pull requests |
| 146 | + id-token: write # to enable use of OIDC for npm provenance |
| 147 | + steps: |
| 148 | + - name: Checkout |
| 149 | + uses: actions/checkout@v4 |
| 150 | + with: |
| 151 | + fetch-depth: 0 |
| 152 | +
|
| 153 | + - name: Setup Node.js |
| 154 | + uses: actions/setup-node@v4 |
| 155 | + with: |
| 156 | + node-version: "lts/*" |
| 157 | +
|
| 158 | + - name: Install goreleaser |
| 159 | + run: npm i -g @goreleaser/goreleaser |
| 160 | +
|
| 161 | + - name: Release Go binary |
| 162 | + run: goreleaser release |
| 163 | + env: |
| 164 | + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 165 | +
|
| 166 | + - name: Setup npm authentication |
| 167 | + run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc |
| 168 | + env: |
| 169 | + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} |
| 170 | +
|
| 171 | + - name: Release NPM package |
| 172 | + env: |
| 173 | + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 174 | + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} |
| 175 | + run: npm publish --access public |
| 176 | +
|
| 177 | +``` |
| 178 | + |
| 179 | +As you can see, in this action we: |
| 180 | + |
| 181 | +- Install Node and use `npm` to install GoReleaser globally |
| 182 | +- Set up NPM authentication (for which you will need an NPM access token configured under `NPM_TOKEN` in your GitHub repository secrets) |
| 183 | +- Release the package to npm via `npm publish` (it is important to specify `--access public` if you do not have a paying account and thus do not have access to private packages) |
| 184 | + |
| 185 | +## 5. Test the Release Pipeline |
| 186 | + |
| 187 | +Once you are ready, create a GitHub repository under `your-name/hello-world` and push all the code you just wrote to the main branch. |
| 188 | + |
| 189 | +Once that is set, you can run: |
| 190 | + |
| 191 | +```bash |
| 192 | +git tag v0.1.0 |
| 193 | +git push origin v0.1.0 |
| 194 | +``` |
| 195 | + |
| 196 | +This way, GitHub will kick off the release action and, in a couple of minutes, you will be able to see the release on GitHub and the package on NPM. |
| 197 | + |
| 198 | +You can then test it by installing it with: |
| 199 | + |
| 200 | +```bash |
| 201 | +npm install @namespace/hello-world |
| 202 | +hello-world # the binary should be available globally |
| 203 | +``` |
| 204 | + |
| 205 | +And you’re done: you’ve successfully built, pushed and installed your first Go binary with NPM! |
0 commit comments