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
2 changes: 2 additions & 0 deletions .changeset/deprecate-superseded-core-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
6 changes: 6 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ jobs:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CHANGELOG_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

- name: Deprecate superseded packages
if: ${{ !cancelled() && steps.changesets.outputs.published == 'true' }}
run: node scripts/deprecate-superseded.mjs '${{ steps.changesets.outputs.publishedPackages }}'
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

# We're running the CI workflow (where node v20 modules are cached) in
# merge_group and not on main, we need to explicitly cache node_modules here so
# that follow-on branches can use the cached version of node_modules rather
Expand Down
75 changes: 75 additions & 0 deletions scripts/deprecate-superseded.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';

const UPGRADE_GUIDE = 'https://clerk.com/docs/guides/development/upgrading/upgrade-guides/core-3';

const supersededPackages = {
'@clerk/clerk-react': `This package is no longer supported. Please use @clerk/react instead. See the upgrade guide for more info: ${UPGRADE_GUIDE}`,
'@clerk/types': `This package is no longer supported. Please import types from @clerk/shared/types instead. See the upgrade guide for more info: ${UPGRADE_GUIDE}`,
'@clerk/clerk-expo': `@clerk/clerk-expo is deprecated. Migrate to @clerk/expo by following the Core 3 upgrade guide ${UPGRADE_GUIDE}`,
};

const { NPM_TOKEN, DRY_RUN } = process.env;
const dryRun = DRY_RUN === '1' || DRY_RUN === 'true';

const run = () => {
const publishedPackages = JSON.parse(process.argv[2] ?? '[]');
const targets = publishedPackages.filter(({ name }) => name in supersededPackages);

if (targets.length === 0) {
console.log('No superseded packages were published, nothing to deprecate.');
return;
}

if (!dryRun && !NPM_TOKEN) {
throw new Error('NPM_TOKEN is required to deprecate packages.');
}

const npmrc = writeUserconfig();
const failures = [];

for (const { name, version } of targets) {
const spec = `${name}@${version}`;
const message = supersededPackages[name];

if (dryRun) {
console.log(`[dry-run] npm deprecate ${spec} ${JSON.stringify(message)}`);
continue;
}

try {
npm(['deprecate', spec, message], npmrc);
const applied = npm(['view', spec, 'deprecated'], npmrc).trim();
if (applied !== message) {
throw new Error(`Expected deprecation message was not applied. Registry returned: ${JSON.stringify(applied)}`);
}
console.log(`Deprecated ${spec}`);
} catch (error) {
failures.push(`${spec}: ${error.message}`);
}
}

fs.rmSync(path.dirname(npmrc), { recursive: true, force: true });

if (failures.length > 0) {
throw new Error(`Failed to deprecate:\n${failures.join('\n')}`);
}
};

const writeUserconfig = () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'deprecate-superseded-'));
const file = path.join(dir, '.npmrc');
fs.writeFileSync(file, `//registry.npmjs.org/:_authToken=${NPM_TOKEN ?? ''}\n`, { mode: 0o600 });
return file;
};

const npm = (args, userconfig) =>
execFileSync('npm', args, {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
env: { ...process.env, npm_config_userconfig: userconfig },
});

run();
Loading