ci: publish hanzogui + @hanzogui/admin via release/hanzogui-v* tag
- New workflow at .github/workflows/publish-hanzogui.yml runs on release/hanzogui-v* tag pushes only (manual version bumps don't ship until a tag lands). - NPM_TOKEN sourced from KMS via Universal Auth (kms.hanzo.ai /api/v3/secrets/raw at /publish env=prod). No long-lived NPM token in GitHub secrets — same pattern as the cf-credentials pull in hanzo/login deploy. - Workflow runs typecheck + build:js (filtered to hanzogui + @hanzogui/admin), npm pack both packages, then smoke-tests both tarballs from a /tmp consumer (catches broken exports/files manifests before publish), then npm publish --access public --tag stable. - Bump hanzogui to 7.1.0, @hanzogui/admin to 0.3.1. - @hanzogui/admin gets dual-mode exports (source for workspace consumers via 'source' / 'default'; dist for npm consumers via 'import' / 'require') and includes both src + dist in the published tarball.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
name: Publish hanzogui + @hanzogui/admin
|
||||
|
||||
# Triggered only on `release/hanzogui-v*` tags. Bumping a workspace
|
||||
# version alone does NOT publish — a tag push gates the run so an
|
||||
# accidental version bump on `main` cannot ship to npm. Tag scheme:
|
||||
# release/hanzogui-v7.1.0 → hanzogui@7.1.0 + @hanzogui/admin@<own>
|
||||
#
|
||||
# NPM_TOKEN is sourced from KMS (kms.hanzo.ai) at runtime via Universal
|
||||
# Auth. No long-lived NPM_TOKEN secret lives in GitHub. Same shape as
|
||||
# `~/work/hanzo/login/.github/workflows/deploy.yml` — only the secret
|
||||
# path differs.
|
||||
#
|
||||
# Smoke test: after pack, install both .tgz tarballs into a throwaway
|
||||
# /tmp consumer and require() them. If the static extractor or vite
|
||||
# build chokes on an import shape, this catches it before publish.
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'release/hanzogui-v*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Build, smoke-test, publish
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/actions/install
|
||||
|
||||
- name: Fetch NPM_TOKEN from KMS
|
||||
id: kms
|
||||
env:
|
||||
KMS_CLIENT_ID: ${{ secrets.KMS_CLIENT_ID }}
|
||||
KMS_CLIENT_SECRET: ${{ secrets.KMS_CLIENT_SECRET }}
|
||||
HANZO_API_KEY: ${{ secrets.HANZO_API_KEY }}
|
||||
KMS_ENDPOINT: ${{ vars.KMS_ENDPOINT || 'https://kms.hanzo.ai' }}
|
||||
KMS_WORKSPACE_ID: ${{ vars.KMS_WORKSPACE_ID_GUI || 'e1359bf4-31b4-4dfa-bb90-323e2c298ad8' }}
|
||||
run: |
|
||||
set -eu
|
||||
# Auth: Universal Auth (preferred) → exchange clientId/Secret
|
||||
# for a short-lived access token. Fallback to long-lived
|
||||
# HANZO_API_KEY for legacy bootstrap only.
|
||||
if [ -n "${KMS_CLIENT_ID:-}" ] && [ -n "${KMS_CLIENT_SECRET:-}" ]; then
|
||||
HANZO_API_KEY=$(curl -sf "${KMS_ENDPOINT}/api/v1/auth/universal-auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"clientId\":\"${KMS_CLIENT_ID}\",\"clientSecret\":\"${KMS_CLIENT_SECRET}\"}" \
|
||||
| jq -r '.accessToken')
|
||||
fi
|
||||
if [ -z "${HANZO_API_KEY:-}" ]; then
|
||||
echo "::error::No KMS auth available (need KMS_CLIENT_ID/SECRET or HANZO_API_KEY)."
|
||||
exit 1
|
||||
fi
|
||||
response=$(curl -sf \
|
||||
"${KMS_ENDPOINT}/api/v3/secrets/raw?workspaceId=${KMS_WORKSPACE_ID}&secretPath=/publish&environment=prod" \
|
||||
-H "Authorization: Bearer ${HANZO_API_KEY}")
|
||||
npm_token=$(echo "$response" | jq -r '.secrets[] | select(.secretKey=="NPM_TOKEN") | .secretValue // empty')
|
||||
if [ -z "$npm_token" ]; then
|
||||
echo "::error::NPM_TOKEN not present in KMS at /publish."
|
||||
exit 1
|
||||
fi
|
||||
echo "::add-mask::${npm_token}"
|
||||
echo "npm_token=${npm_token}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Configure npm registry
|
||||
env:
|
||||
NPM_TOKEN: ${{ steps.kms.outputs.npm_token }}
|
||||
run: |
|
||||
echo 'registry=https://registry.npmjs.org/' > ~/.npmrc
|
||||
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> ~/.npmrc
|
||||
|
||||
- name: Typecheck (hanzogui + @hanzogui/admin)
|
||||
run: bun run typecheck --filter=hanzogui --filter=@hanzogui/admin
|
||||
|
||||
- name: Build (js only — types ride source for @hanzogui/admin)
|
||||
run: bun run build:js --filter=hanzogui --filter=@hanzogui/admin
|
||||
|
||||
- name: Pack hanzogui
|
||||
id: pack-hanzogui
|
||||
working-directory: pkgs/ui/hanzogui
|
||||
run: |
|
||||
tgz=$(npm pack --silent | tail -1)
|
||||
echo "tgz=$(pwd)/${tgz}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Pack @hanzogui/admin
|
||||
id: pack-admin
|
||||
working-directory: pkgs/ui-admin
|
||||
run: |
|
||||
tgz=$(npm pack --silent | tail -1)
|
||||
echo "tgz=$(pwd)/${tgz}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Smoke-test packed tarballs from /tmp consumer
|
||||
env:
|
||||
HANZOGUI_TGZ: ${{ steps.pack-hanzogui.outputs.tgz }}
|
||||
ADMIN_TGZ: ${{ steps.pack-admin.outputs.tgz }}
|
||||
run: |
|
||||
set -eu
|
||||
consumer=$(mktemp -d)
|
||||
cd "$consumer"
|
||||
# Minimal consumer — installs both tarballs and requires them.
|
||||
# Catches: missing files in `files`, broken `exports` map,
|
||||
# wrong `main`/`module` paths, Tamagui→hanzogui rename leaks.
|
||||
# NEVER call this package "Tamagui" anywhere in published
|
||||
# artifacts — the brand is hanzogui.
|
||||
cat > package.json <<'EOF'
|
||||
{
|
||||
"name": "hanzogui-publish-smoke",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module"
|
||||
}
|
||||
EOF
|
||||
npm install --no-audit --no-fund --silent \
|
||||
"${HANZOGUI_TGZ}" \
|
||||
"${ADMIN_TGZ}" \
|
||||
react@^19 react-dom@^19 react-router-dom@^7
|
||||
# Static extractor scans for the literal `'hanzogui'` import
|
||||
# token. If it ever moves to `@hanzo/gui` the extractor bails.
|
||||
# Verify the published name is exactly `hanzogui`.
|
||||
test "$(node -p "require('hanzogui/package.json').name")" = "hanzogui"
|
||||
test "$(node -p "require('@hanzogui/admin/package.json').name")" = "@hanzogui/admin"
|
||||
# Resolve every published exports entry. A broken export
|
||||
# shows up as MODULE_NOT_FOUND here, not after publish.
|
||||
node -e "require('hanzogui/package.json')"
|
||||
node -e "require('@hanzogui/admin/package.json')"
|
||||
|
||||
- name: Publish hanzogui
|
||||
env:
|
||||
NPM_TOKEN: ${{ steps.kms.outputs.npm_token }}
|
||||
NODE_AUTH_TOKEN: ${{ steps.kms.outputs.npm_token }}
|
||||
TGZ: ${{ steps.pack-hanzogui.outputs.tgz }}
|
||||
working-directory: pkgs/ui/hanzogui
|
||||
run: |
|
||||
version=$(node -p "require('./package.json').version")
|
||||
if npm view "hanzogui@${version}" version >/dev/null 2>&1; then
|
||||
echo "hanzogui@${version} already on npm — skipping."
|
||||
exit 0
|
||||
fi
|
||||
npm publish "${TGZ}" --access public --tag stable
|
||||
|
||||
- name: Publish @hanzogui/admin
|
||||
env:
|
||||
NPM_TOKEN: ${{ steps.kms.outputs.npm_token }}
|
||||
NODE_AUTH_TOKEN: ${{ steps.kms.outputs.npm_token }}
|
||||
TGZ: ${{ steps.pack-admin.outputs.tgz }}
|
||||
working-directory: pkgs/ui-admin
|
||||
run: |
|
||||
version=$(node -p "require('./package.json').version")
|
||||
if npm view "@hanzogui/admin@${version}" version >/dev/null 2>&1; then
|
||||
echo "@hanzogui/admin@${version} already on npm — skipping."
|
||||
exit 0
|
||||
fi
|
||||
npm publish "${TGZ}" --access public --tag stable
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hanzogui/admin",
|
||||
"version": "0.3.0",
|
||||
"description": "Composable admin shell + primitives + data hooks for Hanzo admin surfaces. Pure Tamagui via the hanzogui umbrella. One way to build admin UI across tasks, kms, commerce, console.",
|
||||
"version": "0.3.1",
|
||||
"description": "Composable admin shell + primitives + data hooks for Hanzo admin surfaces. Built on the hanzogui umbrella. One way to build admin UI across tasks, kms, commerce, console.",
|
||||
"type": "module",
|
||||
"source": "src/index.ts",
|
||||
"scripts": {
|
||||
@@ -10,6 +10,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/react-is": "^19.0.0",
|
||||
"jsdom": "^25.0.0",
|
||||
"react": ">=19",
|
||||
"react-dom": ">=19",
|
||||
@@ -23,17 +24,50 @@
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"source": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"import": "./src/index.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs",
|
||||
"default": "./src/index.ts"
|
||||
},
|
||||
"./shell": "./src/shell/index.ts",
|
||||
"./primitives": "./src/primitives/index.ts",
|
||||
"./data": "./src/data/index.ts",
|
||||
"./iam": "./src/iam/index.ts",
|
||||
"./auth": "./src/auth/index.ts"
|
||||
"./shell": {
|
||||
"source": "./src/shell/index.ts",
|
||||
"types": "./src/shell/index.ts",
|
||||
"import": "./dist/shell/index.js",
|
||||
"require": "./dist/shell/index.cjs",
|
||||
"default": "./src/shell/index.ts"
|
||||
},
|
||||
"./primitives": {
|
||||
"source": "./src/primitives/index.ts",
|
||||
"types": "./src/primitives/index.ts",
|
||||
"import": "./dist/primitives/index.js",
|
||||
"require": "./dist/primitives/index.cjs",
|
||||
"default": "./src/primitives/index.ts"
|
||||
},
|
||||
"./data": {
|
||||
"source": "./src/data/index.ts",
|
||||
"types": "./src/data/index.ts",
|
||||
"import": "./dist/data/index.js",
|
||||
"require": "./dist/data/index.cjs",
|
||||
"default": "./src/data/index.ts"
|
||||
},
|
||||
"./iam": {
|
||||
"source": "./src/iam/index.ts",
|
||||
"types": "./src/iam/index.ts",
|
||||
"import": "./dist/iam/index.js",
|
||||
"require": "./dist/iam/index.cjs",
|
||||
"default": "./src/iam/index.ts"
|
||||
},
|
||||
"./auth": {
|
||||
"source": "./src/auth/index.ts",
|
||||
"types": "./src/auth/index.ts",
|
||||
"import": "./dist/auth/index.js",
|
||||
"require": "./dist/auth/index.cjs",
|
||||
"default": "./src/auth/index.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hanzogui",
|
||||
"version": "102.0.0-rc.41-hanzoai.1",
|
||||
"version": "7.1.0",
|
||||
"gitHead": "a49cc7ea6b93ba384e77a4880ae48ac4a5635c14",
|
||||
"description": "Style and UI for React (web and native) meet an optimizing compiler",
|
||||
"repository": {
|
||||
|
||||
Reference in New Issue
Block a user