ci: one publish trigger, one pull transport; drop the Zoo Makefile

publish fires on a version change in package.json, the same as every sibling —
the v* tag was a second place the version lived. Its test step no longer carries
continue-on-error, and it installs with pnpm, which the repo moved to.

sync-from-github pulls the mirror into the forge every 10m, fast-forward only.
Nothing carried refs here before, and the repo drifted 3 ahead and 8 behind.

The Makefile was Zoo's, verbatim: Zoo help text, @zooai/logo, docker targets
tagging zooai/logo from a Hanzo repo. It wrapped npm around what are now pnpm
scripts and offered a second way to publish. Deleted, with the stray zoo names
in .gitignore and the menu-bar preview.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-07-27 21:50:59 -07:00
co-authored by Hanzo Dev
parent 8940474e39
commit 7df1ef72fa
6 changed files with 116 additions and 208 deletions
-3
View File
@@ -57,9 +57,6 @@ tmp/
# But keep important files for GitHub display
!menu-bar-preview.html
!dist/zoo-logo.svg
!dist/zoo-logo-mono.svg
!dist/zoo-logo-menubar.svg
# Keep showcase images for README
!docs/assets/*.png
+38 -69
View File
@@ -1,82 +1,51 @@
name: Publish on Tag
name: publish
# Publishes @hanzo/logo when the version in package.json changes on main.
# A version already on the registry is skipped, so re-runs are safe.
on:
push:
tags:
- 'v*' # Match version tags (e.g., v1.0.4)
branches: [main]
paths: ['package.json']
workflow_dispatch:
permissions:
contents: read
jobs:
test:
name: Run Tests
runs-on: hanzo-build-linux-amd64
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build package
run: npm run build
- name: Run tests
run: npm test
continue-on-error: true # Don't fail if no tests configured yet
publish:
name: Publish to NPM
needs: test
runs-on: hanzo-build-linux-amd64
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build package
run: npm run build
- name: Configure npm authentication
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
node-version: 24
registry-url: https://registry.npmjs.org/
- name: Skip if already published
id: check
run: |
npm config set //registry.npmjs.org/:_authToken $NODE_AUTH_TOKEN
npm whoami
- name: Check and publish package
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
CURRENT_VERSION=$(node -p "require('./package.json').version")
PACKAGE_NAME=$(node -p "require('./package.json').name")
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Checking $PACKAGE_NAME@$CURRENT_VERSION"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Check if this version already exists on npm
if npm view "$PACKAGE_NAME@$CURRENT_VERSION" version 2>/dev/null; then
echo "⏭️ Already published - skipping"
echo "ALREADY_PUBLISHED=true" >> $GITHUB_ENV
NAME=$(jq -r .name package.json)
VER=$(jq -r .version package.json)
if curl -fsSL "https://registry.npmjs.org/$NAME/$VER" > /dev/null 2>&1; then
echo "$NAME@$VER already on registry; skipping"
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "🚀 Publishing to npm..."
npm publish --access public
echo "✅ Successfully published $PACKAGE_NAME@$CURRENT_VERSION"
echo "PUBLISHED=true" >> $GITHUB_ENV
echo "PACKAGE_VERSION=$CURRENT_VERSION" >> $GITHUB_ENV
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
# `test` typechecks and then builds — one command for both. The rendered
# images under dist/ are tracked but the compiled dist/*.js and dist/*.d.ts
# are gitignored, so publishing without this ships every icon and no entry
# point. corepack takes the pnpm version from packageManager.
- name: Test
if: steps.check.outputs.skip != 'true'
run: |
corepack enable
pnpm install --frozen-lockfile
pnpm test
- name: Publish
if: steps.check.outputs.skip != 'true'
# npm, not pnpm: pnpm publish adds git checks a CI checkout fails.
# --no-provenance: a token publish 422s without it.
run: npm publish --access public --no-provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+54
View File
@@ -0,0 +1,54 @@
name: sync from github
# Pulls github.com/hanzoai/logo into the forge, which is canonical.
#
# A pull, not a push: the forge runner reaches both ends, so nothing needs a
# credential aimed into the forge. This repo is public, so the fetch needs none
# at all. Fast-forward only — a divergence fails loudly instead of force-pushing.
on:
schedule:
- cron: '*/10 * * * *'
workflow_dispatch: {}
concurrency:
group: sync-from-github
cancel-in-progress: false
jobs:
main:
runs-on: hanzo-build-linux-amd64
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: true
- name: Fast-forward main from github.com
run: |
set -euo pipefail
git fetch --quiet "https://github.com/${{ github.repository }}.git" main
LOCAL="$(git rev-parse HEAD)"
REMOTE="$(git rev-parse FETCH_HEAD)"
if [ "$LOCAL" = "$REMOTE" ]; then
echo "in sync at $LOCAL"
exit 0
fi
if ! git merge-base --is-ancestor "$LOCAL" "$REMOTE"; then
echo "DIVERGED: $LOCAL is not an ancestor of $REMOTE; resolve by hand" >&2
exit 1
fi
echo "fast-forwarding $LOCAL -> $REMOTE"
git push origin "$REMOTE:refs/heads/main"
# The workflow token deliberately does not trigger other workflows, so
# synced commits would never gate. Dispatch each sibling; every one
# guards itself, so a dispatch with nothing to do is a no-op.
for wf in .hanzo/workflows/*.yml; do
name="$(basename "$wf")"
[ "$name" = "sync-from-github.yml" ] && continue
curl -fsS --max-time 20 -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ github.server_url }}/v1/repos/${{ github.repository }}/actions/workflows/$name/dispatches" \
-d '{"ref":"main"}' || echo "dispatch $name failed (non-fatal)"
done
+19 -22
View File
@@ -1,6 +1,6 @@
# @hanzo/logo
Official Hanzo logo package. Provides SVG logos as inline strings, React components, a pure-CSS brand motion layer, and a build pipeline for generating favicons, Apple touch icons, dock icons, and menu bar assets. Published as `@hanzo/logo` on npm (patch-tag `vX.Y.Z` → CI publishes).
Official Hanzo logo package. Provides SVG logos as inline strings, React components, a pure-CSS brand motion layer, and a build pipeline for generating favicons, Apple touch icons, dock icons, and menu bar assets. Published as `@hanzo/logo` on npm (bump `version` on `main` → CI publishes).
## Canon
The Hanzo mark is the **7-path shaded H** (67x67 viewBox): five body blocks + two
@@ -13,32 +13,29 @@ renders it. The flat 5-path H is only for tiny favicons (`getFaviconSVG`).
One way, and it runs on our own stack:
push -> github.com/hanzoai/logo (a mirror)
.github/workflows/sync.yml carries refs onward
-> git.hanzo.ai/hanzoai/logo CANONICAL
.hanzo/workflows/sync-from-github.yml pulls the mirror, every 10m
.hanzo/workflows/publish.yml publishes @hanzo/logo to npm
.hanzo/workflows/deploy.yml builds ghcr.io/hanzoai/logo
-> hanzoai/universe crs/logo.yaml names the tag that is live
-> hanzoai/operator reconciles the App
-> hanzoai/static behind hanzoai/ingress serves logo.hanzo.ai
**git.hanzo.ai is canonical; GitHub is a mirror.** `.github/workflows/` holds
exactly one file, `sync.yml`, and its only job is getting refs to the forge. Every
build, check and publish is a workflow under `.hanzo/workflows/`, which the forge
reads. `.hanzo/workflows` uses GitHub Actions syntax, so a workflow moves between
**git.hanzo.ai is canonical; GitHub is a mirror.** `.github/workflows/` is empty.
Every build, check, publish and the sync itself lives under `.hanzo/workflows/`,
which the forge reads. It uses GitHub Actions syntax, so a workflow moves between
the two by changing directory and nothing else.
`publish.yml` is the SOLE publisher of `@hanzo/logo`. A `v*` tag triggers it, a
version already on the registry is skipped, and it needs `NPM_TOKEN` as a forge
secret — one name, the same one every other Hanzo repo uses. Two loose ends it
inherited and still has:
The sync is a PULL: the forge runner reaches both ends, so nothing needs a
credential aimed into the forge, and a public repo needs none at all. It
fast-forwards only — a divergence fails loudly instead of force-pushing a side.
- its `test` job carries `continue-on-error: true` over `npm test` (typecheck +
build), so a type error does not stop the publish. Hardening that gate belongs
in its own change: flipping it here would let a pre-existing type error block
every release the moment it lands.
- it dropped a `softprops/action-gh-release` step on the way over. That step wrote
GitHub Releases, which the forge does not serve, and it ran AFTER a successful
publish — so on the forge it would have turned a shipped package red.
`publish.yml` is the SOLE publisher of `@hanzo/logo`. It fires when `version` in
`package.json` changes on `main`, skips a version already on the registry, and
runs `pnpm test` first — typecheck, then build. The rendered images under `dist/`
are tracked but the compiled `dist/*.js` and `dist/*.d.ts` are gitignored, so
publishing without that build ships every icon and no entry point. Needs
`NPM_TOKEN` as a forge secret.
### The site
@@ -86,9 +83,10 @@ dist/ # Rendered assets (tracked): svg/, favicon/, apple/, dock/, icons/
## Commands
```bash
make install / build / dev / generate / test / publish / clean # npm wrappers
npm run typecheck # tsc --noEmit
npm run build # clean + compile + build CLI + generate assets
pnpm install # install dev deps
pnpm typecheck # tsc --noEmit
pnpm build # clean + compile + build CLI + generate assets
pnpm test # typecheck + build (the release gate)
```
## Usage
@@ -105,6 +103,5 @@ import { MOTION_CSS, getMotionHTML } from '@hanzo/logo/motion' // Svelte/Tamagui
## Notes
- Variants: color (white fill + shade), mono (black fill + shade), white, menubar
(currentColor), favicon (simplified 5-path for 16-64px), animated (interactive)
- Release: bump patch in package.json (+ lockfile via `npm install`), tag `vX.Y.Z`,
push — `.github/workflows/publish-on-tag.yml` runs build + `npm publish`
- Release: bump the patch in `package.json`, merge to `main`. There is no tag.
- `CLAUDE.md`, `AGENTS.md`, `QWEN.md`, `GEMINI.md` are all symlinks to this file
-109
View File
@@ -1,109 +0,0 @@
# Zoo Logo Generator Makefile
# @zooai/logo - TypeScript logo generator for the Zoo ecosystem
.PHONY: help install build clean dev test publish generate-icons preview lint format typecheck all
# Default target
all: install build
# Help target - displays available commands
help:
@echo "Zoo Logo Generator - Make targets:"
@echo ""
@echo " make install - Install dependencies"
@echo " make build - Build TypeScript files"
@echo " make clean - Remove build artifacts and dependencies"
@echo " make dev - Run in development mode with watch"
@echo " make test - Run tests"
@echo " make lint - Run ESLint"
@echo " make format - Format code with Prettier"
@echo " make typecheck - Run TypeScript type checking"
@echo " make generate - Generate all logo variations and icons"
@echo " make preview - Open preview of generated logos"
@echo " make publish - Publish package to npm"
@echo " make all - Install and build (default)"
@echo ""
# Install dependencies
install:
npm install
# Build TypeScript files
build:
npm run build
# Clean build artifacts and dependencies
clean:
rm -rf dist node_modules package-lock.json
@echo "✓ Cleaned build artifacts and dependencies"
# Development mode with watch
dev:
npm run dev
# Run tests
test:
npm test
# Run linter
lint:
npm run lint
# Format code
format:
npm run format
# Type checking
typecheck:
npm run typecheck
# Generate all logo variations and icons
generate: build
npm run generate
# Preview generated logos (opens in default browser)
preview: generate
@if [ -f "menu-bar-preview.html" ]; then \
open menu-bar-preview.html; \
else \
echo "Generating preview..."; \
npm run preview; \
open menu-bar-preview.html; \
fi
# Publish to npm
publish: clean install build test
npm publish
# Watch for changes and rebuild
watch:
npm run watch
# Install global CLI
install-cli:
npm link
# Uninstall global CLI
uninstall-cli:
npm unlink
# Check for updates
check-updates:
npx npm-check-updates
# Update dependencies
update-deps:
npx npm-check-updates -u
npm install
# Generate documentation
docs:
npx typedoc src/index.ts
# Docker build (for CI/CD)
docker-build:
docker build -t zooai/logo .
# Run in Docker
docker-run:
docker run --rm -v $(PWD)/dist:/app/dist zooai/logo
+5 -5
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zoo Logo Menu Bar Preview</title>
<title>Hanzo Logo Menu Bar Preview</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
@@ -127,7 +127,7 @@
</style>
</head>
<body>
<h1>Zoo Logo Menu Bar Preview</h1>
<h1>Hanzo Logo Menu Bar Preview</h1>
<h2>See how the monochrome logo looks in the Mac menu bar</h2>
<div class="preview-section">
@@ -141,7 +141,7 @@
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
<path d="M3 7h10v2H3z M6 3h4v10H6z"/>
</svg>
<!-- Our Zoo icon -->
<!-- Our Hanzo icon -->
<svg id="menuIconDark" class="menu-icon" viewBox="98 102 812 820"></svg>
<!-- More system icons -->
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
@@ -161,7 +161,7 @@
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
<path d="M3 7h10v2H3z M6 3h4v10H6z"/>
</svg>
<!-- Our Zoo icon -->
<!-- Our Hanzo icon -->
<svg id="menuIconLight" class="menu-icon" viewBox="98 102 812 820"></svg>
<!-- More system icons -->
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
@@ -290,7 +290,7 @@
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'zoo-logo-mono.svg';
a.download = 'hanzo-logo-mono.svg';
a.click();
}