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 Analytics page
**Plugins → Analytics** in the admin sidebar, or "Open analytics" on the
dashboard widget. Editors and admins see it.
-
+
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.
-
+
## Analytics per entry
**Plugins → Analytics per entry**, or "Per entry" on the Analytics page.
-
+
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
-
+
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