diff --git a/.eslintignore b/.eslintignore index 9425b10ca..06a1beac6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,5 @@ !.* -coverage +coverage* lib node_modules pnpm-lock.yaml diff --git a/.github/workflows/hydrate.yml b/.github/workflows/hydrate.yml index be4c34234..8defb82f6 100644 --- a/.github/workflows/hydrate.yml +++ b/.github/workflows/hydrate.yml @@ -5,7 +5,15 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/prepare - run: pnpm run build - - run: pnpm run setup:test + - run: pnpm run hydrate:test + - name: Codecov + uses: codecov/codecov-action@v3 + with: + files: coverage-hydrate/lcov.info + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + path: coverage-hydrate name: Test Hydrate Script diff --git a/.github/workflows/setup.yml b/.github/workflows/setup.yml index a6e97ab48..9377766ea 100644 --- a/.github/workflows/setup.yml +++ b/.github/workflows/setup.yml @@ -4,6 +4,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: ./.github/actions/prepare + - run: pnpm run build - run: pnpm run setup:test name: Test Setup Script diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 52e00329b..d9e0315ee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,8 +7,11 @@ jobs: - run: pnpm run test --coverage - name: Codecov uses: codecov/codecov-action@v3 + - name: Archive code coverage results + uses: actions/upload-artifact@v3 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + name: code-coverage-report + path: coverage name: Test diff --git a/.gitignore b/.gitignore index a8bff3bd5..32ffbca94 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -coverage/ +coverage*/ lib/ node_modules/ diff --git a/.prettierignore b/.prettierignore index ec0950cf9..fb77bd671 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,4 @@ -coverage/ +coverage*/ lib/ pnpm-lock.yaml diff --git a/knip.jsonc b/knip.jsonc index 010042633..34dfd57cb 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -7,5 +7,6 @@ "script/*e2e.js" ], "ignoreBinaries": ["dedupe", "gh"], + "ignoreDependencies": ["c8"], "project": ["src/**/*.ts!", "script/**/*.js"] } diff --git a/package.json b/package.json index 652f45828..37ad95c55 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@typescript-eslint/parser": "^5.59.5", "@vitest/coverage-istanbul": "^0.31.0", "all-contributors-cli": "^6.25.1", + "c8": "^7.13.0", "console-fail-test": "^0.2.3", "cspell": "^6.31.1", "eslint": "^8.40.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f0cbb333..b442ea8a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,6 +39,9 @@ devDependencies: all-contributors-cli: specifier: ^6.25.1 version: 6.25.1 + c8: + specifier: ^7.13.0 + version: 7.13.0 console-fail-test: specifier: ^0.2.3 version: 0.2.3 @@ -365,6 +368,10 @@ packages: to-fast-properties: 2.0.0 dev: true + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + /@clack/core@0.3.2: resolution: {integrity: sha512-FZnsNynwGDIDktx6PEZK1EuCkFpY4ldEX6VYvfl0dqeoLPb9Jpw1xoUXaVcGR8ExmYNm1w2vdGdJkEUYD/2pqg==} dependencies: @@ -1401,6 +1408,10 @@ packages: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true + /@types/istanbul-lib-coverage@2.0.4: + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true + /@types/js-yaml@4.0.5: resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==} dev: true @@ -2117,6 +2128,25 @@ packages: engines: {node: '>= 0.8'} dev: true + /c8@7.13.0: + resolution: {integrity: sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==} + engines: {node: '>=10.12.0'} + hasBin: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@istanbuljs/schema': 0.1.3 + find-up: 5.0.0 + foreground-child: 2.0.0 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-report: 3.0.0 + istanbul-reports: 3.1.5 + rimraf: 3.0.2 + test-exclude: 6.0.0 + v8-to-istanbul: 9.1.0 + yargs: 16.2.0 + yargs-parser: 20.2.9 + dev: true + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -2327,6 +2357,14 @@ packages: wrap-ansi: 6.2.0 dev: true + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -3570,6 +3608,14 @@ packages: is-callable: 1.2.7 dev: true + /foreground-child@2.0.0: + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} + engines: {node: '>=8.0.0'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 3.0.7 + dev: true + /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -6956,6 +7002,15 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /v8-to-istanbul@9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + '@types/istanbul-lib-coverage': 2.0.4 + convert-source-map: 1.9.0 + dev: true + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -7322,6 +7377,19 @@ packages: yargs-parser: 18.1.3 dev: true + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: true + /yargs@17.7.1: resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} engines: {node: '>=12'} diff --git a/script/hydrate-test-e2e.js b/script/hydrate-test-e2e.js index 216832037..a136a9c62 100644 --- a/script/hydrate-test-e2e.js +++ b/script/hydrate-test-e2e.js @@ -1,6 +1,10 @@ import chalk from "chalk"; import { $ } from "execa"; +await $({ + stdio: "inherit", +})`c8 -o ./coverage-hydrate -r html -r lcov node ./lib/hydrate/index.js`; + const { stdout: gitStatus } = await $`git status`; console.log(`Stdout from running \`git status\`:\n${gitStatus}`); @@ -19,6 +23,15 @@ const filesExpectedToBeChanged = new Set([ "knip.jsonc", "package.json", "pnpm-lock.yaml", + ".eslintignore", + ".eslintrc.cjs", + ".github/DEVELOPMENT.md", + ".github/workflows/release.yml", + ".github/workflows/test.yml", + ".gitignore", + ".prettierignore", + ".release-it.json", + "cspell.json", ]); const unstagedModifiedFiles = gitStatus @@ -27,24 +40,29 @@ const unstagedModifiedFiles = gitStatus .map((match) => match.split(/\s+/g)[1]) .filter((filePath) => !filesExpectedToBeChanged.has(filePath)); +console.log("Unexpected modified files are:", unstagedModifiedFiles); + if (unstagedModifiedFiles.length) { + const gitDiffCommand = `git diff HEAD -- ${unstagedModifiedFiles.join(" ")}`; console.log( - `Stdout from running \`git diff\`:${(await $`git diff HEAD`).stdout}` + `Stdout from running \`${gitDiffCommand}\`:\n${ + (await $(gitDiffCommand)).stdout + }` ); console.error( - chalk.red( - [ - "", - "Oh no! Running the hydrate script modified some files:", - ...unstagedModifiedFiles.map((filePath) => ` - ${filePath}`), - "", - "That likely indicates changes made to the repository without", - "corresponding updates to templates in src/hydrate/creation.", - "", - "Please search for those file(s)' name(s) under src/hydrate for", - "the corresponding template and update those as well.", - ].join("\n") - ) + [ + "", + "Oh no! Running the hydrate script modified some files:", + ...unstagedModifiedFiles.map((filePath) => ` - ${filePath}`), + "", + "That likely indicates changes made to the repository without", + "corresponding updates to templates in src/hydrate/creation.", + "", + "Please search for those file(s)' name(s) under src/hydrate for", + "the corresponding template and update those as well.", + ] + .map((line) => chalk.red(line)) + .join("\n") ); process.exitCode = 1; } diff --git a/script/setup-test-e2e.js b/script/setup-test-e2e.js index 44ed23cf1..3a4c39c7e 100644 --- a/script/setup-test-e2e.js +++ b/script/setup-test-e2e.js @@ -9,9 +9,9 @@ const owner = "RNR1"; const title = "New Title Test"; const repository = "new-repository-test"; -const result = - await $`pnpm run setup --description ${description} --owner ${owner} --title ${title} --repository ${repository} --skip-api --skip-restore`; -console.log("Result from pnpm run setup:", result); +await $({ + stdio: "inherit", +})`node ./lib/setup/index.js --description ${description} --owner ${owner} --title ${title} --repository ${repository} --skip-api --skip-restore`; const newPackageJson = JSON.parse( (await fs.readFile("./package.json")).toString() diff --git a/src/hydrate/creation/rootFiles.ts b/src/hydrate/creation/rootFiles.ts index ba1fb739a..ea556e15b 100644 --- a/src/hydrate/creation/rootFiles.ts +++ b/src/hydrate/creation/rootFiles.ts @@ -359,7 +359,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. skipLibCheck: true, sourceMap: true, strict: true, - target: "ES2021", + target: "ES2022", }, include: ["src"], }), diff --git a/src/hydrate/finalize.test.ts b/src/hydrate/finalize.test.ts index 85e30d07d..98a427f50 100644 --- a/src/hydrate/finalize.test.ts +++ b/src/hydrate/finalize.test.ts @@ -24,6 +24,7 @@ describe("finalize", () => { "@types/eslint", "@typescript-eslint/eslint-plugin", "@typescript-eslint/parser", + "all-contributors-cli", "cspell", "eslint", "eslint-config-prettier", @@ -66,6 +67,7 @@ describe("finalize", () => { "@types/eslint", "@typescript-eslint/eslint-plugin", "@typescript-eslint/parser", + "all-contributors-cli", "cspell", "eslint", "eslint-config-prettier", diff --git a/src/hydrate/finalize.ts b/src/hydrate/finalize.ts index d9912170f..a50096ed9 100644 --- a/src/hydrate/finalize.ts +++ b/src/hydrate/finalize.ts @@ -11,6 +11,7 @@ export async function finalize({ releases, unitTests }: FinalizeOptions) { "@types/eslint", "@typescript-eslint/eslint-plugin", "@typescript-eslint/parser", + "all-contributors-cli", "cspell", "eslint", "eslint-config-prettier", @@ -56,7 +57,8 @@ export async function finalize({ releases, unitTests }: FinalizeOptions) { for (const command of [ `pnpm add ${devDependencies.join(" ")} -D`, - `all-contributors generate`, + `npx all-contributors generate`, + `pnpm uninstall all-contributors-cli -D`, "pnpm run format:write", ]) { console.log(chalk.gray(`$ ${command}`)); diff --git a/src/setup/index.ts b/src/setup/index.ts index 7a33e486a..caec91aa1 100644 --- a/src/setup/index.ts +++ b/src/setup/index.ts @@ -1,204 +1,3 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; -import { $ } from "execa"; +import { setup } from "./setup.js"; -import { handlePromptCancel } from "./cli/prompts.js"; -import { - skipSpinnerBlock, - successSpinnerBlock, - withSpinner, -} from "./cli/spinners.js"; -import { getNpmAuthor } from "./settings/getNpmAuthor.js"; -import { getUser } from "./settings/getUser.js"; -import { getInputValuesAndOctokit } from "./settings/inputs.js"; -import { clearChangelog } from "./steps/clearChangelog.js"; -import { hydrateBranchProtectionSettings } from "./steps/hydrateBranchProtectionSettings.js"; -import { hydrateRepositoryLabels } from "./steps/hydrateRepositoryLabels.js"; -import { hydrateRepositorySettings } from "./steps/hydrateRepositorySettings.js"; -import { removeSetupScripts } from "./steps/removeSetupScripts.js"; -import { resetGitTags } from "./steps/resetGitTags.js"; -import { uninstallPackages } from "./steps/uninstallPackages.js"; -import { updateAllContributorsFile } from "./steps/updateAllContributorsFile.js"; -import { updateAllContributorsTable } from "./steps/updateAllContributorsTable.js"; -import { updateLocalFiles } from "./steps/updateLocalFiles.js"; -import { updateReadme } from "./steps/updateReadme.js"; - -let exitCode = 0; -let skipRestore = true; - -try { - console.clear(); - console.log( - chalk.greenBright`Welcome to the`, - chalk.bgGreenBright.black`template-typescript-node-package`, - chalk.greenBright`package setup! 🎉` - ); - console.log(); - - const args = process.argv.slice(2); - const { octokit, values } = await getInputValuesAndOctokit(args); - - skipRestore = !!values.skipRestore; - - prompts.intro( - chalk.blue( - "Let's collect some information to fill out repository details..." - ) - ); - console.log(chalk.gray("│")); - - successSpinnerBlock("Started hydrating package metadata locally."); - - await withSpinner(() => getUser(values.owner), { - startText: "Updating existing contributors details...", - successText: `Updated existing contributors details.`, - stopText: `Error updating existing contributors details.`, - errorText: - "Failed to remove existing contributors & update '.all-contributorsrc' with new changes. ", - }); - - const npmAuthor = await getNpmAuthor(values.owner); - - await withSpinner(() => updateLocalFiles({ ...values, npmAuthor }), { - startText: "Updating all the files with provided details...", - successText: `Updated all the files with provided details.`, - stopText: `Error updating all the files with provided details.`, - errorText: "Failed to update all the files with provided details. ", - }); - - await withSpinner(() => updateAllContributorsFile(values), { - startText: "Updating '.all-contributorsrc' with new repository details...", - successText: `Updated '.all-contributorsrc' with new repository details.`, - stopText: `Error updating '.all-contributorsrc' with new repository details.`, - errorText: - "Failed to update '.all-contributorsrc' with new repository details. ", - }); - - await withSpinner(updateReadme, { - startText: - "Appending template-typescript-node-package notice to 'README.md'...", - successText: `Appended template-typescript-node-package notice to 'README.md'.`, - stopText: `Error appending template-typescript-node-package notice to 'README.md'.`, - errorText: - "Failed to append template-typescript-node-package notice to 'README.md'. ", - }); - - successSpinnerBlock(`Finished hydrating package metadata locally.`); - - await withSpinner(clearChangelog, { - startText: `Clearing CHANGELOG.md...`, - successText: `Cleared CHANGELOG.md.`, - stopText: `Error clearing CHANGELOG.md.`, - errorText: `Could not empty 'CHANGELOG.md'. `, - }); - - await withSpinner(() => updateAllContributorsTable(values), { - startText: `Generating all-contributors table in README.md...`, - successText: `Generated all-contributors table in README.md.`, - stopText: `Error generating all-contributors table in README.md.`, - errorText: `Could not empty 'CHANGELOG.md'. `, - }); - - await withSpinner(resetGitTags, { - startText: `Deleting local git tags...`, - successText: `Deleted local git tags.`, - stopText: `Error deleting local git tags.`, - errorText: `Could not delete local git tags. `, - }); - - if (!octokit) { - skipSpinnerBlock(`Skipping API hydration.`); - } else { - successSpinnerBlock(`Starting API hydration.`); - - await withSpinner(hydrateRepositoryLabels, { - startText: `Hydrating repository labels...`, - successText: `Hydrated repository labels.`, - stopText: `Error hydrating repository labels.`, - errorText: `Could not hydrate repository labels. `, - }); - - await withSpinner(() => hydrateRepositorySettings(octokit, values), { - startText: `Hydrating initial repository settings...`, - successText: `Hydrated initial repository settings.`, - stopText: `Error hydrating initial repository settings.`, - errorText: `Could not hydrate initial repository settings. `, - }); - - await withSpinner(() => hydrateBranchProtectionSettings(octokit, values), { - startText: `Hydrating branch protection settings...`, - successText: `Hydrated branch protection settings.`, - stopText: `Error hydrating branch protection settings.`, - errorText: `Could not hydrate branch protection settings. `, - warnText: `Could not hydrate branch protection settings: private repositories require GitHub Pro for that API.`, - }); - - successSpinnerBlock(`Finished API hydration.`); - } - - await withSpinner(removeSetupScripts, { - startText: `Removing setup scripts...`, - successText: `Removed setup scripts.`, - stopText: `Error removing setup scripts.`, - errorText: `Could not remove setup scripts. `, - }); - - if (values.skipUninstalls) { - skipSpinnerBlock(`Skipping uninstall of packages only used for setup.`); - } else { - await withSpinner(uninstallPackages, { - startText: `Removing packages only used for setup...`, - successText: `Removed packages only used for setup.`, - stopText: `Error removing packages only used for setup.`, - errorText: `Could not remove packages only used for setup. `, - }); - } - - prompts.outro(chalk.blue`Great, looks like everything worked! 🎉`); - - console.log(chalk.blue`You may consider committing these changes:`); - console.log(); - console.log(chalk.gray`git add -A`); - console.log(chalk.gray`git commit -m "chore: hydrated repo"`); - console.log(chalk.gray`git push`); - console.log(); - console.log(chalk.greenBright`See ya! 👋`); - console.log(); - - exitCode = 0; -} catch (error) { - prompts.outro( - chalk.red`Looks like there was a problem. Correct it and try again? 😕` - ); - - console.log(); - console.log(error); - - if (skipRestore) { - console.log(); - console.log(chalk.gray`Skipping restoring local repository, as requested.`); - console.log(); - } else { - const shouldRestore = await prompts.confirm({ - message: - "Do you want to restore the repository to how it was before running setup?", - }); - - handlePromptCancel(shouldRestore); - - if (shouldRestore) { - console.log(); - console.log( - chalk.gray`Resetting repository using`, - chalk.reset`git restore .` - ); - await $`git restore .`; - console.log("Repository is reset. Ready to try again?"); - console.log(); - } - } - - exitCode = 1; -} finally { - process.exit(exitCode); -} +process.exitCode = await setup(process.argv.slice(2)); diff --git a/src/setup/setup.ts b/src/setup/setup.ts new file mode 100644 index 000000000..d642f4894 --- /dev/null +++ b/src/setup/setup.ts @@ -0,0 +1,211 @@ +import * as prompts from "@clack/prompts"; +import chalk from "chalk"; +import { $ } from "execa"; + +import { handlePromptCancel } from "./cli/prompts.js"; +import { + skipSpinnerBlock, + successSpinnerBlock, + withSpinner, +} from "./cli/spinners.js"; +import { getNpmAuthor } from "./settings/getNpmAuthor.js"; +import { getUser } from "./settings/getUser.js"; +import { getInputValuesAndOctokit } from "./settings/inputs.js"; +import { clearChangelog } from "./steps/clearChangelog.js"; +import { hydrateBranchProtectionSettings } from "./steps/hydrateBranchProtectionSettings.js"; +import { hydrateRepositoryLabels } from "./steps/hydrateRepositoryLabels.js"; +import { hydrateRepositorySettings } from "./steps/hydrateRepositorySettings.js"; +import { removeSetupScripts } from "./steps/removeSetupScripts.js"; +import { resetGitTags } from "./steps/resetGitTags.js"; +import { uninstallPackages } from "./steps/uninstallPackages.js"; +import { updateAllContributorsFile } from "./steps/updateAllContributorsFile.js"; +import { updateAllContributorsTable } from "./steps/updateAllContributorsTable.js"; +import { updateLocalFiles } from "./steps/updateLocalFiles.js"; +import { updateReadme } from "./steps/updateReadme.js"; + +export async function setup(args: string[]) { + let exitCode = 0; + let skipRestore = true; + + try { + console.clear(); + console.log( + chalk.greenBright`Welcome to the`, + chalk.bgGreenBright.black`template-typescript-node-package`, + chalk.greenBright`package setup! 🎉` + ); + console.log(); + + const { octokit, values } = await getInputValuesAndOctokit(args); + + skipRestore = !!values.skipRestore; + + prompts.intro( + chalk.blue( + "Let's collect some information to fill out repository details..." + ) + ); + console.log(chalk.gray("│")); + + successSpinnerBlock("Started hydrating package metadata locally."); + + await withSpinner(() => getUser(values.owner), { + startText: "Updating existing contributors details...", + successText: `Updated existing contributors details.`, + stopText: `Error updating existing contributors details.`, + errorText: + "Failed to remove existing contributors & update '.all-contributorsrc' with new changes. ", + }); + + const npmAuthor = await getNpmAuthor(values.owner); + + await withSpinner(() => updateLocalFiles({ ...values, npmAuthor }), { + startText: "Updating all the files with provided details...", + successText: `Updated all the files with provided details.`, + stopText: `Error updating all the files with provided details.`, + errorText: "Failed to update all the files with provided details. ", + }); + + await withSpinner(() => updateAllContributorsFile(values), { + startText: + "Updating '.all-contributorsrc' with new repository details...", + successText: `Updated '.all-contributorsrc' with new repository details.`, + stopText: `Error updating '.all-contributorsrc' with new repository details.`, + errorText: + "Failed to update '.all-contributorsrc' with new repository details. ", + }); + + await withSpinner(updateReadme, { + startText: + "Appending template-typescript-node-package notice to 'README.md'...", + successText: `Appended template-typescript-node-package notice to 'README.md'.`, + stopText: `Error appending template-typescript-node-package notice to 'README.md'.`, + errorText: + "Failed to append template-typescript-node-package notice to 'README.md'. ", + }); + + successSpinnerBlock(`Finished hydrating package metadata locally.`); + + await withSpinner(clearChangelog, { + startText: `Clearing CHANGELOG.md...`, + successText: `Cleared CHANGELOG.md.`, + stopText: `Error clearing CHANGELOG.md.`, + errorText: `Could not empty 'CHANGELOG.md'. `, + }); + + await withSpinner(() => updateAllContributorsTable(values), { + startText: `Generating all-contributors table in README.md...`, + successText: `Generated all-contributors table in README.md.`, + stopText: `Error generating all-contributors table in README.md.`, + errorText: `Could not empty 'CHANGELOG.md'. `, + }); + + await withSpinner(resetGitTags, { + startText: `Deleting local git tags...`, + successText: `Deleted local git tags.`, + stopText: `Error deleting local git tags.`, + errorText: `Could not delete local git tags. `, + }); + + if (!octokit) { + skipSpinnerBlock(`Skipping API hydration.`); + } else { + successSpinnerBlock(`Starting API hydration.`); + + await withSpinner(hydrateRepositoryLabels, { + startText: `Hydrating repository labels...`, + successText: `Hydrated repository labels.`, + stopText: `Error hydrating repository labels.`, + errorText: `Could not hydrate repository labels. `, + }); + + await withSpinner(() => hydrateRepositorySettings(octokit, values), { + startText: `Hydrating initial repository settings...`, + successText: `Hydrated initial repository settings.`, + stopText: `Error hydrating initial repository settings.`, + errorText: `Could not hydrate initial repository settings. `, + }); + + await withSpinner( + () => hydrateBranchProtectionSettings(octokit, values), + { + startText: `Hydrating branch protection settings...`, + successText: `Hydrated branch protection settings.`, + stopText: `Error hydrating branch protection settings.`, + errorText: `Could not hydrate branch protection settings. `, + warnText: `Could not hydrate branch protection settings: private repositories require GitHub Pro for that API.`, + } + ); + + successSpinnerBlock(`Finished API hydration.`); + } + + await withSpinner(removeSetupScripts, { + startText: `Removing setup scripts...`, + successText: `Removed setup scripts.`, + stopText: `Error removing setup scripts.`, + errorText: `Could not remove setup scripts. `, + }); + + if (values.skipUninstalls) { + skipSpinnerBlock(`Skipping uninstall of packages only used for setup.`); + } else { + await withSpinner(uninstallPackages, { + startText: `Removing packages only used for setup...`, + successText: `Removed packages only used for setup.`, + stopText: `Error removing packages only used for setup.`, + errorText: `Could not remove packages only used for setup. `, + }); + } + + prompts.outro(chalk.blue`Great, looks like everything worked! 🎉`); + + console.log(chalk.blue`You may consider committing these changes:`); + console.log(); + console.log(chalk.gray`git add -A`); + console.log(chalk.gray`git commit -m "chore: hydrated repo"`); + console.log(chalk.gray`git push`); + console.log(); + console.log(chalk.greenBright`See ya! 👋`); + console.log(); + + exitCode = 0; + } catch (error) { + prompts.outro( + chalk.red`Looks like there was a problem. Correct it and try again? 😕` + ); + + console.log(); + console.log(error); + + if (skipRestore) { + console.log(); + console.log( + chalk.gray`Skipping restoring local repository, as requested.` + ); + console.log(); + } else { + const shouldRestore = await prompts.confirm({ + message: + "Do you want to restore the repository to how it was before running setup?", + }); + + handlePromptCancel(shouldRestore); + + if (shouldRestore) { + console.log(); + console.log( + chalk.gray`Resetting repository using`, + chalk.reset`git restore .` + ); + await $`git restore .`; + console.log("Repository is reset. Ready to try again?"); + console.log(); + } + } + + exitCode = 1; + } + + return exitCode; +} diff --git a/src/setup/steps/uninstallPackages.ts b/src/setup/steps/uninstallPackages.ts index 72a19b91b..ec827d7bc 100644 --- a/src/setup/steps/uninstallPackages.ts +++ b/src/setup/steps/uninstallPackages.ts @@ -2,5 +2,5 @@ import { $ } from "execa"; export async function uninstallPackages() { await $`pnpm remove chalk execa js-yaml`; - await $`pnpm remove @clack/prompts @octokit/request-error @types/js-yaml @types/prettier all-contributors-cli globby octokit npm-user replace-in-file title-case tsx -D`; + await $`pnpm remove @clack/prompts @octokit/request-error @types/js-yaml @types/prettier all-contributors-cli c8 globby octokit npm-user replace-in-file title-case tsx -D`; } diff --git a/src/setup/steps/updateLocalFiles.ts b/src/setup/steps/updateLocalFiles.ts index ff30fae37..eaa8d51fa 100644 --- a/src/setup/steps/updateLocalFiles.ts +++ b/src/setup/steps/updateLocalFiles.ts @@ -40,6 +40,7 @@ export async function updateLocalFiles({ [`,\n\t\t["src/setup/*.json"`, ``, "./cspell.json"], [`\t\t"src/hydrate/index.ts",\n`, ``, "./knip.jsonc"], [`\t\t"src/setup/index.ts",\n`, ``, "./knip.jsonc"], + [`\t\t"ignoreDependencies": ["c8"],\n`, ``, "./knip.jsonc"], [ `["src/index.ts!", "script/setup*.js"]`, `"src/index.ts!"`,