先日、同時実行数を制限しながら並行実行する関数を書きました。
便利関数を作ったら他のプロジェクトから参照したいですよね。 そこでパッケージレジストリに登録してみました。
正直コピペで実装で十分なのでは?という分量ですが、パッケージ公開の練習です。
ソースコードを準備する
まずは公開するソースコードを準備していきましょう。 ソースコードはGitHubで公開しました。
Denoの開発環境を整える
TypeScriptの開発環境を整えたいのですが、Node+TypeScriptの組み合わせはプロジェクトの立ち上げは意外と面倒です。 そこで今回は Deno を使ってみることにしました。 DenoはTypeScriptの実行ランタイムとして開発されており、特別な設定なしでTypeScriptを実行できます。 Brewでインストールしました。
brew install deno
僕は最近エディターには VS Code を使っているので、Deno用の拡張機能をインストールしました。
インストールしただけでは有効化されません。
ワークスペースの設定ファイル .vscode/settings.json
を編集して、明示的に有効化します。
{
"deno.enable": true,
"deno.lint": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "denoland.vscode-deno",
"[typescript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
}
開発環境が整ったらパッケージ本体のソースコードを書いていきます。
GitHub Actionsでテストを実行する
パッケージとして公開するのであれば、テストを書いて、CIを回しておきたいですよね。 Deno公式がセットアップするためのアクションを公開しているので、これを利用します。
あとは deno test
コマンドを実行するだけです。
on:
push:
pull_request:
jobs:
deno:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: test
run: |
deno test
カバレッジを計測する
--coverage
オプションをつけてテストを実行すると coverage
ディレクトリにカバレッジが出力されます。
カバレッジはJSONファイルで出力されるのですが、これは Deno 独自のものらしく、そのままでは他のサービスと連携できません。
以下のコマンドで、一般的な lcov 形式に変換できます。
deno coverage --lcov > coverage.lcov
カバレッジは Codecov にアップロードすることにしました。
テスト関連のワークフローをまとめると、次のようになります。
on:
push:
pull_request:
jobs:
deno:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: test
run: |
deno test --coverage
deno coverage --lcov > coverage.lcov
- name: upload coverage
uses: codecov/codecov-action@v4
with:
file: coverage.lcov
token: ${{ secrets.CODECOV_TOKEN }}
JSRにパッケージを公開する
今回は Deno のパッケージとして作成したので、 JSR に公開するのがお手軽です。 ここにアップロードしてみましょう。
Denoのパッケージを作成する
JSRはGitHubアカウントでサインアップ可能です。 サインアップしたら jsr.io/new からパッケージを作成しましょう。
Denoのパッケージをアップロードする
パッケージのメタ情報を deno.json
に記載します。
{
"name": "@shogo82148/limit-concurrency",
"version": "0.1.6",
"exports": {
".": "./limit-concurrency.ts"
}
}
あとは deno publish
を実行するだけです。
deno publish
ここでブラウザーが起動するので、案内にしたがって公開許可を行います。
一度ログインしていればパスワードの入力は必要ありません。
今まで .hogerc
にパスワードを書く方式しか知らなかったので、「時代は進化しているんだなぁ」と感動しました。
npmにパッケージを公開する
新興のレジストリも登場してきましたが、JavaScriptのレジストリと言ったら今も npm でしょう。 そういうわけで、 npm にも登録してみます。
Deno のパッケージを npm パッケージに変換する
npmはあくまでもJavaScriptのレジストリなので、TypeScriptのモジュールをアップロードするにはビルドが必要です。 denoland/dnt を使ってビルドしてみました。
- Deno + dntでCJS・ESMに対応したnpmパッケージを作ろう
- dnt — the easiest way to publish a hybrid npm module for ESM and CommonJS
- denoland/dnt
dnt用の設定ファイルを用意します。
// ex. scripts/build_npm.ts
import { build, emptyDir } from "@deno/dnt";
await emptyDir("./npm");
await build({
entryPoints: ["./limit-concurrency.ts"],
outDir: "./npm",
shims: {
deno: true,
},
package: {
// package.json properties
name: "@shogo82148/limit-concurrency",
version: Deno.args[0],
description: "Limit the concurrency of tasks.",
license: "MIT",
repository: {
type: "git",
url: "git+https://github.com/shogo82148/limit-concurrency.git",
},
bugs: {
url: "https://github.com/shogo82148/limit-concurrency/issues",
},
},
postBuild() {
// steps to run after building and before running the tests
Deno.copyFileSync("LICENSE", "npm/LICENSE");
Deno.copyFileSync("README.md", "npm/README.md");
},
});
このスクリプトを実行すると npm/
ディレクトリに npm パッケージが出力されます。
deno run -A scripts/build_npm.ts 0.1.0
npmのパッケージをアップロードする
あとは npm publish
を実行するだけです。
cd npm
npm publish --access public
ブラウザーが起動するので、案内にしたがって公開許可を行います。
GitHub Actionsでリリースを自動化する
せっかくなのでリリースも自動化しましょう。 タグを打ったら、自動的にJSRとnpmに公開するよう設定します。
GitHub Actionsでリリースを自動化する利点
もちろん、リリースの手間が減るというメリットはありますが、それだけではありません。
パッケージのページに
Built and signed on GitHub Actions
と書かれた バッジを表示できます 。
なんかカッコイイ!欲しい!
npmのアクセストークンを取得
npmの場合、公開のためにアクセストークンが必要です。 npmの設定から Access Token を発行し、GitHub の Secretsに保存しておきましょう。
GitHubからJSRへのアクセス許可を設定
JSRの場合、アクセストークンは不要です。 代わりに GitHub Actions からのアクセス許可が必要です。 パッケージの設定から許可を出しておきます。
GitHub Actions のOIDCを有効化する
パッケージのリリースにはOIDCが有効化されている必要があります。 リリースワークフローに以下の行を追加します。
permissions:
contents: read
id-token: write
publishスクリプトを書く
あとは GitHub Actions ワークフローの中で deno publish && npm publish
するだけです。
#!/bin/bash
ROOT=$(git rev-parse --show-toplevel)
set -euxo pipefail
cd "$ROOT"
# publish to jsr
deno publish
# npm publish
deno run -A scripts/build_npm.ts "$(jq -r .version deno.json)"
cd npm
npm publish --access public --provenance ## --provenance オプションがついているのがポイント
まとめ
npmとjsrに簡単なパッケージを公開してみました。 さらに、リリースフローの一部を GitHub Actions で自動化しました。
皆さんもぜひ「Built and signed on GitHub Actions」のバッジをゲットしてください。
コンピューターの森でラビットが舞う
パッケージを変換、夢中で追う
自動リリースの風に乗り
npmとJSRへ、夢を運ぶ
🌟コードの魔法、今広がる🛤✨by CodeRabbit
余談
Denoについて調べていたら、Denoの標準ライブラリに pooledMap という関数を見つけました。 AsyncIterableを受け取り、並列度を制限しながら並行実行するというもの。 これはまさに求めていたもの!
まあ、今回はパッケージ公開のお勉強だったのでヨシ!
参考
- Deno
- denoland/vscode_deno
- denoland/setup-deno
- 【GitHub Actions】DenoのカバレッジをCodecovで出す
- npmに公開していたパッケージをjsrにもpublishしてみた
- マルチランタイム時代のモダン JavaScript レジストリ JSR を使ってみる
- Deno + dntでCJS・ESMに対応したnpmパッケージを作ろう
- dnt — the easiest way to publish a hybrid npm module for ESM and CommonJS
- denoland/dnt
- npm パッケージに認証バッジを付けてもらった話 (npm Provenance を試す)
- Introducing npm package provenance
- TypeScriptで同時実行数を制限しながら並行実行する
- shogo82148/limit-concurrency - GitHub
- @shogo82148/limit-concurrency - jsr
- @shogo82148/limit-concurrency - npm
- @std/async > pooledMap