Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ jobs:
contents: write
id-token: write
pull-requests: write
outputs:
published: ${{ steps.changesets.outputs.published }}
packages: ${{ steps.changesets.outputs.publishedPackages }}
prerelease: ${{ steps.dist-tag.outputs.prerelease }}
latest: ${{ steps.dist-tag.outputs.latest }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down Expand Up @@ -77,11 +82,35 @@ jobs:
commit: 'ci: changeset release'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
verify-release:
name: Verify Published Release
needs: release
if: needs.release.outputs.published == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24
- name: Verify Package Availability and Integrity
run: node scripts/verify-release-artifacts.mjs
env:
PUBLISHED_PACKAGES: ${{ needs.release.outputs.packages }}
- name: Create GitHub Release
if: steps.changesets.outputs.published == 'true'
run: node scripts/create-github-release.mjs ${PRERELEASE_FLAG} ${LATEST_FLAG}
run: |
gh auth setup-git
Comment thread
tannerlinsley marked this conversation as resolved.
Resolved
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
node scripts/create-github-release.mjs ${PRERELEASE_FLAG} ${LATEST_FLAG}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PRERELEASE_FLAG: ${{ steps.dist-tag.outputs.prerelease == 'true' && '--prerelease' }}
LATEST_FLAG: ${{ steps.dist-tag.outputs.latest == 'true' && '--latest' }}
PRERELEASE_FLAG: ${{ needs.release.outputs.prerelease == 'true' && '--prerelease' }}
LATEST_FLAG: ${{ needs.release.outputs.latest == 'true' && '--latest' }}
Comment thread
tannerlinsley marked this conversation as resolved.
Resolved
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"build": "nx run-many --target=build --parallel 5",
"dev": "nx run-many --target=dev --parallel 20",
"test": "pnpm test:unit && pnpm test:e2e",
"test:unit": "nx run-many --target=test",
"test:unit": "nx run-many --target=test && node --test scripts/release-*.test.mjs",
"test:e2e": "nx run @tanstack/cli:test:e2e",
"test:e2e:matrix": "nx run @tanstack/cli:test:e2e:matrix",
"check-outdated": "node scripts/check-outdated-packages.js",
Expand Down
87 changes: 86 additions & 1 deletion scripts/create-github-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { execFileSync, execSync } from 'node:child_process'
import fs, { globSync } from 'node:fs'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { findReleaseForCommit, latestReleaseFlag } from './release-identity.mjs'

const rootDir = path.join(import.meta.dirname, '..')
const ghToken = process.env.GH_TOKEN || process.env.GITHUB_TOKEN
Expand Down Expand Up @@ -64,6 +65,57 @@ const releaseLogs = execSync(
const currentRelease = releaseLogs[0] || 'HEAD'
const previousRelease = releaseLogs[1]

const releaseHead = execFileSync('git', ['rev-parse', 'HEAD'], {
encoding: 'utf8',
}).trim()
const releases = JSON.parse(
execFileSync(
'gh',
[
'api',
'--paginate',
'--slurp',
'repos/TanStack/cli/releases?per_page=100',
],
{ encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 },
),
).flat()
const resolveTagCommit = (tag) => {
try {
return execFileSync('git', ['rev-parse', '--verify', `${tag}^{commit}`], {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'ignore'],
}).trim()
} catch {
return execFileSync(
'gh',
[
'api',
`repos/TanStack/cli/commits/${encodeURIComponent(tag)}`,
'--jq',
'.sha',
],
{ encoding: 'utf8' },
).trim()
}
}
const existingRelease = await findReleaseForCommit(
releases,
releaseHead,
resolveTagCommit,
)
if (existingRelease) {
if (existingRelease.draft) {
throw new Error(
`A draft release already exists for ${releaseHead}: ${existingRelease.html_url}`,
)
}
console.info(
`Release already exists for ${releaseHead}: ${existingRelease.html_url}`,
)
process.exit(0)
}

const bumpedPackages = []
for (const workspaceDir of workspaceDirs) {
const absWorkspaceDir = path.join(rootDir, workspaceDir)
Expand Down Expand Up @@ -224,6 +276,40 @@ ${changelogMd}
${bumpedPackages.map((pkg) => `- ${pkg.name}@${pkg.version}`).join('\n')}
`

let latestHead
if (
isLatest &&
releases.some((release) => !release.draft && !release.prerelease)
) {
const latestRelease = JSON.parse(
execFileSync('gh', ['api', 'repos/TanStack/cli/releases/latest'], {
encoding: 'utf8',
}),
)
latestHead = resolveTagCommit(latestRelease.tag_name)
}
const latestOption = latestReleaseFlag({
requested: process.argv.includes('--latest=false')
? 'false'
: isLatest
? 'true'
: undefined,
releaseHead,
latestHead,
isAncestor: (older, newer) => {
try {
execFileSync('git', ['merge-base', '--is-ancestor', older, newer], {
stdio: 'ignore',
})
return true
} catch (error) {
if (error.status === 1) return false
throw error
}
},
})
const latestFlag = latestOption ? ` ${latestOption}` : ''

let tagExists = false
try {
execSync(`git rev-parse ${tagName}`, { stdio: 'ignore' })
Expand All @@ -238,7 +324,6 @@ if (!tagExists) {
}

const prereleaseFlag = isPrerelease ? '--prerelease' : ''
const latestFlag = isLatest ? ' --latest' : ''
const tmpFile = path.join(tmpdir(), `release-notes-${tagName}.md`)
fs.writeFileSync(tmpFile, body)

Expand Down
Loading
Loading