From b35585535625231063db44cd5c2d891018dc02d3 Mon Sep 17 00:00:00 2001 From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:50:00 +1100 Subject: [PATCH 1/2] ci(changesets): disable auto-merging --- .github/comment-on-release/README.md | 83 ++++++++ .github/comment-on-release/action.yml | 16 ++ .../comment-on-release/comment-on-release.ts | 184 ++++++++++++++++++ .github/workflows/release.yml | 45 +++-- 4 files changed, 308 insertions(+), 20 deletions(-) create mode 100644 .github/comment-on-release/README.md create mode 100644 .github/comment-on-release/action.yml create mode 100644 .github/comment-on-release/comment-on-release.ts diff --git a/.github/comment-on-release/README.md b/.github/comment-on-release/README.md new file mode 100644 index 0000000000..2842797099 --- /dev/null +++ b/.github/comment-on-release/README.md @@ -0,0 +1,83 @@ +# Comment on Release Action + +A reusable GitHub Action that automatically comments on PRs when they are included in a release. + +## What It Does + +When packages are published via Changesets: + +1. Parses each published package's CHANGELOG to find PR numbers in the latest version +2. Groups PRs by number (handling cases where one PR affects multiple packages) +3. Posts a comment on each PR with release info and CHANGELOG links + +## Example Comment + +``` +🎉 This PR has been released! + +- [@tanstack/query-core@5.0.0](https://github.com/TanStack/query/blob/main/packages/query-core/CHANGELOG.md#500) +- [@tanstack/react-query@5.0.0](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md#500) + +Thank you for your contribution! +``` + +## Usage + +Add this step to your `.github/workflows/release.yml` file after the `changesets/action` step: + +```yaml +- name: Run Changesets (version or publish) + id: changesets + uses: changesets/action@v1.5.3 + with: + version: pnpm run changeset:version + publish: pnpm run changeset:publish + commit: 'ci: Version Packages' + title: 'ci: Version Packages' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + +- name: Comment on PRs about release + if: steps.changesets.outputs.published == 'true' + uses: tanstack/config/.github/comment-on-release@main + with: + published-packages: ${{ steps.changesets.outputs.publishedPackages }} +``` + +## Requirements + +- Must be using [Changesets](https://github.com/changesets/changesets) for releases +- CHANGELOGs must include PR links in the format: `[#123](https://github.com/org/repo/pull/123)` +- Requires `pull-requests: write` permission in the workflow +- The `gh` CLI must be available (automatically available in GitHub Actions) + +## Inputs + +| Input | Required | Description | +| -------------------- | -------- | ------------------------------------------------------------------ | +| `published-packages` | Yes | JSON string of published packages from `changesets/action` outputs | + +## How It Works + +The action: + +1. Receives the list of published packages from the Changesets action +2. For each package, reads its CHANGELOG at `packages/{package-name}/CHANGELOG.md` +3. Extracts PR numbers from the latest version section using regex +4. Groups all PRs and tracks which packages they contributed to +5. Posts a single comment per PR listing all packages it was released in +6. Uses the `gh` CLI to post comments via the GitHub API + +## Troubleshooting + +**No comments are posted:** + +- Verify your CHANGELOGs have PR links in the correct format +- Check that `steps.changesets.outputs.published` is `true` +- Ensure the workflow has `pull-requests: write` permission + +**Script fails to find CHANGELOGs:** + +- The script expects packages at `packages/{package-name}/CHANGELOG.md` +- Package name should match after removing the scope (e.g., `@tanstack/query-core` → `query-core`) diff --git a/.github/comment-on-release/action.yml b/.github/comment-on-release/action.yml new file mode 100644 index 0000000000..82066f776f --- /dev/null +++ b/.github/comment-on-release/action.yml @@ -0,0 +1,16 @@ +name: Comment on PRs about release +description: Automatically comments on PRs when they are included in a release +inputs: + published-packages: + description: 'JSON string of published packages from changesets/action' + required: true +runs: + using: composite + steps: + - name: Comment on PRs + shell: bash + run: node ${{ github.action_path }}/comment-on-release.ts + env: + PUBLISHED_PACKAGES: ${{ inputs.published-packages }} + REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} diff --git a/.github/comment-on-release/comment-on-release.ts b/.github/comment-on-release/comment-on-release.ts new file mode 100644 index 0000000000..6b0901de68 --- /dev/null +++ b/.github/comment-on-release/comment-on-release.ts @@ -0,0 +1,184 @@ +#!/usr/bin/env node + +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { execSync } from 'node:child_process' + +interface PublishedPackage { + name: string + version: string +} + +interface PRInfo { + number: number + packages: Array<{ name: string; pkgPath: string; version: string }> +} + +/** + * Parse CHANGELOG.md to extract PR numbers from the latest version entry + */ +function extractPRsFromChangelog( + changelogPath: string, + version: string, +): Array { + try { + const content = readFileSync(changelogPath, 'utf-8') + const lines = content.split('\n') + + let inTargetVersion = false + let foundVersion = false + const prNumbers = new Set() + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + + // Check for version header (e.g., "## 0.21.0") + if (line.startsWith('## ')) { + const versionMatch = line.match(/^## (\d+\.\d+\.\d+)/) + if (versionMatch) { + if (versionMatch[1] === version) { + inTargetVersion = true + foundVersion = true + } else if (inTargetVersion) { + // We've moved to the next version, stop processing + break + } + } + } + + // Extract PR numbers from links like [#302](https://github.com/TanStack/config/pull/302) + if (inTargetVersion) { + const prMatches = line.matchAll( + /\[#(\d+)\]\(https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+\)/g, + ) + for (const match of prMatches) { + prNumbers.add(parseInt(match[1], 10)) + } + } + } + + if (!foundVersion) { + console.warn( + `Warning: Could not find version ${version} in ${changelogPath}`, + ) + } + + return Array.from(prNumbers) + } catch (error) { + console.error(`Error reading changelog at ${changelogPath}:`, error) + return [] + } +} + +/** + * Group PRs by their numbers and collect all packages they contributed to + */ +function groupPRsByNumber( + publishedPackages: Array, +): Map { + const prMap = new Map() + + for (const pkg of publishedPackages) { + const pkgPath = `packages/${pkg.name.replace('@tanstack/', '')}` + const changelogPath = resolve(process.cwd(), pkgPath, 'CHANGELOG.md') + + const prNumbers = extractPRsFromChangelog(changelogPath, pkg.version) + + for (const prNumber of prNumbers) { + if (!prMap.has(prNumber)) { + prMap.set(prNumber, { number: prNumber, packages: [] }) + } + prMap.get(prNumber)!.packages.push({ + name: pkg.name, + pkgPath: pkgPath, + version: pkg.version, + }) + } + } + + return prMap +} + +/** + * Post a comment on a GitHub PR using gh CLI + */ +async function commentOnPR(pr: PRInfo, repository: string): Promise { + const { number, packages } = pr + + // Build the comment body + let comment = `🎉 This PR has been released!\n\n` + + for (const pkg of packages) { + // Link to the package's changelog and version anchor + const changelogUrl = `https://github.com/${repository}/blob/main/${pkg.pkgPath}/CHANGELOG.md#${pkg.version.replaceAll('.', '')}` + comment += `- [${pkg.name}@${pkg.version}](${changelogUrl})\n` + } + + comment += `\nThank you for your contribution!` + + try { + // Use gh CLI to post the comment + execSync(`gh pr comment ${number} --body '${comment.replace(/'/g, '"')}'`, { + stdio: 'inherit', + }) + console.log(`✓ Commented on PR #${number}`) + } catch (error) { + console.error(`✗ Failed to comment on PR #${number}:`, error) + } +} + +/** + * Main function + */ +async function main() { + // Read published packages from environment variable (set by GitHub Actions) + const publishedPackagesJson = process.env.PUBLISHED_PACKAGES + const repository = process.env.REPOSITORY + + if (!publishedPackagesJson) { + console.log('No packages were published. Skipping PR comments.') + return + } + + if (!repository) { + console.log('Repository is missing. Skipping PR comments.') + return + } + + let publishedPackages: Array + try { + publishedPackages = JSON.parse(publishedPackagesJson) + } catch (error) { + console.error('Failed to parse PUBLISHED_PACKAGES:', error) + process.exit(1) + } + + if (publishedPackages.length === 0) { + console.log('No packages were published. Skipping PR comments.') + return + } + + console.log(`Processing ${publishedPackages.length} published package(s)...`) + + // Group PRs by number + const prMap = groupPRsByNumber(publishedPackages) + + if (prMap.size === 0) { + console.log('No PRs found in CHANGELOGs. Nothing to comment on.') + return + } + + console.log(`Found ${prMap.size} PR(s) to comment on...`) + + // Comment on each PR + for (const pr of prMap.values()) { + await commentOnPR(pr, repository) + } + + console.log('✓ Done!') +} + +main().catch((error) => { + console.error('Fatal error:', error) + process.exit(1) +}) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 62d8c16434..964f209eab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,17 +37,17 @@ jobs: - name: Stop Nx Agents if: ${{ always() }} run: npx nx-cloud stop-all-agents - - name: Check for Changesets marked as major - id: major - run: | - echo "found=false" >> $GITHUB_OUTPUT - regex="(major)" - shopt -s nullglob - for file in .changeset/*.md; do - if [[ $(cat $file) =~ $regex ]]; then - echo "found=true" >> $GITHUB_OUTPUT - fi - done + # - name: Check for Changesets marked as major + # id: major + # run: | + # echo "found=false" >> $GITHUB_OUTPUT + # regex="(major)" + # shopt -s nullglob + # for file in .changeset/*.md; do + # if [[ $(cat $file) =~ $regex ]]; then + # echo "found=true" >> $GITHUB_OUTPUT + # fi + # done - name: Run Changesets (version or publish) id: changesets uses: changesets/action@v1.5.3 @@ -59,18 +59,23 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Auto-merge Changesets PR - if: steps.changesets.outputs.hasChangesets == 'true' && steps.major.outputs.found == 'false' - run: | - gh pr merge --squash "$PR_NUMBER" - gh api --method POST /repos/$REPO/dispatches -f 'event_type=release' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPO: ${{ github.repository }} - PR_NUMBER: ${{ steps.changesets.outputs.pullRequestNumber }} + # - name: Auto-merge Changesets PR + # if: steps.changesets.outputs.hasChangesets == 'true' && steps.major.outputs.found == 'false' + # run: | + # gh pr merge --squash "$PR_NUMBER" + # gh api --method POST /repos/$REPO/dispatches -f 'event_type=release' + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # REPO: ${{ github.repository }} + # PR_NUMBER: ${{ steps.changesets.outputs.pullRequestNumber }} - name: Upload coverage to Codecov uses: codecov/codecov-action@v4.6.0 with: directory: packages env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - name: Comment on PRs about release + if: steps.changesets.outputs.published == 'true' + uses: ./.github/comment-on-release + with: + published-packages: ${{ steps.changesets.outputs.publishedPackages }} From 7c9c51726a620043e4a89ecaf835beed4986f966 Mon Sep 17 00:00:00 2001 From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:06:04 +1100 Subject: [PATCH 2/2] Fix knip --- .github/comment-on-release/README.md | 83 -------- .github/comment-on-release/action.yml | 16 -- .../comment-on-release/comment-on-release.ts | 184 ------------------ .github/workflows/release.yml | 2 +- knip.json | 14 +- packages/solid-query-devtools/package.json | 2 +- 6 files changed, 7 insertions(+), 294 deletions(-) delete mode 100644 .github/comment-on-release/README.md delete mode 100644 .github/comment-on-release/action.yml delete mode 100644 .github/comment-on-release/comment-on-release.ts diff --git a/.github/comment-on-release/README.md b/.github/comment-on-release/README.md deleted file mode 100644 index 2842797099..0000000000 --- a/.github/comment-on-release/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Comment on Release Action - -A reusable GitHub Action that automatically comments on PRs when they are included in a release. - -## What It Does - -When packages are published via Changesets: - -1. Parses each published package's CHANGELOG to find PR numbers in the latest version -2. Groups PRs by number (handling cases where one PR affects multiple packages) -3. Posts a comment on each PR with release info and CHANGELOG links - -## Example Comment - -``` -🎉 This PR has been released! - -- [@tanstack/query-core@5.0.0](https://github.com/TanStack/query/blob/main/packages/query-core/CHANGELOG.md#500) -- [@tanstack/react-query@5.0.0](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md#500) - -Thank you for your contribution! -``` - -## Usage - -Add this step to your `.github/workflows/release.yml` file after the `changesets/action` step: - -```yaml -- name: Run Changesets (version or publish) - id: changesets - uses: changesets/action@v1.5.3 - with: - version: pnpm run changeset:version - publish: pnpm run changeset:publish - commit: 'ci: Version Packages' - title: 'ci: Version Packages' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - -- name: Comment on PRs about release - if: steps.changesets.outputs.published == 'true' - uses: tanstack/config/.github/comment-on-release@main - with: - published-packages: ${{ steps.changesets.outputs.publishedPackages }} -``` - -## Requirements - -- Must be using [Changesets](https://github.com/changesets/changesets) for releases -- CHANGELOGs must include PR links in the format: `[#123](https://github.com/org/repo/pull/123)` -- Requires `pull-requests: write` permission in the workflow -- The `gh` CLI must be available (automatically available in GitHub Actions) - -## Inputs - -| Input | Required | Description | -| -------------------- | -------- | ------------------------------------------------------------------ | -| `published-packages` | Yes | JSON string of published packages from `changesets/action` outputs | - -## How It Works - -The action: - -1. Receives the list of published packages from the Changesets action -2. For each package, reads its CHANGELOG at `packages/{package-name}/CHANGELOG.md` -3. Extracts PR numbers from the latest version section using regex -4. Groups all PRs and tracks which packages they contributed to -5. Posts a single comment per PR listing all packages it was released in -6. Uses the `gh` CLI to post comments via the GitHub API - -## Troubleshooting - -**No comments are posted:** - -- Verify your CHANGELOGs have PR links in the correct format -- Check that `steps.changesets.outputs.published` is `true` -- Ensure the workflow has `pull-requests: write` permission - -**Script fails to find CHANGELOGs:** - -- The script expects packages at `packages/{package-name}/CHANGELOG.md` -- Package name should match after removing the scope (e.g., `@tanstack/query-core` → `query-core`) diff --git a/.github/comment-on-release/action.yml b/.github/comment-on-release/action.yml deleted file mode 100644 index 82066f776f..0000000000 --- a/.github/comment-on-release/action.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Comment on PRs about release -description: Automatically comments on PRs when they are included in a release -inputs: - published-packages: - description: 'JSON string of published packages from changesets/action' - required: true -runs: - using: composite - steps: - - name: Comment on PRs - shell: bash - run: node ${{ github.action_path }}/comment-on-release.ts - env: - PUBLISHED_PACKAGES: ${{ inputs.published-packages }} - REPOSITORY: ${{ github.repository }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/comment-on-release/comment-on-release.ts b/.github/comment-on-release/comment-on-release.ts deleted file mode 100644 index 6b0901de68..0000000000 --- a/.github/comment-on-release/comment-on-release.ts +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env node - -import { readFileSync } from 'node:fs' -import { resolve } from 'node:path' -import { execSync } from 'node:child_process' - -interface PublishedPackage { - name: string - version: string -} - -interface PRInfo { - number: number - packages: Array<{ name: string; pkgPath: string; version: string }> -} - -/** - * Parse CHANGELOG.md to extract PR numbers from the latest version entry - */ -function extractPRsFromChangelog( - changelogPath: string, - version: string, -): Array { - try { - const content = readFileSync(changelogPath, 'utf-8') - const lines = content.split('\n') - - let inTargetVersion = false - let foundVersion = false - const prNumbers = new Set() - - for (let i = 0; i < lines.length; i++) { - const line = lines[i] - - // Check for version header (e.g., "## 0.21.0") - if (line.startsWith('## ')) { - const versionMatch = line.match(/^## (\d+\.\d+\.\d+)/) - if (versionMatch) { - if (versionMatch[1] === version) { - inTargetVersion = true - foundVersion = true - } else if (inTargetVersion) { - // We've moved to the next version, stop processing - break - } - } - } - - // Extract PR numbers from links like [#302](https://github.com/TanStack/config/pull/302) - if (inTargetVersion) { - const prMatches = line.matchAll( - /\[#(\d+)\]\(https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+\)/g, - ) - for (const match of prMatches) { - prNumbers.add(parseInt(match[1], 10)) - } - } - } - - if (!foundVersion) { - console.warn( - `Warning: Could not find version ${version} in ${changelogPath}`, - ) - } - - return Array.from(prNumbers) - } catch (error) { - console.error(`Error reading changelog at ${changelogPath}:`, error) - return [] - } -} - -/** - * Group PRs by their numbers and collect all packages they contributed to - */ -function groupPRsByNumber( - publishedPackages: Array, -): Map { - const prMap = new Map() - - for (const pkg of publishedPackages) { - const pkgPath = `packages/${pkg.name.replace('@tanstack/', '')}` - const changelogPath = resolve(process.cwd(), pkgPath, 'CHANGELOG.md') - - const prNumbers = extractPRsFromChangelog(changelogPath, pkg.version) - - for (const prNumber of prNumbers) { - if (!prMap.has(prNumber)) { - prMap.set(prNumber, { number: prNumber, packages: [] }) - } - prMap.get(prNumber)!.packages.push({ - name: pkg.name, - pkgPath: pkgPath, - version: pkg.version, - }) - } - } - - return prMap -} - -/** - * Post a comment on a GitHub PR using gh CLI - */ -async function commentOnPR(pr: PRInfo, repository: string): Promise { - const { number, packages } = pr - - // Build the comment body - let comment = `🎉 This PR has been released!\n\n` - - for (const pkg of packages) { - // Link to the package's changelog and version anchor - const changelogUrl = `https://github.com/${repository}/blob/main/${pkg.pkgPath}/CHANGELOG.md#${pkg.version.replaceAll('.', '')}` - comment += `- [${pkg.name}@${pkg.version}](${changelogUrl})\n` - } - - comment += `\nThank you for your contribution!` - - try { - // Use gh CLI to post the comment - execSync(`gh pr comment ${number} --body '${comment.replace(/'/g, '"')}'`, { - stdio: 'inherit', - }) - console.log(`✓ Commented on PR #${number}`) - } catch (error) { - console.error(`✗ Failed to comment on PR #${number}:`, error) - } -} - -/** - * Main function - */ -async function main() { - // Read published packages from environment variable (set by GitHub Actions) - const publishedPackagesJson = process.env.PUBLISHED_PACKAGES - const repository = process.env.REPOSITORY - - if (!publishedPackagesJson) { - console.log('No packages were published. Skipping PR comments.') - return - } - - if (!repository) { - console.log('Repository is missing. Skipping PR comments.') - return - } - - let publishedPackages: Array - try { - publishedPackages = JSON.parse(publishedPackagesJson) - } catch (error) { - console.error('Failed to parse PUBLISHED_PACKAGES:', error) - process.exit(1) - } - - if (publishedPackages.length === 0) { - console.log('No packages were published. Skipping PR comments.') - return - } - - console.log(`Processing ${publishedPackages.length} published package(s)...`) - - // Group PRs by number - const prMap = groupPRsByNumber(publishedPackages) - - if (prMap.size === 0) { - console.log('No PRs found in CHANGELOGs. Nothing to comment on.') - return - } - - console.log(`Found ${prMap.size} PR(s) to comment on...`) - - // Comment on each PR - for (const pr of prMap.values()) { - await commentOnPR(pr, repository) - } - - console.log('✓ Done!') -} - -main().catch((error) => { - console.error('Fatal error:', error) - process.exit(1) -}) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 964f209eab..8086316f89 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,6 +76,6 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Comment on PRs about release if: steps.changesets.outputs.published == 'true' - uses: ./.github/comment-on-release + uses: tanstack/config/.github/comment-on-release@main with: published-packages: ${{ steps.changesets.outputs.publishedPackages }} diff --git a/knip.json b/knip.json index 963a8a723d..996a0a25a2 100644 --- a/knip.json +++ b/knip.json @@ -10,13 +10,15 @@ "@types/react", "@types/react-dom", "react", - "react-dom", - "markdown-link-extractor" + "react-dom" ], "ignoreWorkspaces": ["examples/**", "integrations/**"], "workspaces": { "packages/angular-query-experimental": { - "entry": ["src/index.ts", "src/inject-queries-experimental/index.ts"] + "entry": [ + "src/devtools/production/index.ts", + "src/devtools-panel/production/index.ts" + ] }, "packages/query-codemods": { "entry": ["src/v4/**/*.cjs", "src/v5/**/*.cjs"], @@ -25,12 +27,6 @@ "packages/vue-query": { "ignore": ["**/__mocks__/**"], "ignoreDependencies": ["vue2", "vue2.7"] - }, - "packages/angular-query-experimental": { - "entry": [ - "src/devtools/production/index.ts", - "src/devtools-panel/production/index.ts" - ] } } } diff --git a/packages/solid-query-devtools/package.json b/packages/solid-query-devtools/package.json index 8e308f16cd..e507449f65 100644 --- a/packages/solid-query-devtools/package.json +++ b/packages/solid-query-devtools/package.json @@ -39,7 +39,7 @@ "types": "./build/index.d.ts", "browser": {}, "exports": { - "@tanstack/custom-condition": "./src/index.ts", + "@tanstack/custom-condition": "./src/index.tsx", "solid": { "development": "./build/dev.jsx", "import": "./build/index.jsx"