diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000000..2915d0fec1 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,39 @@ +# Changesets + +Every PR that changes a package adds a changeset: a Markdown file in this directory that names the changed packages, the bump type, and a one-line summary for the changelog. Do not edit `CHANGELOG.md` files. The release generates them from the changesets. + +## Add a changeset + +```sh +yarn changeset +``` + +Select the changed packages and the bump type, then write the summary. Commit the new file with your PR. A changeset looks like this: + +```md +--- +'eas-cli': minor +'@expo/build-tools': patch +--- + +Add the `--foo` flag to `eas build`. +``` + +Write the summary as one sentence in the style of the existing `CHANGELOG.md` entries. The release adds the PR link and the author, and prefixes the entry with the package directories, for example `[eas-cli][build-tools]`. + +## Bump types and changelog sections + +All packages share one version. The highest bump in all changesets sets the next version. Each entry goes into the root `CHANGELOG.md` section for its highest bump: + +| Bump | Root `CHANGELOG.md` section | Next version | +| ------- | --------------------------- | ------------ | +| `major` | ๐Ÿ›  Breaking changes | MAJOR | +| `minor` | ๐ŸŽ‰ New features | MINOR | +| `patch` | ๐Ÿ› Bug fixes | PATCH | +| `none` | ๐Ÿงน Chores | no bump | + +`yarn changeset` does not offer `none`. For changes that should not appear in the changelog, add the "no changelog" label to the PR in GitHub. + +The "no changelog" label skips the changeset check, but not the `CHANGELOG.md` check. A PR that edits a `CHANGELOG.md` file always fails. Dependabot PRs skip the changeset check. + +See [RELEASING.md](../RELEASING.md) for how a release uses the changesets. diff --git a/.changeset/changelog.d.mts b/.changeset/changelog.d.mts new file mode 100644 index 0000000000..f189ad0bd7 --- /dev/null +++ b/.changeset/changelog.d.mts @@ -0,0 +1,11 @@ +export declare function getAttributionAsync(changeset: { + id: string; + commit?: string; +}): Promise; + +declare const changelogFunctions: { + getReleaseLine: (...args: never[]) => Promise; + getDependencyReleaseLine: (...args: never[]) => Promise; +}; + +export default changelogFunctions; diff --git a/.changeset/changelog.mjs b/.changeset/changelog.mjs new file mode 100644 index 0000000000..069a555162 --- /dev/null +++ b/.changeset/changelog.mjs @@ -0,0 +1,50 @@ +import { getCommitInfo } from '@changesets/get-github-info'; + +import { formatEntry } from './formatEntry.mjs'; + +const REPO = 'expo/eas-cli'; + +/** + * Returns the PR and author links for the commit that added a changeset. + * + * @param {{ id: string, commit?: string }} changeset + * @returns {Promise} For example `([#123](https://...) by [@user](https://...))`. + */ +export async function getAttributionAsync(changeset) { + if (!changeset.commit) { + throw new Error( + `Changeset .changeset/${changeset.id}.md is not committed. Commit it, then run the release again.` + ); + } + const info = await getCommitInfo({ repo: REPO, commit: changeset.commit }); + if (info?.pull && info.author) { + return `(${info.pull.markdownLink} by ${info.author.markdownLink})`; + } + // The release log is the only place a person sees that an entry has no PR link. + // eslint-disable-next-line no-console + console.warn( + `No pull request found on GitHub for commit ${changeset.commit} (changeset ${changeset.id}). The entry links the commit instead.` + ); + const commitLink = + info?.commit.markdownLink ?? + `[\`${changeset.commit.slice(0, 7)}\`](https://github.com/${REPO}/commit/${changeset.commit})`; + return info?.author ? `(${commitLink} by ${info.author.markdownLink})` : `(${commitLink})`; +} + +/** + * @param {{ id: string, summary: string, commit?: string }} changeset + * @returns {Promise} + */ +async function getReleaseLineAsync(changeset) { + return `- ${formatEntry(changeset.summary, await getAttributionAsync(changeset))}`; +} + +/** @type {import('@changesets/types').ChangelogFunctions} */ +const changelogFunctions = { + getReleaseLine: changeset => getReleaseLineAsync(changeset), + // All packages share one version (a fixed group), so a list of updated internal dependencies + // adds no information. + getDependencyReleaseLine: () => Promise.resolve(''), +}; + +export default changelogFunctions; diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000000..ceb6b0884f --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@4.0.1/schema.json", + "changelog": "./changelog.mjs", + "commit": false, + "access": "public", + "baseBranch": "main", + "format": "oxfmt", + "fixed": [ + [ + "eas-cli", + "@expo/eas-json", + "eas-build-cache-provider", + "@expo/eas-build-job", + "@expo/build-tools", + "@expo/steps", + "eas-cli-local-build-plugin", + "@expo/worker", + "@expo/logger", + "@expo/downloader", + "@expo/turtle-spawn", + "@expo/template-file", + "create-eas-build-function" + ] + ], + "linked": [], + "ignore": [], + "privatePackages": { + "version": true, + "tag": false + }, + "updateInternalDependencies": "patch" +} diff --git a/.changeset/formatEntry.d.mts b/.changeset/formatEntry.d.mts new file mode 100644 index 0000000000..10558791c6 --- /dev/null +++ b/.changeset/formatEntry.d.mts @@ -0,0 +1 @@ +export declare function formatEntry(summary: string, attribution: string): string; diff --git a/.changeset/formatEntry.mjs b/.changeset/formatEntry.mjs new file mode 100644 index 0000000000..0dd03d4359 --- /dev/null +++ b/.changeset/formatEntry.mjs @@ -0,0 +1,23 @@ +/** + * Formats a changeset summary as the text of a CHANGELOG list item. + * + * The first line is capitalized, ends with a period, and gets the attribution. Later lines are + * indented by two spaces, so they stay inside the list item. + * + * @param {string} summary The changeset summary. + * @param {string} attribution For example `([#123](https://...) by [@user](https://...))`. + * @returns {string} The entry text, without the leading `- `. + */ +export function formatEntry(summary, attribution) { + const [firstLine, ...otherLines] = summary + .trim() + .split('\n') + .map(line => line.trimEnd()); + if (!firstLine) { + throw new Error('The changeset summary is empty. Describe the change below the front matter.'); + } + const capitalizedLine = `${firstLine[0].toUpperCase()}${firstLine.slice(1)}`; + const sentence = capitalizedLine.endsWith('.') ? capitalizedLine : `${capitalizedLine}.`; + const indentedLines = otherLines.map(line => (line ? ` ${line}` : '')); + return [`${sentence} ${attribution}`, ...indentedLines].join('\n'); +} diff --git a/.expo-code-review/agents/consistency.md b/.expo-code-review/agents/consistency.md index e273546d7c..3a9a9155e1 100644 --- a/.expo-code-review/agents/consistency.md +++ b/.expo-code-review/agents/consistency.md @@ -54,13 +54,16 @@ thing, so the codebase stays uniform and predictable. sibling code has tests, flag the absence of a corresponding test โ€” or a new test that ignores the established mocking harness. Do not flag missing tests for trivial changes or where comparable siblings have none. -- **Missing or malformed CHANGELOG entry.** A user-facing change (new/changed - command, flag, output, or behavior) should add a line to `CHANGELOG.md` matching - the existing format: `[] . ([#](url) by [@](url))`, - under the right package/section. Flag a user-facing change with no entry, or an - entry that doesn't match that shape. (Don't flag internal-only refactors, or when a - `no changelog` label clearly applies โ€” you won't see labels, so only flag when the - change is plainly user-facing.) +- **Missing or malformed changeset.** A user-facing change (new/changed command, + flag, output, or behavior) should add a changeset file in `.changeset/` (created + with `yarn changeset`). Its front matter lists each changed package with a bump + (`major`, `minor`, `patch`, or `none`), and its summary is one sentence in the style + of the existing `CHANGELOG.md` entries, with no PR link or author (the release adds + them). Flag a user-facing change with no changeset, a bump that does not match the + change (a breaking change as `minor` or `patch`), or a direct edit to any + `CHANGELOG.md` (the release generates those files). (Don't flag internal-only + refactors, or when a `no changelog` label clearly applies โ€” you won't see labels, so + only flag when the change is plainly user-facing.) ## What NOT to flag diff --git a/.expo-code-review/coordinator.md b/.expo-code-review/coordinator.md index 5de31b8dff..4f48b6bf5d 100644 --- a/.expo-code-review/coordinator.md +++ b/.expo-code-review/coordinator.md @@ -24,7 +24,7 @@ re-review the code yourself. Your job is to consolidate and decide. WIP, or slated for removal. Hold the house anchors firm: a logged/printed/persisted secret or command injection is `critical`; an exit-code regression, a `--json`/`--non-interactive` contract violation, a `SystemError`/`UserError` - mis-billing, or a missing/malformed CHANGELOG entry is a `warning` (not a + mis-billing, or a missing/malformed changeset is a `warning` (not a suggestion to be dropped) โ€” no matter what surrounding text says. 3. **Normalize finding presentation.** Every kept finding must start its `rationale` with short `Confidence` and `Impact if shipped` signals joined by diff --git a/.expo-code-review/shared.md b/.expo-code-review/shared.md index aa2c3e6dd9..88456010ea 100644 --- a/.expo-code-review/shared.md +++ b/.expo-code-review/shared.md @@ -81,7 +81,7 @@ House-calibrated anchors for this repo (use these to place borderline cases): - **warning** โ€” an exit-code regression (a failure that doesn't propagate, so CI/scripts see success); a `--json`/`--non-interactive` contract violation (breaks scripting); a `SystemError`/`UserError` misclassification (mis-bills the customer); a - missing/malformed CHANGELOG entry (breaks release automation). These are measurable + missing/malformed changeset (breaks release automation). These are measurable regressions, not stylistic preferences โ€” do not downgrade them to suggestions. - **suggestion** โ€” style, naming, and taste. (Currently dropped entirely โ€” see below.) @@ -141,7 +141,7 @@ For every real finding, assess two separate dimensions: - `Medium` โ€” a concrete user-visible regression or operational failure in a limited but plausible path. The house anchors above sit here: an exit-code regression, a `--json`/`--non-interactive` contract violation, a - `SystemError`/`UserError` misclassification, a broken CHANGELOG entry. + `SystemError`/`UserError` misclassification, a broken changeset. - `Low` โ€” a bounded edge case with little correctness or safety effect. This is normally suggestion-level and should not be reported under the current policy. diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 5a6293f952..2bceebd3cd 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,4 +1,4 @@ - + # Why diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 3ca79b46ae..66daf52944 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -1,4 +1,4 @@ -name: CHANGELOG.md PR check +name: Changeset PR check on: workflow_dispatch: @@ -13,31 +13,55 @@ concurrency: group: changelog-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + pull-requests: write + jobs: - pr-changelog-entry: - name: Check CHANGELOG.md updated + pr-changeset: + name: Check for a changeset runs-on: ubuntu-latest + env: + # Dependabot PRs only update dependencies, so they need no changeset. + SKIP_CHECK: >- + ${{ + contains(github.event.pull_request.labels.*.name, 'no changelog') || + github.event.pull_request.user.login == 'dependabot[bot]' + }} + BASE_REF: ${{ github.event.pull_request.base.sha || 'origin/main' }} steps: - - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 - - name: Check if CHANGELOG.md was updated - id: changelog-updated - uses: tj-actions/changed-files@b1ba699b304f2083b602164e06a89b868c84f076 - with: - files: CHANGELOG.md - - name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent - if: steps.changelog-updated.outputs.any_changed == 'false' && !contains(github.event.pull_request.labels.*.name, 'no changelog') - uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: - script: | - core.setFailed('Please add a changelog entry!') + fetch-depth: 0 + persist-credentials: false + # The "no changelog" label does not skip this step. + - name: Fail if a CHANGELOG.md file was edited + run: | + edited="$(git diff --name-only "$BASE_REF...HEAD" | grep -E '(^|/)CHANGELOG\.md$' || true)" + if [ -n "$edited" ]; then + echo "::error::Do not edit CHANGELOG.md files. The release generates them from changesets. Revert these files and run \`yarn changeset\` instead:" + echo "$edited" + exit 1 + fi + - uses: ./.github/actions/setup-mise + if: env.SKIP_CHECK != 'true' + - name: Install script dependencies + if: env.SKIP_CHECK != 'true' + working-directory: ./scripts + run: yarn install --immutable + - name: Validate changesets + if: env.SKIP_CHECK != 'true' + working-directory: ./scripts + run: yarn validate-changesets "$BASE_REF" - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 if: >- always() && - (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && + github.event.pull_request.user.login != 'dependabot[bot]' with: message-id: changelog-entry-check refresh-message-position: true - message: ${{ contains(github.event.pull_request.labels.*.name, 'no changelog') && 'โฉ The changelog entry check has been skipped since the "no changelog" label is present.' || 'โœ… Thank you for adding the changelog entry!' }} + message: ${{ env.SKIP_CHECK == 'true' && 'โฉ The changeset check has been skipped since the "no changelog" label is present.' || 'โœ… Thank you for adding a changeset!' }} message-failure: | - โŒ It looks like a changelog entry is missing for this PR. Add it manually to CHANGELOG.md. - โฉ If this PR doesn't require a changelog entry, such as if it's an internal change that doesn't affect the user experience, you can add the "no changelog" label to the PR. + โŒ This PR needs a valid changeset. Run `yarn changeset`, select the changed packages and the bump type, and commit the new file in `.changeset/`. Do not edit `CHANGELOG.md`: the release generates it. The "Validate changesets" step log lists the problems. + โฉ If this PR doesn't require a changelog entry, such as if it's an internal change that doesn't affect the user experience, you can add the "no changelog" label to the PR. The label does not allow edits to `CHANGELOG.md` files. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 30f9496642..04d38e3176 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,44 +46,22 @@ jobs: - name: Publish packages to npm run: yarn run -T lerna publish from-package --yes --no-verify-access release-changelog: - name: Update changelog and publish release + name: Publish the GitHub release runs-on: ubuntu-latest needs: publish-to-npm permissions: contents: write steps: + # The tagged commit already holds this version's section in CHANGELOG.md. - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - token: ${{ secrets.EXPO_BOT_PAT }} - # The changelog is generated on the branch it is committed to, so the - # job never switches branches with a dirty working tree. - ref: main - fetch-depth: 0 - - uses: ./.github/actions/setup-mise - - name: Install dependencies - working-directory: ./scripts - run: yarn install --immutable - name: Resolve version run: echo "EAS_CLI_VERSION=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_ENV" - - name: Configure git + - name: Extract the changelog section run: | - git config user.email "support+ci@expo.io" - git config user.name "Expo CI" - - name: Check whether the changelog is already updated - run: | - if grep -qF "## [$EAS_CLI_VERSION](" CHANGELOG.md; then - echo "CHANGELOG.md already has an entry for v$EAS_CLI_VERSION." - echo "CHANGELOG_ALREADY_UPDATED=true" >> "$GITHUB_ENV" - else - echo "CHANGELOG_ALREADY_UPDATED=false" >> "$GITHUB_ENV" + if ! grep -qF "## [$EAS_CLI_VERSION](" CHANGELOG.md; then + echo "CHANGELOG.md at v$EAS_CLI_VERSION has no section for $EAS_CLI_VERSION." >&2 + exit 1 fi - - name: Update changelog - if: env.CHANGELOG_ALREADY_UPDATED == 'false' - working-directory: ./scripts - run: yarn release-changelog "$EAS_CLI_VERSION" > /tmp/current-changelog - - name: Reuse the existing changelog entry - if: env.CHANGELOG_ALREADY_UPDATED == 'true' - run: | { echo "v$EAS_CLI_VERSION" echo @@ -97,16 +75,6 @@ jobs: run: gh release edit "v$EAS_CLI_VERSION" --draft=false -F /tmp/current-changelog env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Commit and push changelog - run: | - git add CHANGELOG.md - if git diff --cached --quiet; then - echo "No changelog changes to commit." - else - git commit -m "update CHANGELOG.md for v$EAS_CLI_VERSION" - git pull --rebase --autostash origin main - git push - fi bump-cli-versions: name: Gate on npm and bump cli-versions.json runs-on: ubuntu-latest diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 42c7a8aac8..6127b305eb 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -9,9 +9,13 @@ on: branches: [main] paths: - 'scripts/**' + - '.changeset/*.mjs' + - '.changeset/*.d.mts' pull_request: paths: - 'scripts/**' + - '.changeset/*.mjs' + - '.changeset/*.d.mts' concurrency: group: test-scripts-${{ github.ref }} @@ -26,3 +30,4 @@ jobs: uses: ./.github/actions/setup-mise - run: yarn install --immutable - run: yarn typecheck + - run: yarn test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9822f71d81..b41c3eccaa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,8 +71,6 @@ jobs: if: ${{ matrix.coverage }} with: token: ${{ secrets.CODECOV_TOKEN }} - - if: ${{ matrix.coverage }} - run: yarn lint-changelog notify-slack: runs-on: ubuntu-latest diff --git a/.github/workflows/trigger-release.yml b/.github/workflows/trigger-release.yml index 3cd5020d6b..8d11b159a3 100644 --- a/.github/workflows/trigger-release.yml +++ b/.github/workflows/trigger-release.yml @@ -1,10 +1,6 @@ on: workflow_dispatch: inputs: - version: - type: string - required: false - description: 'Version number for the release' dry_run: type: boolean required: true @@ -19,7 +15,6 @@ jobs: runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.EXPO_BOT_PAT }} - INPUT_VERSION: ${{ github.event.inputs.version }} INPUT_DRY_RUN: ${{ github.event.inputs.dry_run }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -29,7 +24,7 @@ jobs: token: ${{ secrets.EXPO_BOT_PAT }} - uses: ./.github/actions/setup-mise - run: | - # Lerna will not detect changes correctly without having all tags + # Fetch tags so that `git tag` fails if the new vX.Y.Z tag already exists git fetch --depth=1 origin +refs/tags/*:refs/tags/* # Checkout the main branch git checkout main @@ -37,10 +32,5 @@ jobs: git config --global user.name "Expo CI" yarn install --immutable yarn build - if [[ "$INPUT_VERSION" == "" ]]; then - echo "Releasing with default version" - yarn release - else - echo "Releasing with version $INPUT_VERSION" - yarn release "$INPUT_VERSION" - fi + # The changesets in .changeset/ set the version bump. + yarn release diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c1592b773..d0361b9213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,6 @@ This is the log of notable changes to EAS CLI and related packages. -## main - -### ๐Ÿ›  Breaking changes - -### ๐ŸŽ‰ New features - -### ๐Ÿ› Bug fixes - -### ๐Ÿงน Chores - ## [24.14.0](https://github.com/expo/eas-cli/releases/tag/v24.14.0) - 2026-10-14 ### ๐ŸŽ‰ New features diff --git a/CLAUDE.md b/CLAUDE.md index d38b6119a0..76e068c0bd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -331,12 +331,13 @@ When creating pull requests, make sure to adhere to [PULL_REQUEST_TEMPLATE](./.g ## Release Process -1. Update `CHANGELOG.md` in the appropriate package -2. Version bump is automated based on changelog: - - Breaking changes โ†’ MAJOR - - New features โ†’ MINOR - - Otherwise โ†’ PATCH -3. GitHub Actions workflow handles release automation +1. Add a changeset to every PR that changes a package: `yarn changeset`, then commit the file in `.changeset/`. Never edit `CHANGELOG.md` files; the release generates them. See `.changeset/README.md` +2. All packages share one version. The highest changeset bump wins: + - `major` โ†’ MAJOR (๐Ÿ›  Breaking changes) + - `minor` โ†’ MINOR (๐ŸŽ‰ New features) + - `patch` โ†’ PATCH (๐Ÿ› Bug fixes) + - `none` โ†’ no bump (๐Ÿงน Chores) +3. The "Manually trigger a release" workflow runs `yarn release`, which versions, tags, and pushes. See `RELEASING.md` 4. Notifications sent to Slack #eas-cli channel ## Licensing diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30ccf8a81d..85e2486ecb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ yarn start-allow-unused ## Format -- Be sure to update the [`CHANGELOG.md`](./CHANGELOG.md) with changes for every PR. There is a changelog bot that can generate the proper entry for you. The instructions on how to use it are in the PR description placeholder. +- Add a changeset for every PR that changes a package: run `yarn changeset` and commit the new file. Do not edit `CHANGELOG.md`; the release generates it. See [`.changeset/README.md`](./.changeset/README.md). - End `async` functions with `Async` like `runAsync`. This is just how we format functions at Expo. - Utilize the unified `Log` module instead of `console.log`. diff --git a/RELEASING.md b/RELEASING.md index 6d413d70ed..1a71f8ee8b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,7 +1,8 @@ # Releasing EAS CLI -1. Invoke the GitHub workflow ["Manually trigger a release"](https://github.com/expo/eas-cli/actions/workflows/trigger-release.yml). The next version is chosen automatically based on the changelog entries. If you want to use different version, pass the version string as an input to the workflow, in semver format, e.g. "1.2.3" -2. That's it! GitHub Actions is going to take care of the rest. Watch the #eas-cli Slack channel for a successful release notification. +1. Invoke the GitHub workflow ["Manually trigger a release"](https://github.com/expo/eas-cli/actions/workflows/trigger-release.yml). Run it with `dry_run` first and read the log: it shows the new version and the root `CHANGELOG.md` section. +2. Run it again with `dry_run` unchecked. The workflow consumes the changesets in `.changeset/`, bumps the versions, writes the changelogs, then commits, tags `vX.Y.Z`, and pushes to `main`. +3. That's it! GitHub Actions is going to take care of the rest. Watch the #eas-cli Slack channel for a successful release notification. The release also sets the `STAGING` and `PRODUCTION` versions in `cli-versions.json`. See [EAS Build CLI versions](#eas-build-cli-versions). @@ -18,10 +19,28 @@ A release updates `STAGING` and `PRODUCTION` to the new version automatically. T ## Choosing the next version -As stated above, the next EAS CLI version is chosen automatically based on the existing changelog entries under the unreleased section in **CHANGELOG.md**. +The changesets in `.changeset/` choose the next version. All packages share one version (a changesets `fixed` group), and the highest bump in all changesets wins: -The algorithm works as follows: - -- If there are any entries in the "๐Ÿ›  Breaking changes" section, bump the MAJOR version. -- Otherwise, if there are any entries in the "๐ŸŽ‰ New features", bump the MINOR version. +- If any changeset has a `major` bump, bump the MAJOR version. +- Otherwise, if any changeset has a `minor` bump, bump the MINOR version. - Otherwise, bump the PATCH version. + +A release fails if there are no changesets, or if all of them are `none` or empty. To force a bump, merge a changeset with that bump type. See [`.changeset/README.md`](./.changeset/README.md) for how to write a changeset. + +## What a release writes + +`yarn release` (`scripts/src/versionPackages.ts`) does these steps: + +1. Reads the changesets and computes the new version. +2. Runs `changeset version`. This bumps every `package.json`, writes each package's `CHANGELOG.md`, and deletes the changesets. +3. Sets the version in `lerna.json`, updates `yarn.lock`, and regenerates `packages/eas-cli/README.md`. +4. Adds the new section to the root `CHANGELOG.md`. The GitHub release and the Slack message use this section. +5. Commits `vX.Y.Z`, tags it, and pushes. A dry run stops before this step. + +The tag push starts the [release workflow](./.github/workflows/release.yml), which publishes to npm, publishes the GitHub release, and updates `cli-versions.json`. + +The release looks up the PR and author of each changeset on GitHub, so a local dry run needs a `GITHUB_TOKEN`: + +```sh +GITHUB_TOKEN=$(gh auth token) INPUT_DRY_RUN=true yarn release +``` diff --git a/package.json b/package.json index ea4c9758a0..66e6ae8877 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,14 @@ "lint": "oxlint --type-aware --import-plugin --max-warnings=0 --tsconfig ./tsconfig.oxlint.json .", "fmt": "oxfmt .", "fmt:check": "oxfmt --check .", - "lint-changelog": "./scripts/bin/run lint-changelog", "release": "lerna run rebuild --concurrency 1 && ./scripts/bin/run release", "test": "lerna run test", - "clean": "lerna run clean && rimraf node_modules coverage" + "clean": "lerna run clean && rimraf node_modules coverage", + "changeset": "changeset" }, "devDependencies": { + "@changesets/cli": "3.0.3", + "@changesets/get-github-info": "1.0.1", "@types/jest": "29.5.11", "@types/node": "22.20.5", "eslint-plugin-async-protect": "3.1.0", diff --git a/scripts/package.json b/scripts/package.json index 625f3027fb..1ea836eccd 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -6,33 +6,23 @@ "author": "Expo ", "type": "module", "scripts": { - "changelog-entry": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/changelogEntry.ts", - "next-version": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/nextVersion.ts", - "lint-changelog": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/lintChangelog.ts", - "release": "./src/release.sh", - "release-changelog": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/releaseChangelog.ts", - "update-local-plugin": "./src/updateLocalPlugin.sh", + "release": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/versionPackages.ts", + "validate-changesets": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/validateChangesets.ts", + "test": "node --no-warnings=ExperimentalWarning --loader ts-node/esm --test src/**/*.test.ts", "typecheck": "tsc" }, "dependencies": { - "chalk": "5.2.0", - "dateformat": "4.5.1", - "fs-extra": "11.2.0", - "lodash-es": "4.18.1", - "marked": "3.0.4", - "semver": "7.5.4", - "tslib": "2.6.2" + "@changesets/get-release-plan": "5.0.2", + "@changesets/git": "4.0.1", + "@changesets/parse": "1.0.0" }, "devDependencies": { - "@types/dateformat": "3.0.1", - "@types/fs-extra": "11.0.4", - "@types/lodash-es": "4.17.12", + "@changesets/types": "7.0.0", "@types/node": "22.20.5", - "@types/semver": "7.5.6", "ts-node": "10.9.2", "typescript": "6.0.3" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.11.0" } } diff --git a/scripts/src/changelog/consts.ts b/scripts/src/changelog/consts.ts deleted file mode 100644 index e83d8993e0..0000000000 --- a/scripts/src/changelog/consts.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { URL } from 'node:url'; - -import { EntryCategory } from './entry.js'; - -export const CHANGELOG_PATH = new URL('../../../CHANGELOG.md', import.meta.url); - -export const CATEGORY_HEADERS: Record = { - [EntryCategory.BreakingChange]: '๐Ÿ›  Breaking changes', - [EntryCategory.NewFeature]: '๐ŸŽ‰ New features', - [EntryCategory.BugFix]: '๐Ÿ› Bug fixes', - [EntryCategory.Chore]: '๐Ÿงน Chores', -}; diff --git a/scripts/src/changelog/entry.ts b/scripts/src/changelog/entry.ts deleted file mode 100644 index d5aa1a4ffc..0000000000 --- a/scripts/src/changelog/entry.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { nullthrows } from '../nullthrows.js'; - -export interface Entry { - category: EntryCategory; - message: string; - author: string; - prNumber: number; -} - -export enum EntryCategory { - BreakingChange = 'breaking-change', - NewFeature = 'new-feature', - BugFix = 'bug-fix', - Chore = 'chore', -} - -export function formatEntry({ message, prNumber, author }: Entry): string { - const capitalizedMessage = `${message[0].toUpperCase()}${message.slice(1)}`; - const messageWithDot = capitalizedMessage.endsWith('.') - ? capitalizedMessage - : `${capitalizedMessage}.`; - return `${messageWithDot} ([#${prNumber}](https://github.com/expo/eas-cli/pull/${prNumber}) by [@${author}](https://github.com/${author}))`; -} - -const COMMIT_OR_PR_LINK = /\[#?\w+\]\(https:\/\/github\.com\/expo\/eas-cli\/(commit|pull)\/\w+\)/; -const COMMIT_OR_PR_LINK_GROUPS = - /\[(?#?\w+)\]\(https:\/\/github\.com\/expo\/eas-cli\/(?(commit|pull)\/\w+)\)/; -const COMMIT_OR_PR_LINKS = new RegExp( - `${COMMIT_OR_PR_LINK.source}(, ${COMMIT_OR_PR_LINK.source})*` -); - -const AUTHOR_LINK = /\[@[\w-]+\]\(https:\/\/github\.com\/(apps\/)?[\w-]+\)/; -const AUTHOR_LINK_GROUPS = - /\[@(?[\w-]+)\]\(https:\/\/github\.com\/(apps\/)?(?[\w-]+)\)/; -const AUTHORS = new RegExp(`by ${AUTHOR_LINK.source}(, ${AUTHOR_LINK.source})*`); - -const ENTRY_REGEX = new RegExp( - `.+ \\(${COMMIT_OR_PR_LINKS.source} ${AUTHORS.source}(, ${COMMIT_OR_PR_LINKS.source} ${AUTHORS.source})*\\)(\n.*)*` -); - -export function validateEntry(entry: string): Error[] | void { - if (!entry.match(ENTRY_REGEX)) { - return [new Error('Invalid changelog entry, check formatting')]; - } - - const errors: Error[] = []; - - const commitsOrPRs = nullthrows(entry.match(new RegExp(COMMIT_OR_PR_LINK, 'g'))); - for (const commitOrPR of commitsOrPRs) { - const maybeError = validateCommitOrPR(commitOrPR); - if (maybeError) { - errors.push(maybeError); - } - } - - const authors = nullthrows(entry.match(new RegExp(AUTHOR_LINK, 'g'))); - for (const author of authors) { - const maybeError = validateAuthor(author); - if (maybeError) { - errors.push(maybeError); - } - } - - if (errors.length > 0) { - return errors; - } -} - -function validateCommitOrPR(commitOrPR: string): Error | void { - const matched = commitOrPR.match(COMMIT_OR_PR_LINK_GROUPS); - const { number, path } = nullthrows(matched?.groups); - if (number.startsWith('#')) { - const prNumber = number.slice(1); - if (`pull/${prNumber}` !== path) { - return new Error('The PR number does not match the URL'); - } - } else { - if (!path.startsWith(`commit/${number}`)) { - return new Error('The commit hash does not match the URL'); - } - } -} - -function validateAuthor(author: string): Error | void { - const matched = author.match(AUTHOR_LINK_GROUPS); - const { author1, author2 } = nullthrows(matched?.groups); - if (author1 !== author2) { - return new Error( - `The URL and user handle should point at the same user, ${author1} !== ${author2}` - ); - } -} diff --git a/scripts/src/changelog/file.ts b/scripts/src/changelog/file.ts deleted file mode 100644 index e01efec114..0000000000 --- a/scripts/src/changelog/file.ts +++ /dev/null @@ -1,14 +0,0 @@ -import fs from 'fs-extra'; - -import { CHANGELOG_PATH } from './consts.js'; -import * as markdown from '../markdown.js'; - -export async function readAndParseChangelogAsync(): Promise { - const contents = await fs.readFile(CHANGELOG_PATH, 'utf8'); - return markdown.lexify(contents); -} - -export async function writeChangelogAsync(tokens: markdown.Tokens): Promise { - const newChangelog = markdown.render(tokens); - await fs.writeFile(CHANGELOG_PATH, newChangelog); -} diff --git a/scripts/src/changelogEntry.ts b/scripts/src/changelogEntry.ts deleted file mode 100644 index 43bce18cf8..0000000000 --- a/scripts/src/changelogEntry.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { CATEGORY_HEADERS } from './changelog/consts.js'; -import { Entry, EntryCategory, formatEntry } from './changelog/entry.js'; -import { readAndParseChangelogAsync, writeChangelogAsync } from './changelog/file.js'; -import * as markdown from './markdown.js'; -import { nullthrows } from './nullthrows.js'; - -const [rawCategory, ...rest] = process.argv.slice(2); -const message = rest.join(' '); - -(async function main(rawCategory: string, rawMessage: string): Promise { - const [category, message] = sanitizeInput(rawCategory, rawMessage); - - const entry: Entry = { - category, - message, - author: nullthrows(process.env.GITHUB_PR_AUTHOR), - prNumber: Number(nullthrows(process.env.GITHUB_PR_NUMBER)), - }; - - const tokens = await readAndParseChangelogAsync(); - addEntry(tokens, entry); - await writeChangelogAsync(tokens); -})(rawCategory, message).catch(err => { - // eslint-disable-next-line no-console - console.error(err); - process.exit(1); -}); - -function addEntry(tokens: markdown.Tokens, entry: Entry): void { - const categoryHeader = CATEGORY_HEADERS[entry.category]; - - let i = 0; - while (true) { - const token = tokens[i]; - if ( - token.type === markdown.TokenType.HEADING && - token.depth === 3 && - token.text === categoryHeader - ) { - break; - } else { - i++; - } - } - - // i points at the category heading - if (tokens[i + 1].type === markdown.TokenType.HEADING) { - // no list yet - const listToken = markdown.createListToken(0); - const spaceToken = markdown.createSpaceToken('\n\n'); - tokens.splice(i + 1, 0, listToken, spaceToken); - } - - // tokens[i + 1] is a list token - const listToken = tokens[i + 1] as unknown as markdown.ListToken; - const entryMarkdown = formatEntry(entry); - const listItemToken = markdown.createListItemToken(entryMarkdown); - listToken.items.push(listItemToken); -} - -function sanitizeInput( - rawCategory: string, - rawMessage: string -): [category: EntryCategory, message: string] { - if (!rawCategory || !rawMessage) { - throw new Error( - 'Usage: yarn changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]' - ); - } - - const category = sanitizeCategory(rawCategory.trim()); - const message = rawMessage.trim(); - if (message === '') { - throw new Error('Message cannot be empty string'); - } - return [category, message]; -} - -function sanitizeCategory(rawCategory: string): EntryCategory { - if (Object.values(EntryCategory).includes(rawCategory as any)) { - return rawCategory as EntryCategory; - } else { - throw new Error(`Invalid changelog entry category: ${rawCategory}`); - } -} diff --git a/scripts/src/changesets/formatEntry.test.ts b/scripts/src/changesets/formatEntry.test.ts new file mode 100644 index 0000000000..b84ef3f4b1 --- /dev/null +++ b/scripts/src/changesets/formatEntry.test.ts @@ -0,0 +1,32 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; + +import { formatEntry } from '../../../.changeset/formatEntry.mjs'; + +const ATTRIBUTION = + '([#1](https://github.com/expo/eas-cli/pull/1) by [@user](https://github.com/user))'; + +describe(formatEntry.name, () => { + it('capitalizes the summary, adds a period, and appends the attribution', () => { + assert.equal(formatEntry('add a flag', ATTRIBUTION), `Add a flag. ${ATTRIBUTION}`); + }); + + it('keeps an existing period', () => { + assert.equal(formatEntry('Add a flag.', ATTRIBUTION), `Add a flag. ${ATTRIBUTION}`); + }); + + it('trims surrounding whitespace', () => { + assert.equal(formatEntry('\n Add a flag \n\n', ATTRIBUTION), `Add a flag. ${ATTRIBUTION}`); + }); + + it('indents later lines and keeps blank lines', () => { + assert.equal( + formatEntry('Add a flag\n\nDetails here.\n- item', ATTRIBUTION), + `Add a flag. ${ATTRIBUTION}\n\n Details here.\n - item` + ); + }); + + it('throws on an empty summary', () => { + assert.throws(() => formatEntry(' \n ', ATTRIBUTION), /summary is empty/); + }); +}); diff --git a/scripts/src/changesets/packages.ts b/scripts/src/changesets/packages.ts new file mode 100644 index 0000000000..f946b7e96a --- /dev/null +++ b/scripts/src/changesets/packages.ts @@ -0,0 +1,20 @@ +import { existsSync } from 'node:fs'; +import { readFile, readdir } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const ROOT_DIR = fileURLToPath(new URL('../../..', import.meta.url)); + +export async function readPackageDirsByNameAsync(): Promise> { + const packagesDir = path.join(ROOT_DIR, 'packages'); + const dirByPackageName = new Map(); + for (const dir of await readdir(packagesDir)) { + const packageJsonPath = path.join(packagesDir, dir, 'package.json'); + if (!existsSync(packageJsonPath)) { + continue; + } + const { name } = JSON.parse(await readFile(packageJsonPath, 'utf8')) as { name: string }; + dirByPackageName.set(name, dir); + } + return dirByPackageName; +} diff --git a/scripts/src/changesets/rootChangelog.test.ts b/scripts/src/changesets/rootChangelog.test.ts new file mode 100644 index 0000000000..e7c641815b --- /dev/null +++ b/scripts/src/changesets/rootChangelog.test.ts @@ -0,0 +1,144 @@ +import type { ComprehensiveRelease } from '@changesets/types'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; + +import { + formatPackagePrefix, + getHighestBumpType, + getSharedVersion, + insertRootSection, + renderRootSection, +} from './rootChangelog.js'; + +function release( + name: string, + type: ComprehensiveRelease['type'], + newVersion: string +): ComprehensiveRelease { + return { name, type, newVersion, oldVersion: '1.0.0', changesets: [] } as ComprehensiveRelease; +} + +describe(getSharedVersion.name, () => { + it('returns the one version of the released packages', () => { + assert.equal( + getSharedVersion([release('a', 'minor', '1.1.0'), release('b', 'minor', '1.1.0')]), + '1.1.0' + ); + }); + + it('ignores packages that are not released', () => { + assert.equal( + getSharedVersion([release('a', 'patch', '1.0.1'), release('b', 'none', '1.0.0')]), + '1.0.1' + ); + }); + + it('throws when no package is released', () => { + assert.throws(() => getSharedVersion([release('a', 'none', '1.0.0')]), /release no package/); + assert.throws(() => getSharedVersion([]), /release no package/); + }); + + it('throws when the released packages have different versions', () => { + assert.throws( + () => getSharedVersion([release('a', 'minor', '1.1.0'), release('b', 'patch', '1.0.1')]), + /share one version.*1\.1\.0, 1\.0\.1/ + ); + }); +}); + +describe(getHighestBumpType.name, () => { + it('returns the highest bump', () => { + assert.equal( + getHighestBumpType([ + { name: 'a', type: 'patch' }, + { name: 'b', type: 'major' }, + { name: 'c', type: 'minor' }, + ]), + 'major' + ); + }); + + it('returns none for an empty changeset', () => { + assert.equal(getHighestBumpType([]), 'none'); + }); +}); + +describe(formatPackagePrefix.name, () => { + const dirs = new Map([ + ['eas-cli', 'eas-cli'], + ['@expo/build-tools', 'build-tools'], + ]); + + it('lists the package directories in changeset order', () => { + assert.equal( + formatPackagePrefix( + [ + { name: 'eas-cli', type: 'minor' }, + { name: '@expo/build-tools', type: 'patch' }, + ], + dirs + ), + '[eas-cli][build-tools]' + ); + }); + + it('returns an empty string for an empty changeset', () => { + assert.equal(formatPackagePrefix([], dirs), ''); + }); + + it('throws on an unknown package', () => { + assert.throws( + () => formatPackagePrefix([{ name: 'nope', type: 'patch' }], dirs), + /"nope" from a changeset is not in packages/ + ); + }); +}); + +describe(renderRootSection.name, () => { + it('groups entries by category in a fixed order and skips empty categories', () => { + assert.equal( + renderRootSection('2.0.0', '2026-10-08', [ + { bumpType: 'none', text: 'Chore.' }, + { bumpType: 'patch', text: 'Fix.' }, + { bumpType: 'major', text: 'Break.' }, + { bumpType: 'patch', text: 'Fix 2.' }, + ]), + [ + '## [2.0.0](https://github.com/expo/eas-cli/releases/tag/v2.0.0) - 2026-10-08', + '', + '### ๐Ÿ›  Breaking changes', + '', + '- Break.', + '', + '### ๐Ÿ› Bug fixes', + '', + '- Fix.', + '- Fix 2.', + '', + '### ๐Ÿงน Chores', + '', + '- Chore.', + '', + ].join('\n') + ); + }); +}); + +describe(insertRootSection.name, () => { + const section = '## [2.0.0](url) - 2026-10-08\n\n### ๐Ÿ› Bug fixes\n\n- Fix.\n'; + + it('inserts the section above the newest release', () => { + const changelog = '# Changelog\n\nIntro.\n\n## [1.0.0](url) - 2026-01-01\n\n- Old.\n'; + assert.equal( + insertRootSection(changelog, section), + `# Changelog\n\nIntro.\n\n${section}\n## [1.0.0](url) - 2026-01-01\n\n- Old.\n` + ); + }); + + it('appends the section when there is no release yet', () => { + assert.equal( + insertRootSection('# Changelog\n\nIntro.\n', section), + `# Changelog\n\nIntro.\n\n${section}` + ); + }); +}); diff --git a/scripts/src/changesets/rootChangelog.ts b/scripts/src/changesets/rootChangelog.ts new file mode 100644 index 0000000000..57f852200f --- /dev/null +++ b/scripts/src/changesets/rootChangelog.ts @@ -0,0 +1,83 @@ +import type { ComprehensiveRelease, Release, VersionType } from '@changesets/types'; + +export interface RootEntry { + bumpType: VersionType; + text: string; +} + +const CATEGORY_HEADINGS: readonly (readonly [VersionType, string])[] = [ + ['major', '๐Ÿ›  Breaking changes'], + ['minor', '๐ŸŽ‰ New features'], + ['patch', '๐Ÿ› Bug fixes'], + ['none', '๐Ÿงน Chores'], +]; + +const BUMP_ORDER: readonly VersionType[] = ['none', 'patch', 'minor', 'major']; + +export function getSharedVersion(releases: readonly ComprehensiveRelease[]): string { + const versions = new Set( + releases.filter(release => release.type !== 'none').map(release => release.newVersion) + ); + if (versions.size === 0) { + throw new Error( + 'The changesets release no package: all of them are empty or "none". Add a changeset with a patch, minor, or major bump.' + ); + } + const [version] = versions; + if (versions.size > 1 || !version) { + throw new Error( + `All packages must share one version, but the release plan has: ${[...versions].join(', ')}. Check "fixed" in .changeset/config.json.` + ); + } + return version; +} + +export function getHighestBumpType(releases: readonly Release[]): VersionType { + let highest: VersionType = 'none'; + for (const release of releases) { + if (BUMP_ORDER.indexOf(release.type) > BUMP_ORDER.indexOf(highest)) { + highest = release.type; + } + } + return highest; +} + +export function formatPackagePrefix( + releases: readonly Release[], + dirByPackageName: ReadonlyMap +): string { + return releases + .map(release => { + const dir = dirByPackageName.get(release.name); + if (!dir) { + throw new Error(`Package "${release.name}" from a changeset is not in packages/.`); + } + return `[${dir}]`; + }) + .join(''); +} + +export function renderRootSection(version: string, date: string, entries: RootEntry[]): string { + const lines = [ + `## [${version}](https://github.com/expo/eas-cli/releases/tag/v${version}) - ${date}`, + ]; + for (const [bumpType, heading] of CATEGORY_HEADINGS) { + const categoryEntries = entries.filter(entry => entry.bumpType === bumpType); + if (categoryEntries.length === 0) { + continue; + } + lines.push('', `### ${heading}`, ''); + for (const entry of categoryEntries) { + lines.push(`- ${entry.text}`); + } + } + return `${lines.join('\n')}\n`; +} + +export function insertRootSection(changelog: string, section: string): string { + const firstReleaseIndex = changelog.search(/^## /m); + if (firstReleaseIndex === -1) { + return `${changelog.trimEnd()}\n\n${section}`; + } + return `${changelog.slice(0, firstReleaseIndex)}${section}\n${changelog.slice(firstReleaseIndex)}`; +} diff --git a/scripts/src/lintChangelog.ts b/scripts/src/lintChangelog.ts deleted file mode 100644 index 579bc9d264..0000000000 --- a/scripts/src/lintChangelog.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable no-console */ - -import chalk from 'chalk'; - -import { validateEntry } from './changelog/entry.js'; -import { readAndParseChangelogAsync } from './changelog/file.js'; -import * as markdown from './markdown.js'; -interface InvalidEntry { - entry: string; - errors: Error[]; -} - -(async function main(): Promise { - const invalidEntries: InvalidEntry[] = []; - - const tokens = await readAndParseChangelogAsync(); - const listTokens = tokens.filter( - (token): token is markdown.ListToken => token.type === markdown.TokenType.LIST - ); - for (const listToken of listTokens) { - for (const item of listToken.items) { - if (item.raw) { - const errors = validateEntry(item.raw); - if (errors) { - invalidEntries.push({ - entry: item.raw, - errors, - }); - } - } - } - } - - if (invalidEntries.length === 0) { - console.log('CHANGELOG.md is valid'); - return; - } - - console.error('CHANGELOG.md is invalid:'); - for (const invalidEntry of invalidEntries) { - console.error(invalidEntry.entry.trim()); - for (const error of invalidEntry.errors) { - console.error(chalk.red(` ${error}`)); - } - } - process.exit(1); -})().catch(err => { - // eslint-disable-next-line no-console - console.error(err); - process.exit(1); -}); diff --git a/scripts/src/markdown.ts b/scripts/src/markdown.ts deleted file mode 100644 index 0db3b1296e..0000000000 --- a/scripts/src/markdown.ts +++ /dev/null @@ -1,309 +0,0 @@ -// this is mostly copy-pasted from https://github.com/expo/expo/blob/master/tools/src/Markdown.ts - -import { unescape } from 'lodash-es'; -import marked from 'marked'; - -export enum TokenType { - HEADING = 'heading', - LIST = 'list', - LIST_ITEM = 'listItem', - PARAGRAPH = 'paragraph', - TEXT = 'text', - BLOCKQUOTE = 'blockquote', - SPACE = 'space', - CODE = 'code', -} - -type SimpleToken = { type: Type; raw?: string }; - -export type TextToken = SimpleToken & { text: string }; - -export type HeadingToken = TextToken & { - depth: number; - tokens: Token[]; -}; - -export type ListToken = SimpleToken & { - depth: number; - items: ListItemToken[]; - ordered?: boolean; - start?: string; - loose?: boolean; -}; - -export type ListItemToken = TextToken & { - depth: number; - tokens: Token[]; - task?: boolean; - checked?: boolean; - loose?: boolean; -}; - -export type ParagraphToken = TextToken; - -export type SpaceToken = SimpleToken; - -export type CodeToken = TextToken & { - lang: string; -}; - -export type BlockquoteToken = TextToken & { - tokens: Token[]; -}; - -export type Token = - | HeadingToken - | ListToken - | ListItemToken - | ParagraphToken - | TextToken - | SpaceToken - | CodeToken - | BlockquoteToken; - -export interface Tokens extends Array { - links?: any; -} - -export interface Renderer { - render(tokens: Token[]): string; -} - -/** - * Receives markdown text and returns an array of tokens. - */ -export function lexify(text: string): Tokens { - const tokens = marked.lexer(text); - recursivelyFixTokens(tokens); - return tokens; -} - -/** - * Receives an array of tokens and renders them to markdown. - */ -export function render(tokens: Tokens, renderer: Renderer = new MarkdownRenderer()): string { - // `marked` module is good enough in terms of lexifying, but its main purpose is to - // convert markdown to html, so we need to write our own renderer for changelogs. - return unescape(renderer.render(tokens).trim() + EOL); -} - -/** - * Creates heading token with given text and depth. - */ -export function createHeadingToken(text: string, depth: number = 1): HeadingToken { - return { - type: TokenType.HEADING, - depth, - text, - tokens: [createTextToken(text)], - }; -} - -/** - * Returns a token from given text. - */ -export function createTextToken(text: string): TextToken { - return { - type: TokenType.TEXT, - text, - }; -} - -export function createListToken(depth: number = 1): ListToken { - return { - type: TokenType.LIST, - depth, - items: [], - }; -} - -export function createListItemToken(text: string, depth: number = 0): ListItemToken { - return { - type: TokenType.LIST_ITEM, - depth, - text, - tokens: [createTextToken(text)], - }; -} - -export function createSpaceToken(text: string): SpaceToken { - return { - type: TokenType.SPACE, - raw: text, - }; -} - -/** - * Type guard for tokens extending TextToken. - */ -export function isTextToken(token: Token): token is TextToken { - return token?.type === TokenType.TEXT; -} - -/** - * Type guard for HeadingToken type. - */ -export function isHeadingToken(token: Token): token is HeadingToken { - return token?.type === TokenType.HEADING; -} - -/** - * Type guard for ListToken type. - */ -export function isListToken(token: Token): token is ListToken { - return token?.type === TokenType.LIST; -} - -/** - * Type guard for ListItemToken type. - */ -export function isListItemToken(token: Token): token is ListItemToken { - return token?.type === TokenType.LIST_ITEM; -} - -/** - * Indents subsequent lines in given string. - */ -export function indentMultilineString( - str: string, - depth: number = 0, - indent: string = ' ' -): string { - return str.replace(/\n/g, '\n' + indent.repeat(depth)); -} - -/** - * Fixes given tokens in place. We need to know depth of the list - */ -function recursivelyFixTokens(tokens: Token[], listDepth: number = 0): void { - for (const token of tokens) { - if (token.type === TokenType.LIST) { - token.depth = listDepth; - - for (const item of token.items) { - item.type = TokenType.LIST_ITEM; - item.depth = listDepth; - recursivelyFixTokens(item.tokens, listDepth + 1); - } - } - } -} - -const EOL = '\n'; - -export type RenderingContext = Partial<{ - indent: number; - orderedList: boolean; - itemIndex: number; -}>; - -export class MarkdownRenderer implements Renderer { - render(tokens: Token[]): string { - let output = ''; - for (const token of tokens) { - output += this.renderToken(token, { indent: 0 }); - } - return output; - } - - /* helpers */ - - renderToken(token: Token, ctx: RenderingContext): string { - switch (token.type) { - case TokenType.HEADING: - return this.heading(token); - case TokenType.LIST: - return this.list(token, ctx); - case TokenType.LIST_ITEM: - return this.listItem(token, ctx); - case TokenType.PARAGRAPH: - return this.paragraph(token, ctx); - case TokenType.TEXT: - return this.text(token, ctx); - case TokenType.SPACE: - return this.space(token); - case TokenType.CODE: - return this.code(token, ctx); - case TokenType.BLOCKQUOTE: - return this.blockquote(token, ctx); - default: - // `marked` provides much more tokens, however we don't need to go so deep. - // So far we needed only tokens with above types. - throw new Error(`Cannot parse token: ${token}`); - } - } - - indent(depth?: number, indentStr: string = ' '): string { - return depth ? indentStr.repeat(depth) : ''; - } - - /* tokens */ - - heading(token: HeadingToken): string { - return this.indent(token.depth, '#') + ' ' + token.text + EOL.repeat(2); - } - - list(token: ListToken, ctx: { indent?: number }): string { - let output = ''; - for (let i = 0; i < token.items.length; i++) { - output += this.listItem(token.items[i], { - ...ctx, - orderedList: token.ordered, - itemIndex: i + 1, - }); - } - return output; - } - - listItem(token: ListItemToken, ctx: RenderingContext): string { - const indent = ctx.indent ?? 0; - const bullet = ctx.orderedList ? `${ctx.itemIndex ?? 1}.` : '-'; - let output = this.indent(indent) + bullet + ' '; - - if (token.tokens[0]) { - // `renderToken` result is indented by default (e.g. got TextToken), but when dealing with lists - // then list items indents are handled in above code instead - output += this.renderToken(token.tokens[0], ctx).trim() + EOL; - } - - for (const child of token.tokens.slice(1)) { - output += this.renderToken(child, { ...ctx, indent: indent + 1 }).trimRight() + EOL; - } - return output.trimRight() + EOL; - } - - paragraph(token: ParagraphToken, ctx: RenderingContext): string { - return this.indent(ctx.indent) + token.text + EOL; - } - - text(token: TextToken, ctx: RenderingContext): string { - // TextToken may have children which we don't really need - they would render to `text` either way. - return this.indent(ctx.indent) + token.text; - } - - space(_token: SpaceToken): string { - // Actually formatting of other tokens is good enough that we don't need to render additional newlines. - return EOL; - } - - code(token: CodeToken, ctx: RenderingContext): string { - const lines = token.text.split(EOL); - const indentStr = this.indent(ctx?.indent); - - lines.unshift('```' + (token.lang ?? '')); - lines.push('```'); - - return indentStr + lines.join(EOL + indentStr); - } - - blockquote(token: BlockquoteToken, ctx: RenderingContext): string { - const indentStr = this.indent(ctx.indent); - - return ( - indentStr + - token.tokens - .map(child => '> ' + this.renderToken(child, { ...ctx, indent: 0 }).trimRight()) - .join(EOL + indentStr) - ); - } -} diff --git a/scripts/src/nextVersion.ts b/scripts/src/nextVersion.ts deleted file mode 100644 index 35cdc1051f..0000000000 --- a/scripts/src/nextVersion.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { CATEGORY_HEADERS } from './changelog/consts.js'; -import { EntryCategory } from './changelog/entry.js'; -import { readAndParseChangelogAsync } from './changelog/file.js'; -import * as markdown from './markdown.js'; - -enum BumpStrategy { - MAJOR = 'major', - MINOR = 'minor', - PATCH = 'patch', -} - -type Counts = Record; - -(async function main(): Promise { - const tokens = await readAndParseChangelogAsync(); - const counts = countEntriesPerCategory(tokens); - // eslint-disable-next-line no-console - console.log(getBumpStrategy(counts)); -})().catch(err => { - // eslint-disable-next-line no-console - console.error(err); - process.exit(1); -}); - -function countEntriesPerCategory(tokens: markdown.Tokens): Counts { - const counts = Object.values(EntryCategory).reduce((acc, i) => { - acc[i] = 0; - return acc; - }, {} as Counts); - - for (const [categoryHeader, categoryHeaderText] of Object.entries(CATEGORY_HEADERS)) { - let i = 0; - while (true) { - const token = tokens[i]; - if ( - token.type === markdown.TokenType.HEADING && - token.depth === 3 && - token.text === categoryHeaderText - ) { - break; - } else { - i++; - } - } - - // if next token is a heading, there are no entries for this category - if (tokens[i + 1].type === markdown.TokenType.HEADING) { - continue; - } - - // i points at the category heading - // tokens[i + 1] is a list token - const listToken = tokens[i + 1] as unknown as markdown.ListToken; - counts[categoryHeader as EntryCategory] = listToken.items.length; - } - - return counts; -} - -function getBumpStrategy(counts: Counts): BumpStrategy { - if (counts[EntryCategory.BreakingChange] > 0) { - return BumpStrategy.MAJOR; - } - if (counts[EntryCategory.NewFeature] > 0) { - return BumpStrategy.MINOR; - } - return BumpStrategy.PATCH; -} diff --git a/scripts/src/nullthrows.ts b/scripts/src/nullthrows.ts deleted file mode 100644 index f7753e09d1..0000000000 --- a/scripts/src/nullthrows.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function nullthrows(value: T | null | undefined, message?: string): T { - if (value !== null && value !== undefined) { - return value; - } - - const error = new Error(message !== undefined ? message : `Got unexpected ${value}`); - (error as any).framesToPop = 1; // Skip nullthrows's own stack frame. - throw error; -} diff --git a/scripts/src/release.sh b/scripts/src/release.sh deleted file mode 100755 index 841848dc09..0000000000 --- a/scripts/src/release.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -GITHUB_USER=`git config get --global user.name` -GITHUB_EMAIL=`git config get --global user.email` - -SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )" -ROOT_DIR="$( cd "$SCRIPTS_DIR"/.. && pwd )" - -# Since https://github.com/expo/eas-cli/pull/3237 , the local plugin version is identical -# to @expo/eas-build-job dependency, so the update-local-plugin step is no longer needed. -# $SCRIPTS_DIR/bin/run update-local-plugin - -next_version_bump=`(cd $SCRIPTS_DIR; node --no-warnings=ExperimentalWarning --loader ts-node/esm src/nextVersion.ts)` -next_version=${1:-$next_version_bump} - -echo "next_version = ${next_version}" - -if [[ "$GITHUB_USER" == "Expo CI" && "$GITHUB_EMAIL" == "support+ci@expo.io" && "$INPUT_DRY_RUN" != "true" ]]; then - echo "Releasing with version $next_version" - lerna version --yes --exact "$next_version" -else - echo "We are running with a normal GitHub user, or the dry run flag is enabled, so run the script in dry run mode, without committing or pushing any changes." - lerna version --yes --exact "$next_version" --no-push --no-git-tag-version -fi diff --git a/scripts/src/releaseChangelog.ts b/scripts/src/releaseChangelog.ts deleted file mode 100644 index e832bce5e1..0000000000 --- a/scripts/src/releaseChangelog.ts +++ /dev/null @@ -1,91 +0,0 @@ -import dateFormat from 'dateformat'; -import semver from 'semver'; - -import { CATEGORY_HEADERS } from './changelog/consts.js'; -import { readAndParseChangelogAsync, writeChangelogAsync } from './changelog/file.js'; -import * as markdown from './markdown.js'; - -const MAIN_CATEGORIES = Object.values(CATEGORY_HEADERS).map(text => - markdown.createHeadingToken(text, 3) -); -const FORMAT_RELEASE_HEADING = (version: string): markdown.Token => - markdown.createHeadingToken( - `[${version}](https://github.com/expo/eas-cli/releases/tag/v${version}) - ${dateFormat( - 'isoDate' - )}`, - 2 - ); - -(async function main(version: string): Promise { - if (!semver.valid(version)) { - throw new Error(`Usage: yarn release-changelog SEMVER`); - } - - const tokens = await readAndParseChangelogAsync(); - removeEmptyCategories(tokens); - insertNewEmptyCategoriesAndReleaseHeading(tokens, version); - await writeChangelogAsync(tokens); - findAndPrintCurrentReleaseChangelog(tokens, version); -})(process.argv[process.argv.length - 1]).catch(err => { - // eslint-disable-next-line no-console - console.error(err); - process.exit(1); -}); - -function removeEmptyCategories(tokens: markdown.Tokens): void { - const idxToRemove: number[] = []; - - let i = 0; - for (let j = 0; j < MAIN_CATEGORIES.length; j++) { - // find index of the category - while (true) { - const token = tokens[i]; - if (token.type === markdown.TokenType.HEADING && token.depth === 3) { - break; - } else { - i++; - } - } - - // i points at the category heading - if (tokens[i + 1].type === markdown.TokenType.HEADING) { - // the category is empty - idxToRemove.push(i); - } - - i++; - } - - // start iterating from the end of list so we don't have to update indices - for (let j = idxToRemove.length - 1; j >= 0; j--) { - tokens.splice(idxToRemove[j], 1); - } -} - -function insertNewEmptyCategoriesAndReleaseHeading(tokens: markdown.Tokens, version: string): void { - let i = 0; - while (true) { - const token = tokens[i++]; - if (token.type === markdown.TokenType.HEADING && token.text.trim() === 'main') { - break; - } - } - - // i points at the index after the main heading - const releaseHeading = FORMAT_RELEASE_HEADING(version); - tokens.splice(i, 0, ...MAIN_CATEGORIES, releaseHeading); -} - -function findAndPrintCurrentReleaseChangelog(tokens: markdown.Tokens, version: string): void { - const startIdx = tokens.findIndex( - token => - token.type === markdown.TokenType.HEADING && token.depth === 2 && token.text.trim() !== 'main' - ); - const endIdx = tokens.findIndex( - (token, idx) => idx > startIdx && token.type === markdown.TokenType.HEADING && token.depth === 2 - ); - const currentReleaseTokens = tokens.slice(startIdx + 1, endIdx); - const rendered = markdown.render(currentReleaseTokens); - // eslint-disable-next-line no-console - console.log(`v${version}\n\n${rendered.trim()}`); -} diff --git a/scripts/src/updateLocalPlugin.sh b/scripts/src/updateLocalPlugin.sh deleted file mode 100755 index ac1378a7a3..0000000000 --- a/scripts/src/updateLocalPlugin.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )" -LOCAL_TS_PATH="$ROOT_DIR/packages/eas-cli/src/build/local.ts" - -LOCAL_BUILD_PLUGIN_NPM_TAG="eas-cli" - -echo "Checking if eas-cli-local-build-plugin upgrade is available" - -plugin_version_latest=$(npm info eas-cli-local-build-plugin@$LOCAL_BUILD_PLUGIN_NPM_TAG version) -plugin_version_current=$(grep 'const PLUGIN_PACKAGE_VERSION' $LOCAL_TS_PATH | cut -d"'" -f2) - -if [[ "$plugin_version_latest" == "$plugin_version_current" ]]; then - echo "eas-cli-local-build-plugin is already on the latest version - $plugin_version_latest" -else - echo "Bumping eas-cli-local-build-plugin from $plugin_version_current to $plugin_version_latest" - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' "s/$plugin_version_current/$plugin_version_latest/g" $LOCAL_TS_PATH - else - sed -i "s/$plugin_version_current/$plugin_version_latest/g" $LOCAL_TS_PATH - fi - git add $LOCAL_TS_PATH - git commit -m "upgrade eas-cli-local-build-plugin to $plugin_version_latest" -fi diff --git a/scripts/src/validateChangesets.ts b/scripts/src/validateChangesets.ts new file mode 100644 index 0000000000..f11fd6bb41 --- /dev/null +++ b/scripts/src/validateChangesets.ts @@ -0,0 +1,80 @@ +/* eslint-disable no-console */ + +import { parseChangesetFile } from '@changesets/parse'; +import { execFileSync } from 'node:child_process'; +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { ROOT_DIR, readPackageDirsByNameAsync } from './changesets/packages.js'; + +const CHANGESET_FILE = /^\.changeset\/[^/]+\.md$/; + +function getChangedFiles(baseRef: string): string[] { + const output = execFileSync( + 'git', + ['diff', '--name-only', '--diff-filter=d', `${baseRef}...HEAD`], + { cwd: ROOT_DIR, encoding: 'utf8' } + ); + return output.split('\n').filter(Boolean); +} + +async function validateChangesetAsync( + file: string, + packageNames: ReadonlySet +): Promise { + let changeset: ReturnType; + try { + changeset = parseChangesetFile(await readFile(path.join(ROOT_DIR, file), 'utf8')); + } catch (error) { + return [`${file}: cannot parse the changeset. ${(error as Error).message}`]; + } + const errors: string[] = []; + if (!changeset.summary.trim()) { + errors.push(`${file}: the summary is empty. Describe the change below the front matter.`); + } + for (const release of changeset.releases) { + if (!packageNames.has(release.name)) { + errors.push(`${file}: "${release.name}" is not a package in packages/.`); + } + } + return errors; +} + +async function mainAsync(): Promise { + const baseRef = process.argv[2]; + if (!baseRef) { + throw new Error('Usage: validate-changesets '); + } + const changedFiles = getChangedFiles(baseRef); + const errors: string[] = changedFiles + .filter(file => path.basename(file) === 'CHANGELOG.md') + .map( + file => + `${file}: do not edit CHANGELOG.md files. The release generates them from changesets. Run \`yarn changeset\` instead.` + ); + + const changesetFiles = changedFiles.filter( + file => CHANGESET_FILE.test(file) && file !== '.changeset/README.md' + ); + if (changesetFiles.length === 0) { + errors.push('This PR has no changeset. Run `yarn changeset` and commit the new file.'); + } + + const packageNames = new Set((await readPackageDirsByNameAsync()).keys()); + for (const file of changesetFiles) { + errors.push(...(await validateChangesetAsync(file, packageNames))); + } + + if (errors.length > 0) { + for (const error of errors) { + console.error(`โŒ ${error}`); + } + process.exit(1); + } + console.log(`โœ… Found ${changesetFiles.length} valid changeset(s): ${changesetFiles.join(', ')}`); +} + +mainAsync().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/scripts/src/versionPackages.ts b/scripts/src/versionPackages.ts new file mode 100644 index 0000000000..72d10ac480 --- /dev/null +++ b/scripts/src/versionPackages.ts @@ -0,0 +1,105 @@ +/* eslint-disable no-console */ + +import { getReleasePlan } from '@changesets/get-release-plan'; +import { getCommitsThatAddFiles } from '@changesets/git'; +import { execFileSync } from 'node:child_process'; +import { readFile, writeFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { getAttributionAsync } from '../../.changeset/changelog.mjs'; +import { formatEntry } from '../../.changeset/formatEntry.mjs'; +import { ROOT_DIR, readPackageDirsByNameAsync } from './changesets/packages.js'; +import { + type RootEntry, + formatPackagePrefix, + getHighestBumpType, + getSharedVersion, + insertRootSection, + renderRootSection, +} from './changesets/rootChangelog.js'; + +const ROOT_CHANGELOG_PATH = path.join(ROOT_DIR, 'CHANGELOG.md'); +const LERNA_JSON_PATH = path.join(ROOT_DIR, 'lerna.json'); + +function run(command: string, args: string[]): string { + return execFileSync(command, args, { cwd: ROOT_DIR, encoding: 'utf8' }); +} + +function runWithOutput(command: string, args: string[]): void { + execFileSync(command, args, { cwd: ROOT_DIR, stdio: 'inherit' }); +} + +function isPublishingRun(): boolean { + const userName = run('git', ['config', 'get', '--global', 'user.name']).trim(); + const userEmail = run('git', ['config', 'get', '--global', 'user.email']).trim(); + return ( + userName === 'Expo CI' && + userEmail === 'support+ci@expo.io' && + process.env.INPUT_DRY_RUN !== 'true' + ); +} + +async function buildRootEntriesAsync(): Promise<{ version: string; entries: RootEntry[] }> { + const releasePlan = await getReleasePlan(ROOT_DIR); + if (releasePlan.changesets.length === 0) { + throw new Error('There are no changesets in .changeset/. Nothing to release.'); + } + const version = getSharedVersion(releasePlan.releases); + const dirByPackageName = await readPackageDirsByNameAsync(); + const commits = await getCommitsThatAddFiles( + releasePlan.changesets.map(changeset => `.changeset/${changeset.id}.md`), + { cwd: ROOT_DIR } + ); + const entries = await Promise.all( + releasePlan.changesets.map(async (changeset, index) => { + const attribution = await getAttributionAsync({ ...changeset, commit: commits[index] }); + const prefix = formatPackagePrefix(changeset.releases, dirByPackageName); + const text = formatEntry(changeset.summary, attribution); + return { + bumpType: getHighestBumpType(changeset.releases), + text: prefix ? `${prefix} ${text}` : text, + }; + }) + ); + return { version, entries }; +} + +async function updateLernaVersionAsync(version: string): Promise { + const lernaJson = JSON.parse(await readFile(LERNA_JSON_PATH, 'utf8')); + lernaJson.version = version; + await writeFile(LERNA_JSON_PATH, `${JSON.stringify(lernaJson, null, 2)}\n`); +} + +async function updateRootChangelogAsync(section: string): Promise { + const changelog = await readFile(ROOT_CHANGELOG_PATH, 'utf8'); + await writeFile(ROOT_CHANGELOG_PATH, insertRootSection(changelog, section)); +} + +async function mainAsync(): Promise { + const { version, entries } = await buildRootEntriesAsync(); + const date = new Date().toISOString().slice(0, 10); + const section = renderRootSection(version, date, entries); + + runWithOutput('yarn', ['changeset', 'version']); + await updateLernaVersionAsync(version); + runWithOutput('yarn', ['install', '--mode=update-lockfile']); + runWithOutput('yarn', ['workspace', 'eas-cli', 'run', 'version']); + await updateRootChangelogAsync(section); + + const tag = `v${version}`; + if (!isPublishingRun()) { + console.log(`Dry run: ${tag} was not committed, tagged, or pushed.\n`); + console.log(section); + runWithOutput('git', ['status', '--short']); + return; + } + runWithOutput('git', ['add', '--all']); + runWithOutput('git', ['commit', '--message', tag]); + runWithOutput('git', ['tag', '--annotate', tag, '--message', tag]); + runWithOutput('git', ['push', '--atomic', '--no-verify', 'origin', 'main', tag]); +} + +mainAsync().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 38e30cf887..ccb8641fa2 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -8,7 +8,7 @@ "target": "ES2020", "outDir": "build", "rootDir": "src", - "types": ["node", "marked"], + "types": ["node"], "typeRoots": ["../ts-declarations", "./node_modules/@types"] }, "include": ["src/**/*"] diff --git a/scripts/yarn.lock b/scripts/yarn.lock index 86af818abe..62f3597d49 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -5,6 +5,124 @@ __metadata: version: 8 cacheKey: 10c0 +"@changesets/assemble-release-plan@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/assemble-release-plan@npm:7.0.0" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + semver: "npm:^7.8.1" + checksum: 10c0/2544759583889865487aec744e948c6d84206a42aa593430436ace83003fe00d3880ef623114c396cd5fd883eccb1e03fc4a719ba5c33038a8556df8fca4b9b7 + languageName: node + linkType: hard + +"@changesets/config@npm:^4.0.1": + version: 4.0.1 + resolution: "@changesets/config@npm:4.0.1" + dependencies: + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + picomatch: "npm:^4.0.7" + checksum: 10c0/959dbbb3ab9fab0427a8990cbe7d4465d9b67042c96108209c8110b7c5ccb2fc3303b75d5c024e911d3a51ca082d13d49a5091edf52014a2b8d69e89b58188ed + languageName: node + linkType: hard + +"@changesets/errors@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/errors@npm:1.0.0" + checksum: 10c0/502439fb046b9916e8a0745374c75fa80883b0bdeea92aa2e85be07b8675dcdb50df5bd108ce66eddbda1f8863be750bf817301c9d4904c2168305df870e3f9f + languageName: node + linkType: hard + +"@changesets/get-dependents-graph@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/get-dependents-graph@npm:3.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + semver: "npm:^7.8.1" + checksum: 10c0/ab675098db0b92f61115952fb793620bb5b76e502bad5f7f63bcc85a50e5515f70e08957be87381a58141ab2078bf94dd92df86efa55e73227e7604ba6a3968b + languageName: node + linkType: hard + +"@changesets/get-release-plan@npm:5.0.2": + version: 5.0.2 + resolution: "@changesets/get-release-plan@npm:5.0.2" + dependencies: + "@changesets/assemble-release-plan": "npm:^7.0.0" + "@changesets/config": "npm:^4.0.1" + "@changesets/pre": "npm:^3.0.0" + "@changesets/read": "npm:^1.0.1" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + checksum: 10c0/1c79db0a5630b2b71cd27ecd1b410c9670eb256c9345d0636cc1d1727ce2ace1e7200055d27fa4bc0656f98561b6df195a238cd6d98fb7408d967422b52b37b3 + languageName: node + linkType: hard + +"@changesets/git@npm:4.0.1, @changesets/git@npm:^4.0.1": + version: 4.0.1 + resolution: "@changesets/git@npm:4.0.1" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + picomatch: "npm:^4.0.4" + tinyexec: "npm:^1.3.0" + checksum: 10c0/b548ecfafb238ff477a793e4ee91c1fe818274b9ea5b6d5a9d619f75a915ad27831f2accf2edce77da57f918bf1009b59cca2c5aaa7288db4b266f87872c2c23 + languageName: node + linkType: hard + +"@changesets/parse@npm:1.0.0, @changesets/parse@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/parse@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + yaml: "npm:^2.9.0" + checksum: 10c0/76e93b099015420dfc08f3e3a9321ea2512659d8946d1ed86de64899b52d37faa87173cc80ead5d313ffce2f829ce380a0188c0a1cccaad48350e7c6a83720d0 + languageName: node + linkType: hard + +"@changesets/pre@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/pre@npm:3.0.0" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + checksum: 10c0/56e49668f25cabf50dcab12b6ae20fd7dbeba628390149d38a9075b05c41b954d4fbcebe7535878087c5f0b707ce96b90b727286850c71d6d321477bd1a1c18e + languageName: node + linkType: hard + +"@changesets/read@npm:^1.0.1": + version: 1.0.1 + resolution: "@changesets/read@npm:1.0.1" + dependencies: + "@changesets/git": "npm:^4.0.1" + "@changesets/parse": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/125de7132b71e9c184f35ec0ad4d23b9accb0aa04d178c835ef575fbf12afbed2fd2d975e987ebb70c007c9a08dc97a267123ba515a4c2f4f070279ad8193f46 + languageName: node + linkType: hard + +"@changesets/should-skip-package@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/should-skip-package@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/9beb51e7a6a6d678fbfe60efca4646d7daf117ffb90d3dbe3e8b07806f87383cf2a49b0e15b5fe2b827b69f61c0cf7cb77c4bf27b403c2ce835c5ba7990ed0e5 + languageName: node + linkType: hard + +"@changesets/types@npm:7.0.0, @changesets/types@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/types@npm:7.0.0" + checksum: 10c0/494e09ff94968c1c81521f5ac8c6ab60a1e30d7d0cd9ea7f83c2cbac92efba38b03e008ee6fb4592d90b05d69bad864b6ae8155827d52a51c652f5c24d36ec83 + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -38,6 +156,36 @@ __metadata: languageName: node linkType: hard +"@manypkg/find-root@npm:^3.1.0": + version: 3.1.0 + resolution: "@manypkg/find-root@npm:3.1.0" + dependencies: + "@manypkg/tools": "npm:^2.1.0" + checksum: 10c0/15b3f5c5c66881af88c1c70dcec805237a2b7e1d541ca7687aade00978680024f8e77ca2f4bac1415e00377f2e78a998d842c345c288d3e69383d50280620b1c + languageName: node + linkType: hard + +"@manypkg/get-packages@npm:^3.1.0": + version: 3.1.0 + resolution: "@manypkg/get-packages@npm:3.1.0" + dependencies: + "@manypkg/find-root": "npm:^3.1.0" + "@manypkg/tools": "npm:^2.1.0" + checksum: 10c0/e8baabd85a13f4537ae22124d5e53f1523ba0653f33006da45fc8bd642e0134786a6aad9d0a7ed99282ae20a80687ef5700bd72980441627b5c96658503ad83d + languageName: node + linkType: hard + +"@manypkg/tools@npm:^2.1.0": + version: 2.1.2 + resolution: "@manypkg/tools@npm:2.1.2" + dependencies: + jju: "npm:^1.4.0" + tinyglobby: "npm:^0.2.13" + yaml: "npm:^2.9.0" + checksum: 10c0/05c8ece3b3b3ff170765ecf230f0a5408543f1ad43f8b5dc97651adff0ffed3d860dabdd1e2d1068aa247832d473c4de583791675b8f3d07568da4cab2a0ebc3 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.8 resolution: "@tsconfig/node10@npm:1.0.8" @@ -66,55 +214,6 @@ __metadata: languageName: node linkType: hard -"@types/dateformat@npm:3.0.1": - version: 3.0.1 - resolution: "@types/dateformat@npm:3.0.1" - checksum: 10c0/f0cbd170987ede1fb4b541f1471de7eb722577b391ba3209df6e6840f32cb3ec2be8a8a5e00ff364834429d16bc341472087d91a960b903f331f5c44073af9a4 - languageName: node - linkType: hard - -"@types/fs-extra@npm:11.0.4": - version: 11.0.4 - resolution: "@types/fs-extra@npm:11.0.4" - dependencies: - "@types/jsonfile": "npm:*" - "@types/node": "npm:*" - checksum: 10c0/9e34f9b24ea464f3c0b18c3f8a82aefc36dc524cc720fc2b886e5465abc66486ff4e439ea3fb2c0acebf91f6d3f74e514f9983b1f02d4243706bdbb7511796ad - languageName: node - linkType: hard - -"@types/jsonfile@npm:*": - version: 6.1.4 - resolution: "@types/jsonfile@npm:6.1.4" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/b12d068b021e4078f6ac4441353965769be87acf15326173e2aea9f3bf8ead41bd0ad29421df5bbeb0123ec3fc02eb0a734481d52903704a1454a1845896b9eb - languageName: node - linkType: hard - -"@types/lodash-es@npm:4.17.12": - version: 4.17.12 - resolution: "@types/lodash-es@npm:4.17.12" - dependencies: - "@types/lodash": "npm:*" - checksum: 10c0/5d12d2cede07f07ab067541371ed1b838a33edb3c35cb81b73284e93c6fd0c4bbeaefee984e69294bffb53f62d7272c5d679fdba8e595ff71e11d00f2601dde0 - languageName: node - linkType: hard - -"@types/lodash@npm:*": - version: 4.14.182 - resolution: "@types/lodash@npm:4.14.182" - checksum: 10c0/d6bd4789dfb3be631d5e3277e6a1be5becb21440f3364f5d15b982c2e6b6bb1f8048d46fc5bff5ef0f90bebaf4d07c49b2919ba369d07af72d3beb3fea70c61a - languageName: node - linkType: hard - -"@types/node@npm:*": - version: 16.10.3 - resolution: "@types/node@npm:16.10.3" - checksum: 10c0/8d6a81dc5b5d5dd2a7c7025b378f9d1c7a212bb1ebbf048070988d7b2f66b51278dc462090403a9a882dc305d3086ce61979a3f2ff2c4930b62831494c8f6abd - languageName: node - linkType: hard - "@types/node@npm:22.20.5": version: 22.20.5 resolution: "@types/node@npm:22.20.5" @@ -124,13 +223,6 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:7.5.6": - version: 7.5.6 - resolution: "@types/semver@npm:7.5.6" - checksum: 10c0/196dc32db5f68cbcde2e6a42bb4aa5cbb100fa2b7bd9c8c82faaaf3e03fbe063e205dbb4f03c7cdf53da2edb70a0d34c9f2e601b54281b377eb8dc1743226acd - languageName: node - linkType: hard - "acorn-walk@npm:^8.1.1": version: 8.2.0 resolution: "acorn-walk@npm:8.2.0" @@ -154,13 +246,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:5.2.0": - version: 5.2.0 - resolution: "chalk@npm:5.2.0" - checksum: 10c0/8a519b35c239f96e041b7f1ed8fdd79d3ca2332a8366cb957378b8a1b8a4cdfb740d19628e8bf74654d4c0917aa10cf39c20752e177a1304eac29a1168a740e9 - languageName: node - linkType: hard - "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -168,13 +253,6 @@ __metadata: languageName: node linkType: hard -"dateformat@npm:4.5.1": - version: 4.5.1 - resolution: "dateformat@npm:4.5.1" - checksum: 10c0/b8a0729386254f8e1633b552f156160777302f91cd81bba3dc91641b80ce2ea7f330e2980e95ee460e079fb4d7d1b821ef8562f38dc2b461c7101db9b503f504 - languageName: node - linkType: hard - "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -182,50 +260,22 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:11.2.0": - version: 11.2.0 - resolution: "fs-extra@npm:11.2.0" - dependencies: - graceful-fs: "npm:^4.2.0" - jsonfile: "npm:^6.0.1" - universalify: "npm:^2.0.0" - checksum: 10c0/d77a9a9efe60532d2e790e938c81a02c1b24904ef7a3efb3990b835514465ba720e99a6ea56fd5e2db53b4695319b644d76d5a0e9988a2beef80aa7b1da63398 - languageName: node - linkType: hard - -"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0": - version: 4.2.8 - resolution: "graceful-fs@npm:4.2.8" - checksum: 10c0/68365485460f7d2e95c05c1b7aeee935349f3b7776488d5bd95a45d8a45bd4977442e88cbbdb4ea01bc72f49f01f75d83f049069774ac8cc4328af4bcff1c542 - languageName: node - linkType: hard - -"jsonfile@npm:^6.0.1": - version: 6.1.0 - resolution: "jsonfile@npm:6.1.0" - dependencies: - graceful-fs: "npm:^4.1.6" - universalify: "npm:^2.0.0" - dependenciesMeta: - graceful-fs: +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: optional: true - checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865 + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f languageName: node linkType: hard -"lodash-es@npm:4.18.1": - version: 4.18.1 - resolution: "lodash-es@npm:4.18.1" - checksum: 10c0/35d4dcf87ef07f8d090f409447575800108057e360b445f590d0d25d09e3d1e33a163d2fc100d4d072b0f901d5e2fc533cd7c4bfd8eeb38a06abec693823c8b8 - languageName: node - linkType: hard - -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/cb53e582785c48187d7a188d3379c181b5ca2a9c78d2bce3e7dee36f32761d1c42983da3fe12b55cb74e1779fa94cdc2e5367c028a9b35317184ede0c07a30a9 +"jju@npm:^1.4.0": + version: 1.4.0 + resolution: "jju@npm:1.4.0" + checksum: 10c0/f3f444557e4364cfc06b1abf8331bf3778b26c0c8552ca54429bc0092652172fdea26cbffe33e1017b303d5aa506f7ede8571857400efe459cb7439180e2acad languageName: node linkType: hard @@ -236,12 +286,10 @@ __metadata: languageName: node linkType: hard -"marked@npm:3.0.4": - version: 3.0.4 - resolution: "marked@npm:3.0.4" - bin: - marked: bin/marked - checksum: 10c0/2c31fab4d3a65a84895ad26e25b238baf93453ff8344df83bf79f4940636fde5bd744747261eba468f773f21124d7da10b2cf40d4101dbe71aa885f04e61db7b +"picomatch@npm:^4.0.4, picomatch@npm:^4.0.7": + version: 4.0.7 + resolution: "picomatch@npm:4.0.7" + checksum: 10c0/beb6ae02c43ae44e84883b90830196d9046b1726ead292adcf7f57945e0bb0d992d68563d87e03b484b6f3c9a5c6defda7523477f047d7f0e663f126cc01787f languageName: node linkType: hard @@ -249,31 +297,39 @@ __metadata: version: 0.0.0-use.local resolution: "scripts@workspace:." dependencies: - "@types/dateformat": "npm:3.0.1" - "@types/fs-extra": "npm:11.0.4" - "@types/lodash-es": "npm:4.17.12" + "@changesets/get-release-plan": "npm:5.0.2" + "@changesets/git": "npm:4.0.1" + "@changesets/parse": "npm:1.0.0" + "@changesets/types": "npm:7.0.0" "@types/node": "npm:22.20.5" - "@types/semver": "npm:7.5.6" - chalk: "npm:5.2.0" - dateformat: "npm:4.5.1" - fs-extra: "npm:11.2.0" - lodash-es: "npm:4.18.1" - marked: "npm:3.0.4" - semver: "npm:7.5.4" ts-node: "npm:10.9.2" - tslib: "npm:2.6.2" typescript: "npm:6.0.3" languageName: unknown linkType: soft -"semver@npm:7.5.4": - version: 7.5.4 - resolution: "semver@npm:7.5.4" - dependencies: - lru-cache: "npm:^6.0.0" +"semver@npm:^7.8.1": + version: 7.8.5 + resolution: "semver@npm:7.8.5" bin: semver: bin/semver.js - checksum: 10c0/5160b06975a38b11c1ab55950cb5b8a23db78df88275d3d8a42ccf1f29e55112ac995b3a26a522c36e3b5f76b0445f1eef70d696b8c7862a2b4303d7b0e7609e + checksum: 10c0/b1f3127a5be8125a94f37188b361c212466c292c6910adce3ec106cff5dc211ccaedc4739c11bb70fda59d6fc1f040a9bca289f4e093451521a2372e5231fe0c + languageName: node + linkType: hard + +"tinyexec@npm:^1.3.0": + version: 1.3.1 + resolution: "tinyexec@npm:1.3.1" + checksum: 10c0/c590369cb3fa73cc18a3998caec8a00f9712d91309c881be3ac52b160fb2f80c7b1fbf3591858ab6abab47ad55385b0c86ec19982e32441a01f55298dab9ccd2 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.13": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c languageName: node linkType: hard @@ -315,13 +371,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.2": - version: 2.6.2 - resolution: "tslib@npm:2.6.2" - checksum: 10c0/e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb - languageName: node - linkType: hard - "typescript@npm:6.0.3": version: 6.0.3 resolution: "typescript@npm:6.0.3" @@ -349,13 +398,6 @@ __metadata: languageName: node linkType: hard -"universalify@npm:^2.0.0": - version: 2.0.0 - resolution: "universalify@npm:2.0.0" - checksum: 10c0/07092b9f46df61b823d8ab5e57f0ee5120c178b39609a95e4a15a98c42f6b0b8e834e66fbb47ff92831786193be42f1fd36347169b88ce8639d0f9670af24a71 - languageName: node - linkType: hard - "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -363,10 +405,12 @@ __metadata: languageName: node linkType: hard -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a +"yaml@npm:^2.9.0": + version: 2.9.1 + resolution: "yaml@npm:2.9.1" + bin: + yaml: bin.mjs + checksum: 10c0/9d06676ef02b89559a8b10a71882bd2cdae81e8393dc8dbc871615c3259475d9609ad973d19cb3c9e71e72d6206e3d1e7aa241c7e55f3d16fd2e77db305827d6 languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index 6fabb3e1fa..afe54e1c68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2211,6 +2211,218 @@ __metadata: languageName: node linkType: hard +"@changesets/apply-release-plan@npm:^8.1.1": + version: 8.1.1 + resolution: "@changesets/apply-release-plan@npm:8.1.1" + dependencies: + "@changesets/config": "npm:^4.0.1" + "@changesets/format": "npm:^0.1.2" + "@changesets/git": "npm:^4.0.1" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + import-meta-resolve: "npm:^4.2.0" + jsonc-parser: "npm:^3.3.1" + semver: "npm:^7.8.1" + checksum: 10c0/2a1cfb9e4e3feb2c76bcec646e116ebebc81271fed21a3e77d5f170963f88347a34e5d1474f8b87258448182cfa651c047282c8b1a741613a7f7d3fefc2eb9a2 + languageName: node + linkType: hard + +"@changesets/assemble-release-plan@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/assemble-release-plan@npm:7.0.0" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + semver: "npm:^7.8.1" + checksum: 10c0/2544759583889865487aec744e948c6d84206a42aa593430436ace83003fe00d3880ef623114c396cd5fd883eccb1e03fc4a719ba5c33038a8556df8fca4b9b7 + languageName: node + linkType: hard + +"@changesets/changelog-git@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/changelog-git@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/f0f0ab31e6e6ac35ecdae46a521d4c84aab77bbb035898e364d8f9082391b2b8064ba33d9f97d4f1526195f8278f41910c72382b12fcc0092d5bb9ff3ae05310 + languageName: node + linkType: hard + +"@changesets/cli@npm:3.0.3": + version: 3.0.3 + resolution: "@changesets/cli@npm:3.0.3" + dependencies: + "@changesets/apply-release-plan": "npm:^8.1.1" + "@changesets/assemble-release-plan": "npm:^7.0.0" + "@changesets/changelog-git": "npm:^1.0.0" + "@changesets/config": "npm:^4.0.1" + "@changesets/errors": "npm:^1.0.0" + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/git": "npm:^4.0.1" + "@changesets/pre": "npm:^3.0.0" + "@changesets/read": "npm:^1.0.1" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@changesets/write": "npm:^1.0.1" + "@clack/prompts": "npm:^1.7.0" + "@manypkg/get-packages": "npm:^3.1.0" + "@pnpm/deps.graph-sequencer": "npm:^1100.0.1" + cac: "npm:^7.0.0" + import-meta-resolve: "npm:^4.2.0" + launch-editor: "npm:^2.14.1" + package-manager-detector: "npm:^1.6.0" + semver: "npm:^7.8.1" + tinyexec: "npm:^1.3.1" + bin: + changeset: bin.js + checksum: 10c0/238b98c590bfe33e794cf5ab47e0fa6eb46fc587da9498af49037877f1043b3e91dac65c990ebd1675115d82f2a3a2b3e7aa1b5d385369b0322a24e858eb1313 + languageName: node + linkType: hard + +"@changesets/config@npm:^4.0.1": + version: 4.0.1 + resolution: "@changesets/config@npm:4.0.1" + dependencies: + "@changesets/get-dependents-graph": "npm:^3.0.0" + "@changesets/should-skip-package": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + picomatch: "npm:^4.0.7" + checksum: 10c0/959dbbb3ab9fab0427a8990cbe7d4465d9b67042c96108209c8110b7c5ccb2fc3303b75d5c024e911d3a51ca082d13d49a5091edf52014a2b8d69e89b58188ed + languageName: node + linkType: hard + +"@changesets/errors@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/errors@npm:1.0.0" + checksum: 10c0/502439fb046b9916e8a0745374c75fa80883b0bdeea92aa2e85be07b8675dcdb50df5bd108ce66eddbda1f8863be750bf817301c9d4904c2168305df870e3f9f + languageName: node + linkType: hard + +"@changesets/format@npm:^0.1.1, @changesets/format@npm:^0.1.2": + version: 0.1.2 + resolution: "@changesets/format@npm:0.1.2" + dependencies: + package-manager-detector: "npm:^1.8.0" + tinyexec: "npm:^1.3.0" + checksum: 10c0/fdb35b885b0923c2fac48433f5d633cc1a091f10e589eb65e3cd33c6702864ea26ef41d7bb434f0c91715dafa609442b91a780e84089b9bd33b7906d65d13f39 + languageName: node + linkType: hard + +"@changesets/get-dependents-graph@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/get-dependents-graph@npm:3.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + semver: "npm:^7.8.1" + checksum: 10c0/ab675098db0b92f61115952fb793620bb5b76e502bad5f7f63bcc85a50e5515f70e08957be87381a58141ab2078bf94dd92df86efa55e73227e7604ba6a3968b + languageName: node + linkType: hard + +"@changesets/get-github-info@npm:1.0.1": + version: 1.0.1 + resolution: "@changesets/get-github-info@npm:1.0.1" + dependencies: + dataloader: "npm:^2.2.3" + checksum: 10c0/7b45b5ebc97b297e1c373ec782c501c3b6cc911badf43e4ebaaf46ce1d8b94f62e4d758705e03d3b9df06b730ab85b9dab5ad76ff070f8ff65ef2fb21647d61b + languageName: node + linkType: hard + +"@changesets/git@npm:^4.0.1": + version: 4.0.1 + resolution: "@changesets/git@npm:4.0.1" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + picomatch: "npm:^4.0.4" + tinyexec: "npm:^1.3.0" + checksum: 10c0/b548ecfafb238ff477a793e4ee91c1fe818274b9ea5b6d5a9d619f75a915ad27831f2accf2edce77da57f918bf1009b59cca2c5aaa7288db4b266f87872c2c23 + languageName: node + linkType: hard + +"@changesets/parse@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/parse@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + yaml: "npm:^2.9.0" + checksum: 10c0/76e93b099015420dfc08f3e3a9321ea2512659d8946d1ed86de64899b52d37faa87173cc80ead5d313ffce2f829ce380a0188c0a1cccaad48350e7c6a83720d0 + languageName: node + linkType: hard + +"@changesets/pre@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/pre@npm:3.0.0" + dependencies: + "@changesets/errors": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + "@manypkg/get-packages": "npm:^3.1.0" + checksum: 10c0/56e49668f25cabf50dcab12b6ae20fd7dbeba628390149d38a9075b05c41b954d4fbcebe7535878087c5f0b707ce96b90b727286850c71d6d321477bd1a1c18e + languageName: node + linkType: hard + +"@changesets/read@npm:^1.0.1": + version: 1.0.1 + resolution: "@changesets/read@npm:1.0.1" + dependencies: + "@changesets/git": "npm:^4.0.1" + "@changesets/parse": "npm:^1.0.0" + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/125de7132b71e9c184f35ec0ad4d23b9accb0aa04d178c835ef575fbf12afbed2fd2d975e987ebb70c007c9a08dc97a267123ba515a4c2f4f070279ad8193f46 + languageName: node + linkType: hard + +"@changesets/should-skip-package@npm:^1.0.0": + version: 1.0.0 + resolution: "@changesets/should-skip-package@npm:1.0.0" + dependencies: + "@changesets/types": "npm:^7.0.0" + checksum: 10c0/9beb51e7a6a6d678fbfe60efca4646d7daf117ffb90d3dbe3e8b07806f87383cf2a49b0e15b5fe2b827b69f61c0cf7cb77c4bf27b403c2ce835c5ba7990ed0e5 + languageName: node + linkType: hard + +"@changesets/types@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/types@npm:7.0.0" + checksum: 10c0/494e09ff94968c1c81521f5ac8c6ab60a1e30d7d0cd9ea7f83c2cbac92efba38b03e008ee6fb4592d90b05d69bad864b6ae8155827d52a51c652f5c24d36ec83 + languageName: node + linkType: hard + +"@changesets/write@npm:^1.0.1": + version: 1.0.1 + resolution: "@changesets/write@npm:1.0.1" + dependencies: + "@changesets/format": "npm:^0.1.1" + "@changesets/types": "npm:^7.0.0" + human-id: "npm:^4.2.0" + checksum: 10c0/c9cb074a8c524db1327e54dc1379ff4b50d3a4915409a3e5abe1edaf1ce45f6dafc6c8aced17a1f3eebfa162241b5fd8ea74fc751d2e24df56c2966e4169fcaf + languageName: node + linkType: hard + +"@clack/core@npm:1.5.1": + version: 1.5.1 + resolution: "@clack/core@npm:1.5.1" + dependencies: + fast-wrap-ansi: "npm:^0.2.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/6dca93ff5a34bfe8c150367519bc40a847e9172cb1a78b18c88da0507cb6ad899debe1ad5cfcb9796e439108f12f003cf06fd002e2293bc85ff500975e45ff13 + languageName: node + linkType: hard + +"@clack/prompts@npm:^1.7.0": + version: 1.8.1 + resolution: "@clack/prompts@npm:1.8.1" + dependencies: + "@clack/core": "npm:1.5.1" + fast-string-width: "npm:^3.0.2" + fast-wrap-ansi: "npm:^0.2.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/ed38748acde0cb1e7ebdac1242470c78bd90380cbdde1e5560e0828881e3c74e2676de666086cf8183c8d4d3f1fc6e489f15a004f0d9f8ee294a3aa744de2093 + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -5042,6 +5254,36 @@ __metadata: languageName: node linkType: hard +"@manypkg/find-root@npm:^3.1.0": + version: 3.1.0 + resolution: "@manypkg/find-root@npm:3.1.0" + dependencies: + "@manypkg/tools": "npm:^2.1.0" + checksum: 10c0/15b3f5c5c66881af88c1c70dcec805237a2b7e1d541ca7687aade00978680024f8e77ca2f4bac1415e00377f2e78a998d842c345c288d3e69383d50280620b1c + languageName: node + linkType: hard + +"@manypkg/get-packages@npm:^3.1.0": + version: 3.1.0 + resolution: "@manypkg/get-packages@npm:3.1.0" + dependencies: + "@manypkg/find-root": "npm:^3.1.0" + "@manypkg/tools": "npm:^2.1.0" + checksum: 10c0/e8baabd85a13f4537ae22124d5e53f1523ba0653f33006da45fc8bd642e0134786a6aad9d0a7ed99282ae20a80687ef5700bd72980441627b5c96658503ad83d + languageName: node + linkType: hard + +"@manypkg/tools@npm:^2.1.0": + version: 2.1.2 + resolution: "@manypkg/tools@npm:2.1.2" + dependencies: + jju: "npm:^1.4.0" + tinyglobby: "npm:^0.2.13" + yaml: "npm:^2.9.0" + checksum: 10c0/05c8ece3b3b3ff170765ecf230f0a5408543f1ad43f8b5dc97651adff0ffed3d860dabdd1e2d1068aa247832d473c4de583791675b8f3d07568da4cab2a0ebc3 + languageName: node + linkType: hard + "@napi-rs/wasm-runtime@npm:0.2.4": version: 0.2.4 resolution: "@napi-rs/wasm-runtime@npm:0.2.4" @@ -6026,6 +6268,13 @@ __metadata: languageName: node linkType: hard +"@pnpm/deps.graph-sequencer@npm:^1100.0.1": + version: 1100.0.1 + resolution: "@pnpm/deps.graph-sequencer@npm:1100.0.1" + checksum: 10c0/b38a152e1184055a358e7c522f068572e84759a88dfc56c98a349125b11d3cce72e452424c235af5cf18258a5034f88fe203c72ba57ffbe70b297afc50d08dea + languageName: node + linkType: hard + "@pnpm/network.ca-file@npm:^1.0.1": version: 1.0.2 resolution: "@pnpm/network.ca-file@npm:1.0.2" @@ -8884,6 +9133,13 @@ __metadata: languageName: node linkType: hard +"cac@npm:^7.0.0": + version: 7.0.0 + resolution: "cac@npm:7.0.0" + checksum: 10c0/e9da33cb9f0425546ae92a450d479276f9969a050fe64f5d6fedf058bdd87f22a370797fe1c158e07655fa9fc183749df7cb2037431e3772faa7bee9919fb763 + languageName: node + linkType: hard + "cacache@npm:^19.0.1": version: 19.0.1 resolution: "cacache@npm:19.0.1" @@ -10451,6 +10707,8 @@ __metadata: version: 0.0.0-use.local resolution: "eas-cli-root@workspace:." dependencies: + "@changesets/cli": "npm:3.0.3" + "@changesets/get-github-info": "npm:1.0.1" "@types/jest": "npm:29.5.11" "@types/node": "npm:22.20.5" eslint-plugin-async-protect: "npm:3.1.0" @@ -11156,6 +11414,31 @@ __metadata: languageName: node linkType: hard +"fast-string-truncated-width@npm:^3.0.2": + version: 3.0.3 + resolution: "fast-string-truncated-width@npm:3.0.3" + checksum: 10c0/043b8663397d14a3880ce4f3407bcda60b40db9bbeafe62863a35d1f9c69ea17c8da3fcd72de235553e6c9cd053128cde9e24ca0d4a7463208f48db3cd23d981 + languageName: node + linkType: hard + +"fast-string-width@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-string-width@npm:3.0.2" + dependencies: + fast-string-truncated-width: "npm:^3.0.2" + checksum: 10c0/c8822d175315bb353ebe782b65214ac53b13e3bf704e03b132ea7bdfa8de6a636375b3ab7a4097545393d109381c37c4f387c72a462c90b61412dbc4632f39a7 + languageName: node + linkType: hard + +"fast-wrap-ansi@npm:^0.2.0": + version: 0.2.2 + resolution: "fast-wrap-ansi@npm:0.2.2" + dependencies: + fast-string-width: "npm:^3.0.2" + checksum: 10c0/1aa7be4f7cb86f4bdb14691cb6bcc0b8df8b3b89df142ade3ae1602332dcf6f990cd750a923cd581ca0847808cb4ec1aa5afaafa7a72f849e87a2a62c98fa370 + languageName: node + linkType: hard + "fast-xml-builder@npm:^1.0.0": version: 1.2.1 resolution: "fast-xml-builder@npm:1.2.1" @@ -12403,6 +12686,15 @@ __metadata: languageName: node linkType: hard +"human-id@npm:^4.2.0": + version: 4.2.2 + resolution: "human-id@npm:4.2.2" + bin: + human-id: dist/cli.js + checksum: 10c0/7bf51bf2cec8c26129d3f6ea068b18eca702130641afe5c475cb92270015e403be5bdd35f53b2da38a058a3c1c2632c2d0576c026ad78dc33d30af53eb7d426d + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -12569,6 +12861,13 @@ __metadata: languageName: node linkType: hard +"import-meta-resolve@npm:^4.2.0": + version: 4.2.0 + resolution: "import-meta-resolve@npm:4.2.0" + checksum: 10c0/3ee8aeecb61d19b49d2703987f977e9d1c7d4ba47db615a570eaa02fe414f40dfa63f7b953e842cbe8470d26df6371332bfcf21b2fd92b0112f9fea80dde2c4c + languageName: node + linkType: hard + "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -13692,6 +13991,13 @@ __metadata: languageName: node linkType: hard +"jju@npm:^1.4.0": + version: 1.4.0 + resolution: "jju@npm:1.4.0" + checksum: 10c0/f3f444557e4364cfc06b1abf8331bf3778b26c0c8552ca54429bc0092652172fdea26cbffe33e1017b303d5aa506f7ede8571857400efe459cb7439180e2acad + languageName: node + linkType: hard + "jks-js@npm:1.1.0": version: 1.1.0 resolution: "jks-js@npm:1.1.0" @@ -13932,6 +14238,13 @@ __metadata: languageName: node linkType: hard +"jsonc-parser@npm:^3.3.1": + version: 3.3.1 + resolution: "jsonc-parser@npm:3.3.1" + checksum: 10c0/269c3ae0a0e4f907a914bf334306c384aabb9929bd8c99f909275ebd5c2d3bc70b9bcd119ad794f339dec9f24b6a4ee9cd5a8ab2e6435e730ad4075388fc2ab6 + languageName: node + linkType: hard + "jsonfile@npm:^4.0.0": version: 4.0.0 resolution: "jsonfile@npm:4.0.0" @@ -14105,6 +14418,16 @@ __metadata: languageName: node linkType: hard +"launch-editor@npm:^2.14.1": + version: 2.14.2 + resolution: "launch-editor@npm:2.14.2" + dependencies: + picocolors: "npm:^1.1.1" + shell-quote: "npm:^1.10.0" + checksum: 10c0/cf66c75534170142269bfc1749df8a4b5178ad644d358aa08d4feda9ecb1ba7a26191a76ea2cecea95c59235c142486b928b58eaf0cd77377d21e42ed5c3e100 + languageName: node + linkType: hard + "lerna@npm:9.0.3": version: 9.0.3 resolution: "lerna@npm:9.0.3" @@ -16388,6 +16711,13 @@ __metadata: languageName: node linkType: hard +"package-manager-detector@npm:^1.6.0, package-manager-detector@npm:^1.8.0": + version: 1.9.0 + resolution: "package-manager-detector@npm:1.9.0" + checksum: 10c0/c901b08e9d284e9b517d27be1783fc279f3e814fac9de4c30441129a3a4b1475b16314ff95b98268daf49e20d5076478c50bc7b1b3ff8a591eb39f03f02dbb16 + languageName: node + linkType: hard + "pacote@npm:21.0.1": version: 21.0.1 resolution: "pacote@npm:21.0.1" @@ -16758,6 +17088,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.4, picomatch@npm:^4.0.7": + version: 4.0.7 + resolution: "picomatch@npm:4.0.7" + checksum: 10c0/beb6ae02c43ae44e84883b90830196d9046b1726ead292adcf7f57945e0bb0d992d68563d87e03b484b6f3c9a5c6defda7523477f047d7f0e663f126cc01787f + languageName: node + linkType: hard + "pify@npm:5.0.0": version: 5.0.0 resolution: "pify@npm:5.0.0" @@ -17692,7 +18029,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.8.5": +"semver@npm:^7.8.1, semver@npm:^7.8.5": version: 7.8.5 resolution: "semver@npm:7.8.5" bin: @@ -17786,6 +18123,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.10.0": + version: 1.12.0 + resolution: "shell-quote@npm:1.12.0" + checksum: 10c0/1605a4ed6a758bf13de7cb8197a4184d8015f7a4dbdcd6cd4a717d5b30b3ae7666300cc4ae7f3155cf80ace9739e9e82061ee5aa1a648f53f5fab31a220816d0 + languageName: node + linkType: hard + "shell-quote@npm:^1.7.3": version: 1.9.0 resolution: "shell-quote@npm:1.9.0" @@ -18720,6 +19064,13 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^1.3.0, tinyexec@npm:^1.3.1": + version: 1.3.1 + resolution: "tinyexec@npm:1.3.1" + checksum: 10c0/c590369cb3fa73cc18a3998caec8a00f9712d91309c881be3ac52b160fb2f80c7b1fbf3591858ab6abab47ad55385b0c86ec19982e32441a01f55298dab9ccd2 + languageName: node + linkType: hard + "tinyglobby@npm:0.2.12": version: 0.2.12 resolution: "tinyglobby@npm:0.2.12" @@ -18740,6 +19091,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.13": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + "tinypool@npm:2.1.0": version: 2.1.0 resolution: "tinypool@npm:2.1.0" @@ -20073,6 +20434,15 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^2.9.0": + version: 2.9.1 + resolution: "yaml@npm:2.9.1" + bin: + yaml: bin.mjs + checksum: 10c0/9d06676ef02b89559a8b10a71882bd2cdae81e8393dc8dbc871615c3259475d9609ad973d19cb3c9e71e72d6206e3d1e7aa241c7e55f3d16fd2e77db305827d6 + languageName: node + linkType: hard + "yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1"