githubEdit

code-pull-requestVersioning

Shopfoo uses Semantic Releasearrow-up-right to automate versioning and changelog generation. The version is determined from commit messages following the Conventional Commitsarrow-up-right specification, computed during CI, embedded into the deployed application, and displayed in its About page.

Overview

The versioning pipeline runs in GitHub Actions and follows this flow:

  1. A developer triggers the release workflow manually.

  2. Semantic Release analyzes commit messages since the last release and determines the next version number.

  3. The version and release date are written into package.json.

  4. A changelog entry is generated in CHANGELOG.md.

  5. Both files are committed and a GitHub release is created.

  6. The application is built and deployed to Azure.

The version is then visible in the application's About page, imported directly from package.json at build time.

Commit conventions and release rules

Commit messages follow the Conventional Commits specification, written with Commitjiarrow-up-right β€” a dotnet tool that adds a Gitmojiarrow-up-right-inspired emoji between the prefix and the description (e.g. feat: ✨ add stock adjustment). The emoji is purely decorative and does not affect versioning β€” only the prefix matters.

Semantic Release uses the conventionalcommits preset to map commit types to version bumps. The rules are defined in .releaserc.json:

Commit type
Version bump

breaking

Major

feat

Minor

fix

Patch

perf

Patch

refactor

Patch

test

No release

chore

No release

docs

No release

revert

No release

tidy

No release

wip

No release

For example, a commit message like feat: add stock adjustment triggers a minor version bump, while fix: correct price calculation triggers a patch bump.

Semantic Release configuration

The .releaserc.json file orchestrates six plugins in order:

Each plugin has a specific role:

  • commit-analyzer β€” Determines the version bump from commit messages.

  • release-notes-generator β€” Generates structured release notes from commits.

  • changelog β€” Appends the release notes to CHANGELOG.md.

  • npm β€” Updates the version field in package.json (publishing is disabled since this is a private package).

  • exec β€” Runs update-release-date.js to stamp the current date into package.json.

  • git β€” Commits the updated package.json and CHANGELOG.md with a [skip ci] marker to avoid retriggering the pipeline.

  • github β€” Creates a GitHub release with the generated notes.

Release date script

The update-release-date.js script is called by the exec plugin during the release. It writes today's date (in YYYY-MM-DD format) into a custom releaseDate field in package.json:

After a release, package.json contains both the version and the release date:

Version in the application

The F# client imports metadata directly from package.json using Fable's [<ImportMember>] attribute:

Vite resolves the import from package.json at build time, so the values are inlined into the JavaScript bundle. There is no runtime fetch β€” the version is baked into the compiled output.

The About page displays this information using DaisyUI badges:

This renders as something like: 🏷️ Version 1.2.0 (2025-12-30).

circle-info

See the About page section in General Features for a screenshot and a description of the information displayed.

GitHub Actions workflow

The release pipeline is defined in .github/workflows/release.yml and consists of three jobs:

Release

Runs Semantic Release via the cycjimmy/semantic-release-action action. It outputs two values consumed by downstream jobs:

  • new-release-published β€” Whether a new version was created.

  • new-release-version β€” The version string (e.g. 1.2.0).

The checkout uses fetch-depth: 0 so that Semantic Release has access to the full commit history for analysis.

Build

Runs only if a new release was published (or if force_deploy is enabled). It checks out the code at the release tag, sets up .NET 9 and Node.js 22, then runs dotnet run Publish β€” the FAKE build target that compiles the server, transpiles the F# client via Fable, and bundles the front-end via Vite. The output is uploaded as a shopfoo-app artifact.

Deploy

Downloads the build artifact and deploys it to an Azure Web App.

Workflow inputs

The workflow is triggered manually (workflow_dispatch) with two optional inputs:

  • dry-run β€” Runs Semantic Release without actually publishing. Useful for previewing what version would be created.

  • force_deploy β€” Deploys from the latest existing release tag, bypassing the need for a new release. Useful for redeployments after infrastructure changes.

Changelog

The CHANGELOG.md file is generated automatically by the @semantic-release/changelog plugin. Each release entry includes:

  • A SemVer comparison link to the GitHub diff (e.g. v1.1.0...v1.2.0).

  • The release date.

  • Commits grouped by type (Features, Bug Fixes, etc.).

  • Links to individual commits on GitHub.

Data flow summary

The full versioning lifecycle:

  1. Commit β€” Developers write commits following Conventional Commits with Commitjiarrow-up-right (feat: ✨ add feature, fix: πŸ› fix bug, etc.).

  2. Release trigger β€” A maintainer manually triggers the release workflow in GitHub Actions.

  3. Version calculation β€” Semantic Release analyzes commits since the last tag and determines the next SemVer version.

  4. File updates β€” package.json gets the new version and releaseDate; CHANGELOG.md gets a new entry.

  5. Git commit and tag β€” The updated files are committed with [skip ci] and a Git tag is created.

  6. GitHub release β€” A GitHub release is published with auto-generated notes.

  7. Build β€” Fable transpiles the F# client; Vite bundles the front-end, inlining the version from package.json.

  8. Deploy β€” The built artifact is deployed to Azure Web App.

  9. Display β€” The About page shows the version and release date imported from package.json.

Last updated