Skip to content

Commit

Permalink
Allow publishing binaries via NPM (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
dave4420 authored Mar 31, 2024
1 parent 449729b commit 8bb0964
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 46 deletions.
37 changes: 20 additions & 17 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
on:
push:
branches-ignore:
- 'trunk'
push:
branches-ignore:
- "trunk"

jobs:
tests:
name: 'Run tests'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version-file: './go.mod'
- uses: actions/setup-node@v3
with:
node-version-file: './.nvmrc'
cache: npm
- run: npm ci
- run: npm test
tests:
name: "Run tests"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: "Setup Go"
uses: actions/setup-go@v4
with:
go-version-file: "./go.mod"
- name: "Setup Node"
uses: actions/setup-node@v3
with:
node-version-file: "./.nvmrc"
cache: npm
- run: npm ci
- run: npm test
26 changes: 23 additions & 3 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Version to release'
description: "Version to release"
required: true

permissions:
Expand All @@ -22,13 +22,14 @@ jobs:
run: |
git config --global user.name '${{ github.actor }}'
git config --global user.email '${{ github.actor }}@users.noreply.github.com'
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v4
- name: Check version format
run: |
bin/check-version.pl
- uses: actions/setup-go@v4
with:
go-version-file: './go.mod'
go-version-file: "./go.mod"
- name: Build
run: |
bin/build-binaries.sh
Expand All @@ -42,3 +43,22 @@ jobs:
files: |
out/*
tag_name: ${{ env.BINARY_VERSION }}

publish-to-npm:
needs: release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: "Setup Node"
uses: actions/setup-node@v3
with:
node-version-file: "./.nvmrc"
cache: npm
- name: Download release files
run: |
gh release download ${{ github.event.inputs.version }} --dir out
- run: bin/build-npm-packages.sh
- name: "Setup npm"
run: "npm config set -- '//registry.npmjs.org/:_authToken' '${{ secrets.NPM_RELEASE_TOKEN }}'"
- run: bin/publish-npm-packages.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/npm
/out
/test
gen-elm-wrappers
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright 2023 Dave Hinton
Copyright 2023–2024 Dave Hinton

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ like to use for the keys of a `Dict`, or the elements of a `Set`.
Unfortunately, for both of these cases, your type has to be `comparable`,
and custom types cannot be `comparable`. What to do?

Solution: add some config to your `elm.json`, run `gen-elm-wrappers`,
Solution: add a `gen-elm-wrappers.json`, run `gen-elm-wrappers`,
and it will generate one or more Elm modules that wrap `Dict` or `Set`,
so that you can use them with your custom type.

## Installing

I haven’t wrapped this all up nicely in an NPM package yet.

For now, you need to run:
You can [download prebuilt binaries](https://github.com/dave4420/gen-elm-wrappers/releases).
Each file is a self-contained executable for the appropriate platform.
Rename it to `gen-elm-wrappers` and move it to somewhere on your `$PATH`.

- `brew install go`
Or to install from source:

- `brew install go` (or whatever the best way of installing Go on your laptop is)
- don’t worry, you don’t need to know Go to _use_ this
- `npm ci`
- `npm test`
Expand Down Expand Up @@ -122,7 +126,7 @@ I’m not looking for a job, no.
- Full stack or backend
- Permanent only (no contracting)
- IC only (no line management)
- Remote (UK timezone ± 1 hour) or on-site/hybrid (London/Medway); not willing to relocate
- Remote (UK timezone ± an hour or two) or on-site/hybrid (London/Medway); not willing to relocate
- I prefer to work with statically typed languages (e.g. Typescript, not plain Javascript)
- not blockchain (except for catching crims), not ad tech (unless it’s surveillance-free),
don’t really want to work for a hedge fund
Expand Down
1 change: 1 addition & 0 deletions bin/build-binaries.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ rm -rf out
mkdir out

list-binaries-to-build | while read GOOS GOARCH ; do
# keep in sync with build-npm-packages.sh
case $GOOS in
android|ios)
# get an error when building for android
Expand Down
124 changes: 124 additions & 0 deletions bin/build-npm-packages.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/bin/bash
set -euo pipefail

: ${BINARY_VERSION?}

rm -rf npm
mkdir npm

package_name=gen-elm-wrappers

# general approach taken from <https://sentry.engineering/blog/publishing-binaries-on-npm>

list-binaries-to-build() {
perl <targets.txt -ne 'print if not /^#/'
}

cp -r npm-main-template npm/$package_name
cp README.md npm/$package_name/README.md
printf '{' >> npm/$package_name/arch-packages.json
comma=''

list-binaries-to-build | while read GOOS GOARCH ; do
BINARY_EXT=''
# keep in sync with build-binaries.sh
case $GOOS in
aix)
process_platform=aix
;;
darwin)
process_platform=darwin
;;
freebsd)
process_platform=freebsd
;;
linux)
process_platform=linux
;;
openbsd)
process_platform=openbsd
;;
windows)
process_platform=win32
BINARY_EXT=.exe
;;
*)
continue
esac

case $GOARCH in
386)
process_arch=ia32
;;
amd64)
process_arch=x64
;;
arm)
process_arch=arm
;;
arm64)
process_arch=arm64
;;
s390x)
process_arch=s390
;;
loong64)
process_arch=loong64
;;
s390x)
process_arch=s390x
;;
*)
continue
esac

arch_package_name=@dave4420/$package_name-$process_platform-$process_arch
mkdir -p npm/$arch_package_name/bin
cat <<PACKAGE_JSON > npm/$arch_package_name/package.json
{
"name": "$arch_package_name",
"version": "$BINARY_VERSION",
"os": [ "$process_platform" ],
"cpu": [ "$process_arch" ]
}
PACKAGE_JSON
cat <<README > npm/$arch_package_name/README.md
You should not need to install this package directly.
Instead,
- either install [$package_name](https://www.npmjs.com/package/$package_name)
- or manually [download the binary for your architecture](https://github.com/dave4420/gen-elm-wrappers/releases/tag/$BINARY_VERSION)
README
cp \
out/gen-elm-wrappers-$GOOS-$GOARCH-$BINARY_VERSION$BINARY_EXT \
npm/$arch_package_name/bin/gen-elm-wrappers$BINARY_EXT
printf '%s\n "%s": "%s"' \
"$comma" \
"${process_platform}-${process_arch}" \
$arch_package_name \
>> npm/$package_name/arch-packages.json
comma=','
done

printf '\n}\n' >> npm/$package_name/arch-packages.json

node -e '
const pj = require("./package.json");
pj.version = process.env.BINARY_VERSION;
delete pj.private;
pj.scripts = {
"postinstall": "node ./install.js"
};
pj.bin = "bin/cli.js";
delete pj.devDependencies;
pj.optionalDependencies = {};
fs.readdirSync("npm").forEach((dir) => {
if (dir === "'$package_name'") {
return;
}
pj.optionalDependencies[dir] = process.env.BINARY_VERSION;
});
process.stdout.write(JSON.stringify(pj, null, 2));
process.stdout.write("\n");
' > npm/$package_name/package.json
19 changes: 19 additions & 0 deletions bin/publish-npm-packages.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
set -exuo pipefail
IFS=$'\n\t'

cd npm

for package in $(find . -type d -exec sh -c '[ -f {}/package.json ]' \; -print) ; do
# ordered so that the package with the shortest name is published last,
# after its dependencies
if ! npm publish $package ; then
printf 'Error code %d; hopefully already published?\n' $?
fi
npm view "$(
node -e "
const package = require('$package/package.json');
process.stdout.write(`${package.name}@${package.version}\n`);
"
)"
done
25 changes: 25 additions & 0 deletions npm-main-template/bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env node

// Lookup table for all platforms and binary distribution packages
const BINARY_DISTRIBUTION_PACKAGES = require("./arch-packages.json");

// Windows binaries end with .exe so we need to special case them.
const binaryName =
process.platform === "win32" ? "gen-elm-wrappers.exe" : "gen-elm-wrappers";

// Determine package name for this platform
const platformSpecificPackageName =
BINARY_DISTRIBUTION_PACKAGES[`${process.platform}-${process.arch}`];

function getBinaryPath() {
try {
// Resolving will fail if the optionalDependency was not installed
return require.resolve(`${platformSpecificPackageName}/bin/${binaryName}`);
} catch (e) {
return require("path").join(__dirname, "..", binaryName);
}
}

require("child_process").execFileSync(getBinaryPath(), process.argv.slice(2), {
stdio: "inherit",
});
Loading

0 comments on commit 8bb0964

Please sign in to comment.