Goal
Establish deterministic, tag-driven release engineering for vm75/inditrans.
After this milestone, the repository should have one clear release contract:
vX.Y.Z Git tag
├── release identity
├── validated package versions
├── validated changelog entry
├── full CI/test/build gate
└── publish
├── npm (@vm75/inditrans)
├── pub.dev (Flutter package)
└── GitHub Release
The release workflow must never publish merely because code was pushed to main. Publishing happens only for an intentional SemVer tag such as v0.14.0.
Preconditions
- Milestone 1 is complete, committed, pushed, and merged to
main.
- The release-engineering work starts from the final Milestone 1 tree, not from an earlier repository layout.
- Do not create the first production release tag as part of this implementation PR. The first tag should be created deliberately after this milestone is merged and the release procedure has been verified.
Scope
1. Establish the repository release contract
Use SemVer tags:
Rules:
- The Git tag is the authoritative release identity.
- The tag must match
vMAJOR.MINOR.PATCH.
- Package versions must match the tag without the leading
v.
- A release tag must not be reused or moved.
- CI on
main never publishes.
- Release CI runs only for matching version tags.
- Failed validation/test/build jobs must prevent publication.
The existing package manifests remain package metadata:
flutter/pubspec.yaml
nodejs/package.json
Do not introduce a second independent release-version source.
2. Consolidate changelog management
Create a root CHANGELOG.md using the Keep a Changelog structure.
It should contain:
# Changelog
All notable changes to this project will be documented in this file.
## [Unreleased]
## [0.13.0] - YYYY-MM-DD
...
Requirements:
- Keep an
[Unreleased] section at the top.
- Maintain release sections in descending version order.
- Use SemVer release headings.
- Record user-visible changes rather than implementation-only commit noise.
- A release should move the relevant entries from
[Unreleased] into the dated release section.
- Link/version references may be added at the bottom if useful.
Consolidate the existing package changelogs into the root changelog so there is one source of release history:
flutter/CHANGELOG.md
nodejs/CHANGELOG.md
Do not leave two independently maintained changelogs.
3. Define and validate version consistency
Add a small repository-level validation tool, preferably:
tool/verify_release.dart
It should accept a release tag/version and verify at minimum:
- tag is valid SemVer with the required
v prefix;
flutter/pubspec.yaml version equals the tag version without v;
nodejs/package.json version equals the tag version without v;
- the root changelog contains a matching release section;
- the release is not accidentally being built from a mismatched version.
Keep the validation deterministic and suitable for CI.
If the existing .version file is still used by the post-Milestone-1 codebase, migrate its consumers as part of this work and remove it only after there is no remaining functional dependency. The desired end state is that package manifests plus the Git tag provide the release contract rather than an unrelated mutable version file.
4. Move GitHub Actions to the repository root
Create:
.github/
workflows/
ci.yml
release.yml
The workflows belong at repository level because the project contains multiple published packages.
Do not retain a second competing CI/release system under nodejs/.github/workflows/.
5. Implement normal CI
.github/workflows/ci.yml should run for:
- pull requests targeting
main;
- pushes to
main.
It must not publish packages.
CI should exercise the complete supported project surface, including the existing native, WASM/Node.js, and Flutter test/build paths as appropriate to the final Milestone 1 structure.
At minimum, preserve and formalize the current checks:
- native tests/build;
- Node.js package tests/build/style checks;
- Flutter tests/build checks;
- formatting/static analysis checks where already supported;
- version consistency checks where applicable.
Use currently supported GitHub Actions versions rather than the legacy checkout@v2 / cache@v2 pattern.
Keep permissions least-privilege.
6. Implement tag-driven release workflow
Create .github/workflows/release.yml.
Trigger only on tags matching:
The workflow must:
- Check out the exact tagged commit.
- Derive the release version from the tag.
- Validate the tag/version/changelog contract.
- Run the relevant test/build suite.
- Build the npm package.
- Build/prepare the Flutter package.
- Publish npm.
- Publish the Flutter package to pub.dev.
- Create a GitHub Release associated with the same tag.
- Fail before any publication if validation/tests/builds fail.
Do not publish from main pushes.
7. Publishing authentication
Prefer modern trusted publishing/OIDC mechanisms supported by npm and pub.dev rather than long-lived package tokens.
If a provider still requires a secret, use a protected GitHub Actions secret/environment and document exactly which secret is required.
Do not put credentials in the repository, workflow source, package manifests, or generated files.
Use least-privilege GitHub token permissions. The release job should request only the permissions it needs, including the permission required to create the GitHub Release.
8. Remove the old branch-based publishing path
The existing Node.js workflow publishes from pushes to master. Replace that behavior completely.
Specifically:
- remove the old branch-based publish workflow;
- do not publish from
master;
- use
main as the repository development/default branch;
- remove the dependency on
mikeal/merge-release@master;
- do not replace one branch-based publishing mechanism with another.
The final repository must have one release path.
9. Revisit Makefile publishing targets
The Makefile may remain useful for local development, testing, and builds.
Release publication should be owned by GitHub Actions.
Remove or clearly demote local publish targets if they create a competing release mechanism. Local commands may remain as explicit package-level build/test helpers, but there must be no ambiguity about the canonical release process.
10. Make release preparation explicit
Document the release procedure in the repository, preferably in the README or a dedicated release-development document.
The documented flow should be:
- Develop normally against
main.
- Add user-visible changes under
CHANGELOG.md → [Unreleased].
- Prepare a release by setting both package versions to the intended version and moving changelog entries into a dated release section.
- Merge the release-preparation change to
main.
- Confirm CI is green.
- Create an annotated tag such as
v0.14.0 at the exact release commit.
- Push the tag.
- Release CI validates, tests, builds, publishes, and creates the GitHub Release.
- If release CI fails before publication, fix the release commit/workflow and retry using a corrected immutable release tag according to the documented recovery procedure. Never move an existing published tag.
Do not automate tag creation as part of normal CI.
Expected repository shape
The desired end state is approximately:
inditrans/
├── CHANGELOG.md
├── README.md
├── LICENSE
├── Makefile
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── release.yml
├── flutter/
│ └── pubspec.yaml
├── nodejs/
│ └── package.json
├── tool/
│ ├── bump_version.dart
│ └── verify_release.dart
└── ...
Adjust this to the final Milestone 1 layout rather than blindly preserving paths that Milestone 1 intentionally changed.
Failure and safety requirements
- Never publish when version validation fails.
- Never publish when tests/builds fail.
- Never publish from an ordinary branch push.
- Never publish from an unrecognized tag.
- Never silently publish one package with a different version from the other.
- Never move/reuse an existing release tag.
- Do not expose package credentials in logs.
- A rerun of an already successful release must be considered: package registries may reject duplicate versions, so the workflow/documentation should make the expected recovery behavior explicit.
- GitHub Release creation should correspond to the exact tag that triggered the workflow.
Verification / testing
Before considering the milestone complete:
- Verify PR CI runs on a normal PR and does not publish.
- Verify a push to
main runs CI and does not publish.
- Verify a non-version tag does not trigger the release workflow.
- Verify a
vX.Y.Z tag triggers release validation.
- Verify a deliberately mismatched package version causes release validation to fail before publishing.
- Verify a missing changelog release section causes validation to fail before publishing.
- Verify the full test/build suite passes on the tagged commit.
- Verify the npm package contains the intended version.
- Verify the Flutter package contains the intended version.
- Verify the GitHub Release is attached to the triggering tag.
- Where possible, exercise registry publication in a safe/dry-run manner before the first real release.
- Confirm there is no remaining workflow that publishes from
master, main, or another branch push.
For the first production release after this milestone, use a deliberate version/tag and verify both registries and the GitHub Release manually.
Acceptance criteria
Out of scope
- Changing transliteration behavior.
- Adding new scripts/languages.
- Refactoring the native transliteration core.
- Changing package APIs unrelated to release engineering.
- Automatically bumping versions on every commit.
- Automatically creating release tags from CI.
Goal
Establish deterministic, tag-driven release engineering for
vm75/inditrans.After this milestone, the repository should have one clear release contract:
The release workflow must never publish merely because code was pushed to
main. Publishing happens only for an intentional SemVer tag such asv0.14.0.Preconditions
main.Scope
1. Establish the repository release contract
Use SemVer tags:
v0.13.0v0.13.1v0.14.0Rules:
vMAJOR.MINOR.PATCH.v.mainnever publishes.The existing package manifests remain package metadata:
flutter/pubspec.yamlnodejs/package.jsonDo not introduce a second independent release-version source.
2. Consolidate changelog management
Create a root
CHANGELOG.mdusing the Keep a Changelog structure.It should contain:
Requirements:
[Unreleased]section at the top.[Unreleased]into the dated release section.Consolidate the existing package changelogs into the root changelog so there is one source of release history:
flutter/CHANGELOG.mdnodejs/CHANGELOG.mdDo not leave two independently maintained changelogs.
3. Define and validate version consistency
Add a small repository-level validation tool, preferably:
tool/verify_release.dartIt should accept a release tag/version and verify at minimum:
vprefix;flutter/pubspec.yamlversion equals the tag version withoutv;nodejs/package.jsonversion equals the tag version withoutv;Keep the validation deterministic and suitable for CI.
If the existing
.versionfile is still used by the post-Milestone-1 codebase, migrate its consumers as part of this work and remove it only after there is no remaining functional dependency. The desired end state is that package manifests plus the Git tag provide the release contract rather than an unrelated mutable version file.4. Move GitHub Actions to the repository root
Create:
The workflows belong at repository level because the project contains multiple published packages.
Do not retain a second competing CI/release system under
nodejs/.github/workflows/.5. Implement normal CI
.github/workflows/ci.ymlshould run for:main;main.It must not publish packages.
CI should exercise the complete supported project surface, including the existing native, WASM/Node.js, and Flutter test/build paths as appropriate to the final Milestone 1 structure.
At minimum, preserve and formalize the current checks:
Use currently supported GitHub Actions versions rather than the legacy
checkout@v2/cache@v2pattern.Keep permissions least-privilege.
6. Implement tag-driven release workflow
Create
.github/workflows/release.yml.Trigger only on tags matching:
The workflow must:
Do not publish from
mainpushes.7. Publishing authentication
Prefer modern trusted publishing/OIDC mechanisms supported by npm and pub.dev rather than long-lived package tokens.
If a provider still requires a secret, use a protected GitHub Actions secret/environment and document exactly which secret is required.
Do not put credentials in the repository, workflow source, package manifests, or generated files.
Use least-privilege GitHub token permissions. The release job should request only the permissions it needs, including the permission required to create the GitHub Release.
8. Remove the old branch-based publishing path
The existing Node.js workflow publishes from pushes to
master. Replace that behavior completely.Specifically:
master;mainas the repository development/default branch;mikeal/merge-release@master;The final repository must have one release path.
9. Revisit Makefile publishing targets
The Makefile may remain useful for local development, testing, and builds.
Release publication should be owned by GitHub Actions.
Remove or clearly demote local
publishtargets if they create a competing release mechanism. Local commands may remain as explicit package-level build/test helpers, but there must be no ambiguity about the canonical release process.10. Make release preparation explicit
Document the release procedure in the repository, preferably in the README or a dedicated release-development document.
The documented flow should be:
main.CHANGELOG.md→[Unreleased].main.v0.14.0at the exact release commit.Do not automate tag creation as part of normal CI.
Expected repository shape
The desired end state is approximately:
Adjust this to the final Milestone 1 layout rather than blindly preserving paths that Milestone 1 intentionally changed.
Failure and safety requirements
Verification / testing
Before considering the milestone complete:
mainruns CI and does not publish.vX.Y.Ztag triggers release validation.master,main, or another branch push.For the first production release after this milestone, use a deliberate version/tag and verify both registries and the GitHub Release manually.
Acceptance criteria
vMAJOR.MINOR.PATCH.flutter/pubspec.yamlandnodejs/package.jsonmust match the tag version.CHANGELOG.mdexists with an[Unreleased]section.tool/verify_release.dart(or an equivalent repository-level validator) enforces the release contract..github/workflows/.mainand never publishes.masterpublishing is removed.mikeal/merge-releaseis removed from the release path.Out of scope