diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 0588772..16fd667 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,8 +1,9 @@ name: pages # Builds the @openhop/web bundle in fragment mode (no API server, flows in -# the URL hash) and deploys to GitHub Pages. Repo Settings → Pages must be -# set to "Source: GitHub Actions" for the deploy to succeed. +# the URL hash) and deploys to GitHub Pages. The workflow auto-enables Pages +# (with the GitHub Actions source) on first run via `enablement: true`, so +# fresh clones don't have to flip the toggle in repo Settings → Pages. on: push: @@ -33,9 +34,13 @@ jobs: - name: Build web (fragment mode) run: npm run build -w @openhop/web env: - VITE_BASE: /OpenHop/ + # Repo is naorsabag/openhop (lowercase). The Pages site URL — + # https://naorsabag.github.io/openhop/ — is the asset base path. + VITE_BASE: /openhop/ VITE_FRAGMENT_MODE: "1" - uses: actions/configure-pages@v5 + with: + enablement: true - uses: actions/upload-pages-artifact@v3 with: path: packages/web/dist diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..5ec1fbb --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,83 @@ +name: publish + +# Publishes @openhop/web and the openhop CLI to npm whenever the version in +# either package.json on master differs from what's already on the registry +# under the `beta` tag. Lets us ship a release just by merging a "chore: bump" +# PR — no manual `npm publish` step in someone's terminal. +# +# Web is published before CLI because the CLI's package.json depends on the +# web version. If only one bumped, only that one runs (the other is a no-op). + +on: + push: + branches: [master] + paths: + - "packages/web/package.json" + - "packages/cli/package.json" + - "packages/cli/src/index.ts" + - "packages/server/package.json" + workflow_dispatch: + +permissions: + contents: read + id-token: write + +concurrency: + group: publish + cancel-in-progress: false + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version: 22 + registry-url: https://registry.npmjs.org/ + cache: npm + + - run: npm ci + + - name: Resolve target versions + id: versions + run: | + web_version=$(node -p "require('./packages/web/package.json').version") + cli_version=$(node -p "require('./packages/cli/package.json').version") + echo "web=$web_version" >> "$GITHUB_OUTPUT" + echo "cli=$cli_version" >> "$GITHUB_OUTPUT" + + - name: Check if @openhop/web@${{ steps.versions.outputs.web }} is already on npm + id: web_status + run: | + if npm view "@openhop/web@${{ steps.versions.outputs.web }}" version > /dev/null 2>&1; then + echo "publish=false" >> "$GITHUB_OUTPUT" + echo "::notice::@openhop/web@${{ steps.versions.outputs.web }} already on registry, skipping" + else + echo "publish=true" >> "$GITHUB_OUTPUT" + fi + + - name: Check if openhop@${{ steps.versions.outputs.cli }} is already on npm + id: cli_status + run: | + if npm view "openhop@${{ steps.versions.outputs.cli }}" version > /dev/null 2>&1; then + echo "publish=false" >> "$GITHUB_OUTPUT" + echo "::notice::openhop@${{ steps.versions.outputs.cli }} already on registry, skipping" + else + echo "publish=true" >> "$GITHUB_OUTPUT" + fi + + # Publish web first — the CLI's package.json depends on the web version. + - name: Publish @openhop/web + if: steps.web_status.outputs.publish == 'true' + working-directory: packages/web + run: npm publish --tag beta --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish openhop CLI + if: steps.cli_status.outputs.publish == 'true' + working-directory: packages/cli + run: npm publish --tag beta --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/packages/cli/package.json b/packages/cli/package.json index 52919d4..e7d731a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -37,8 +37,7 @@ "test": "vitest run", "test:cli-contract": "vitest run __tests__/contract.test.ts", "build": "tsc --noEmit && esbuild src/index.ts --bundle --platform=node --format=esm --external:commander --external:yaml --external:zod --external:@openhop/server --external:@openhop/web --external:fastify --external:@fastify/static --external:@fastify/http-proxy --outfile=dist/index.js --banner:js='#!/usr/bin/env node'", - "prepack": "rm -rf skills && cp -r ../../skills ./skills", - "prepare": "npm run build" + "prepack": "npm run build && rm -rf skills && cp -r ../../skills ./skills" }, "dependencies": { "@fastify/http-proxy": "^11.4.4", diff --git a/packages/web/__tests__/share-url.test.ts b/packages/web/__tests__/share-url.test.ts index bbb8cb0..74c91e5 100644 --- a/packages/web/__tests__/share-url.test.ts +++ b/packages/web/__tests__/share-url.test.ts @@ -47,12 +47,12 @@ describe('share-url encode/decode', () => { expect(decodeFragment('!!!~~~')).toBeNull() }) - it('buildShareUrl uses Vite BASE_URL so dev (/) and Pages (/OpenHop/) both work', () => { + it('buildShareUrl uses Vite BASE_URL so dev (/) and Pages (/openhop/) both work', () => { const dev = buildShareUrl(SAMPLE_YAML, 'http://localhost:8788', '/') expect(dev).toMatch(/^http:\/\/localhost:8788\/#[A-Za-z0-9_+\-$.]+$/) - const pages = buildShareUrl(SAMPLE_YAML, 'https://naorsabag.github.io', '/OpenHop/') - expect(pages).toMatch(/^https:\/\/naorsabag\.github\.io\/OpenHop\/#[A-Za-z0-9_+\-$.]+$/) + const pages = buildShareUrl(SAMPLE_YAML, 'https://naorsabag.github.io', '/openhop/') + expect(pages).toMatch(/^https:\/\/naorsabag\.github\.io\/openhop\/#[A-Za-z0-9_+\-$.]+$/) // Hash content matches encodeFragment for both URLs (proves the BASE_URL // only affects the path, never the encoded payload). diff --git a/packages/web/src/lib/share-url.ts b/packages/web/src/lib/share-url.ts index f2acd95..8ffadc6 100644 --- a/packages/web/src/lib/share-url.ts +++ b/packages/web/src/lib/share-url.ts @@ -28,7 +28,7 @@ export function decodeFragment(fragment: string): string | null { /** * Build the full sharable URL for a flow's YAML. Uses Vite's `BASE_URL` so the - * Pages deploy at `/OpenHop/` and dev at `/` both produce correct links. + * Pages deploy at `/openhop/` and dev at `/` both produce correct links. */ export function buildShareUrl(yamlText: string, origin: string, baseUrl: string): string { const fragment = encodeFragment(yamlText)