diff --git a/.changeset/analytics-registry-description.md b/.changeset/analytics-registry-description.md new file mode 100644 index 0000000..d5ed062 --- /dev/null +++ b/.changeset/analytics-registry-description.md @@ -0,0 +1,5 @@ +--- +"@eisbachcode/emdash-plugin-analytics": patch +--- + +Shortens the plugin description to fit the EmDash registry, which refuses a package description longer than 140 characters. It now reads: "Cloudflare Web Analytics on the EmDash dashboard and next to your content: traffic, top pages, referrers, countries and views per entry." diff --git a/.github/workflows/emdash-release.yml b/.github/workflows/emdash-release.yml new file mode 100644 index 0000000..4da836b --- /dev/null +++ b/.github/workflows/emdash-release.yml @@ -0,0 +1,173 @@ +name: "Publish EmDash plugins" + +on: + workflow_call: + inputs: + published-packages: + description: "Changesets Action published-packages JSON output" + required: true + type: string + workflow_dispatch: + inputs: + package: + description: "Plugin ID to publish" + required: true + type: string + +permissions: {} + +concurrency: + group: emdash-release-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + plan: + name: "Plan plugin releases" + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + selectors: ${{ steps.changesets.outputs.selectors || steps.manual.outputs.selectors }} + steps: + - name: "Check out repository" + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: "Set up pnpm" + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + + - name: "Set up Node.js" + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: 24 + + - name: "Plan Changesets plugin releases" + id: changesets + if: ${{ github.event_name == 'workflow_call' }} + shell: bash + env: + EMDASH_PUBLISHED_PACKAGES: ${{ inputs.published-packages }} + run: | + set -euo pipefail + pnpm dlx @emdash-cms/plugin-cli@0.12.0 release plan --published-packages "${EMDASH_PUBLISHED_PACKAGES}" --dir . + + - name: "Plan manual plugin release" + id: manual + if: ${{ github.event_name == 'workflow_dispatch' }} + shell: bash + env: + EMDASH_RELEASE_SELECTOR: ${{ inputs.package }} + run: | + set -euo pipefail + pnpm dlx @emdash-cms/plugin-cli@0.12.0 release plan --package "${EMDASH_RELEASE_SELECTOR}" --dir . + + # Build and publish run in separate jobs; this one gets no id-token. + build: + name: "Build ${{ matrix.selector }}" + needs: plan + if: ${{ needs.plan.outputs.selectors != '[]' }} + strategy: + fail-fast: false + matrix: + selector: ${{ fromJSON(needs.plan.outputs.selectors) }} + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: "Check repository visibility" + if: ${{ github.event.repository.visibility != 'public' }} + run: | + echo "::error title=Public repository required::EmDash releases currently require a public GitHub repository because private and internal repository attestations cannot yet be verified." + exit 1 + + - name: "Check out repository" + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + + - name: "Set up pnpm" + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + + - name: "Set up Node.js" + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: 24 + cache: pnpm + + - name: "Install dependencies" + run: pnpm install --frozen-lockfile + + - name: "Prepare plugin release" + id: prepare + shell: bash + env: + EMDASH_RELEASE_SELECTOR: "${{ matrix.selector }}" + run: | + set -euo pipefail + pnpm dlx @emdash-cms/plugin-cli@0.12.0 release prepare "${EMDASH_RELEASE_SELECTOR}" --dir . --out-dir .emdash-release + + - name: "Record the publisher" + shell: bash + env: + PUBLISHER_DID: ${{ steps.prepare.outputs.publisher-did }} + run: printf '%s\n' "${PUBLISHER_DID}" > .emdash-release/publisher-did + + - name: "Hand over the bundle" + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: emdash-release-${{ matrix.selector }} + path: | + ${{ steps.prepare.outputs.bundle-file }} + .emdash-release/publisher-did + if-no-files-found: error + retention-days: 1 + + publish: + name: "Publish ${{ matrix.selector }}" + needs: [plan, build] + if: ${{ needs.plan.outputs.selectors != '[]' }} + strategy: + fail-fast: false + matrix: + selector: ${{ fromJSON(needs.plan.outputs.selectors) }} + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # Required for the audience-bound GitHub OIDC token. + attestations: write # Required to sign build provenance. + steps: + - name: "Download the bundle" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: emdash-release-${{ matrix.selector }} + path: .emdash-release + + - name: "Read the bundle" + id: bundle + shell: bash + run: | + set -euo pipefail + shopt -s nullglob + files=(.emdash-release/*.tar.gz) + if [ "${#files[@]}" -ne 1 ]; then + echo "::error::expected one plugin bundle, found ${#files[@]}" + exit 1 + fi + echo "bundle-file=${files[0]}" >> "${GITHUB_OUTPUT}" + echo "publisher-did=$(cat .emdash-release/publisher-did)" >> "${GITHUB_OUTPUT}" + + - name: "Create build provenance" + id: attest + uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3 + with: + subject-path: ${{ steps.bundle.outputs.bundle-file }} + + - name: "Publish plugin" + uses: emdash-cms/emdash/apps/release-action@main # zizmor: ignore[unpinned-uses] -- configured by --action-ref + with: + service-url: https://releases.emdashcms.com + publisher-did: ${{ steps.bundle.outputs.publisher-did }} + bundle-file: ${{ steps.bundle.outputs.bundle-file }} + provenance-file: ${{ steps.attest.outputs.bundle-path }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c62560d..f36264d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,9 +9,10 @@ # The file name is part of that trust configuration on npmjs.com; renaming the # file breaks publishing until the setting is changed too. # -# Only the publish job may mint the OIDC token. It installs nothing from the -# lockfile's scripts and builds nothing: the pack job builds without any -# credential and hands over tarballs. +# Only the publish job may mint an OIDC token for npm. It installs nothing +# from the lockfile's scripts and builds nothing: the pack job builds without +# any credential and hands over tarballs. The publish job runs in the `npm` +# environment, which the trusted publisher on npmjs.com requires. name: release on: @@ -93,9 +94,13 @@ jobs: publish: needs: pack runs-on: ubuntu-latest + environment: npm permissions: contents: write id-token: write + outputs: + published: ${{ steps.changesets.outputs.published }} + published-packages: ${{ steps.changesets.outputs['published-packages'] }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -109,6 +114,20 @@ jobs: # .npmrc written by setup-node) and, for npm itself, 11.5.1 or later. - run: pnpm --version && npm --version - run: pnpm install --frozen-lockfile --ignore-scripts - - uses: changesets/action/publish@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 + - id: changesets + uses: changesets/action/publish@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 with: pack-dir-artifact-id: ${{ needs.pack.outputs.pack-dir-artifact-id }} + + # Publishes the same versions to the EmDash plugin registry. Only packages + # with an emdash-plugin.jsonc go there; the plan step skips the rest. + publish-emdash-plugins: + needs: publish + if: needs.publish.outputs.published == 'true' + uses: ./.github/workflows/emdash-release.yml + with: + published-packages: ${{ needs.publish.outputs['published-packages'] }} + permissions: + contents: read + id-token: write + attestations: write diff --git a/AGENTS.md b/AGENTS.md index 997ccb9..4f7060f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,6 +58,12 @@ changeset or not, so a public package would go out with its first merge to changeset. That first publish is manual (`pnpm publish` in the package): npm's trusted-publisher setting can only be added to a package that exists. +Listing images go in `images/` and are declared under +`release.artifacts.screenshots` in the manifest. Never use a `screenshots/` +folder: the registry bundle takes it whole and refuses a bundle over 256 KB +or an image over 128 KB. The registry also refuses a manifest `description` +over 140 graphemes, which `emdash-plugin validate` does not check. + Never name a package script `publish`, `version` or `prepare`: npm and pnpm run scripts with those names on their own during a publish or a version bump. `emdash-plugin init` generates `"publish": "emdash-plugin publish"`, @@ -67,6 +73,14 @@ is `registry:publish`, and `prepublishOnly` builds before any publish. `.github/workflows/release.yml` does the npm side: with changesets on main it opens a "Version Packages" pull request, and merging that publishes through npm trusted publishing. npm trusts that workflow by its file name, -so renaming it breaks publishing until the setting on npmjs.com changes too. The workspace -installs with pnpm 11; `allowBuilds` in `pnpm-workspace.yaml` lets esbuild +so renaming it breaks publishing until the setting on npmjs.com changes too. +After an npm publish it calls `emdash-release.yml`, generated by +`emdash-plugin release setup` and edited since, which publishes the same +versions of every package with an `emdash-plugin.jsonc` to the EmDash +registry. Its build and publish run in separate jobs; only the publish job +holds `id-token`, and it installs and builds nothing. The npm publish job +runs in the `npm` environment, which the npm trusted publisher requires. +Keep both. + +The workspace installs with pnpm 11; `allowBuilds` in `pnpm-workspace.yaml` lets esbuild and workerd run their install scripts, which the test host needs. diff --git a/packages/analytics/README.md b/packages/analytics/README.md index 1a322a1..3cfe9f0 100644 --- a/packages/analytics/README.md +++ b/packages/analytics/README.md @@ -19,14 +19,14 @@ A **Traffic** card on the dashboard: visits and page views for the last seven days against the week before, the five most viewed pages with the entry they belong to, and when the numbers were last synced. -The Traffic widget: visits and page views for the last 7 days with their change against the previous week, and the five most viewed pages with entry title, path and views +The Traffic widget: visits and page views for the last 7 days with their change against the previous week, and the five most viewed pages with entry title, path and views ## The Analytics page **Plugins → Analytics** in the admin sidebar, or "Open analytics" on the dashboard widget. Editors and admins see it. -The Analytics page over 30 days: visits and page views with their change, a daily chart, top entries with collection, path, views and visits, and tables of referrers and countries +The Analytics page over 30 days: visits and page views with their change, a daily chart, top entries with collection, path, views and visits, and tables of referrers and countries The page asks the provider live on every load, so it has numbers from the first minute after setup; it does not wait for the sync. It only ever asks @@ -67,13 +67,13 @@ views, so the right one can be copied into the settings. The check only reads; values are entered in the plugin's settings form. It makes one request to Cloudflare. -The setup check with every row OK: data source, site URL, content index with 8 entries matched, scheduled sync and last sync +The setup check with every row OK: data source, site URL, content index with 8 entries matched, scheduled sync and last sync ## Analytics per entry **Plugins → Analytics per entry**, or "Per entry" on the Analytics page. -Analytics per entry: every published entry with its collection, path, page views over 7 and 30 days and publish date, most viewed first +Analytics per entry: every published entry with its collection, path, page views over 7 and 30 days and publish date, most viewed first Every published entry with its page views over the last 7 and 30 days, today included, by collection or across all of them. Click a column header @@ -101,7 +101,7 @@ URL. ## The editor panel -The Analytics panel in the entry editor: page views over the last 7 and 30 days and the path they are counted at +The Analytics panel in the entry editor: page views over the last 7 and 30 days and the path they are counted at In the entry editor, the **Analytics** panel shows that entry's 7- and 30-day page views and the path they are counted at, and on a translated entry diff --git a/packages/analytics/emdash-plugin.jsonc b/packages/analytics/emdash-plugin.jsonc index dff7124..1e1eb00 100644 --- a/packages/analytics/emdash-plugin.jsonc +++ b/packages/analytics/emdash-plugin.jsonc @@ -19,11 +19,11 @@ "requires": { "env:emdash": ">=0.39.0" }, "artifacts": { "screenshots": [ - { "file": "screenshots/analytics-page.png", "lang": "en" }, - { "file": "screenshots/per-entry.png", "lang": "en" }, - { "file": "screenshots/widget.png", "lang": "en" }, - { "file": "screenshots/editor-panel.png", "lang": "en" }, - { "file": "screenshots/setup-check.png", "lang": "en" } + { "file": "images/analytics-page.png", "lang": "en" }, + { "file": "images/per-entry.png", "lang": "en" }, + { "file": "images/widget.png", "lang": "en" }, + { "file": "images/editor-panel.png", "lang": "en" }, + { "file": "images/setup-check.png", "lang": "en" } ] } }, @@ -34,7 +34,11 @@ // Registry search matches the description and the keywords, so every // provider this plugin supports is named in both. - "description": "Cloudflare Web Analytics on the EmDash dashboard and next to your content: visits, page views, top pages, referrers and countries, with per-entry performance mapped back to the entries that produced it.", + // + // The registry profile refuses a description over 140 graphemes. + // `emdash-plugin validate` accepts up to 1,024 characters, so the failure + // only shows at `release setup` or publish. + "description": "Cloudflare Web Analytics on the EmDash dashboard and next to your content: traffic, top pages, referrers, countries and views per entry.", "keywords": ["analytics", "cloudflare", "dashboard", "widget", "content"], // Trust contract. diff --git a/packages/analytics/screenshots/analytics-page.png b/packages/analytics/images/analytics-page.png similarity index 100% rename from packages/analytics/screenshots/analytics-page.png rename to packages/analytics/images/analytics-page.png diff --git a/packages/analytics/screenshots/editor-panel.png b/packages/analytics/images/editor-panel.png similarity index 100% rename from packages/analytics/screenshots/editor-panel.png rename to packages/analytics/images/editor-panel.png diff --git a/packages/analytics/screenshots/per-entry.png b/packages/analytics/images/per-entry.png similarity index 100% rename from packages/analytics/screenshots/per-entry.png rename to packages/analytics/images/per-entry.png diff --git a/packages/analytics/screenshots/setup-check.png b/packages/analytics/images/setup-check.png similarity index 100% rename from packages/analytics/screenshots/setup-check.png rename to packages/analytics/images/setup-check.png diff --git a/packages/analytics/screenshots/widget.png b/packages/analytics/images/widget.png similarity index 100% rename from packages/analytics/screenshots/widget.png rename to packages/analytics/images/widget.png