Skip to content

Commit

Permalink
ci(release/js): add GitHub Actions workflow for releasing js package
Browse files Browse the repository at this point in the history
  • Loading branch information
WSH032 committed Dec 22, 2024
1 parent e644ef0 commit 4b51cca
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 2 deletions.
105 changes: 105 additions & 0 deletions .github/workflows/publish-js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Publish javascript 🟢‌ package to npmjs 📦

on:
push:
tags:
# e.g., js/tauri-plugin-pytauri-api/v0.1.0
- js/*/v*

defaults:
run:
shell: bash

# NOTE: It's better not to use cache for release workflow.
jobs:
dry-run:
name: pnpm publish dry-run
runs-on: ubuntu-latest
outputs:
package: ${{ steps.dry-run.outputs.package }}
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"

# see: <https://github.com/pnpm/action-setup>
- name: Install pnpm
uses: pnpm/action-setup@v4
# see: <https://github.com/actions/setup-node>
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json

- name: pnpm install
run: pnpm install --frozen-lockfile
- name: pnpm build frontend
run: pnpm -r build

- name: Dry-run release
id: dry-run
run: python ./release.py ${{ github.ref }}

publish:
needs:
- dry-run
name: Publish to npmjs 📦
runs-on: ubuntu-latest
environment:
name: npmjs
url: https://npmjs.com/package/${{ needs.dry-run.outputs.package }}
permissions:
# <https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions>
id-token: write # pnpm provenance
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"

# see: <https://github.com/pnpm/action-setup>
- name: Install pnpm
uses: pnpm/action-setup@v4
# see: <https://github.com/actions/setup-node>
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json
# issue: <https://github.com/pnpm/pnpm/issues/3141#issuecomment-1305563972>
registry-url: https://registry.npmjs.org

- name: pnpm install
run: pnpm install --frozen-lockfile
- name: pnpm build frontend
run: pnpm -r build

- name: release
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
# issue: <https://github.com/pnpm/pnpm/issues/6607#issuecomment-2075092418>
NPM_CONFIG_PROVENANCE: true
run: python ./release.py --no-dry-run ${{ github.ref }}

github-release:
needs:
- publish
name: Create GitHub release 🏷️
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for creating release
steps:

# TODO, FIXME: `pnpm pack` dont support workspaces for now,
# so we cant store the .tgz file for now.
# see: <https://github.com/pnpm/pnpm/issues/4351>

- name: Create release
uses: ncipollo/release-action@v1
with:
draft: true
body: ${{ github.event.head_commit.message }}
35 changes: 33 additions & 2 deletions release.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
import argparse
import asyncio
import sys
from argparse import ArgumentTypeError
from asyncio import create_subprocess_exec
from enum import Enum
from logging import basicConfig, getLogger
from os import getenv
from shutil import which
from typing import NamedTuple, NoReturn

logger = getLogger(__name__)
Expand All @@ -39,6 +41,12 @@ def parse(release_tag: str):
release_tag = release_tag.removeprefix("refs/tags/")

kind, package, version = release_tag.split("/")

if version[0] != "v":
raise ArgumentTypeError(
f"version number should start with 'v', got: {version}"
)

return ReleaseTag(Kind(kind), package, version[1:])

def write_to_github_output(self) -> None:
Expand Down Expand Up @@ -98,7 +106,7 @@ async def release_rs(package: str, no_dry_run: bool) -> int:


async def release_py(package: str, no_dry_run: bool) -> int:
# https://docs.astral.sh/uv/guides/publish/
# <https://docs.astral.sh/uv/guides/publish/>
args = ["build", "--package", package, "--no-sources", "--color", "always"]
if no_dry_run:
raise RuntimeError(
Expand All @@ -112,6 +120,29 @@ async def release_py(package: str, no_dry_run: bool) -> int:
return proc.returncode


async def release_js(package: str, no_dry_run: bool) -> int:
# <https://pnpm.io/cli/publish>

# NOTE: `--no-git-checks` is necessary,
# because we run publishing on tag, instead of on a branch (i.e. not `main`)
args = ["publish", "--filter", package, "--access", "public", "--no-git-checks"]

if not no_dry_run:
args.append("--dry-run")

# on windows, `pnpm` is actually `pnpm.cmd`,
# so we need to use `which` to find the actual program
program = which("pnpm")
if program is None:
raise FileNotFoundError("`pnpm` is not found in PATH")

proc = await create_subprocess_exec(program, *args)
await proc.wait()

assert proc.returncode is not None
return proc.returncode


if __name__ == "__main__":
basicConfig(level="INFO")

Expand All @@ -133,7 +164,7 @@ async def main() -> int:
elif release_tag.kind == Kind.PY:
return await release_py(release_tag.package, no_dry_run)
elif release_tag.kind == Kind.JS:
raise NotImplementedError()
return await release_js(release_tag.package, no_dry_run)
else:
_assert_never(release_tag.kind)

Expand Down

0 comments on commit 4b51cca

Please sign in to comment.