diff --git a/.changeset/lucky-flies-search.md b/.changeset/lucky-flies-search.md new file mode 100644 index 00000000000..1c2edc43fce --- /dev/null +++ b/.changeset/lucky-flies-search.md @@ -0,0 +1,4 @@ +--- +--- + +chore: release deprecation script diff --git a/.github/workflows/release-deprecate.yaml b/.github/workflows/release-deprecate.yaml new file mode 100644 index 00000000000..61807d69d16 --- /dev/null +++ b/.github/workflows/release-deprecate.yaml @@ -0,0 +1,29 @@ +name: "Deprecate Old Versions" + +on: + workflow_dispatch: + inputs: + deprecate_versions: + type: boolean + description: Deprecate versions? Otherwise dry-run mode will be used. + default: false + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + deprecate-npm-versions: + name: Deprecate versions next, pr and rc + runs-on: buildjet-4vcpu-ubuntu-2204 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI Setup + uses: ./.github/actions/ci-setup + + - name: Deprecate + run: pnpm release:deprecate + env: + DEPRECATE_VERSIONS: ${{ github.event.inputs.deprecate_versions }} diff --git a/.github/workflows/release-unpublish.yaml b/.github/workflows/release-unpublish.yaml deleted file mode 100644 index 77cba5e6ea3..00000000000 --- a/.github/workflows/release-unpublish.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: "Unpublish old versions" - -on: - workflow_dispatch: - inputs: - delete_packages: - type: boolean - description: Delete packages? otherwise dry-run mode will be used - default: false - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - clean-npm-versions: - name: Unpublish versions next and pr - runs-on: buildjet-4vcpu-ubuntu-2204 - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/ci-setup - - uses: FuelLabs/github-actions/setups/npm@master - with: - npm-token: ${{ secrets.NPM_TOKEN }} - - run: | - node ./scripts/release-unpublish.js - env: - DELETE_PACKAGES: ${{ github.event.inputs.delete_packages}} diff --git a/package.json b/package.json index 49d473cfa96..8233d69d518 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "changeset:update-changelog": "tsx ./scripts/changeset/update-changelog.mts", "changeset:get-latest-release": "tsx ./scripts/changeset/get-latest-release.mts", "changeset:dependabot": "./scripts/changeset/dependabot-changeset.sh", + "release:deprecate": "tsx ./scripts/release-deprecate.ts", "forc:update": "tsx ./scripts/forc-update", "forc:check": "./scripts/forc-check.sh", "forc:format": "./scripts/forc-format.sh", diff --git a/scripts/release-deprecate.ts b/scripts/release-deprecate.ts new file mode 100644 index 00000000000..922b967cc76 --- /dev/null +++ b/scripts/release-deprecate.ts @@ -0,0 +1,86 @@ +import { compare } from 'compare-versions'; +import { exec } from 'node:child_process'; +import { readFileSync, readdirSync } from 'node:fs'; +import { join } from 'node:path'; + +const { log, error } = console; + +const deprecateTags = /next|pr|rc/; +const { version: currentVersion } = JSON.parse( + readFileSync(join(process.cwd(), '/packages/fuels/package.json')).toString() +); +const deprecateVersions = process.env.DEPRECATE_VERSIONS === 'true'; + +const getPublicPackages = () => { + const packagesDir = join(__dirname, '../packages'); + const packages = readdirSync(packagesDir, { withFileTypes: true }); + const packagesNames = packages.map((p) => { + try { + const packageContent = readFileSync(join(packagesDir, p.name, 'package.json'), 'utf8'); + const packageJson = JSON.parse(packageContent.toString()); + return packageJson.private ? null : packageJson.name; + } catch (err) { + return null; + } + }); + return packagesNames.filter((p) => !!p); +}; + +const getVersionsToDeprecate = async (packageName: string) => { + const { versions } = await fetch(`https://registry.npmjs.org/${packageName}`).then((resp) => + resp.json() + ); + + // Only deprecate certain tags + const validVersions = Object.keys(versions).filter( + (version) => version.search(deprecateTags) > -1 && !compare(version, currentVersion, '>=') + ); + + // Remove the latest next tag from the deprecation list + const latestNextVersion = validVersions.filter((version) => version.search('next') > -1).pop(); + return validVersions.filter((version) => version !== latestNextVersion); +}; + +const main = async () => { + const packages = getPublicPackages(); + await Promise.allSettled( + packages.map(async (packageName) => { + const versionsToDeprecate = await getVersionsToDeprecate(packageName); + + log('The following versions will be deprecated:'); + log(versionsToDeprecate.map((v) => ` - ${v}`).join('\n')); + + if (deprecateVersions) { + await Promise.allSettled( + versionsToDeprecate.map( + async (versionToDelete) => + new Promise((resolve, reject) => { + exec( + `npm deprecate ${packageName}@${versionToDelete} "Version no longer supported."`, + (err, _stdout, stderr) => { + if (err) { + log(`❌ Error ${packageName}@${versionToDelete} not deprecated!\n`); + reject(err); + return; + } + if (stderr) { + log(`❌ Error ${packageName}@${versionToDelete} not deprecated!\n`); + reject(new Error(stderr)); + return; + } + log(`✅ Package ${packageName}@${versionToDelete} deprecated!\n`); + resolve(true); + } + ); + }) + ) + ); + } + }) + ); +}; + +main().catch((err) => { + error(err); + process.exit(1); +}); diff --git a/scripts/release-unpublish.js b/scripts/release-unpublish.js deleted file mode 100644 index d88f3e91ed9..00000000000 --- a/scripts/release-unpublish.js +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -const { compare } = require('compare-versions'); -const { readFileSync, readdirSync } = require('node:fs'); -const { join } = require('node:path'); -const util = require('node:util'); -const exec = util.promisify(require('node:child_process').exec); - -const DELETE_TAGS = /next|pr/; -const { version: CURRENT_VERSION } = require('../packages/fuels/package.json'); - -const DELETE_PACKAGES = process.env.DELETE_PACKAGES === 'true'; -const dryRun = DELETE_PACKAGES ? '' : '--dry-run'; - -const { log, error } = console; - -const getPublicPackages = () => { - const packagesDir = join(__dirname, '../packages'); - const packages = readdirSync(packagesDir, { withFileTypes: true }); - const packagesNames = packages.map((p) => { - try { - const packageContent = readFileSync(join(packagesDir, p.name, 'package.json'), 'utf8'); - const packageJson = JSON.parse(packageContent.toString()); - return packageJson.private ? null : packageJson.name; - } catch (err) { - return null; - } - }); - return packagesNames.filter((p) => !!p); -}; - -const main = async () => { - const packages = getPublicPackages(); - await packages.map(async (packageName) => { - log(`📦 Fetching ${packageName} versions`); - const { versions: packageVersions } = await fetch( - `https://registry.npmjs.org/${packageName}` - ).then((resp) => resp.json()); - - const versionsToDelete = Object.keys(packageVersions).filter( - (packageVersion) => - packageVersion.search(DELETE_TAGS) > -1 && !compare(packageVersion, CURRENT_VERSION, '>=') - ); - log('The following versions will be deleted:'); - log(versionsToDelete.map((v) => ` - ${v}`).join('\n')); - versionsToDelete.map(async (versionToDelete) => { - const { stderr } = await exec(`npm unpublish ${packageName}@${versionToDelete} ${dryRun}`); - if (stderr) { - log(`❌ Error ${packageName}@${versionToDelete} not deleted!\n`); - } else { - log(`✅ Package ${packageName}@${versionToDelete} deleted!\n`); - } - }); - }); -}; - -main().catch((err) => { - error(err); - process.exit(1); -});