Skip to content

Update EmDash

This guide is for site operators: people who run a site built on EmDash and want it on a newer release. The updater covers every direct emdash and @emdash-cms/* dependency in the project. Changes to your own collections and fields are covered in Evolving a Deployed Site.

EmDash follows semantic versioning:

  • A patch release carries backwards-compatible fixes and small improvements.
  • A minor release carries backwards-compatible features.
  • A major release can require changes to an existing project. Its release entries identify affected projects and the required actions.

emdash and @emdash-cms/cloudflare are released together and share one version number. @emdash-cms/cloudflare depends on the exact matching emdash version, so update the two packages in one step. Plugin packages such as @emdash-cms/plugin-forms have their own version numbers and declare the minimum emdash version they need.

The releases page has one entry per package and version. upgrade-emdash fetches the changelog range for each direct package from its exact GitHub release tag. It removes generated dependency-bump entries, preserves the complete authored major, minor, and patch entries, and deduplicates entries repeated across packages.

Creating a site from a template copies the template’s files, such as layouts, components, styles, and astro.config.mjs, into your project. The updater changes direct EmDash dependencies and refreshes the EmDash agent skills. It leaves the site’s application and configuration files for the project owner or an agent to change from the upgrade work order. Later changes to a template apply to new sites only. To adopt one in an existing site, compare your files with the current version in the templates directory and copy the changes you want.

The updater changes project files and installed packages without connecting to the site’s database, so it does not require an upgrade-specific backup before it runs. After installation, .emdash/UPGRADE.md identifies any added core migrations.

Before starting or deploying the upgraded application, create a recovery point when the work order lists added migrations, emdash migrate --status reports an earlier pending migration, or a release entry requires a data-changing project action. Follow Backups and recovery for the configured database. EmDash’s JSON backup cannot restore a site.

The updater requires emdash 0.35.0 or later, the first release that reports its core migrations. For an older site, create a recovery point, update emdash and the other direct @emdash-cms/* packages to 0.35.0 with the project’s package manager, and review the package changelogs up to that release. Then run the updater for the rest of the update.

The updater reads packages from node_modules, so it does not support Yarn Plug’n’Play installs. For a Yarn project that uses Plug’n’Play, set nodeLinker: node-modules in .yarnrc.yml and run yarn install before running the updater.

Check the Node.js version on the machine that builds the site and, for a Node.js deployment, on the server. Getting Started lists the supported versions.

Run the updater from the site’s project directory. It reads the installed package versions and core migration identity, then resolves the selected npm dist-tag separately for every direct EmDash package. The default tag is latest; use --to next, --to beta, or another dist-tag to select a release channel. --to does not accept an exact version.

  1. Inspect the update without changing files.

    Terminal window
    npx upgrade-emdash@latest --dry-run

    The plan reports the exact target for each direct package and the authored changelog entries crossed by the update. The target migration delta is calculated after the packages are installed. Run npx upgrade-emdash@latest --json when another tool needs the pre-install plan as structured data.

  2. Apply the dependency and skill updates.

    Terminal window
    npx upgrade-emdash@latest

    The command preserves each dependency’s existing caret, tilde, or exact version style and runs the project’s package manager. A preview build URL, git source, or other range is replaced with a caret range on the release. For a catalog: dependency, the command updates the catalog entry in pnpm-workspace.yaml instead, so every workspace package that uses that entry moves to the release. The command stops for workspace:, local path, and npm: alias specifiers and explains what to change. Package versions can differ: each direct package receives the exact version published under the selected tag.

    It then runs npx --yes skills@1.7.0 add emdash-cms/skills -y in the project and compares the installed core migration identity with the identity captured before installation. It does not apply a migration.

  3. Open .emdash/UPGRADE.md and complete its project actions.

    The work order includes the filtered changelog entries, added core migration names, and the project’s exact build and migration commands. When the upgrade adds core migrations, it requires a verified recovery point before the upgraded application starts or deploys. The synced upgrading-emdash skill tells an agent where to find the file and how to complete it. The agent should assess every entry against the project rather than adopting unrelated new features.

  4. Build the site.

    Terminal window
    pnpm build

    The build writes the migration manifest for the installed version. If the build fails, see If the site breaks after an update.

  5. Start the site locally and open the admin at /_emdash/admin.

    Terminal window
    pnpm dev

    The EmDash integration generates emdash-env.d.ts when the dev server starts. Pending core migrations run on the first request. If this environment connects to a database with pending migrations, complete the work order’s backup requirement before opening the site.

Deploy the build the same way as any other change. The following command deploys a Cloudflare site; for a Node.js deployment, restart the server process with the new build.

Terminal window
pnpm wrangler deploy

With the default runtime migration mode, auto, the deployed site applies pending core migrations on its first request. A Cloudflare Worker leaves large content migrations to emdash migrate and answers with 503 until they are applied. Do not deploy a build with pending migrations until the required recovery point exists. To apply migrations before the new code receives traffic, and to verify the deployed database afterwards, follow Manage Core Database Migrations. Its emdash migrate --check command exits non-zero when the deployed database has pending or unknown migrations for the installed version.

After the deploy, open the admin, load at least one public page, edit and publish a disposable entry, and upload and retrieve a disposable media file. If the site uses scheduled tasks or sandboxed plugins, verify those paths too.

Most releases need nothing beyond the steps above. The entries below cover releases that change data EmDash already stored, or cannot repair it, and say when that needs an action from you.

Changed: reference fields bind to relations

Section titled “Changed: reference fields bind to relations”

A reference field used to hold the target entry’s ID in a column on its collection’s table, and named its target collection in a field option that the admin panel could not set.

A reference field is now an entry picker backed by a relation, and its links live outside the collection table. Updating binds each reference field that named a target collection to a new relation and copies the entry IDs in its column in as links, so the field becomes a picker with its selection intact. The column is left in place and the update deletes nothing.

A field is left unbound when it:

  • names no target collection, or names one that no longer exists
  • is marked searchable or indexed
  • needs the relation slug {collection}_{field} and that slug is already taken
  • selects different entries in different locales of the same entry, on any entry

That last one is about where links live. A link belongs to an entry’s translation group, so one selection is shared by all of its translations, while the old column was per locale. A field whose locales disagree has no single selection to carry over — merging them would hand each locale the other’s entries, and picking one locale’s answer would discard the rest — so the update leaves the field alone and both values readable in the column.

Two cases that look like disagreement are not. Locales that name their own translations of one entry have selected that entry once, so the update binds the field and the link resolves to each locale’s own version. A locale that selected nothing contradicts no other locale, so the update binds the field and the group’s one selection applies to every translation, including the empty one.

An unbound field keeps behaving as it did. Its column holds the entry ID, the value saves and loads, and the field can still be indexed and used as a content-list filter. In the entry editor it renders as a text box rather than a picker.

Open an entry in each collection that has a reference field. A field that renders as a picker needs nothing. For a field that still renders as a text box, follow Bind a field that has no relation, which creates the relation and copies the field’s stored IDs in as links. On a multi-locale site, settle which entry each translation should point at before binding, since binding keeps one selection for all of them.

In earlier versions, a reference field marked searchable broke its collection: adding one to a collection with search turned on failed, and after a seed turned search on, creating or saving any entry in that collection failed with no such column in the server log.

Site search and content-list search ignore the searchable flag on a reference field bound to a relation. Updating does not repair a collection that a seed already broke, because its search index still expects the field.

If creating or saving entries in a collection fails and the server log shows no such column, open the collection under Content Types, clear the Search checkbox, and save. Then select Search again and save. This rebuilds the collection’s search index, and the collection accepts new and edited entries again.

  • The build fails, or a page of your own errors at runtime: read the release entries marked Breaking for the versions you skipped and make the changes they state.
  • A plugin fails to load: read the plugin’s own release entry and Upgrading plugins on your site.
  • An error names an Astro API or an @astrojs/* package: EmDash requires Astro 6 or later. Astro’s upgrade guide explains how to update astro and its official integrations together.
  • To return to the previous release, reinstall the matching previous package versions and redeploy that artifact. Reinstalling does not undo core migrations. If the previous artifact cannot use the migrated database, stop traffic and restore the pre-update database and artifact together; restore media only if the update changed it.