diff --git a/.forgejo/workflows/publish.yml b/.forgejo/workflows/publish.yml new file mode 100644 index 0000000..8e5b311 --- /dev/null +++ b/.forgejo/workflows/publish.yml @@ -0,0 +1,179 @@ +# ============================================================================= +# Forgejo Actions Workflow - TypeScript/npm Package Publishing +# ============================================================================= +# Standardized template for TypeScript packages published to Forgejo npm registry +# +# Features: +# - Configuration-driven (reads package.json `_` metadata) +# - Workspace dependency transformation (workspace:* → *) +# - Version existence check (prevents redundant publishes) +# - Graceful error handling (missing scripts don't break CI) +# - Self-signed cert support (internal Forgejo registry) +# +# Usage: +# 1. Copy to: /.forgejo/workflows/publish.yml +# 2. Ensure package.json has metadata: +# "_": { "registry": "forgejo", "publish": true, "build": true } +# 3. Commit and push to main/master +# +# Secrets required: +# - NPM_TOKEN: Forgejo npm registry token +# ============================================================================= + +name: Build and Publish + +on: + push: + branches: [main, master] + workflow_dispatch: + +env: + NODE_VERSION: '22' + PNPM_VERSION: '9' + +jobs: + build-and-publish: + runs-on: ubuntu-latest + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Setup pnpm + run: | + npm install -g pnpm@${{ env.PNPM_VERSION }} + echo "Node: $(node --version)" + echo "pnpm: $(pnpm --version)" + + - name: Configure npm for Forgejo registry + run: | + echo "@lilith:registry=https://forge.nasty.sh/api/packages/lilith/npm/" > .npmrc + echo "//forge.nasty.sh/api/packages/lilith/npm/:_authToken=\${NPM_TOKEN}" >> .npmrc + echo "strict-ssl=false" >> .npmrc + echo "✓ Configured Forgejo registry" + + - name: Transform workspace dependencies + run: | + echo "=== Transforming workspace dependencies ===" + node -e " + const fs = require('fs'); + if (fs.existsSync('package.json')) { + const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); + const transform = (deps) => { + if (!deps) return deps; + for (const [name, version] of Object.entries(deps)) { + if (version.startsWith('workspace:') || version.startsWith('file:')) { + console.log(' Transformed:', name, version, '→ *'); + deps[name] = '*'; + } + } + return deps; + }; + pkg.dependencies = transform(pkg.dependencies); + pkg.devDependencies = transform(pkg.devDependencies); + pkg.peerDependencies = transform(pkg.peerDependencies); + fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2)); + } + " + echo "✓ Workspace dependencies transformed" + + - name: Install dependencies + run: | + echo "=== Installing dependencies ===" + pnpm install --no-frozen-lockfile + echo "✓ Dependencies installed" + + - name: Validate + run: | + echo "=== Running validation ===" + # Run typecheck if available + if grep -q '"typecheck"' package.json 2>/dev/null; then + echo "Running typecheck..." + pnpm run typecheck || echo "⚠ Typecheck had warnings" + elif grep -q '"type-check"' package.json 2>/dev/null; then + echo "Running type-check..." + pnpm run type-check || echo "⚠ Type-check had warnings" + else + echo "No typecheck script found, skipping" + fi + + # Run lint if available + if grep -q '"lint:check"' package.json 2>/dev/null; then + echo "Running lint:check..." + pnpm run lint:check || echo "⚠ Lint had warnings" + elif grep -q '"lint"' package.json 2>/dev/null; then + echo "Running lint..." + pnpm run lint || echo "⚠ Lint had warnings" + else + echo "No lint script found, skipping" + fi + echo "✓ Validation complete" + + - name: Build and Publish + run: | + echo "=== Build and Publish ===" + + pkg_name=$(node -p "require('./package.json').name") + pkg_version=$(node -p "require('./package.json').version") + should_build=$(node -p "require('./package.json')._?.build === true") + should_publish=$(node -p "require('./package.json')._?.publish === true") + registry=$(node -p "require('./package.json')._?.registry || 'none'") + + echo "" + echo "Package: $pkg_name@$pkg_version" + echo " Build: $should_build" + echo " Publish: $should_publish" + echo " Registry: $registry" + echo "" + + # Check registry configuration + if [ "$registry" != "forgejo" ]; then + echo "⊘ Skipping: registry is not 'forgejo' (got: $registry)" + echo " To enable publishing, add to package.json:" + echo " \"_\": { \"registry\": \"forgejo\", \"publish\": true, \"build\": true }" + exit 0 + fi + + # Build if configured + if [ "$should_build" = "true" ]; then + echo "=== Building package ===" + if pnpm run build 2>&1; then + echo "✓ Build successful" + elif npx tsc 2>&1; then + echo "✓ Build successful (via tsc)" + else + echo "⚠ Build failed or no build command found" + fi + else + echo "⊘ Build skipped (build: false)" + fi + + # Publish if configured + if [ "$should_publish" = "true" ]; then + echo "" + echo "=== Checking if version already published ===" + if npm view "$pkg_name@$pkg_version" version 2>/dev/null; then + echo "✓ Version $pkg_version already published to registry" + echo " No action needed" + else + echo "→ Version $pkg_version not found in registry" + echo "" + echo "=== Publishing to Forgejo registry ===" + if npm publish --access public --no-git-checks 2>&1; then + echo "" + echo "✓ Successfully published $pkg_name@$pkg_version" + echo " Registry: https://forge.nasty.sh/lilith/-/packages/npm/$pkg_name" + else + echo "✗ Publish failed" + exit 1 + fi + fi + else + echo "⊘ Publish skipped (publish: false)" + fi