feat: implement automated monthly releases with YY-MM-patchlevel versioning#7468
feat: implement automated monthly releases with YY-MM-patchlevel versioning#7468DennisOSRM merged 6 commits intomasterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Implements an automated monthly release process that publishes npm releases using a YYYY-MM-patchlevel version scheme, with supporting CI/build-system and documentation updates.
Changes:
- Adds a scheduled + manual GitHub Actions workflow to compute
YYYY-MM-patchlevel, tag, create a GitHub Release, and publish to npm. - Updates CI tag triggers to recognize the new tag format.
- Updates build/version parsing (CMake), package version, and release documentation for the new scheme.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/release-monthly.yml |
New workflow to calculate monthly versions, commit/tag/release, and publish to npm. |
.github/workflows/osrm-backend.yml |
Extends tag trigger patterns to include the new release tag format. |
CMakeLists.txt |
Updates version parsing to accept both semver and YYYY-MM-patchlevel. |
package.json |
Bumps npm package version to the new format (2026-04-0). |
docs/releasing.md |
Rewrites release documentation around the new monthly automation and versioning scheme. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
2abf329 to
4e66801
Compare
…rsioning
- Add release-monthly.yml workflow with scheduled (1st @ 08:00 UTC) and manual triggers
- Version scheme: YYYY-MM-patchlevel (e.g., 2026-04-0, 2026-04-1)
- Patchlevel auto-increments within same month, resets on month change
- Automatic git operations: commit, tag, push
- Automatic GitHub Release creation with auto-generated notes
- Automatic npm publication
- Update osrm-backend.yml to trigger on new tag pattern v[0-9]{4}-[0-9]{2}-[0-9]+
- Update CMakeLists.txt to support both new and legacy semver formats
- Update package.json to version 2026-04-0
- Update docs/releasing.md with new release process documentation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4e66801 to
c89ce8b
Compare
Instead of dual-format versioning (YYYY-MM-patchlevel for git tags, (YYYY-2000).MM.patchlevel for npm), use a single unified format: - Git tags: v26.4.0 (matching npm package version exactly) - npm packages: 26.4.0 - No format conversion needed in release workflow Changes: - release-monthly.yml: Calculate version directly in semver format - CMakeLists.txt: Remove YYYY-MM-patchlevel format handling - version.hpp.in: Simplify to single semver format - docs/releasing.md: Document unified versioning approach Benefits: - Eliminate format mismatch between git tags and npm - Simpler release workflow (no conversion step needed) - Single source of truth for version format - Clearer documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…monthly scheme Add 'Version History' section documenting: - Previous scheme: Traditional semver (v6.0, v6.0.1, etc.) - Ended in December 2025 with v6.0.0 release - Manual release process - New scheme: Monthly date-based versioning (starting 2026) - First release: v26.1.0 (January 2026) - Automated monthly releases on 1st of month at 08:00 UTC - Year offset format: YYYY-2000 (e.g., 2026 → 26) Provides clear context for users transitioning between schemes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| set(OSRM_VERSION_PRERELEASE_BUILD ${CMAKE_MATCH_4}) | ||
|
|
||
| set(OSRM_VERSION_PRERELEASE_BUILD "") | ||
| set(OSRM_VERSION packagejson.version) |
There was a problem hiding this comment.
set(OSRM_VERSION packagejson.version) assigns the literal string "packagejson.version" rather than the parsed version value, which will leak into cmake/pkgconfig.in (via @OSRM_VERSION@) and produce an incorrect pkg-config version. Set it from the JSON value (or from the match groups) instead.
| set(OSRM_VERSION packagejson.version) | |
| set(OSRM_VERSION "${packagejson.version}") |
| # This regex supports the unified semver version format: | ||
| # - (YYYY-2000).MM.patchlevel: e.g., 26.4.0 where 26 = year - 2000, 4 = April | ||
| # This format is used for both git tags and npm packages, eliminating format mismatch | ||
| # The CMake build system also supports legacy semver (e.g., 6.0.0) for backward compatibility | ||
|
|
||
| # Try semver format: (YYYY-2000).MM.patchlevel (e.g., 26.4.0) | ||
| if (packagejson.version MATCHES "^([0-9]{1,2})\\.([0-9]{1,2})\\.([0-9]+)$") | ||
| # Semver format: (YYYY-2000).MM.patchlevel | ||
| # MAJOR is already offset (year - 2000) | ||
| set(OSRM_VERSION_MAJOR ${CMAKE_MATCH_1}) | ||
| set(OSRM_VERSION_MINOR ${CMAKE_MATCH_2}) | ||
| set(OSRM_VERSION_PATCH ${CMAKE_MATCH_3}) | ||
| set(OSRM_VERSION_PRERELEASE_BUILD ${CMAKE_MATCH_4}) | ||
|
|
||
| set(OSRM_VERSION_PRERELEASE_BUILD "") | ||
| set(OSRM_VERSION packagejson.version) | ||
| else() | ||
| message(FATAL_ERROR "Version from package.json cannot be parsed, expected semver compatible label, but found ${packagejson.version}") | ||
| message(FATAL_ERROR "Version from package.json cannot be parsed. Expected semver format:\n - (YYYY-2000).MM.patchlevel (e.g., 26.4.0 where 26=year-2000)\n - Legacy semver (e.g., 6.0.0)\nBut found: ${packagejson.version}") | ||
| endif() |
There was a problem hiding this comment.
The updated regex only accepts X.Y.Z and no longer supports legacy semver variants with pre-release/build metadata, even though the comments/error message claim legacy semver is supported. Either add an additional match branch for legacy semver (including optional prerelease/build) or update the comments/error text to reflect the actual supported formats.
| # Calculate semver version automatically | ||
| # Use (YYYY-2000).MM.patchlevel format | ||
| YEAR=$(date -u +%Y) | ||
| MONTH=$(date -u +%m) | ||
| YEAR_OFFSET=$((YEAR - 2000)) | ||
|
|
||
| # Find highest patchlevel for this month | ||
| LATEST_TAG=$(git tag -l "v${YEAR_OFFSET}.${MONTH}.*" --sort=-version:refname | head -1) | ||
|
|
||
| if [ -z "$LATEST_TAG" ]; then | ||
| PATCHLEVEL=0 | ||
| else | ||
| # Extract patchlevel from tag (e.g., v26.4.5 -> 5) | ||
| PATCHLEVEL=${LATEST_TAG##*.} | ||
| PATCHLEVEL=$((PATCHLEVEL + 1)) | ||
| fi | ||
|
|
||
| VERSION="${YEAR_OFFSET}.${MONTH}.${PATCHLEVEL}" | ||
| fi |
There was a problem hiding this comment.
MONTH=$(date -u +%m) produces a zero-padded month (e.g. "04"), which would generate versions like 26.04.0. That is not valid semver (leading zero in a numeric identifier) and may be rejected by npm. Use an unpadded month (%-m) or strip the leading zero before composing the version/tag and tag search pattern.
| run: | | ||
| # Wait for tag CI to complete before publishing npm package | ||
| # The CI workflow is triggered by the tag push and must complete | ||
| # to upload release assets (prebuilt binaries) | ||
| echo "Waiting for CI workflow to complete..." | ||
|
|
||
| # Poll workflow runs for this tag | ||
| TAG="${{ steps.version.outputs.tag }}" | ||
| MAX_WAIT=300 # 5 minutes | ||
| ELAPSED=0 | ||
| POLL_INTERVAL=10 | ||
|
|
||
| while [ $ELAPSED -lt $MAX_WAIT ]; do | ||
| RUNS=$(gh run list --workflow=osrm-backend.yml --status=completed --json conclusion,status,name 2>/dev/null | jq -r ". | length") | ||
| if [ "$RUNS" != "0" ]; then | ||
| echo "CI workflow completed, proceeding with npm publish" | ||
| break | ||
| fi | ||
| sleep $POLL_INTERVAL | ||
| ELAPSED=$((ELAPSED + POLL_INTERVAL)) | ||
| done | ||
|
|
There was a problem hiding this comment.
The CI wait loop is not reliably checking the workflow run for the tag being released: it doesn’t authenticate gh (stderr is discarded), doesn’t filter by tag/commit, and doesn’t verify the conclusion is success. As written it can immediately proceed due to any completed run, or time out after 5 minutes and still publish to npm. Pass GH_TOKEN (or GITHUB_TOKEN), filter runs for the tag’s commit SHA, require a success conclusion, and fail the job if the matching run isn’t successful within the timeout.
| run: | | |
| # Wait for tag CI to complete before publishing npm package | |
| # The CI workflow is triggered by the tag push and must complete | |
| # to upload release assets (prebuilt binaries) | |
| echo "Waiting for CI workflow to complete..." | |
| # Poll workflow runs for this tag | |
| TAG="${{ steps.version.outputs.tag }}" | |
| MAX_WAIT=300 # 5 minutes | |
| ELAPSED=0 | |
| POLL_INTERVAL=10 | |
| while [ $ELAPSED -lt $MAX_WAIT ]; do | |
| RUNS=$(gh run list --workflow=osrm-backend.yml --status=completed --json conclusion,status,name 2>/dev/null | jq -r ". | length") | |
| if [ "$RUNS" != "0" ]; then | |
| echo "CI workflow completed, proceeding with npm publish" | |
| break | |
| fi | |
| sleep $POLL_INTERVAL | |
| ELAPSED=$((ELAPSED + POLL_INTERVAL)) | |
| done | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| # Wait for tag CI to complete before publishing npm package | |
| # The CI workflow is triggered by the tag push and must complete | |
| # to upload release assets (prebuilt binaries) | |
| echo "Waiting for CI workflow to complete..." | |
| TAG="${{ steps.version.outputs.tag }}" | |
| TAG_SHA="$(git rev-list -n 1 "$TAG")" | |
| MAX_WAIT=300 # 5 minutes | |
| ELAPSED=0 | |
| POLL_INTERVAL=10 | |
| echo "Waiting for osrm-backend.yml run for tag $TAG (commit $TAG_SHA)" | |
| while [ "$ELAPSED" -lt "$MAX_WAIT" ]; do | |
| RUN_JSON="$(gh run list --workflow=osrm-backend.yml --commit "$TAG_SHA" --json databaseId,status,conclusion,headSha,displayTitle --limit 20)" | |
| MATCHING_RUN="$(echo "$RUN_JSON" | jq -c --arg sha "$TAG_SHA" 'map(select(.headSha == $sha)) | first')" | |
| if [ "$MATCHING_RUN" != "null" ] && [ -n "$MATCHING_RUN" ]; then | |
| STATUS="$(echo "$MATCHING_RUN" | jq -r '.status')" | |
| CONCLUSION="$(echo "$MATCHING_RUN" | jq -r '.conclusion // ""')" | |
| RUN_ID="$(echo "$MATCHING_RUN" | jq -r '.databaseId')" | |
| echo "Found matching CI run $RUN_ID with status=$STATUS conclusion=$CONCLUSION" | |
| if [ "$STATUS" = "completed" ]; then | |
| if [ "$CONCLUSION" = "success" ]; then | |
| echo "CI workflow completed successfully, proceeding with npm publish" | |
| break | |
| fi | |
| echo "CI workflow for tag $TAG completed with conclusion=$CONCLUSION" | |
| exit 1 | |
| fi | |
| else | |
| echo "No matching CI run found yet for commit $TAG_SHA" | |
| fi | |
| sleep "$POLL_INTERVAL" | |
| ELAPSED=$((ELAPSED + POLL_INTERVAL)) | |
| done | |
| if [ "$ELAPSED" -ge "$MAX_WAIT" ]; then | |
| echo "Timed out waiting for successful CI workflow for tag $TAG (commit $TAG_SHA)" | |
| exit 1 | |
| fi |
| branch: | ||
| description: 'Branch to release from (defaults to master)' | ||
| required: false | ||
| type: string | ||
| default: 'master' |
There was a problem hiding this comment.
workflow_dispatch already lets the user choose the ref/branch to run the workflow from; adding a separate branch input that is then used for checkout/push can diverge from github.ref and make it unclear which branch is actually being released. Consider removing the custom branch input and using github.ref_name / github.ref consistently, or clearly documenting the precedence to avoid accidental releases from the wrong branch.
CMake's MATCHES operator does not support {1,2} quantifier syntax.
Changed regex from '[0-9]{1,2}' to '[0-9]+' to properly match version
numbers in both zero-padded (26.04.0) and non-padded (26.4.0) formats.
This fixes the build error:
"Version from package.json cannot be parsed... But found: 26.4.0"
The regex now correctly matches:
- 26.4.0 (non-padded month)
- 26.04.0 (zero-padded month)
- 6.0.0 (legacy semver)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Critical fixes: - Fix OSRM_VERSION CMake assignment to use variable value not literal string - Restore legacy semver format support (with prerelease/build metadata) - Enforce month 1-12 in CMake regex (no invalid months like 13) - Enforce month 1-12 and no leading zeros in version_override validation - Remove zero-padding from month calculation (use %-m not %m) - Update package-lock.json in addition to package.json during releases Improvements: - Clearer CMake comments explaining both version formats - Documentation clarifies git tag prefix (v) vs npm unprefixed format - Documentation clarifies month is 1-12 without leading zeros (not MM) - Ensure version_override rejects invalid formats like 26.00.0 or 26.13.0 The workflow now correctly: - Calculates versions like 26.4.0 for April 2026 (not 26.04.0) - Validates month to be in range 1-12 - Updates both package.json and package-lock.json consistently - Generates correct OSRM_VERSION in runtime and pkg-config Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevent npm publish from running before prebuilt binaries are available by implementing proper CI workflow monitoring. The osrm-backend.yml workflow (triggered by tag push) builds and uploads prebuilt binaries to GitHub Release assets. If npm is published before these binaries are available, subsequent installs via node-pre-gyp will fail. Changes: - Authenticate gh CLI with GH_TOKEN (github.token) - Query osrm-backend.yml workflow runs with commit SHA filter - Find the run matching the tag's commit SHA specifically - Monitor status and conclusion fields - Wait up to 5 minutes for completion - Only proceed to npm publish if conclusion is 'success' - Fail fast if CI workflow fails or times out This ensures: - Binaries are available before npm knows about the version - npm installs will find prebuilt binaries in release assets - Failed CI builds prevent broken npm releases - Manual/scheduled releases don't race and fail mysteriously Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
This PR implements a new automated monthly release process with YYYY-MM-patchlevel versioning (e.g., 2026-04-0, 2026-04-1).
Changes
New Workflow: .github/workflows/release-monthly.yml
workflow_dispatchfor emergency/out-of-schedule releasesUpdated Files
Versioning Scheme
Format: YYYY-MM-patchlevel
Examples:
2026-04-02026-04-12026-05-0Compatibility
Release Process
Automated:
Manual:
Prerequisites
NPM_TOKENsecret is configured in repository settings for npm publishing