diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6108132 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,152 @@ +name: CI + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - closed + branches: + - master + - main + push: + branches: + - master + - main + +permissions: + contents: read + +jobs: + test: + name: Test and coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: yarn + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run lint and tests with coverage + run: yarn lint && yarn test:coverage + + - name: Build package + run: yarn build + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + files: ./tests/coverage/lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false + + publish: + name: Publish to npm + runs-on: ubuntu-latest + needs: test + permissions: + contents: read + id-token: write + if: >- + github.event_name == 'pull_request' && + github.event.action == 'closed' && + github.event.pull_request.merged == true && + ( + github.event.pull_request.base.ref == 'master' || + github.event.pull_request.base.ref == 'main' + ) && + ( + github.event.pull_request.head.ref == 'feat' || + startsWith(github.event.pull_request.head.ref, 'feat/') || + github.event.pull_request.head.ref == 'fix' || + startsWith(github.event.pull_request.head.ref, 'fix/') || + github.event.pull_request.head.ref == 'break' || + startsWith(github.event.pull_request.head.ref, 'break/') + ) + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: yarn + registry-url: https://registry.npmjs.org/ + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build package + run: yarn build + + - name: Compute release version + id: release + shell: bash + run: | + set -euo pipefail + + package_name="$(node -p "require('./package.json').name")" + current_version="$(npm view "$package_name" version)" + branch_name="${HEAD_BRANCH}" + + case "$branch_name" in + fix|fix/*) + bump="patch" + ;; + feat|feat/*) + bump="minor" + ;; + break|break/*) + bump="major" + ;; + *) + echo "Unsupported release branch: $branch_name" + exit 1 + ;; + esac + + next_version="$( + node - "$current_version" "$bump" <<'NODE' + const [version, bump] = process.argv.slice(2); + const parts = version.split('.').map(Number); + + if (parts.length !== 3 || parts.some((part) => !Number.isInteger(part))) { + throw new Error(`Unsupported npm version: ${version}`); + } + + if (bump === 'patch') parts[2] += 1; + if (bump === 'minor') { + parts[1] += 1; + parts[2] = 0; + } + if (bump === 'major') { + parts[0] += 1; + parts[1] = 0; + parts[2] = 0; + } + + process.stdout.write(parts.join('.')); + NODE + )" + + npm version "$next_version" --no-git-tag-version + + echo "package_name=$package_name" >> "$GITHUB_OUTPUT" + echo "current_version=$current_version" >> "$GITHUB_OUTPUT" + echo "next_version=$next_version" >> "$GITHUB_OUTPUT" + env: + HEAD_BRANCH: ${{ github.event.pull_request.head.ref }} + + - name: Publish package + run: npm publish --access public --tag latest diff --git a/README.md b/README.md index 04716f8..0abbcec 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -[![npm version](https://badge.fury.io/js/@haskou%2Fvalue-objects.svg)](https://badge.fury.io/js/@haskou%2Fvalue-objects) +[![CI](https://github.com/haskou/value-objects/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/haskou/value-objects/actions/workflows/ci.yml?query=branch%3Amaster) +[![codecov](https://codecov.io/gh/haskou/value-objects/branch/master/graph/badge.svg)](https://codecov.io/gh/haskou/value-objects) +[![npm version](https://img.shields.io/npm/v/@haskou/value-objects.svg)](https://www.npmjs.com/package/@haskou/value-objects) +[![license](https://img.shields.io/npm/l/@haskou/value-objects.svg)](LICENSE.txt) # Value Objects @@ -195,10 +198,23 @@ npm test npm run build ``` +## 🌿 Release Branches + +Publishing is handled by CI when a pull request is merged into the default +branch (`master`, or `main` after a branch rename). Use these branch prefixes +to choose the npm version bump: + +- `fix/*` - patch release +- `feat/*` - minor release +- `break/*` - major release + +Branches without one of these prefixes still run CI, but they do not publish +to npm. Publishing uses npm Trusted Publishing from the `ci.yml` workflow. + ## 🤝 Contributing 1. Fork the repository -2. Create a branch: `git checkout -b my-feature` +2. Create a branch: `git checkout -b feat/my-feature` 3. Make your changes and add tests 4. Run tests: `npm test` 5. Submit a pull request