@jscutlery/semver

repository·main·Indexed 21 days ago

https://github.com/jscutlery/semver

An Nx plugin for automating SemVer-based versioning and CHANGELOG generation using Conventional Commits. It supports independent and synced versioning modes, tag-based version calculation, and includes executors for automating releases on GitHub and GitLab.

Tokens
10.8K
Snippets
31
Records
38
Agent score
72%

What's inside @jscutlery/semver

  1. Understand Independent vs Synced versioning modes

    main

    The @jscutlery/semver plugin supports two primary modes for managing versions in an Nx workspace:

    • Independent mode (default): Allows multiple projects to be versioned independently. You release only the specific packages that have changed, which is ideal for rapid, incremental adoption and avoiding unnecessary updates for consumers.
    • Synced mode: Allows multiple projects to be versioned in a locked/synced mode. All package versions are tied together. This is useful for single-product workspaces, but note that a major change in any single project will trigger a major version bump for all projects in the workspace.
  2. How version calculation works

    main

    This package is tag-based. It does not read package.json to determine the current version. Instead:

    1. It searches for a git tag matching the configured --tagPrefix (e.g., demo-x.y.z).
    2. If no tag is found, it falls back to 0.0.0 and calculates the version based on all changes since the first commit.
    3. If tags exist, it retrieves the latest one and calculates the new version based on commits since that tag.

    Important Notes:

    • It checks commit history to see if source files changed since the last version.
    • Merge commits are ignored when calculating the next bump.
    • Major zero versions (0.x.y) are treated as initial development; minor version compatibility (caret/tilde) is not guaranteed in this range.
  3. Configure GitHub Actions for CI/CD release

    main

    To run @jscutlery/semver in a GitHub workflow, ensure you use fetch-depth: 0 in your checkout step to retrieve the full history required for version calculation. You must also configure a Git user and provide the GITHUB_TOKEN environment variable.

    Example workflow configuration:

    name: release
    
    on:
      - workflow_dispatch
    
    jobs:
      release:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
            with:
              fetch-depth: 0
          - name: Use Node.js
            uses: actions/setup-node@v4
            with:
              node-version: '20'
          - name: Setup Git
            run: |
              git config user.name "GitHub Bot"
              git config user.email "gituser@example.com"
          - run: pnpm install --frozen-lockfile
          - name: Version
            shell: bash
            run: pnpm nx affected --base=last-release --target=version --parallel=1
            env:
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          - name: Tag last-release
            shell: bash
            run: |
              git tag -f last-release
              git push origin last-release --force
  4. Use the @jscutlery/semver:gitlab executor

    main

    The @jscutlery/semver:gitlab executor is used to create GitLab Releases. It requires the GitLab Release CLI to be installed on your machine.

    Manual Execution

    You can run the executor manually via Nx by providing a --tag:

    nx run my-project:gitlab --tag v1.0.0

    Automated Configuration with post-targets

    The recommended way to use this executor is via post-targets. This allows the version target to automatically trigger the GitLab release after a version bump.

    {
      "targets": {
        "version": {
          "executor": "@jscutlery/semver:version",
          "options": {
            "postTargets": ["my-project:gitlab"]
          }
        },
        "gitlab": {
          "executor": "@jscutlery/semver:gitlab",
          "options": {
            "tag": "${tag}",
            "description": "${notes}"
          }
        }
      }
    }
  5. Use the @jscutlery/semver:github executor to create GitHub Releases

    main

    The @jscutlery/semver:github executor automates the creation of GitHub Releases. It requires the GitHub CLI to be installed on the machine where the command is executed.

    Manual Execution

    You can run the executor manually via Nx by specifying the tag:

    nx run my-project:github --tag v1.0.0 [...options]

    CI/CD Integration

    When running in GitHub Actions, ensure you provide the GITHUB_TOKEN environment variable so the executor can authenticate with GitHub:

    - name: Version
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: npx nx affected --target=version
  6. Release projects in Independent mode

    main

    In Independent mode, you can release a specific project by running the version target for that project. You can also use the nx affected command to automatically identify and release only the packages that have changed.

    To release a specific project:

    nx run my-project:version [...options]

    To release only affected packages:

    nx affected --target version [...options]
  7. Migrate to Nx Release

    main

    You can migrate from @jscutlery/semver to the official Nx Release by running the following Nx generator. This will remove existing @jscutlery/semver configurations and set up Nx Release for your projects.

    Important Notes:

    • The migration does not support sync mode.
    • Complex or highly customized configurations may require manual adjustments after running the generator.
    • You must manually update any custom scripts or CI workflows to align with the new Nx release workflow.
    nx g @jscutlery/semver:migrate-nx-release
  8. Configure GitLab CI for CI/CD release

    main

    To run @jscutlery/semver in GitLab CI, you may need to configure a deploy key to allow the runner to push changes back to the repository. Ensure you set the remote URL with the appropriate token and use the -o ci.skip flag when pushing to avoid triggering recursive pipelines.

    Example GitLab CI configuration:

    stages:
      - release
    
    release:
      rules:
        - if: $CI_COMMIT_BRANCH == "master"
          when: manual
      stage: release
      image: node:20.19.3
      before_script:
        - git config --global user.name "GitLab Bot"
        - git config --global user.email "gituser@example.com"
        - git remote set-url origin http://gitlab-ci-token:${DEPLOY_KEY}@gitlab.com/org/project.git
      script:
        - pnpm install --frozen-lockfile
        - pnpm nx affected --target=version --base=last-release --parallel=1
        - git tag -f last-release
        - git push origin last-release --force -o ci.skip
  9. Configure @jscutlery/semver:github with post-targets

    main

    The recommended way to use this executor is via post-targets. This allows the @jscutlery/semver:version executor to automatically trigger the GitHub release after a version bump.

    Using a full Changelog file

    To use an existing file (like CHANGELOG.md) as the release notes, use the notesFile option:

    {
      "targets": {
        "version": {
          "executor": "@jscutlery/semver:version",
          "options": {
            "postTargets": ["my-project:github"]
          }
        },
        "github": {
          "executor": "@jscutlery/semver:github",
          "options": {
            "tag": "${tag}",
            "notesFile": "./libs/my-project/CHANGELOG.md"
          }
        }
      }
    }

    To avoid including the entire changelog in every release, use the ${notes} context provided by @jscutlery/semver:version. This ensures only the new changes are included in the GitHub release notes:

    {
      "targets": {
        "version": {
          "executor": "@jscutlery/semver:version",
          "options": {
            "postTargets": ["my-project:github"]
          }
        },
        "github": {
          "executor": "@jscutlery/semver:github",
          "options": {
            "tag": "${tag}",
            "notes": "${notes}"
          }
        }
      }
    }
  10. Install @jscutlery/semver

    main

    To set up the @jscutlery/semver Nx plugin in your workspace, install the package as a development dependency and then run the installation generator.

    npm install -D @jscutlery/semver
    nx g @jscutlery/semver:install
  11. Customize the commit parser

    main

    If you use an adapted version of conventional commits, you can customize the commit parser using commitParserOptions. This allows you to define custom headerPattern and headerCorrespondence to map commit parts to specific fields.

    {
      "executor": "@jscutlery/semver:version",
      "options": {
        "commitParserOptions": {
          "headerPattern": "^([A-Z]{3,}-\\d{1,5}):? (chore|build|ci|docs|feat|fix|perf|refactor|test)(?:\\(([\\w-]+)\\))?\\S* (.+)$",
          "headerCorrespondence": ["ticketReference", "type", "scope", "subject"]
        }
      }
    }