ci: better release workflow (#10325)

* ci: better release workflow

* ci: simplify + use changelog

* ci(release): better parsing and exclusions

* ci(release): remove tree log

* ci(release): improve logs

* ci(release): properly check inputs

* ci(release): better promise handling

Co-authored-by: Aura <kyradiscord@gmail.com>

* ci: refactor release to use bun

* ci(release): whitespace

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* ci(release): add dev release handling

* ci(release): fixes from testing

* ci(release): make the promise run

* ci(release): when specifying package, skip exclusions

* ci(dev): create-discord-bot dev release

* ci(release): improve changelog detection

* ci: fix typo and allow releasing branches

* ci(release): set make_latest for gh releases

* ci(release): add ssh_key so pushed tags run workflow

---------

Co-authored-by: Aura <kyradiscord@gmail.com>
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
ckohen
2025-07-25 02:56:02 -07:00
committed by GitHub
parent f2fec9177f
commit 6cdfa3864b
12 changed files with 646 additions and 22 deletions

View File

@@ -3,6 +3,10 @@ on:
schedule:
- cron: '0 */12 * * *'
workflow_dispatch:
inputs:
dry_run:
type: boolean
default: false
jobs:
npm-publish:
name: npm publish
@@ -55,32 +59,18 @@ jobs:
node-version: 22
registry-url: https://registry.npmjs.org/
- name: Check the current development version
id: release-check
run: |
if [[ $(npm view ${{ matrix.package }}@dev version | grep -e "$(git rev-parse --short HEAD)") ]]; \
then echo "RELEASE=0" >> "$GITHUB_OUTPUT"; \
else echo "RELEASE=1" >> "$GITHUB_OUTPUT"; \
fi
- name: Install dependencies
if: steps.release-check.outputs.release == '1'
uses: ./packages/actions/src/pnpmCache
- name: Build dependencies
if: steps.release-check.outputs.release == '1'
run: pnpm run build
- name: Publish package
if: steps.release-check.outputs.release == '1'
run: |
pnpm --filter=${{ matrix.package }} run release --preid "dev.$(date +%s)-$(git rev-parse --short HEAD)" --skip-changelog
pnpm --filter=${{ matrix.package }} publish --provenance --no-git-checks --tag dev || true
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
- name: Deprecate prior development releases
if: steps.release-check.outputs.release == '1'
run: pnpm exec npm-deprecate --name "*dev*" --message "This version is deprecated. Please use a newer version." --package ${{ matrix.package }}
- name: Publish packages
uses: ./packages/actions/src/releasePackages
with:
exclude: '@discordjs/docgen'
dry: ${{ inputs.dry_run }}
dev: true
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

75
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
name: Release
on:
workflow_dispatch:
inputs:
ref:
description: 'The branch, tag or SHA to checkout'
required: true
default: 'main'
package:
description: 'The published name of a single package to release'
type: choice
required: false
options:
- all
- discord.js
- '@discordjs/brokers'
- '@discordjs/builders'
- '@discordjs/collection'
- '@discordjs/core'
- 'create-discord-bot'
- '@discordjs/docgen'
- '@discordjs/formatters'
- '@discordjs/next'
- '@discordjs/proxy'
- '@discordjs/rest'
- '@discordjs/util'
- '@discordjs/voice'
- '@discordjs/ws'
exclude:
description: 'Comma separated list of packages to exclude from release (if not depended upon)'
required: false
type: string
default: '@discordjs/docgen,@discordjs/next'
dry_run:
type: boolean
default: false
jobs:
npm-publish:
name: npm publish
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
if: github.repository_owner == 'discordjs'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || '' }}
ssh_key: ${{ secrets.DEPLOY_KEY_CI_RELEASE_TAGS }}
- name: Install Node.js v20
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org/
- name: Install dependencies
uses: ./packages/actions/src/pnpmCache
- name: Build dependencies
run: pnpm run build
- name: Release packages
uses: ./packages/actions/src/releasePackages
with:
package: ${{ inputs.package }}
exclude: ${{ inputs.exclude }}
dry: ${{ inputs.dry_run }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -22,7 +22,8 @@
"prepare": "is-ci || husky",
"update": "pnpm --recursive update --interactive",
"update:latest": "pnpm --recursive update --interactive --latest",
"create-package": "turbo gen create-package --args"
"create-package": "turbo gen create-package --args",
"release": "bun ./packages/actions/src/releasePackages/index.ts"
},
"type": "module",
"contributors": [

View File

@@ -42,12 +42,14 @@
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.1",
"@actions/glob": "^0.5.0",
"@aws-sdk/client-s3": "^3.844.0",
"@discordjs/scripts": "workspace:^",
"@vercel/blob": "^1.1.1",
"@vercel/postgres": "^0.10.0",
"cloudflare": "^4.4.1",
"commander": "^14.0.0",
"meilisearch": "^0.38.0",
"p-limit": "^6.2.0",
"p-queue": "^8.1.0",
@@ -55,6 +57,8 @@
"undici": "7.11.0"
},
"devDependencies": {
"@npm/types": "^2.1.0",
"@types/bun": "^1.2.19",
"@types/node": "^22.16.3",
"@vitest/coverage-v8": "^3.2.4",
"cross-env": "^7.0.3",

View File

@@ -0,0 +1,24 @@
name: 'Release Packages'
description: 'Tags and releases any unreleased packages'
inputs:
dev:
description: 'Releases development versions of packages (skips tagging and github releases)'
default: false
dry:
descrption: 'Perform a dry run that skips publishing and outputs logs indicating what would have happened'
default: false
package:
description: 'The published name of a single package to release'
exclude:
description: 'Comma separated list of packages to exclude from release (if not depended upon)'
runs:
using: composite
steps:
- uses: oven-sh/setup-bun@v1
- run: bun packages/actions/src/releasePackages/index.ts
shell: bash
env:
INPUT_DEV: ${{ inputs.dev }}
INPUT_DRY: ${{ inputs.dry }}
INPUT_PACKAGE: ${{ inputs.package }}
INPUT_EXCLUDE: ${{ inputs.exclude }}

View File

@@ -0,0 +1,230 @@
import { info, warning } from '@actions/core';
import type { PackageJSON, PackumentVersion } from '@npm/types';
import { $, file, write } from 'bun';
const nonNodePackages = new Set(['@discordjs/proxy-container']);
interface pnpmTreeDependency {
from: string;
path: string;
version: string;
}
interface pnpmTree {
dependencies?: Record<string, pnpmTreeDependency>;
name?: string;
path: string;
private?: boolean;
unsavedDependencies?: Record<string, pnpmTreeDependency>;
version?: string;
}
export interface ReleaseEntry {
changelog?: string;
dependsOn?: string[];
name: string;
version: string;
}
async function fetchDevVersion(pkg: string) {
try {
const res = await fetch(`https://registry.npmjs.org/${pkg}/dev`);
if (!res.ok) return null;
const packument = (await res.json()) as PackumentVersion;
return packument.version;
} catch {
return null;
}
}
async function getReleaseEntries(dev: boolean, dry: boolean) {
const releaseEntries: ReleaseEntry[] = [];
const packageList: pnpmTree[] =
await $`pnpm list --recursive --only-projects --filter {packages/\*} --prod --json`.json();
const commitHash = (await $`git rev-parse --short HEAD`.text()).trim();
for (const pkg of packageList) {
// Don't release private packages ever (npm will error anyways)
if (pkg.private) continue;
// Just in case
if (!pkg.version || !pkg.name) continue;
if (nonNodePackages.has(pkg.name)) continue;
const release: ReleaseEntry = {
name: pkg.name,
version: pkg.version,
};
if (dev) {
const devVersion = await fetchDevVersion(pkg.name);
if (devVersion?.endsWith(commitHash)) {
// Write the currently released dev version so when pnpm publish runs on dependents they depend on the dev versions
if (dry) {
info(`[DRY] ${pkg.name}@${devVersion} already released. Editing package.json version.`);
} else {
const pkgJson = (await file(`${pkg.path}/package.json`).json()) as PackageJSON;
pkgJson.version = devVersion;
await write(`${pkg.path}/package.json`, JSON.stringify(pkgJson, null, '\t'));
}
release.version = devVersion;
} else if (dry) {
info(`[DRY] Bumping ${pkg.name} via git-cliff.`);
release.version = `${pkg.version}.DRY-dev.${Math.round(Date.now() / 1_000)}-${commitHash}`;
} else {
await $`pnpm --filter=${pkg.name} run release --preid "dev.${Math.round(Date.now() / 1_000)}-${commitHash} --skip-changelog"`;
// Read again instead of parsing the output to be sure we're matching when checking against npm
const pkgJson = (await file(`${pkg.path}/package.json`).json()) as PackageJSON;
release.version = pkgJson.version;
}
}
// Only need changelog for releases published to github
else {
try {
// Find and parse changelog to post in github release
const changelogFile = await file(`${pkg.path}/CHANGELOG.md`).text();
let changelogLines: string[] = [];
let foundChangelog = false;
for (const line of changelogFile.split('\n')) {
if (line.startsWith('# [')) {
if (foundChangelog) {
if (changelogLines.at(-1) === '') {
changelogLines = changelogLines.slice(2, -1);
}
break;
}
// Check changelog release version and assume no changelog if version does not match
if (!line.startsWith(`# [${release.name === 'discord.js' ? `` : `${release.name}@`}${release.version}]`)) {
break;
}
foundChangelog = true;
}
if (foundChangelog) {
changelogLines.push(line);
}
}
release.changelog = changelogLines.join('\n');
} catch (error) {
// Probably just no changelog file but log just in case
warning(`Error parsing changelog for ${pkg.name}, will use auto generated: ${error}`);
}
}
if (pkg.dependencies) {
release.dependsOn = Object.keys(pkg.dependencies);
}
releaseEntries.push(release);
}
return releaseEntries;
}
export async function generateReleaseTree(dev: boolean, dry: boolean, packageName?: string, exclude?: string[]) {
let releaseEntries = await getReleaseEntries(dev, dry);
// Try to early return if the package doesn't have deps
if (packageName && packageName !== 'all') {
const releaseEntry = releaseEntries.find((entry) => entry.name === packageName);
if (!releaseEntry) {
throw new Error(`Package ${packageName} not releaseable`);
}
if (!releaseEntry.dependsOn) {
return [[releaseEntry]];
}
}
// Generate the whole tree first, then prune if specified
const releaseTree: ReleaseEntry[][] = [];
const didRelease = new Set<string>();
while (releaseEntries.length) {
const nextBranch: ReleaseEntry[] = [];
const unreleased: ReleaseEntry[] = [];
for (const entry of releaseEntries) {
if (!entry.dependsOn) {
nextBranch.push(entry);
continue;
}
const allDepsReleased = entry.dependsOn.every((dep) => didRelease.has(dep));
if (allDepsReleased) {
nextBranch.push(entry);
} else {
unreleased.push(entry);
}
}
// Update didRelease in a second loop to avoid loop order issues
for (const release of nextBranch) {
didRelease.add(release.name);
}
if (releaseEntries.length === unreleased.length) {
throw new Error(
`One or more packages have dependents that can't be released: ${unreleased.map((entry) => entry.name).join(',')}`,
);
}
releaseTree.push(nextBranch);
releaseEntries = unreleased;
}
// Prune exclusions
if ((!packageName || packageName === 'all') && Array.isArray(exclude) && exclude.length) {
const neededPackages = new Set<string>();
const excludedReleaseTree: ReleaseEntry[][] = [];
for (const releaseBranch of releaseTree.reverse()) {
const newThisBranch: ReleaseEntry[] = [];
for (const entry of releaseBranch) {
if (exclude.includes(entry.name) && !neededPackages.has(entry.name)) {
continue;
}
newThisBranch.push(entry);
for (const dep of entry.dependsOn ?? []) {
neededPackages.add(dep);
}
}
if (newThisBranch.length) excludedReleaseTree.unshift(newThisBranch);
}
return excludedReleaseTree;
}
if (!packageName || packageName === 'all') {
return releaseTree;
}
// Prune the tree for the specified package
const neededPackages = new Set<string>([packageName]);
const packageReleaseTree: ReleaseEntry[][] = [];
for (const releaseBranch of releaseTree.reverse()) {
const newThisBranch: ReleaseEntry[] = [];
for (const entry of releaseBranch) {
if (neededPackages.has(entry.name)) {
newThisBranch.push(entry);
for (const dep of entry.dependsOn ?? []) {
neededPackages.add(dep);
}
}
}
if (newThisBranch.length) packageReleaseTree.unshift(newThisBranch);
}
return packageReleaseTree;
}

View File

@@ -0,0 +1,47 @@
import { getInput, startGroup, endGroup, getBooleanInput, info } from '@actions/core';
import { program } from 'commander';
import { generateReleaseTree } from './generateReleaseTree.js';
import { releasePackage } from './releasePackage.js';
const excludeInput = getInput('exclude');
let dryInput = false;
let devInput = false;
try {
devInput = getBooleanInput('dev');
} catch {
// We're not running in actions
}
try {
dryInput = getBooleanInput('dry');
} catch {
// We're not running in actions or the input isn't set (cron)
}
program
.name('release packages')
.description('releases monorepo packages with proper sequencing')
.argument('[package]', "release a specific package (and it's dependencies)", getInput('package'))
.option(
'-e, --exclude <packages...>',
'exclude specific packages from releasing (will still release if necessary for another package)',
excludeInput ? excludeInput.split(',') : [],
)
.option('--dry', 'skips actual publishing and outputs logs instead', dryInput)
.option('--dev', 'publishes development versions and skips tagging / github releases', devInput)
.parse();
const { exclude, dry, dev } = program.opts<{ dev: boolean; dry: boolean; exclude: string[] }>();
const packageName = program.args[0]!;
const tree = await generateReleaseTree(dev, dry, packageName, exclude);
for (const branch of tree) {
startGroup(`Releasing ${branch.map((entry) => `${entry.name}@${entry.version}`).join(', ')}`);
await Promise.all(branch.map(async (release) => releasePackage(release, dev, dry)));
endGroup();
}
info(
`Successfully released ${tree.map((branch) => branch.map((entry) => `${entry.name}@${entry.version}`).join(', ')).join(', ')}`,
);

View File

@@ -0,0 +1,87 @@
import process from 'node:process';
import { setInterval, clearInterval } from 'node:timers';
import { info, warning } from '@actions/core';
import { getOctokit, context } from '@actions/github';
import { $ } from 'bun';
import type { ReleaseEntry } from './generateReleaseTree.js';
let octokit: ReturnType<typeof getOctokit> | undefined;
if (process.env.GITHUB_TOKEN) {
octokit = getOctokit(process.env.GITHUB_TOKEN);
}
async function checkRegistry(release: ReleaseEntry) {
const res = await fetch(`https://registry.npmjs.org/${release.name}/${release.version}`);
return res.ok;
}
async function gitTagAndRelease(release: ReleaseEntry, dry: boolean) {
const tagName = `${release.name === 'discord.js' ? `` : `${release.name}@`}${release.version}`;
// Don't throw, if this exits non-zero it's probably because the tag already exists
await $`git tag ${tagName}`.nothrow();
if (dry) {
info(`[DRY] Tag "${tagName}" created, skipping push and release creation.`);
return;
}
await $`git push origin ${tagName}`;
try {
await octokit?.rest.repos.createRelease({
...context.repo,
tag_name: tagName,
name: tagName,
body: release.changelog ?? '',
generate_release_notes: release.changelog === undefined,
make_latest: release.name === 'discord.js' ? 'true' : 'false',
});
} catch (error) {
warning(`Failed to create github release: ${error}`);
}
}
export async function releasePackage(release: ReleaseEntry, dev: boolean, dry: boolean) {
// Sanity check against the registry first
if (await checkRegistry(release)) {
info(`${release.name}@${release.version} already published, skipping.`);
return;
}
if (dry) {
info(`[DRY] Releasing ${release.name}@${release.version}`);
} else {
await $`pnpm --filter=${release.name} publish --provenance --no-git-checks ${dev ? '--tag=dev' : ''}`;
}
if (!dev) await gitTagAndRelease(release, dry);
if (dry) return;
const before = performance.now();
// Poll registry to ensure next publishes won't fail
await new Promise<void>((resolve, reject) => {
const interval = setInterval(async () => {
if (await checkRegistry(release)) {
clearInterval(interval);
resolve();
return;
}
if (performance.now() > before + 5 * 60 * 1_000) {
clearInterval(interval);
reject(new Error(`Release for ${release.name} failed.`));
}
}, 15_000);
});
if (dev) {
// Send and forget, deprecations are less important than releasing other dev versions and can be done manually
void $`pnpm exec npm-deprecate --name "*dev*" --message "This version is deprecated. Please use a newer version." --package ${release.name}`
.nothrow()
// eslint-disable-next-line promise/prefer-await-to-then
.then(() => {});
}
}

View File

@@ -1,6 +1,10 @@
{
"$schema": "https://json.schemastore.org/tsconfig.json",
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node"],
"skipLibCheck": true
},
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.cjs", "src/**/*.mjs", "bin"],
"exclude": ["node_modules"]
}

View File

@@ -4,6 +4,7 @@ export default createTsupConfig({
entry: [
'src/index.ts',
'src/formatTag/index.ts',
'src/releasePackages/index.ts',
'src/uploadDocumentation/index.ts',
'src/uploadSearchIndices/index.ts',
'src/uploadSplitDocumentation/index.ts',
@@ -11,4 +12,5 @@ export default createTsupConfig({
dts: false,
format: 'esm',
minify: 'terser',
target: 'esnext',
});

View File

@@ -1,6 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/ui",
"private": true,
"version": "0.1.0",
"description": "",
"scripts": {

159
pnpm-lock.yaml generated
View File

@@ -444,6 +444,9 @@ importers:
'@actions/core':
specifier: ^1.11.1
version: 1.11.1
'@actions/github':
specifier: ^6.0.1
version: 6.0.1
'@actions/glob':
specifier: ^0.5.0
version: 0.5.0
@@ -462,6 +465,9 @@ importers:
cloudflare:
specifier: ^4.4.1
version: 4.4.1(encoding@0.1.13)
commander:
specifier: ^14.0.0
version: 14.0.0
meilisearch:
specifier: ^0.38.0
version: 0.38.0(encoding@0.1.13)
@@ -478,6 +484,12 @@ importers:
specifier: 7.11.0
version: 7.11.0
devDependencies:
'@npm/types':
specifier: ^2.1.0
version: 2.1.0
'@types/bun':
specifier: ^1.2.19
version: 1.2.19(@types/react@19.1.8)
'@types/node':
specifier: ^22.16.3
version: 22.16.3
@@ -1940,6 +1952,9 @@ packages:
'@actions/exec@1.1.1':
resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==}
'@actions/github@6.0.1':
resolution: {integrity: sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==}
'@actions/glob@0.5.0':
resolution: {integrity: sha512-tST2rjPvJLRZLuT9NMUtyBjvj9Yo0MiJS3ow004slMvm8GFM+Zv9HvMJ7HWzfUyJnGrJvDsYkWBaaG3YKXRtCw==}
@@ -3475,6 +3490,10 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@npm/types@2.1.0':
resolution: {integrity: sha512-humQVe2BrWR7Yum5hGDYBnIPnnZJvKSOH/I4QN1ZL2bdb4c4zQHaHupEJ3cOkSJ07G3YfN793ptbNh196BWLgA==}
engines: {node: '>=18.6.0'}
'@npmcli/agent@3.0.0':
resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==}
engines: {node: ^18.17.0 || >=20.5.0}
@@ -3511,10 +3530,18 @@ packages:
resolution: {integrity: sha512-/1uFzjVcfzqrgCeGW7+SZ4hv0qLWmKXVzFahZGJ6QuJBj6Myt9s17+JL86i76NV9YSnJRcGXJYQbAU0rn1YTCQ==}
engines: {node: ^18.17.0 || >=20.5.0}
'@octokit/auth-token@4.0.0':
resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==}
engines: {node: '>= 18'}
'@octokit/auth-token@5.1.1':
resolution: {integrity: sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==}
engines: {node: '>= 18'}
'@octokit/core@5.2.0':
resolution: {integrity: sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==}
engines: {node: '>= 18'}
'@octokit/core@6.1.2':
resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==}
engines: {node: '>= 18'}
@@ -3523,27 +3550,61 @@ packages:
resolution: {integrity: sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==}
engines: {node: '>= 18'}
'@octokit/endpoint@9.0.6':
resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==}
engines: {node: '>= 18'}
'@octokit/graphql@7.1.0':
resolution: {integrity: sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==}
engines: {node: '>= 18'}
'@octokit/graphql@8.1.2':
resolution: {integrity: sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==}
engines: {node: '>= 18'}
'@octokit/openapi-types@20.0.0':
resolution: {integrity: sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==}
'@octokit/openapi-types@22.2.0':
resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==}
'@octokit/plugin-paginate-rest@9.2.2':
resolution: {integrity: sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '5'
'@octokit/plugin-rest-endpoint-methods@10.4.1':
resolution: {integrity: sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '5'
'@octokit/plugin-retry@7.1.2':
resolution: {integrity: sha512-XOWnPpH2kJ5VTwozsxGurw+svB2e61aWlmk5EVIYZPwFK5F9h4cyPyj9CIKRyMXMHSwpIsI3mPOdpMmrRhe7UQ==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=6'
'@octokit/request-error@5.1.1':
resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==}
engines: {node: '>= 18'}
'@octokit/request-error@6.1.6':
resolution: {integrity: sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==}
engines: {node: '>= 18'}
'@octokit/request@8.4.1':
resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==}
engines: {node: '>= 18'}
'@octokit/request@9.1.4':
resolution: {integrity: sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==}
engines: {node: '>= 18'}
'@octokit/types@12.6.0':
resolution: {integrity: sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==}
'@octokit/types@13.6.2':
resolution: {integrity: sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==}
@@ -6027,6 +6088,9 @@ packages:
'@types/body-parser@1.19.5':
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
'@types/bun@1.2.19':
resolution: {integrity: sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg==}
'@types/chai@5.2.2':
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
@@ -7406,6 +7470,9 @@ packages:
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
engines: {node: '>=10.0.0'}
before-after-hook@2.2.3:
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
before-after-hook@3.0.2:
resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==}
@@ -7485,6 +7552,11 @@ packages:
resolution: {integrity: sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==}
engines: {node: '>=18.20'}
bun-types@1.2.19:
resolution: {integrity: sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ==}
peerDependencies:
'@types/react': ^19
bundle-require@5.1.0:
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -8354,6 +8426,9 @@ packages:
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
engines: {node: '>= 0.6'}
deprecation@2.3.1:
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -13379,6 +13454,9 @@ packages:
unist-util-visit@5.0.0:
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
universal-user-agent@6.0.1:
resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==}
universal-user-agent@7.0.2:
resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==}
@@ -13957,6 +14035,16 @@ snapshots:
dependencies:
'@actions/io': 1.1.3
'@actions/github@6.0.1':
dependencies:
'@actions/http-client': 2.2.3
'@octokit/core': 5.2.0
'@octokit/plugin-paginate-rest': 9.2.2(@octokit/core@5.2.0)
'@octokit/plugin-rest-endpoint-methods': 10.4.1(@octokit/core@5.2.0)
'@octokit/request': 8.4.1
'@octokit/request-error': 5.1.1
undici: 5.29.0
'@actions/glob@0.5.0':
dependencies:
'@actions/core': 1.11.1
@@ -15885,6 +15973,8 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.18.0
'@npm/types@2.1.0': {}
'@npmcli/agent@3.0.0':
dependencies:
agent-base: 7.1.3
@@ -15953,8 +16043,20 @@ snapshots:
'@npmcli/redact@3.0.0': {}
'@octokit/auth-token@4.0.0': {}
'@octokit/auth-token@5.1.1': {}
'@octokit/core@5.2.0':
dependencies:
'@octokit/auth-token': 4.0.0
'@octokit/graphql': 7.1.0
'@octokit/request': 8.4.1
'@octokit/request-error': 5.1.1
'@octokit/types': 13.6.2
before-after-hook: 2.2.3
universal-user-agent: 6.0.1
'@octokit/core@6.1.2':
dependencies:
'@octokit/auth-token': 5.1.1
@@ -15970,14 +16072,37 @@ snapshots:
'@octokit/types': 13.6.2
universal-user-agent: 7.0.2
'@octokit/endpoint@9.0.6':
dependencies:
'@octokit/types': 13.6.2
universal-user-agent: 6.0.1
'@octokit/graphql@7.1.0':
dependencies:
'@octokit/request': 8.4.1
'@octokit/types': 13.6.2
universal-user-agent: 6.0.1
'@octokit/graphql@8.1.2':
dependencies:
'@octokit/request': 9.1.4
'@octokit/types': 13.6.2
universal-user-agent: 7.0.2
'@octokit/openapi-types@20.0.0': {}
'@octokit/openapi-types@22.2.0': {}
'@octokit/plugin-paginate-rest@9.2.2(@octokit/core@5.2.0)':
dependencies:
'@octokit/core': 5.2.0
'@octokit/types': 12.6.0
'@octokit/plugin-rest-endpoint-methods@10.4.1(@octokit/core@5.2.0)':
dependencies:
'@octokit/core': 5.2.0
'@octokit/types': 12.6.0
'@octokit/plugin-retry@7.1.2(@octokit/core@6.1.2)':
dependencies:
'@octokit/core': 6.1.2
@@ -15985,10 +16110,23 @@ snapshots:
'@octokit/types': 13.6.2
bottleneck: 2.19.5
'@octokit/request-error@5.1.1':
dependencies:
'@octokit/types': 13.6.2
deprecation: 2.3.1
once: 1.4.0
'@octokit/request-error@6.1.6':
dependencies:
'@octokit/types': 13.6.2
'@octokit/request@8.4.1':
dependencies:
'@octokit/endpoint': 9.0.6
'@octokit/request-error': 5.1.1
'@octokit/types': 13.6.2
universal-user-agent: 6.0.1
'@octokit/request@9.1.4':
dependencies:
'@octokit/endpoint': 10.1.2
@@ -15997,6 +16135,10 @@ snapshots:
fast-content-type-parse: 2.0.0
universal-user-agent: 7.0.2
'@octokit/types@12.6.0':
dependencies:
'@octokit/openapi-types': 20.0.0
'@octokit/types@13.6.2':
dependencies:
'@octokit/openapi-types': 22.2.0
@@ -19299,6 +19441,12 @@ snapshots:
'@types/connect': 3.4.38
'@types/node': 22.16.3
'@types/bun@1.2.19(@types/react@19.1.8)':
dependencies:
bun-types: 1.2.19(@types/react@19.1.8)
transitivePeerDependencies:
- '@types/react'
'@types/chai@5.2.2':
dependencies:
'@types/deep-eql': 4.0.2
@@ -21363,6 +21511,8 @@ snapshots:
basic-ftp@5.0.5: {}
before-after-hook@2.2.3: {}
before-after-hook@3.0.2: {}
bent@7.3.12:
@@ -21443,6 +21593,11 @@ snapshots:
builtin-modules@4.0.0: {}
bun-types@1.2.19(@types/react@19.1.8):
dependencies:
'@types/node': 22.16.3
'@types/react': 19.1.8
bundle-require@5.1.0(esbuild@0.25.5):
dependencies:
esbuild: 0.25.5
@@ -22359,6 +22514,8 @@ snapshots:
depd@1.1.2: {}
deprecation@2.3.1: {}
dequal@2.0.3: {}
destr@2.0.3: {}
@@ -29085,6 +29242,8 @@ snapshots:
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
universal-user-agent@6.0.1: {}
universal-user-agent@7.0.2: {}
universalify@2.0.1: {}