SDK Release Guide

September 6, 2026 · View on GitHub

Manifest-first release policy for all Toggly SDK packages in this repository.

Policy

  1. Bump version and CHANGELOG in your PR/commit when you change a publishable package.
  2. Merge to develop.
  3. Run the release workflow with defaults (release_mode: publish).
  4. The workflow publishes the manifest version — it does not bump by default.

npm public packages (@ops-ai/*)

Before shipping a new public npm package:

  1. Add a row to .github/package-registry/npm-packages.json (name, manifest, changelog, workflow, docsUrl).
  2. Satisfy the metadata contract enforced by verify-npm-metadata.mjs (author Toggly <support@toggly.io>, MIT, exact docs homepage, repository.url + repository.directory, GitHub bugs, publishConfig.access: public, required keywords). Never publish #develop or /tree/develop/ links.
  3. Point workflow at an existing sdk-*-release.yml (or add one) that publishes with --provenance.
  4. Configure npm Trusted Publisher for that package → repository ops-ai/Toggly.FeatureManagement, exact workflow filename, environment npm-publish. Set oidcReady: true in the inventory only after a successful OIDC publish, then remove NODE_AUTH_TOKEN / secrets.NPM_TOKEN fallback from that workflow.
  5. Run locally: node --test .github/package-registry/verify-npm-metadata.test.mjs and node --test .github/package-registry/verify-npm-trusted-publishing.test.mjs.

Governance notes: .github/package-registry/npm-governance-status.md.

NuGet (Toggly.*)

Trusted publishing: nuget.org policy must match repository ops-ai/Toggly.FeatureManagement, workflow sdk-dotnet-release.yml, environment nuget-publish. NuGet/login@v1 user is the trust-policy creator username (opsai), not the package-owner org (Toggly). Key Vault signing is unchanged. Do not store a NuGet API key in GitHub once OIDC has succeeded.

Workflow inputs

InputDefaultDescription
release_modepublishpublish uses the version in the manifest; auto_bump is a legacy escape hatch
bump_typepatchOnly used when release_mode is auto_bump
Package selectorsvariese.g. Flutter package, Node packages, Nuxt packages

How version resolution works

The shared action .github/actions/resolve-release-version compares the manifest version to the registry (or git tag when registry: none):

ResultMeaning
publishManifest is ahead of registry — publish proceeds
skipVersion already on registry — nothing to do
failManifest is behind registry — bump version in a PR first

Developer checklist

1. Implement change
2. Bump version + CHANGELOG in the same commit
3. Merge to develop
4. Actions → Run release workflow → leave defaults
5. Confirm summary shows action=publish

Troubleshooting

"manifest X is behind registry Y"

The registry has a newer version than your repo. Bump the manifest in a PR before releasing.

Workflow skipped (action=skip)

That version is already published. Bump the manifest if you have unreleased changes.

Emergency release without a version bump

Use release_mode: auto_bump and choose bump_type. This updates the manifest, publishes, and commits the bump back. Prefer fixing the manifest in a PR instead.

Flutter publish (pub.dev OIDC)

Publishing uses GitHub Actions OIDC — no upload token or Google sign-in in CI.

One-time setup per package on pub.dev AdminAutomated publishing:

PackageTag pattern
feature_flags_togglyflutter-sdk-v{{version}}
feature_flags_toggly_secure_storageflutter-secure_storage-v{{version}}
feature_flags_toggly_diskflutter-disk-v{{version}}
feature_flags_toggly_sqliteflutter-sqlite-v{{version}}
feature_flags_toggly_isarflutter-isar-v{{version}}

For each package:

  1. Repository: ops-ai/Toggly.FeatureManagement
  2. Tag pattern: row from the table above (not v{{version}})
  3. Enable Enable publishing from push events (tag push triggers publish)
  4. Optionally enable Enable publishing from workflow_dispatch events

Release flow:

  1. Run Flutter SDKs - Build & Publish on develop (workflow_dispatch) — validates, tests, pushes a signed tag.
  2. The tag push starts a second run that publishes to pub.dev via OIDC and creates the GitHub Release.

pub.dev requires the publish job to run on a tag ref, not a branch — that is why publish is split from tagging.

Tag already exists (tag-and-push failed)

If an earlier run pushed the tag but publish failed (e.g. dry-run validation), re-running from develop fails with tag already exists.

Retry publish without a new tag:

  1. Actions → Flutter SDKs - Build & PublishRun workflow
  2. Set Use workflow from to the release tag (e.g. flutter-secure_storage-v0.1.2)
  3. Package can stay as-is (inferred from the tag on tag refs)

That run skips tag-and-push and executes the publish job only. The publish job checks out develop (not the tag commit) so publish fixes on develop apply, as long as the manifest version still matches the tag.

If develop has moved past that version, bump the manifest and push a new tag instead.

Monorepo packages (Nuxt, Next, Remix, React Native, Node server)

Each sibling package is published at its own manifest version. Core being ahead of a sibling is not a failure: already-published versions are skipped. Do not bump unrelated siblings just to satisfy a lockstep check.

Workflows

WorkflowManifestRegistry
sdk-javascript-release.ymlpackage.jsonnpm
sdk-react-release.ymlpackage.jsonnpm
sdk-angular-release.ymlprojects/ngx-feature-flags-toggly/package.jsonnpm
sdk-node-server-release.ymlper-package package.jsonnpm
sdk-flutter-release.ymlpubspec.yamlpub.dev
sdk-dotnet-release.ymlDirectory.Build.propsNuGet
sdk-rust-release.ymlCargo.tomlcrates.io
sdk-python-release.ymlpyproject.tomlPyPI
sdk-ruby-release.ymllib/toggly/version.rbRubyGems
sdk-go-release.ymltoggly-go/VERSIONgit tag (go-sdk-v*)
sdk-php-release.ymlcomposer.jsonPackagist
cli-build-release.ymlToggly.CLI/VERSIONgit tag (cli-v*)

PHP monorepo workflow: sdk-php-release.yml requires Toggly.FeatureManagement.PHP/ in the checkout. If that directory is absent, use the release workflow in the standalone Toggly.FeatureManagement.PHP repository instead.

See package-versioning rule for semver and changelog conventions.