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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion .github/workflows/publish-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ on:
- cron: '0 */12 * * *'
workflow_dispatch:
inputs:
pull:
description: 'The pull number to check out'
required: false
default: 'main'
tag:
description: 'The tag to use, generally a feature name'
required: false
type: string
dry_run:
description: 'Perform a dry run that skips publishing and outputs logs indicating what would have happened'
type: boolean
Expand All @@ -19,10 +27,27 @@ jobs:
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
if: github.repository_owner == 'discordjs'
steps:
- uses: actions/create-github-app-token@v2
id: app-token
with:
app-id: ${{ vars.DISCORDJS_APP_ID }}
private-key: ${{ secrets.DISCORDJS_APP_KEY_RELEASE }}

- name: Decide ref
id: ref
run: |
if [ -n "${{ github.event.inputs.pull }}" ]; then
echo "ref=refs/pull/${{ github.event.inputs.pull }}/head" >> $GITHUB_OUTPUT
else
echo "ref=refs/heads/main" >> $GITHUB_OUTPUT
fi

- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
ref: ${{ steps.ref.outputs.ref }}

- name: Install Node.js v22
uses: actions/setup-node@v5
Expand All @@ -37,12 +62,42 @@ jobs:
- name: Build dependencies
run: pnpm run build

- name: Publish packages
- name: Checkout main repository (non-main ref)
if: ${{ steps.ref.outputs.ref != 'refs/heads/main' }}
uses: actions/checkout@v5
with:
path: 'main'

- name: Install action deps (non-main ref)
if: ${{ steps.ref.outputs.ref != 'refs/heads/main' }}
shell: bash
working-directory: ./main
env:
COREPACK_ENABLE_STRICT: 0
run: |
pnpm self-update 10
pnpm install --filter @discordjs/actions --frozen-lockfile --prefer-offline --loglevel error

- name: Publish packages (non-main ref)
if: ${{ steps.ref.outputs.ref != 'refs/heads/main' }}
uses: ./main/packages/actions/src/releasePackages
with:
exclude: '@discordjs/docgen'
dry: ${{ inputs.dry_run }}
dev: true
tag: ${{ inputs.tag || 'dev' }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish packages (main ref)
if: ${{ steps.ref.outputs.ref == 'refs/heads/main' }}
uses: ./packages/actions/src/releasePackages
with:
exclude: '@discordjs/docgen'
dry: ${{ inputs.dry_run }}
dev: true
tag: ${{ inputs.tag || 'dev' }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions packages/actions/src/releasePackages/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ inputs:
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)'
tag:
description: 'The tag to use, generally a feature name'
runs:
using: composite
steps:
Expand All @@ -22,3 +24,4 @@ runs:
INPUT_DRY: ${{ inputs.dry }}
INPUT_PACKAGE: ${{ inputs.package }}
INPUT_EXCLUDE: ${{ inputs.exclude }}
INPUT_TAG: ${{ inputs.tag }}
18 changes: 9 additions & 9 deletions packages/actions/src/releasePackages/generateReleaseTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export interface ReleaseEntry {
version: string;
}

async function fetchDevVersion(pkg: string) {
async function fetchDevVersion(pkg: string, tag: string) {
try {
const res = await fetch(`https://registry.npmjs.org/${pkg}/dev`);
const res = await fetch(`https://registry.npmjs.org/${pkg}/${tag}`);
if (!res.ok) return null;
const packument = (await res.json()) as PackumentVersion;
return packument.version;
Expand All @@ -37,7 +37,7 @@ async function fetchDevVersion(pkg: string) {
}
}

async function getReleaseEntries(dev: boolean, dry: boolean) {
async function getReleaseEntries(dry: boolean, devTag?: string) {
const releaseEntries: ReleaseEntry[] = [];
const packageList: pnpmTree[] =
await $`pnpm list --recursive --only-projects --filter {packages/\*} --prod --json`.json();
Expand All @@ -57,8 +57,8 @@ async function getReleaseEntries(dev: boolean, dry: boolean) {
version: pkg.version,
};

if (dev) {
const devVersion = await fetchDevVersion(pkg.name);
if (devTag) {
const devVersion = await fetchDevVersion(pkg.name, devTag);
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) {
Expand All @@ -72,9 +72,9 @@ async function getReleaseEntries(dev: boolean, dry: boolean) {
release.version = devVersion;
} else if (dry) {
info(`[DRY] Bumping ${pkg.name} via git-cliff.`);
release.version = `${pkg.version}.DRY-dev.${timestamp}-${commitHash}`;
release.version = `${pkg.version}.DRY-${devTag}.${timestamp}-${commitHash}`;
} else {
await $`pnpm --filter=${pkg.name} run release --preid "dev.${timestamp}-${commitHash}" --skip-changelog`;
await $`pnpm --filter=${pkg.name} run release --preid "${devTag}.${timestamp}-${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;
Expand Down Expand Up @@ -129,8 +129,8 @@ async function getReleaseEntries(dev: boolean, dry: boolean) {
return releaseEntries;
}

export async function generateReleaseTree(dev: boolean, dry: boolean, packageName?: string, exclude?: string[]) {
let releaseEntries = await getReleaseEntries(dev, dry);
export async function generateReleaseTree(dry: boolean, devTag?: string, packageName?: string, exclude?: string[]) {
let releaseEntries = await getReleaseEntries(dry, devTag);
// Try to early return if the package doesn't have deps
if (packageName && packageName !== 'all') {
const releaseEntry = releaseEntries.find((entry) => entry.name === packageName);
Expand Down
20 changes: 17 additions & 3 deletions packages/actions/src/releasePackages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,29 @@ program
)
.option('--dry', 'skips actual publishing and outputs logs instead', dryInput)
.option('--dev', 'publishes development versions and skips tagging / github releases', devInput)
.option('--tag <tag>', 'tag to use for dev releases (defaults to "dev")', getInput('tag'))
.parse();

const { exclude, dry, dev } = program.opts<{ dev: boolean; dry: boolean; exclude: string[] }>();
const {
exclude,
dry,
dev,
tag: inputTag,
} = program.opts<{ dev: boolean; dry: boolean; exclude: string[]; tag: string }>();

// All this because getInput('tag') will return empty string when not set :P
if (!dev && inputTag.length) {
throw new Error('The --tag option can only be used with --dev');
}

const tag = inputTag.length ? inputTag : dev ? 'dev' : undefined;

const [packageName] = program.processedArgs as [string];
const tree = await generateReleaseTree(dry, tag, packageName, exclude);

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)));
await Promise.all(branch.map(async (release) => releasePackage(release, dry, tag)));
endGroup();
}

Expand Down
14 changes: 7 additions & 7 deletions packages/actions/src/releasePackages/releasePackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async function gitTagAndRelease(release: ReleaseEntry, dry: boolean) {
}
}

export async function releasePackage(release: ReleaseEntry, dev: boolean, dry: boolean, doGitRelease = !dev) {
export async function releasePackage(release: ReleaseEntry, dry: boolean, devTag?: string, doGitRelease = !devTag) {
// Sanity check against the registry first
if (await checkRegistry(release)) {
info(`${release.name}@${release.version} already published, skipping.`);
Expand All @@ -51,11 +51,11 @@ export async function releasePackage(release: ReleaseEntry, dev: boolean, dry: b
if (dry) {
info(`[DRY] Releasing ${release.name}@${release.version}`);
} else {
await $`pnpm --filter=${release.name} publish --provenance --no-git-checks ${dev ? '--tag=dev' : ''}`;
await $`pnpm --filter=${release.name} publish --provenance --no-git-checks ${devTag ? `--tag=${devTag}` : ''}`;
}

// && !dev just to be sure
if (doGitRelease && !dev) await gitTagAndRelease(release, dry);
// && !devTag just to be sure
if (doGitRelease && !devTag) await gitTagAndRelease(release, dry);

if (dry) return;

Expand All @@ -77,9 +77,9 @@ export async function releasePackage(release: ReleaseEntry, dev: boolean, dry: b
}, 15_000);
});

if (dev) {
if (devTag) {
// 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}`
void $`pnpm exec npm-deprecate --name "*${devTag}*" --message "This version is deprecated. Please use a newer version." --package ${release.name}`
.nothrow()
// eslint-disable-next-line promise/prefer-await-to-then
.then(() => {});
Expand All @@ -90,6 +90,6 @@ export async function releasePackage(release: ReleaseEntry, dev: boolean, dry: b
await $`pnpm --filter=create-discord-bot run rename-to-app`;
// eslint-disable-next-line require-atomic-updates
release.name = 'create-discord-app';
await releasePackage(release, dev, dry, false);
await releasePackage(release, dry, devTag, false);
}
}