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
99 changes: 99 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: CI

# No `paths:` filter: required checks must always report a status, or PRs
# that don't touch matching paths would wait forever on a skipped check.
on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test:
name: test (${{ matrix.package }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# tiny-mustache has no tests yet; add it here once it does.
package:
- eslint-config-forumone-es5
- eslint-config-forumone-react
defaults:
run:
working-directory: ./packages/${{ matrix.package }}
steps:
- name: Checkout Code
uses: actions/checkout@v5
with:
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24

# The react package depends on the es5 package via a `file:` link, so
# es5's own dependencies (e.g. @eslint/js) must be installed too.
- name: Install es5 Dependencies (linked by react)
if: matrix.package == 'eslint-config-forumone-react'
working-directory: ./packages/eslint-config-forumone-es5
run: npm ci

- name: Install Dependencies
run: npm ci

- name: Run Tests
run: npm test

# Single stable check name to require in the ruleset, regardless of how
# the matrix above changes.
tests-pass:
name: tests-pass
runs-on: ubuntu-latest
needs: [test]
if: always()
steps:
- name: Verify all test jobs succeeded
env:
RESULT: ${{ needs.test.result }}
run: |
if [[ "$RESULT" != "success" ]]; then
echo "Test jobs finished with result: $RESULT" >&2
exit 1
fi

version-bumped:
name: version-bumped
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false

- name: Require at least one package version change
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
changed=0
for pkg in packages/*/package.json; do
head_version=$(node -p "require('./$pkg').version")
if base_json=$(git show "$BASE_SHA:$pkg" 2>/dev/null); then
base_version=$(node -p "JSON.parse(process.argv[1]).version" "$base_json")
else
base_version="(new package)"
fi
if [[ "$head_version" != "$base_version" ]]; then
Comment thread
kmonahan marked this conversation as resolved.
Resolved
echo "✔ $pkg: $base_version -> $head_version"
changed=1
else
echo " $pkg: unchanged ($head_version)"
fi
done
if [[ "$changed" -eq 0 ]]; then
echo "No package version changed. Bump the version of at least one package." >&2
exit 1
fi
162 changes: 162 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Publish packages

# Publishes any package whose package.json version is not yet on npm.
# To release: bump `version` (and update the CHANGELOG) in a PR, then merge to main.
# Packages whose version is already on npm are skipped.

on:
push:
branches: [main]
paths:
- 'packages/**'
- '.github/workflows/publish.yml'
workflow_dispatch:

# Never run two publishes at once, and don't cancel one midway.
concurrency:
group: publish
cancel-in-progress: false

jobs:
publish-npm:
# workflow_dispatch can be run on any branch; only ever publish from main.
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package:
- eslint-config-forumone-es5
- eslint-config-forumone-react
- tiny-mustache
defaults:
run:
working-directory: ./packages/${{ matrix.package }}
permissions:
contents: read
id-token: write # npm trusted publishing (OIDC)
steps:
- name: Checkout Code
uses: actions/checkout@v5
with:
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
registry-url: 'https://registry.npmjs.org'

- name: Reject pre-release versions
run: |
VERSION=$(node -p "require('./package.json').version")
if [[ "$VERSION" == *-* ]]; then
echo "Pre-release version '$VERSION' is not supported by this workflow." >&2
exit 1
fi

- name: Check whether this version is already on npm
id: check
run: |
NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
# Prints nothing if the package or version doesn't exist yet.
FOUND=$(npm view "$NAME@$VERSION" version 2>/dev/null || true)
if [[ -n "$FOUND" ]]; then
echo "$NAME@$VERSION is already published; skipping."
echo "needed=false" >> "$GITHUB_OUTPUT"
else
echo "$NAME@$VERSION is not on npm; will publish."
echo "needed=true" >> "$GITHUB_OUTPUT"
fi

- name: Install Dependencies
# tiny-mustache has no lockfile or dependencies
if: steps.check.outputs.needed == 'true' && hashFiles(format('packages/{0}/package-lock.json', matrix.package)) != ''
run: npm ci

- name: Run tests
if: steps.check.outputs.needed == 'true'
run: |
# Skip packages with no real test script (e.g. the npm-init stub).
TEST_SCRIPT=$(node -p "require('./package.json').scripts?.test ?? ''")
if [[ -z "$TEST_SCRIPT" || "$TEST_SCRIPT" == *"no test specified"* ]]; then
echo "No test script; skipping."
else
npm test
fi

- name: Publish to npm
# Authenticates via OIDC trusted publishing; provenance is attached automatically.
# Pre-release versions were already rejected above, so this always publishes `latest`.
if: steps.check.outputs.needed == 'true'
run: npm publish

# Create a GitHub release for the ES5 config (only when its version has no release yet,
# i.e. it was just published), then publish it to GitHub Packages.
release-es5:
needs: publish-npm
# Run even if another package's publish failed; the npm check below guards correctness.
if: ${{ !cancelled() && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
tag: ${{ steps.release.outputs.tag }}
created: ${{ steps.release.outputs.created }}
steps:
- name: Checkout Code
uses: actions/checkout@v5
with:
persist-credentials: false

- name: Create release if this version has none
id: release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
VERSION=$(node -p "require('./packages/eslint-config-forumone-es5/package.json').version")
TAG="eslint-config-es5-v$VERSION"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

# The version must be on npm (published now or earlier) before we release it.
# Registry metadata can lag a freshly published version, so retry for ~90s.
ON_NPM=false
for attempt in 1 2 3 4 5 6; do
if [[ -n "$(npm view "@forumone/eslint-config-es5@$VERSION" version 2>/dev/null || true)" ]]; then
ON_NPM=true
break
fi
echo "Attempt $attempt: @forumone/eslint-config-es5@$VERSION not visible on npm yet."
[[ $attempt -lt 6 ]] && sleep 15
done
if [[ "$ON_NPM" != "true" ]]; then
echo "@forumone/eslint-config-es5@$VERSION is not on npm; not creating a release."
echo "created=false" >> "$GITHUB_OUTPUT"
exit 0
fi

if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists; nothing to do."
echo "created=false" >> "$GITHUB_OUTPUT"
exit 0
fi

gh release create "$TAG" \
--target "$GITHUB_SHA" \
--title "@forumone/eslint-config-es5 $VERSION" \
--generate-notes
echo "created=true" >> "$GITHUB_OUTPUT"

# Releases created with GITHUB_TOKEN don't fire `release: published`,
# so call the GitHub Packages workflow directly.
publish-es5-github:
needs: release-es5
if: needs.release-es5.outputs.created == 'true'
uses: ./.github/workflows/release-eslint-config-es5.yml
with:
tag_name: ${{ needs.release-es5.outputs.tag }}
permissions:
contents: read
packages: write
13 changes: 10 additions & 3 deletions .github/workflows/release-eslint-config-es5.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ name: Release @forumone/eslint-config-es5 on GitHub

on:
release:
types: [published] # Triggers when you create a GitHub release
types: [published] # Triggers when you create a GitHub release manually
workflow_call: # Called by publish.yml, since releases it creates can't trigger this workflow
inputs:
tag_name:
description: Release tag, e.g. eslint-config-es5-v5.0.1
required: true
type: string

jobs:
publish-registry:
# Only run for releases tagged for this package, e.g. eslint-config-es5-v4.0.1
if: startsWith(github.event.release.tag_name, 'eslint-config-es5-v')
# (inputs.tag_name is set when called from publish.yml; otherwise use the release event)
if: startsWith(inputs.tag_name || github.event.release.tag_name, 'eslint-config-es5-v')
runs-on: ubuntu-latest
defaults:
run:
Expand All @@ -30,7 +37,7 @@ jobs:

- name: Validate release tag matches package version
env:
ACTUAL_TAG: ${{ github.event.release.tag_name }}
ACTUAL_TAG: ${{ inputs.tag_name || github.event.release.tag_name }}
run: |
VERSION=$(node -p "require('./package.json').version")
EXPECTED_TAG="eslint-config-es5-v$VERSION"
Expand Down
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
Forum One JavaScript Tools
================================
Modules
-------
# Forum One JavaScript Tools

### eslint-config-forumone-es5
## Modules

Forum One's base JavaScript ESLint configuration.
### eslint-config-forumone-es5 (`@forumone/eslint-config-es5`)

Forum One's ESLint configuration for JavaScript and TypeScript.

### eslint-config-forumone-react (`@forumone/eslint-config-react`)

Forum One's ESLint configuration for React.

### tiny-mustache (`@forumone/tiny-mustache`)

Fork of tiny-mustache for Forum One.

## Releasing

Releases are automated by `.github/workflows/publish.yml` on every push to `main`.

1. In your PR, bump `version` in the package's `package.json` and update its `CHANGELOG.md`.
2. Merge to `main`. For each package, CI checks whether that version is already on npm. If it isn't, CI runs the package's tests and publishes it to npm. Packages whose version is unchanged are skipped.
3. If `@forumone/eslint-config-es5` was released, CI also creates a GitHub release tagged `eslint-config-es5-v<version>` and publishes the package to GitHub Packages (`release-eslint-config-es5.yml`).

Pre-release versions (e.g. `1.0.0-alpha.1`) are not supported; the workflow fails if a package version contains a `-`.

npm publishing uses [trusted publishing](https://docs.npmjs.com/trusted-publishers) (OIDC), so there is no npm token. Each package must have `forumone/javascript` and the workflow filename `publish.yml` configured as a trusted publisher on npmjs.com.
35 changes: 0 additions & 35 deletions packages/eslint-config-forumone-angular/README.md

This file was deleted.

Loading
Loading