Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/analytics-registry-description.md
Original file line number Diff line number Diff line change
@@ -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."
173 changes: 173 additions & 0 deletions .github/workflows/emdash-release.yml
Original file line number Diff line number Diff line change
@@ -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 }}
27 changes: 23 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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
18 changes: 16 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"`,
Expand All @@ -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.
10 changes: 5 additions & 5 deletions packages/analytics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/screenshots/widget.png" width="554" alt="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">
<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/images/widget.png" width="554" alt="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.

<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/screenshots/analytics-page.png" width="820" alt="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">
<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/images/analytics-page.png" width="820" alt="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
Expand Down Expand Up @@ -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.

<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/screenshots/setup-check.png" width="820" alt="The setup check with every row OK: data source, site URL, content index with 8 entries matched, scheduled sync and last sync">
<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/images/setup-check.png" width="820" alt="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.

<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/screenshots/per-entry.png" width="820" alt="Analytics per entry: every published entry with its collection, path, page views over 7 and 30 days and publish date, most viewed first">
<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/images/per-entry.png" width="820" alt="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
Expand Down Expand Up @@ -101,7 +101,7 @@ URL.

## The editor panel

<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/screenshots/editor-panel.png" width="375" alt="The Analytics panel in the entry editor: page views over the last 7 and 30 days and the path they are counted at">
<img src="https://raw.githubusercontent.com/eisbachcode/emdash-plugins/main/packages/analytics/images/editor-panel.png" width="375" alt="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
Expand Down
16 changes: 10 additions & 6 deletions packages/analytics/emdash-plugin.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
]
}
},
Expand All @@ -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.
Expand Down
Loading