Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Use this skill when the user asks to:
## Workflow
1. Collect facts:
```bash
yarn test:coverage
yarn test
```
2. Read uncovered details, not only percentages.
Use:
Expand All @@ -48,7 +48,7 @@ Use:
```bash
yarn lint
yarn typecheck
yarn test:coverage
yarn test
```

## Controlled Failure Patterns
Expand Down
8 changes: 6 additions & 2 deletions .github/actions/setup-node-deps/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ inputs:
runs:
using: composite
steps:
- name: Enable Corepack
shell: bash
run: corepack enable

- name: Setup Node.js
if: ${{ inputs.registry-url == '' }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}
cache: yarn
cache-dependency-path: yarn.lock

- name: Setup Node.js with registry
if: ${{ inputs.registry-url != '' }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}
registry-url: ${{ inputs.registry-url }}
Expand Down
174 changes: 130 additions & 44 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,89 @@ on:
workflow_dispatch:
inputs:
release:
description: "Release type"
description: Release type
required: true
default: "auto"
default: auto
type: choice
options:
- auto
- patch
- minor
- major
prerelease:
description: "Prerelease channel"
description: Prerelease channel
required: true
default: "none"
default: none
type: choice
options:
- none
- alpha
- beta
- rc
npm_tag:
description: "npm dist-tag for publication"
description: npm dist-tag (auto selects latest or the prerelease channel)
required: true
default: "latest"
default: auto
type: string

permissions:
contents: write
id-token: write
contents: read

concurrency:
group: release-${{ github.ref }}
group: release
cancel-in-progress: false

jobs:
release:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: write
outputs:
tag: ${{ steps.version.outputs.tag }}
npm_tag: ${{ steps.validate.outputs.npm_tag }}

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Validate release branch and npm tag
id: validate
env:
NPM_TAG: ${{ inputs.npm_tag }}
PRERELEASE: ${{ inputs.prerelease }}
run: |
if [ "$NPM_TAG" = "latest" ] && [ "$GITHUB_REF" != "refs/heads/main" ]; then
echo "Release to npm tag 'latest' must be started from the main branch."
if [ "$GITHUB_REF_TYPE" != branch ]; then
echo "Release must run from a branch."
exit 1
fi
if [ "$PRERELEASE" = none ] && [ "$GITHUB_REF_NAME" != main ]; then
echo "Stable releases must run from main."
exit 1
fi
if [ "$NPM_TAG" = auto ]; then
if [ "$PRERELEASE" = none ]; then
NPM_TAG=latest
else
NPM_TAG="$PRERELEASE"
fi
fi
if [[ ! "$NPM_TAG" =~ ^[a-z][a-z0-9._-]*$ ]]; then
echo "Invalid npm dist-tag: $NPM_TAG"
exit 1
fi
if [ "$PRERELEASE" != none ] && [ "$NPM_TAG" = latest ]; then
echo "Prereleases cannot be published to latest."
exit 1
fi
echo "npm_tag=$NPM_TAG" >> "$GITHUB_OUTPUT"

- name: Setup Node.js and dependencies
uses: ./.github/actions/setup-node-deps
with:
node-version: 24.x
registry-url: https://registry.npmjs.org

- name: Configure git user
run: |
Expand All @@ -72,45 +98,105 @@ jobs:
yarn lint
yarn typecheck
yarn test
yarn test:d
yarn test:package

- name: Run release script
- name: Create release commit and tag
id: version
env:
RELEASE_TYPE: ${{ inputs.release }}
PRERELEASE: ${{ inputs.prerelease }}
YARN_ENABLE_IMMUTABLE_INSTALLS: 'false'
run: |
case "$RELEASE_TYPE" in
auto)
RELEASE_SCRIPT="release"
;;
patch)
RELEASE_SCRIPT="release:patch"
;;
minor)
RELEASE_SCRIPT="release:minor"
;;
major)
RELEASE_SCRIPT="release:major"
;;
*)
echo "Unknown release type: $RELEASE_TYPE"
exit 1
;;
esac

CMD=(yarn run "$RELEASE_SCRIPT")
if [ "$PRERELEASE" != "none" ]; then
PREVIOUS_VERSION=$(node -p "require('./package.json').version")
CMD=(yarn release)
if [ "$RELEASE_TYPE" != auto ]; then
CMD+=(--release-as "$RELEASE_TYPE")
fi
if [ "$PRERELEASE" != none ]; then
CMD+=(--prerelease "$PRERELEASE")
fi

"${CMD[@]}"

- name: Build package
run: yarn build
VERSION=$(node -p "require('./package.json').version")
if [ "$VERSION" = "$PREVIOUS_VERSION" ]; then
echo "No new release was created."
exit 1
fi
TAG="v$VERSION"
test "$(git rev-parse "refs/tags/$TAG^{commit}")" = "$(git rev-parse HEAD)"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Push release commit and tag
run: git push --atomic origin "HEAD:$GITHUB_REF_NAME" "refs/tags/${{ steps.version.outputs.tag }}"

publish:
needs: release
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
id-token: write

steps:
- name: Checkout release tag
uses: actions/checkout@v6
with:
ref: refs/tags/${{ needs.release.outputs.tag }}
persist-credentials: false

- name: Setup Node.js and dependencies
uses: ./.github/actions/setup-node-deps
with:
node-version: 24.x
registry-url: https://registry.npmjs.org

- name: Verify npm supports trusted publishing
run: |
node -e "const [major, minor, patch] = process.argv[1].split('.').map(Number); if (major < 11 || (major === 11 && (minor < 5 || (minor === 5 && patch < 1)))) process.exit(1)" "$(npm --version)"

- name: Push release commit and tags
run: git push origin "HEAD:${GITHUB_REF_NAME}" --follow-tags
- name: Check released package
run: |
yarn lint
yarn typecheck
yarn test
yarn test:d
yarn test:package

- name: Check published version
id: published
run: |
NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
npm view "$NAME" versions --json > "$RUNNER_TEMP/published-versions.json"
PUBLISHED=$(node -e "const fs = require('node:fs'); const versions = JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); console.log([].concat(versions).includes(process.argv[2]))" "$RUNNER_TEMP/published-versions.json" "$VERSION")
echo "published=$PUBLISHED" >> "$GITHUB_OUTPUT"

- name: Publish to npm
- name: Publish to npm with trusted publishing
if: steps.published.outputs.published == 'false'
env:
NPM_TAG: ${{ inputs.npm_tag }}
run: npm publish --access public --tag "$NPM_TAG"
NPM_TAG: ${{ needs.release.outputs.npm_tag }}
run: |
unset NODE_AUTH_TOKEN NPM_TOKEN
npm publish --access public --tag "$NPM_TAG"

github-release:
needs: [release, publish]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release.outputs.tag }}
PRERELEASE: ${{ inputs.prerelease }}
run: |
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
exit 0
fi
CMD=(gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --verify-tag --generate-notes --title "$TAG")
if [ "$PRERELEASE" != none ]; then
CMD+=(--prerelease --latest=false)
fi
"${CMD[@]}"
16 changes: 11 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

steps:
- name: Using branch ${{ github.ref }} for repository ${{ github.repository }}.
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Run actionlint
run: make actionlint
Expand All @@ -30,7 +30,7 @@ jobs:

steps:
- name: Using branch ${{ github.ref }} for repository ${{ github.repository }}.
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup Node.js and dependencies
uses: ./.github/actions/setup-node-deps
Expand All @@ -51,21 +51,27 @@ jobs:

steps:
- name: Using branch ${{ github.ref }} for repository ${{ github.repository }}.
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup Node.js and dependencies
uses: ./.github/actions/setup-node-deps
with:
node-version: ${{ matrix.node-version }}

- name: Check TypeScript
run: yarn typecheck

- name: Run tests
run: yarn test:coverage
run: yarn test

- name: Run type tests
run: yarn test:d

- name: Check packed package consumers
run: yarn test:package

- name: Upload coverage to GitHub Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: coverage-${{ matrix.node-version }}
path: coverage/
Expand Down
12 changes: 0 additions & 12 deletions .versionrc.json

This file was deleted.

11 changes: 11 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,14 @@ enableGlobalCache: false
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.12.0.cjs

# The TypeScript config loader needs peers forwarded through @commitlint/load.
packageExtensions:
"@commitlint/load@21.2.3":
peerDependencies:
"@types/node": "*"
typescript: ">=5"
"@commitlint/cli@21.2.3":
peerDependencies:
"@types/node": "*"
typescript: ">=5"
14 changes: 5 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,10 @@ yarn lint
```bash
yarn typecheck
```
- Test:
- Test (coverage is enabled by default; reports are written to `coverage/`):
```bash
yarn test
```
- Coverage:
```bash
yarn test:coverage
```
- Build:
```bash
yarn build
Expand All @@ -73,10 +69,10 @@ yarn build

## Important Project Rules
- Commit messages follow Conventional Commits.
- Before creating any commit, always reread `skills/commit-workflow/SKILL.md` and follow it as the source of truth for commit splitting, wording, scopes, and lockfile policy.
- Before creating any commit, always reread `.agents/skills/commit-workflow/SKILL.md` and follow it as the source of truth for commit splitting, wording, scopes, and lockfile policy.
- Getter/helper functions must be free of side effects. Side effects are allowed only by prior agreement and only when there are strong, explicit reasons.

## Local Skills
- `skills/commit-workflow/SKILL.md` - rules for splitting changes into commits and writing changelog-friendly Conventional Commit messages.
- `skills/coverage-recovery/SKILL.md` - workflow for analyzing uncovered code paths and improving test coverage without adding artificial tests.
- `skills/yarn-lock-conflict-resolution/SKILL.md` - safe procedure for resolving `yarn.lock` conflicts during merge or rebase.
- `.agents/skills/commit-workflow/SKILL.md` - rules for splitting changes into commits and writing changelog-friendly Conventional Commit messages.
- `.agents/skills/coverage-recovery/SKILL.md` - workflow for analyzing uncovered code paths and improving test coverage without adding artificial tests.
- `.agents/skills/yarn-lock-conflict-resolution/SKILL.md` - safe procedure for resolving `yarn.lock` conflicts during merge or rebase.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
All notable changes to this project will be documented in this file. Release notes are generated by [@modulify/conventional-release](https://github.com/modulify/conventional/tree/main/packages/conventional-release) from Conventional Commits.

### [0.2.1](https://github.com/modulify/validator/compare/v0.2.0...v0.2.1) (2026-03-09)

Expand Down
Loading
Loading