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
10 changes: 6 additions & 4 deletions .changeset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ not `fixed`: a patch that only touches angular still must not bump and republish
three packages whose tarballs did not change.

**`onlyUpdatePeerDependentsWhenOutOfRange`** stops a core release from
rewriting each adapter's `@dynamic-field-kit/core` peer range. The adapters use
only core APIs that have existed since 1.3.0, so `^1.3.0` is the honest range —
tightening it on every core release would force consumers to upgrade core for
features their adapter does not call.
rewriting each adapter's `@dynamic-field-kit/core` peer range. The floor is the
oldest core that has every API the adapter imports, and it moves by hand when an
adapter starts calling something new: since 1.12.0 the adapters import
`resolveVisible` and `makeLabelId`, so the range is `^1.12.0`. Tightening it on
every core release would force consumers to upgrade core for features their
adapter does not call.
33 changes: 31 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ updates:
prefix: chore
include: scope
ignore:
# @angular/compiler-cli 21 accepts typescript <6.1.0. Move this with Angular.
# The Angular 21 toolchain takes typescript >=5.9 <6.0 (@angular-devkit/build-angular,
# @ngtools/webpack, ng-packagr; compiler-cli alone would take <6.1): 6.0.3 failed npm ci
# with ERESOLVE (#110). Move this with Angular.
- dependency-name: typescript
versions: ['>=6.1.0']
versions: ['>=6.0.0']
# The Angular package is built and tested with Angular 21 (see packages/angular/README.md).
# Moving that toolchain to a new major is a change of its own, checked by
# scripts/verify-angular-peer-range.js, not a dependency bump.
Expand All @@ -39,6 +41,33 @@ updates:
update-types: ['version-update:semver-major']
- dependency-name: 'ng-packagr'
update-types: ['version-update:semver-major']
# @angular/build 21 has vitest ^4 as a peer, and npm 10 (Node 22, this repo's CI) stops
# with "Cannot read properties of null (reading 'edgesOut')" on vitest 5 (#100).
# @angular/build 22 accepts ^5: move vitest with the Angular toolchain.
- dependency-name: 'vitest'
update-types: ['version-update:semver-major']
- dependency-name: '@vitest/*'
update-types: ['version-update:semver-major']
# The React Storybook has its own lockfile. One weekly pull request for all of it:
# Storybook's packages only work at one version together, and the rest is build tooling.
- package-ecosystem: npm
directory: /storybook
schedule:
interval: weekly
groups:
storybook:
patterns:
- '*'
commit-message:
prefix: chore
include: scope
ignore:
# Same hold as the root: vitest moves with the Angular toolchain there.
- dependency-name: 'vitest'
update-types: ['version-update:semver-major']
# Matches the root's TypeScript hold below 6.0.
- dependency-name: typescript
versions: ['>=6.0.0']
- package-ecosystem: github-actions
directory: /
schedule:
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/cache-cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Cache cleanup

# Actions caches share a 10 GB limit per repository, and over it GitHub evicts on its
# own, caches still in use among them. Two kinds are dead weight:
# - a pull request's caches, which only that pull request can read: deleted when it
# closes, and by the weekly sweep for any closed before this workflow existed;
# - on a branch, older copies of a cache a newer run saved again under a new key
# (a changed lockfile, a new Gradle state): the sweep deletes a copy left unused for a
# day longer than the newest one of its kind. GitHub would only drop it after 7 days.
on:
pull_request_target:
types: [closed]
schedule:
- cron: '23 3 * * 0'
workflow_dispatch:

# Nothing is checked out: the workflow reads only the pull request number.
permissions:
actions: write
contents: read
pull-requests: read

jobs:
cleanup:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Delete caches nothing will read again
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
gh api --paginate "repos/$GH_REPO/actions/caches?per_page=100" --jq '.actions_caches[]' > caches.json
if [ -n "$PR_NUMBER" ]; then
open='[]'
filter='map(select(.ref == "refs/pull/'"$PR_NUMBER"'/merge"))'
else
open=$(gh pr list --state open --limit 500 --json number --jq '[.[] | "refs/pull/\(.number)/merge"]')
filter='
def t: .last_accessed_at[0:19] + "Z" | fromdateiso8601;
def kind: .key | sub("[-_|][0-9a-f]{12,}.*$"; "");
map(select(.ref | startswith("refs/pull/")) | select(.ref as $r | $open | index($r) | not))
+ (map(select(.ref | startswith("refs/pull/") | not))
| group_by([.ref, kind])
| map((map(t) | max) as $newest | map(select(t < $newest - 86400)))
| add // [])'
fi
jq -rs --argjson open "$open" "$filter | .[] | [.id, .size_in_bytes, .ref, .key] | @tsv" caches.json > delete.tsv
deleted=0; bytes=0
while IFS=$'\t' read -r id size ref key; do
if gh api -X DELETE "repos/$GH_REPO/actions/caches/$id" > /dev/null; then
echo "deleted $ref $key"
deleted=$((deleted + 1)); bytes=$((bytes + size))
fi
done < delete.tsv
echo "### Deleted $deleted caches, $((bytes / 1048576)) MB" >> "$GITHUB_STEP_SUMMARY"
40 changes: 38 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,37 @@ jobs:
gates:
uses: ./.github/workflows/quality-gates.yml

# The React Storybook lives outside the workspaces with its own lockfile, so
# its install never touches the packages' dependency tree. Its stories read
# the packages from source: a story broken by an API change fails here.
storybook:
name: Storybook
runs-on: ubuntu-latest
defaults:
run:
working-directory: storybook
steps:
- uses: actions/checkout@v7

- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache-dependency-path: storybook/package-lock.json

- name: Install Storybook dependencies
run: npm ci --no-audit --no-fund

- name: Typecheck the stories
run: npm run typecheck

- name: Render every story
run: npm test

- name: Build Storybook
run: npm run build-storybook

changeset:
name: Changeset present
runs-on: ubuntu-latest
Expand All @@ -24,11 +55,16 @@ jobs:
# none left;
# - any PR into master, which carries changes that were already released
# from develop - their changesets are gone by definition, and demanding a
# new one would mean releasing the same work twice.
# new one would mean releasing the same work twice;
# - Dependabot's PRs, which change development dependencies almost always.
# Each one failed here until an empty changeset was pushed to it by hand.
# A dependency update that does need a release gets its changeset with
# the release.
if: >-
github.event_name == 'pull_request' &&
github.base_ref == 'develop' &&
!startsWith(github.head_ref, 'changeset-release/')
!startsWith(github.head_ref, 'changeset-release/') &&
!startsWith(github.head_ref, 'dependabot/')
steps:
- uses: actions/checkout@v7
with:
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/deploy-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
- 'example/**'
- 'handbook/**'
- 'docs/**'
- 'storybook/**'
- 'README.md'
- '.github/workflows/deploy-pages.yml'
workflow_dispatch:
Expand Down Expand Up @@ -94,11 +95,20 @@ jobs:
npm install --no-audit --no-fund
npm run build

# Reads the packages from source (storybook/vite.shared.ts), so it does
# not need the build above; it has its own lockfile and install.
- name: Build Storybook
working-directory: storybook
run: |
npm ci --no-audit --no-fund
npm run build-storybook

- name: Assemble the site
run: |
mkdir -p _site
cp example/pages-index.html _site/index.html
cp -r handbook/.vitepress/dist _site/handbook
cp -r storybook/storybook-static _site/storybook
cp -r example/react-app/out _site/react
cp -r example/vue-app/dist _site/vue
cp -r example/svelte-app/dist _site/svelte
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/quality-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ jobs:
- name: Install workspace dependencies
run: npm ci

# `npm install`, not `npm ci`: example/angular-app gitignores its
# package-lock.json, so there is no lockfile to clean-install from. The
# other two still honour their committed lockfiles.
# Every example has a committed lockfile, which `npm install` honours. angular-app's
# was gitignored until 15/10/2026: CI then resolved it fresh each run, and npm 10 (Node
# 22) crashed resolving it: "Cannot read properties of null (reading 'edgesOut')".
- name: Install example dependencies
working-directory: example/${{ matrix.app }}
run: npm install --no-audit --no-fund
Expand Down
67 changes: 50 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,19 @@ jobs:
git tag -a "v$version" -m "Release v$version" -m "$published"
git push origin "v$version"
fi

# A package that moved on its own (angular 1.11.1 next to core 1.11.0) has no
# v<version> tag to stand for it: it gets one named after itself.
own=$(node scripts/own-release-packages.js)
while read -r tag pkg pv; do
[ -n "$tag" ] || continue
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
echo "Tag $tag already exists - leaving it alone."
else
git tag -a "$tag" -m "Release $tag"
git push origin "$tag"
fi
done <<< "$own"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand All @@ -174,26 +187,44 @@ jobs:

if gh release view "v$version" >/dev/null 2>&1; then
echo "Release v$version already exists - leaving it alone."
exit 0
else
{
echo 'All five packages ship at this version with npm provenance:'
echo
for pkg in core react vue angular svelte; do
echo "- \`@dynamic-field-kit/$pkg@$(node -p "require('./packages/$pkg/package.json').version")\`"
done
echo
awk -v heading="## $version" \
'$0 == heading { found = 1; next } /^## / { if (found) exit } found' \
packages/core/CHANGELOG.md
} > "$RUNNER_TEMP/release-notes.md"

gh release create "v$version" \
--title "v$version" \
--notes-file "$RUNNER_TEMP/release-notes.md" \
--verify-tag \
--latest
fi

{
echo 'All five packages ship at this version with npm provenance:'
echo
for pkg in core react vue angular svelte; do
echo "- \`@dynamic-field-kit/$pkg@$(node -p "require('./packages/$pkg/package.json').version")\`"
done
echo
awk -v heading="## $version" \
# A package that moved on its own gets a Release of its own, from its own
# CHANGELOG. It is not marked latest: v<version> stays the release people land on.
own=$(node scripts/own-release-packages.js)
while read -r tag pkg pv; do
[ -n "$tag" ] || continue
if gh release view "$tag" >/dev/null 2>&1; then
echo "Release $tag already exists - leaving it alone."
continue
fi
awk -v heading="## $pv" \
'$0 == heading { found = 1; next } /^## / { if (found) exit } found' \
packages/core/CHANGELOG.md
} > "$RUNNER_TEMP/release-notes.md"

gh release create "v$version" \
--title "v$version" \
--notes-file "$RUNNER_TEMP/release-notes.md" \
--verify-tag \
--latest
"packages/$pkg/CHANGELOG.md" > "$RUNNER_TEMP/release-notes-$pkg.md"
gh release create "$tag" \
--title "$tag" \
--notes-file "$RUNNER_TEMP/release-notes-$pkg.md" \
--verify-tag \
--latest=false
done <<< "$own"
env:
GH_TOKEN: ${{ github.token }}

Expand All @@ -202,4 +233,6 @@ jobs:
run: |
echo 'Dry run - nothing was committed, tagged or published.'
echo 'Versions that would have shipped are listed above.'
echo 'Packages that would get a tag and a Release of their own (tag, package, version):'
node scripts/own-release-packages.js
git --no-pager diff --stat
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ handbook/.vitepress/cache/

# svelte-package's working copy of the Svelte adapter's source
.svelte-kit/

# Storybook's static build
storybook-static/
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ Thumbs.db

# svelte-package's working copy of the Svelte adapter's source
.svelte-kit/

# Storybook's static build
storybook-static/
Loading
Loading