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
22 changes: 21 additions & 1 deletion .commitlintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
{
"extends": [
"@commitlint/config-conventional"
]
],
"rules": {
"type-enum": [
2,
"always",
[
"build",
"chore",
"ci",
"deps",
"docs",
"feat",
"fix",
"perf",
"refactor",
"revert",
"style",
"test"
]
]
}
}
50 changes: 41 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
- 'feature/**'
pull_request:
branches:
- main
- develop
- 'feature/**'
workflow_call:
Expand All @@ -20,19 +21,21 @@ on:
jobs:
compute-matrix:
name: Compute matrix
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.set.outputs.matrix }}
name_suffix: ${{ steps.set.outputs.name_suffix }}
steps:
- id: set
env:
FULL_MATRIX: ${{ inputs.full || (github.event_name == 'pull_request' && github.base_ref == 'main') }}
run: |
if [[ "${{ inputs.full }}" == "true" ]]; then
echo 'matrix={"os":["ubuntu-latest","windows-latest"],"node":[18,20,22]}' >> $GITHUB_OUTPUT
echo 'name_suffix=(full matrix)' >> $GITHUB_OUTPUT
if [[ "$FULL_MATRIX" == "true" ]]; then
echo 'matrix={"os":["ubuntu-24.04","windows-latest"],"node":[18,20,22]}' >> "$GITHUB_OUTPUT"
echo 'name_suffix=(full matrix)' >> "$GITHUB_OUTPUT"
else
echo 'matrix={"os":["ubuntu-latest"],"node":[20]}' >> $GITHUB_OUTPUT
echo 'name_suffix=' >> $GITHUB_OUTPUT
echo 'matrix={"os":["ubuntu-24.04"],"node":[20]}' >> "$GITHUB_OUTPUT"
echo 'name_suffix=' >> "$GITHUB_OUTPUT"
fi

build-and-test:
Expand All @@ -46,7 +49,7 @@ jobs:
matrix: ${{ fromJSON(needs.compute-matrix.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Configure git EOL (Windows)
if: runner.os == 'Windows'
Expand All @@ -55,7 +58,7 @@ jobs:
git config core.eol lf

- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: ${{ matrix.node }}
cache: npm
Expand All @@ -77,7 +80,36 @@ jobs:

- name: Upload coverage artifact
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: coverage-${{ matrix.os }}-node${{ matrix.node }}
path: coverage

renovate-config:
name: Validate Renovate configuration
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Use Node.js
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24

- name: Validate configuration
run: npx --yes --package renovate@44.155.8 -- renovate-config-validator --strict renovate.json

ci-success:
name: CI passed
if: ${{ always() }}
needs: [compute-matrix, build-and-test, renovate-config]
runs-on: ubuntu-24.04
permissions: {}
steps:
- name: Require all checks to pass
env:
CHECKS_PASSED: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'skipped') }}
run: test "$CHECKS_PASSED" = true
10 changes: 6 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,25 @@ jobs:

release:
name: Release & Publish
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
needs: ci
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
fetch-tags: true

- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22
cache: 'npm'

- name: Update npm
run: npm install -g npm@latest
run: |
npm install -g npm@11.7.0
npm -v

- name: Install dependencies
run: npm ci
Expand Down
63 changes: 63 additions & 0 deletions .github/workflows/renovate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Update Dependencies

on:
schedule:
- cron: '15 2 * * 0'
timezone: America/Los_Angeles
workflow_dispatch:

permissions:
contents: read

concurrency:
group: renovate-dependencies
cancel-in-progress: false

jobs:
renovate:
name: Create or update dependency PR
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- name: Check GitHub App configuration
env:
APP_CLIENT_ID: ${{ vars.RENOVATE_APP_CLIENT_ID }}
APP_PRIVATE_KEY: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
run: |
if [[ -z "$APP_CLIENT_ID" || -z "$APP_PRIVATE_KEY" ]]; then
echo '::error::Set RENOVATE_APP_CLIENT_ID (Actions variable) and RENOVATE_APP_PRIVATE_KEY (Actions secret). See CONTRIBUTING.md.'
exit 1
fi

- name: Create repository-scoped GitHub App token
id: app-token
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
# Omitting owner/repositories scopes the shared app's token to this repository.
client-id: ${{ vars.RENOVATE_APP_CLIENT_ID }}
private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}

- name: Resolve bot identity
id: bot
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
run: |
BOT_LOGIN="${APP_SLUG}[bot]"
BOT_ID=$(gh api "/users/${BOT_LOGIN}" --jq .id)
printf 'username=%s\n' "$BOT_LOGIN" >> "$GITHUB_OUTPUT"
printf 'git-author=%s <%s+%s@users.noreply.github.com>\n' "$BOT_LOGIN" "$BOT_ID" "$BOT_LOGIN" >> "$GITHUB_OUTPUT"

- name: Run Renovate
uses: renovatebot/github-action@f0f41e5f2e706165f6814b1d0f0d0e319de7d1d9 # v46.3.8
with:
token: ${{ steps.app-token.outputs.token }}
renovate-version: 44.155.8
env:
RENOVATE_PLATFORM: github
RENOVATE_REPOSITORIES: ${{ github.repository }}
RENOVATE_AUTODISCOVER: 'false'
RENOVATE_ONBOARDING: 'false'
RENOVATE_REQUIRE_CONFIG: required
RENOVATE_USERNAME: ${{ steps.bot.outputs.username }}
RENOVATE_GIT_AUTHOR: ${{ steps.bot.outputs.git-author }}
3 changes: 2 additions & 1 deletion .release-it.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ function getContributors() {
const types = new Map([
["feat", "✨ Features"],
["fix", "🐛 Bug Fixed"],
["deps", "📦 Dependencies"],
["perf", "⚡️ Performance Improvements"],
["refactor", "🛠️ Refactoring"],
["docs", "📝 Documentation"],
Expand Down Expand Up @@ -154,7 +155,7 @@ function whatBump(commits, currentVersion = pkg.version) {
isMinor = true;
}

if (["fix", "perf", "refactor", "ci"].includes(type)) {
if (["fix", "deps", "perf", "refactor"].includes(type)) {
isPatch = true;
}
}
Expand Down
42 changes: 36 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ git switch -c feature/<short-name>

## Local setup

Node.js 20 is the default CI environment. The full release gate also exercises Node.js 18, 20, and 22.
Node.js 20 is the default CI environment. Pull requests into `main` and the release gate also exercise Node.js 18, 20, and 22.

```bash
git clone git@github.com:addon-stack/inject-script.git
Expand All @@ -48,7 +48,8 @@ Useful commands:
| `npm run typecheck` | Type-check package sources |
| `npm run test:types` | Type-check public API contract fixtures |
| `npm run test` | Build and run the Jest suite |
| `npm run test:ci` | Build and run Jest with coverage |
| `npm run test:ci` | Build, run Jest with coverage, and check dependency-release integration |
| `npm run test:tooling` | Verify Renovate, commitlint, and changelog integration |
| `npm run release` | Perform a real maintainer release through release-it |

`npm run release` is not a dry run. Do not execute it unless you are intentionally publishing a release and have the required maintainer access.
Expand All @@ -71,11 +72,12 @@ When changing public behavior, update the implementation, types, tests, and READ

## Tests

The test suite covers three different contracts:
The test suite covers the following contracts:

- `tests/types.test.ts` verifies compile-time API behavior.
- `tests/inject-script.test.cjs` verifies MV2/MV3 runtime behavior against the built package.
- `tests/release-it.test.cjs` verifies release versioning policy.
- `tests/dependency-release.test.mjs` verifies the real parser, commitlint CLI, and generated dependency release notes.

For adapter changes, cover the affected combinations where relevant:

Expand Down Expand Up @@ -107,7 +109,7 @@ Local hooks provide additional protection:
- `commit-msg` validates Conventional Commits with commitlint.
- `pre-push` runs `typecheck` and the full `test:ci` command.

GitHub Actions repeats lint, typecheck, tests, coverage, and production build checks. Release runs use the full operating-system and Node.js matrix.
GitHub Actions repeats lint, typecheck, tests, coverage, and production build checks. Pull requests into `main` and release runs use the full operating-system and Node.js matrix. The aggregate `CI passed` check also requires strict Renovate configuration validation; failed, cancelled, or skipped prerequisites fail this check.

## Commit messages

Expand All @@ -125,6 +127,8 @@ Common examples:
```text
feat(target): add document targeting
fix(mv2): preserve partial timeout results
deps: update dependencies
ci: update workflow actions
refactor(results): centralize native normalization
test(types): cover interface arguments
docs: simplify the quick start
Expand All @@ -145,12 +149,38 @@ Release versions are derived from commit history by `release-it` and `@release-i
- A breaking change increments major at `1.x` and newer.
- A breaking change increments minor while the package is on `0.x`.
- `feat` and `revert` increment minor.
- `fix`, `perf`, `refactor`, and `ci` increment patch.
- `docs`, `test`, `build`, `chore`, and `style` do not trigger a release by themselves.
- `fix`, `deps`, `perf`, and `refactor` increment patch.
- `docs`, `test`, `build`, `chore`, `ci`, and `style` do not trigger a release by themselves.
- When several changes are present, the highest applicable increment wins.

Do not edit released changelog entries manually. `CHANGELOG.md` is generated from Conventional Commits during release.

## Automated dependency updates

The **Update Dependencies** workflow runs every Sunday at **02:15 America/Los_Angeles** and can also be started from **Actions → Update Dependencies → Run workflow**. The time follows Los Angeles daylight saving rules.

Renovate reads `renovate.json` from `main` and updates only `@addon-core/browser` and `@types/chrome`, together with their required lockfile changes. Stable updates of any size are grouped in one `deps/update-dependencies` branch and one PR into `main`, including Chrome-types-only updates. Other direct dependencies and standalone lockfile maintenance are disabled.

On each run, Renovate refreshes its branch from the current `main` when needed. An open bot-managed PR is updated in place. After merging, a future update can recreate the same branch name. No available update means no empty PR. Manually committing to an open bot branch pauses automatic updates to protect those commits.

The bot creates `deps: update dependencies` commits. Ordinary dependency-only changes trigger a **patch** release and appear under **📦 Dependencies** in the changelog and GitHub release notes. Keep the `deps: ...` title when squashing. Breaking markers and other commits still follow the normal release policy.

The App token triggers the normal PR checks: lint, source and public API type checks, build, tests, and strict Renovate configuration validation. **Merging remains manual.** Failed checks leave the PR open and do not publish a release. After merge, the existing Release workflow repeats the full checks, publishes the package, and syncs `main` back into `develop`.

Keep `conventional-commits-parser` 6.x as an explicit development dependency. The release tooling needs parser 6.x through optional peers, while commitlint needs parser 5.x. Explicitly declaring parser 6.x prevents npm lockfile-only regeneration from dropping the release parser. After changing it, regenerate the lockfile in a clean directory and verify `npm ci` and the release tooling tests.

### Connect the shared GitHub App

Reuse the existing organization App **addon-stack-dependency-updates**; each package keeps its own workflow and dependency rules.

1. In the `addon-stack` organization settings, open **GitHub Apps → Configure** for the existing installation and add **inject-script** under **Only select repositories**. Keep existing repositories selected. The existing App permissions are sufficient; no new App or key is needed.
2. Under **Secrets and variables → Actions**, add `inject-script` to the selected repositories for both the organization variable `RENOVATE_APP_CLIENT_ID` and the organization secret `RENOVATE_APP_PRIVATE_KEY`. The workflow creates a short-lived token scoped to this repository only. Renovate does not need an npm publishing token.
3. Merge the setup from `develop` into `main`. Both scheduled runs and the manual workflow entry need the workflow file on the default branch.
4. Run **Update Dependencies** manually and inspect the first PR. Configure the PR check **CI passed** as required for `main` and require an up-to-date branch. Do not give the dependency App a branch-protection bypass.
5. Account for release automation before enforcing branch rules: `release-it` pushes version/changelog commits to `main`, and the sync step writes to `develop`. Rules requiring PRs for all writes need a suitable exception for the release writer; the dependency App is not the release writer.

For another package, repeat the installation and organization-variable/secret access steps, then add its workflow, dependency allowlist, release policy, tests, and documentation. GitHub supports [changing the repositories available to an installed App](https://docs.github.com/en/apps/using-github-apps/reviewing-and-modifying-installed-github-apps) and [sharing organization secrets with selected repositories](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets).

## Pull requests

Open regular pull requests against `develop` and include:
Expand Down
Loading