diff --git a/.github/workflows/release-deprecate.yaml b/.github/workflows/release-deprecate.yaml index 0d70a886526..d8957d4a26a 100644 --- a/.github/workflows/release-deprecate.yaml +++ b/.github/workflows/release-deprecate.yaml @@ -11,14 +11,6 @@ on: type: string description: Filter by package name default: "" - filter_by_package_version: - type: string - description: Filter by package version - default: "" - chunk_size: - type: number - description: Chunk size - default: 1000 concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -56,5 +48,3 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_DEPLOY_TOKEN }} DEPRECATE_VERSIONS: ${{ github.event.inputs.deprecate_versions }} FILTER_BY_PACKAGE_NAME: ${{ github.event.inputs.filter_by_package_name }} - FILTER_BY_VERSION: ${{ github.event.inputs.filter_by_package_version }} - CHUNK_SIZE: ${{ github.event.inputs.chunk_size }} diff --git a/package.json b/package.json index fc72e1793b6..4d1c05343bf 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,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", + "release:deprecate": "tsx ./scripts/release-deprecate.mts", "forc:update": "tsx ./scripts/forc-update", "forc:check": "./scripts/forc-check.sh", "forc:format": "./scripts/forc-format.sh", diff --git a/scripts/release-deprecate.mts b/scripts/release-deprecate.mts new file mode 100644 index 00000000000..15a18a7afc9 --- /dev/null +++ b/scripts/release-deprecate.mts @@ -0,0 +1,113 @@ +import { globSync } from "glob"; +import { execSync } from "node:child_process"; +import { readFileSync } from "node:fs"; + +const { log, error } = console; + +/** + * Parse ENVS + */ +const SHOULD_DEPRECATE_VERSIONS: boolean = + process.env.DEPRECATE_VERSIONS === "true"; +const FILTER_BY_PACKAGE_NAME: string = process.env.FILTER_BY_PACKAGE_NAME ?? ""; + +/** + * Restricted tags that can be deprecated + */ +const DEPRECIABLE_TAGS: string[] = ["0.0.0-next", "0.0.0-pr", "0.0.0-rc"]; + +/** + * Packages that are no longer published to npm + * + * TODO: Consider deprecating these packages + */ +const NO_LONGER_MAINTAINED_PACKAGES: string[] = [ + "@fuel-ts/merkle-shared", + "@fuel-ts/merklesum", + "@fuel-ts/sparsemerkle", + "@fuel-ts/providers", + "@fuel-ts/example-contract", + "@fuel-ts/wallet", + "@fuel-ts/typechain-target-fuels", + "@fuel-ts/testcases", + "@fuel-ts/wordlists", + "@fuel-ts/mnemonic", + "@fuel-ts/signer", + "@fuel-ts/hdwallet", + "@fuel-ts/constants", + "@fuel-ts/interfaces", + "@fuel-ts/keystore", + "@fuel-ts/wallet-manager", + "@fuel-ts/predicate", + "@fuel-ts/asm", + "@fuel-ts/fuel-core", + "@fuel-ts/forc", +]; + +const MAINTAINED_PACKAGES: string[] = globSync("**/package.json") + // Read in the package.json file + .map((fileName) => { + const packageJson = JSON.parse(readFileSync(fileName, "utf-8")); + return { + path: fileName, + contents: packageJson, + }; + }) + // Filter out private packages + .filter((pkg) => !pkg.contents.private) + .map((pkg) => pkg.contents.name); + +let packages: string[] = MAINTAINED_PACKAGES; + +// Only by using filter by package name, are we allowed to deprecate the no longer maintained packages +if (FILTER_BY_PACKAGE_NAME !== "") { + const allPackages = [ + ...MAINTAINED_PACKAGES, + ...NO_LONGER_MAINTAINED_PACKAGES, + ]; + packages = allPackages.filter((pkg) => pkg === FILTER_BY_PACKAGE_NAME); + + // Ensure that we have found a package + if (packages.length === 0) { + error(`❌ No package found with name: ${FILTER_BY_PACKAGE_NAME}`); + process.exit(1); + } +} + +/** + * Construct the depreciable package and versions + */ +const depreciablePackageAndVersions = packages.flatMap((pkgName) => + DEPRECIABLE_TAGS.map((tag) => `${pkgName}@${tag}`), +); +log( + "The following packages and versions will be deprecated\n", + depreciablePackageAndVersions.join("\n"), + "----------------------------------\n", +); + +/** + * Deprecate the packages and versions + */ +for await (const packageAndVersion of depreciablePackageAndVersions) { + log(`Deprecating ${packageAndVersion}`); + + const dryRun = SHOULD_DEPRECATE_VERSIONS ? "" : "--dry-run "; + const command = `npm deprecate ${packageAndVersion} ${dryRun}"Version no longer supported."`; + + try { + const result = execSync(command); + log(`✅ Deprecated ${packageAndVersion}`); + log(result.toString()); + } catch (err) { + error(`❌ Error - unable to deprecate ${packageAndVersion}`); + error(err); + process.exit(1); + } + + await new Promise((resolve) => { + setTimeout(() => { + resolve(true); + }, 1000); + }); +} diff --git a/scripts/release-deprecate.ts b/scripts/release-deprecate.ts deleted file mode 100644 index 297fa01b620..00000000000 --- a/scripts/release-deprecate.ts +++ /dev/null @@ -1,102 +0,0 @@ -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 SHOULD_DEPRECATE_VERSIONS: boolean = process.env.DEPRECATE_VERSIONS === 'true'; -const CHUNK_SIZE: number = process.env.CHUNK_SIZE ? parseInt(process.env.CHUNK_SIZE, 10) : 1000; -const FILTER_BY_PACKAGE_NAME: string = process.env.FILTER_BY_PACKAGE_NAME ?? ''; -const FILTER_BY_VERSION: string = process.env.FILTER_BY_VERSION ?? ''; - -const getPublicPackages = (): string[] => { - 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): Promise => { - 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 () => { - let packages = getPublicPackages(); - if (FILTER_BY_PACKAGE_NAME !== '') { - packages = packages.filter((packageName) => packageName === FILTER_BY_PACKAGE_NAME); - } - - await Promise.allSettled( - packages.map(async (packageName) => { - const allVersions = await getVersionsToDeprecate(packageName); - - let versionsToDeprecate = allVersions.splice(0, CHUNK_SIZE); - if (FILTER_BY_VERSION !== '') { - versionsToDeprecate = versionsToDeprecate.filter( - (version) => version === FILTER_BY_VERSION - ); - } - - log('The following versions will be deprecated:'); - log(versionsToDeprecate.map((v) => ` - ${v}`).join('\n')); - - if (SHOULD_DEPRECATE_VERSIONS) { - 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`); - error(err); - reject(err); - return; - } - if (stderr) { - log(`❌ Error ${packageName}@${versionToDelete} not deprecated!\n`); - error(stderr); - reject(new Error(stderr)); - return; - } - log(`✅ Package ${packageName}@${versionToDelete} deprecated!\n`); - resolve(true); - } - ); - }) - ) - ); - } - }) - ); -}; - -main().catch((err) => { - error(err); - process.exit(1); -});