Skip to content

chore: bring in updates for create's interactive CLI #1781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
249c5b7
chore: bring in updates for create's interactive CLI
JoshuaKGoldberg Dec 19, 2024
7e3aab4
delete main, not just comment
JoshuaKGoldberg Dec 19, 2024
fb6693c
[email protected], with suggestions
JoshuaKGoldberg Dec 20, 2024
dba9bb3
Apply suggestions from code review
JoshuaKGoldberg Dec 20, 2024
3d3b465
bring in alpha.4s
JoshuaKGoldberg Dec 20, 2024
409adf8
pnpm dedupe
JoshuaKGoldberg Dec 20, 2024
cfc7719
Started fixing tests
JoshuaKGoldberg Dec 20, 2024
e16639a
Merge branch 'main'
JoshuaKGoldberg Dec 21, 2024
901af8d
Merge branch 'main'
JoshuaKGoldberg Dec 21, 2024
bfb8588
Factor directory . in tests
JoshuaKGoldberg Dec 21, 2024
9b72b67
Merge branch 'main'
JoshuaKGoldberg Dec 21, 2024
4cc4aba
Oops, no vitest in creation/index.ts
JoshuaKGoldberg Dec 21, 2024
d891999
Merge branch 'main' into create-cli
JoshuaKGoldberg Dec 21, 2024
1028831
fix create index.test.ts
JoshuaKGoldberg Dec 21, 2024
b12fb57
fix create index.test.ts fetchers again
JoshuaKGoldberg Dec 22, 2024
ea33115
Merge branch 'main'
JoshuaKGoldberg Dec 22, 2024
885787b
Remove --offline :(
JoshuaKGoldberg Dec 22, 2024
122c785
Update tests
JoshuaKGoldberg Dec 22, 2024
a27f12a
Update package.json snapshot
JoshuaKGoldberg Dec 22, 2024
5d3685e
Fixups for relativity
JoshuaKGoldberg Dec 22, 2024
93b70df
Reset directory changes, since chdir already happened
JoshuaKGoldberg Dec 23, 2024
7c68ea6
A bit of linting
JoshuaKGoldberg Dec 23, 2024
7b52362
A bit more reverting
JoshuaKGoldberg Dec 23, 2024
ca07d3b
Persistent chdir/cwd change maybe
JoshuaKGoldberg Dec 24, 2024
1dbe38e
More aggressively keep the updated cwd
JoshuaKGoldberg Dec 24, 2024
a01c09b
Fix up linting and testing
JoshuaKGoldberg Dec 24, 2024
0948e21
Merge branch 'main' into create-cli
JoshuaKGoldberg Dec 24, 2024
3941867
Merge branch 'main' into create-cli
JoshuaKGoldberg Dec 24, 2024
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
8 changes: 4 additions & 4 deletions docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ These required options determine the options that will be substituted into the t
- `--description` _(`string`)_: Sentence case description of the repository (e.g. `Quickstart-friendly TypeScript package with lots of great repository tooling. ✨`)
- `--owner` _(`string`)_: GitHub organization or user the repository is underneath (e.g. `JoshuaKGoldberg`)
- `--repository` _(`string`)_: The kebab-case name of the repository (e.g. `create-typescript-app`)
- `--title` _(`string`)_: Title Case title for the repository to be used in documentation (e.g. `Create TypeScript App`)
- `--title` _(`string`)_: Title Case title for the repository (e.g. `Create TypeScript App`)

For example, pre-populating all core required options and also creating a new repository:

Expand All @@ -53,16 +53,16 @@ The setup scripts also allow for optional overrides of the following inputs whos

- `--access` _(`"public" | "restricted"`)_: Which [`npm publish --access`](https://docs.npmjs.com/cli/commands/npm-publish#access) to release npm packages with (by default, `"public"`)
- `--author` _(`string`)_: Username on npm to publish packages under (by default, an existing npm author, or the currently logged in npm user, or `owner.toLowerCase()`)
- `--bin` _(`string`)_: value to set in `package.json`'s `"bin"` property, per [FAQs > How can I use `bin`?](./FAQs.md#how-can-i-use-bin)
- `--bin` _(`string`)_: Value to set in `package.json`'s `"bin"` property, per [FAQs > How can I use `bin`?](./FAQs.md#how-can-i-use-bin)
- `--directory` _(`string`)_: Directory to create the repository in (by default, the same name as the repository)
- `--email` _(`string`)_: Email address to be listed as the point of contact in docs and packages (e.g. `[email protected]`)
- Optionally, `--email-github` _(`string`)_ and/or `--email-npm` _(`string`)_ may be provided to use different emails in `.md` files and `package.json`, respectively
- `--funding` _(`string`)_: GitHub organization or username to mention in `funding.yml` (by default, `owner`)
- `--guide` _(`string`)_: Link to a contribution guide to place at the top of the development docs
- `--guide` _(`string`)_: Link to a contribution guide to place at the top of development docs
- `--guide-title` _(`string`)_: If `--guide` is provided or detected from an existing DEVELOPMENT.md, the text title to place in the guide link
- `--keywords` _(`string[]`)_: Any number of keywords to include in `package.json` (by default, none)
- This can be specified any number of times, like `--keywords apple --keywords "banana cherry"`
- `--logo` _(`string`)_: Local image file in the repository to display near the top of the README.md as a logo
- `--logo` _(`string`)_: Local image file in the repository to display near the top of the README.md
- `--logo-alt` _(`string`)_: If `--logo` is provided or detected from an existing README.md, alt text that describes the image (will be prompted for if not provided)
- `--preserve-generated-from` _(`boolean`)_: Whether to keep the GitHub repository _generated from_ notice (by default, `false`)

Expand Down
117 changes: 89 additions & 28 deletions src/next/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import gitUrlParse from "git-url-parse";
import { inputFromFile } from "input-from-file";
import { inputFromFileJSON } from "input-from-file-json";
import lazyValue from "lazy-value";
import path from "node:path";
import npmUser from "npm-user";
import { z } from "zod";

Expand All @@ -16,13 +17,25 @@ import { readGuide } from "../shared/options/createOptionDefaults/readGuide.js";
import { readPackageData } from "../shared/packages.js";
import { tryCatchLazyValueAsync } from "../shared/tryCatchLazyValueAsync.js";
import { AllContributorsData } from "../shared/types.js";
import { inputFromScript } from "./inputs/inputFromScript.js";
import { swallowError } from "./utils/swallowError.js";

// TODO: Add a concept of Block-specific Options...

export const base = createBase({
options: {
access: z.union([z.literal("public"), z.literal("restricted")]).optional(),
author: z.string().optional(),
bin: z.string().optional(),
access: z
.union([z.literal("public"), z.literal("restricted")])
.optional()
.describe("which `npm publish --access` to release npm packages with"),
author: z
.string()
.optional()
.describe("username on npm to publish packages under"),
bin: z
.string()
.optional()
.describe('value to set in `package.json`\'s `"bin"` property.'),
contributors: z
.array(
z.object({
Expand All @@ -33,9 +46,16 @@ export const base = createBase({
profile: z.string(),
}),
)
.optional(),
description: z.string(),
documentation: z.string().optional(),
.optional()
.describe("allcontributors contributors to store in .all-contributorsrc"),
description: z
.string()
.describe("sentence case description of the repository"),
directory: z.string(),
documentation: z
.string()
.optional()
.describe("any additional docs to add to .github/DEVELOPMENT.md"),
email: z
.union([
z.string(),
Expand All @@ -46,46 +66,77 @@ export const base = createBase({
])
.transform((email) =>
typeof email === "string" ? { github: email, npm: email } : email,
)
.describe(
"email address to be listed as the point of contact in docs and packages",
),
funding: z.string().optional(),
funding: z
.string()
.optional()
.describe("GitHub organization or username to mention in `funding.yml`"),
guide: z
.object({
href: z.string(),
title: z.string(),
})
.optional(),
hideTemplatedBy: z.boolean().optional(),
keywords: z.array(z.string()).optional(),
login: z.string().optional(),
.optional()
.describe(
"link to a contribution guide to place at the top of development docs",
),
hideTemplatedBy: z
.boolean()
.optional()
.describe(
"whether to hide the 'created by ...' notice at the bottom of the README.md",
),
keywords: z
.array(z.string())
.optional()
.describe("any number of keywords to include in `package.json`"),
logo: z
.object({
alt: z.string(),
src: z.string(),
})
.optional(),
.optional()
.describe(
"local image file in the repository to display near the top of the README.md",
),
node: z
.object({
minimum: z.string(),
pinned: z.string().optional(),
})
.optional(),
owner: z.string(),
.optional()
.describe("node.js engine version(s) to pin and require a minimum of"),
owner: z
.string()
.describe("GitHub organization or user the repository is underneath"),
packageData: z
.object({
dependencies: z.record(z.string(), z.string()).optional(),
devDependencies: z.record(z.string(), z.string()).optional(),
scripts: z.record(z.string(), z.string()).optional(),
})
.optional(),
preserveGeneratedFrom: z.boolean().optional(),
repository: z.string(),
title: z.string(),
version: z.string().optional(),
.optional()
.describe("additional properties to include in `package.json`"),
repository: z
.string()
.describe("sentence case description of the repository"),
title: z.string().describe("title case title for the repository"),
version: z
.string()
.optional()
.describe("package version to publish as and store in `package.json`"),
},
produce({ options, take }) {
// TODO: Make directory required at the create level
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const directory = options.directory!;

const allContributors = lazyValue(async () => {
const contributions = (await take(inputFromFileJSON, {
filePath: ".all-contributorsrc",
filePath: path.join(directory, ".all-contributorsrc"),
})) as AllContributorsData;

return contributions.contributors;
Expand All @@ -94,30 +145,38 @@ export const base = createBase({
const documentation = lazyValue(async () =>
swallowError(
await take(inputFromFile, {
filePath: ".github/DEVELOPMENT.md",
filePath: path.join(directory, ".github/DEVELOPMENT.md"),
}),
),
);

const nvmrc = lazyValue(
async () =>
await take(inputFromFile, {
filePath: ".nvmrc",
filePath: path.join(directory, ".nvmrc"),
}),
);

const githubCliUser = lazyValue(async () => {
return swallowError(
await take(inputFromScript, {
command: "gh config get user -h github.com",
}),
)?.stdout?.toString();
});

// TODO: Make these all use take

const gitDefaults = tryCatchLazyValueAsync(async () =>
gitUrlParse(await gitRemoteOriginUrl()),
gitUrlParse(await gitRemoteOriginUrl({ cwd: options.directory })),
);

const npmDefaults = tryCatchLazyValueAsync(async () => {
const whoami = (await execaCommand(`npm whoami`)).stdout;
return whoami ? await npmUser(whoami) : undefined;
});

const packageData = lazyValue(readPackageData);
const packageData = lazyValue(async () => readPackageData(directory));
const packageAuthor = lazyValue(async () =>
parsePackageAuthor(await packageData()),
);
Expand Down Expand Up @@ -149,12 +208,14 @@ export const base = createBase({
description: async () => (await packageData()).description,
documentation,
email: async () => readEmails(npmDefaults, packageAuthor),
funding: readFunding,
guide: readGuide,
funding: async () => readFunding(directory),
guide: async () => readGuide(directory),
login: author,
node,
owner: async () =>
(await gitDefaults())?.organization ?? (await packageAuthor()).author,
(await gitDefaults())?.organization ??
(await packageAuthor()).author ??
(await githubCliUser()),
packageData: async () => {
const original = await packageData();

Expand All @@ -168,7 +229,7 @@ export const base = createBase({
options.repository ??
(await gitDefaults())?.name ??
(await packageData()).name,
...readDefaultsFromReadme(),
...readDefaultsFromReadme(directory),
version,
};
},
Expand Down
12 changes: 7 additions & 5 deletions src/next/blocks/blockRepositoryBranchProtection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const blockRepositoryBranchProtection = base.createBlock({
id: "branch-protection",
async send({ octokit }) {
await octokit.request(
`PUT /repos/{owner}/{repository}/branches/{main}/protection`,
`PUT /repos/{owner}/{repo}/branches/{branch}/protection`,
{
allow_deletions: false,
allow_force_pushes: true,
Expand All @@ -31,10 +31,12 @@ export const blockRepositoryBranchProtection = base.createBlock({
required_linear_history: false,
required_pull_request_reviews: null,
required_status_checks: {
checks: addons.requiredStatusChecks.map((context) => ({
context,
})),
strict: false,
contexts: addons.requiredStatusChecks,
strict: true,
// checks: addons.requiredStatusChecks.map((context) => ({
// context,
// })),
// strict: false,
},
restrictions: null,
},
Expand Down
15 changes: 13 additions & 2 deletions src/next/blocks/blockRepositoryLabels.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { setGitHubRepositoryLabels } from "set-github-repository-labels";

import { outcomeLabels } from "../../steps/initializeGitHubRepository/outcomeLabels.js";
import { base } from "../base.js";

Expand All @@ -7,8 +9,17 @@ export const blockRepositoryLabels = base.createBlock({
},
produce({ options }) {
return {
scripts: [
`npx set-github-repository-labels --labels ${JSON.stringify(outcomeLabels)} --owner "${options.owner}" --repository "${options.repository}"`,
requests: [
{
id: "repository-labels",
async send() {
await setGitHubRepositoryLabels({
labels: outcomeLabels,
owner: options.owner,
repository: options.repository,
});
},
},
],
};
},
Expand Down
2 changes: 1 addition & 1 deletion src/next/blocks/blockRepositorySettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const blockRepositorySettings = base.createBlock({
allow_auto_merge: true,
allow_rebase_merge: false,
allow_squash_merge: true,
default_branch: "main",
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only part of the API that can't run on an empty repository at the moment. I don't mind removing it for now to simplify things. The new default branch in repositories is main now for a while anyway.

// default_branch: "main",
delete_branch_on_merge: true,
description: options.description,
has_wiki: false,
Expand Down
1 change: 1 addition & 0 deletions src/next/blocks/options.fakes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BaseOptions } from "../base.js";
export const optionsBase = {
access: "public",
description: "Test description",
directory: ".",
email: {
github: "[email protected]",
npm: "[email protected]",
Expand Down
15 changes: 15 additions & 0 deletions src/next/inputs/inputFromScript.ts
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: I'll split this out into its own first-party create package.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createInput } from "create";
import { z } from "zod";

export const inputFromScript = createInput({
args: {
command: z.string(),
},
async produce({ args, runner }) {
try {
return await runner(args.command);
} catch (error) {
return error instanceof Error ? error : new Error(error as string);
}
},
});
10 changes: 5 additions & 5 deletions src/next/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
name: "TypeScript App",
},
default: "common",
presets: [
{ label: "common", preset: presetCommon },
{ label: "everything", preset: presetEverything },
{ label: "minimal", preset: presetMinimal },
],
presets: {
common: { label: "Common", preset: presetCommon },

Check failure on line 13 in src/next/template.ts

View workflow job for this annotation

GitHub Actions / Are The Types Wrong?

Object literal may only specify known properties, and 'common' does not exist in type 'TemplatePresetListing<"common", ZodRawShape>[]'.

Check failure on line 13 in src/next/template.ts

View workflow job for this annotation

GitHub Actions / Build

Object literal may only specify known properties, and 'common' does not exist in type 'TemplatePresetListing<"common", ZodRawShape>[]'.

Check failure on line 13 in src/next/template.ts

View workflow job for this annotation

GitHub Actions / Lint

Object literal may only specify known properties, and 'common' does not exist in type 'TemplatePresetListing<"common", ZodRawShape>[]'.

Check failure on line 13 in src/next/template.ts

View workflow job for this annotation

GitHub Actions / Test Creation Script

Object literal may only specify known properties, and 'common' does not exist in type 'TemplatePresetListing<"common", ZodRawShape>[]'.

Check failure on line 13 in src/next/template.ts

View workflow job for this annotation

GitHub Actions / Test Initialization Script

Object literal may only specify known properties, and 'common' does not exist in type 'TemplatePresetListing<"common", ZodRawShape>[]'.

Check failure on line 13 in src/next/template.ts

View workflow job for this annotation

GitHub Actions / Test Migration Script

Object literal may only specify known properties, and 'common' does not exist in type 'TemplatePresetListing<"common", ZodRawShape>[]'.

Check failure on line 13 in src/next/template.ts

View workflow job for this annotation

GitHub Actions / Type Check

Object literal may only specify known properties, and 'common' does not exist in type 'TemplatePresetListing<"common", ZodRawShape>[]'.
everything: { label: "Everything", preset: presetEverything },
minimal: { label: "Minimal", preset: presetMinimal },
},
});

export default template;
6 changes: 3 additions & 3 deletions src/shared/options/createOptionDefaults/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function createOptionDefaults(promptedOptions?: PromptedOptions) {
return whoami ? await npmUser(whoami) : undefined;
});

const packageData = lazyValue(readPackageData);
const packageData = lazyValue(async () => readPackageData("."));
const packageAuthor = lazyValue(async () =>
parsePackageAuthor(await packageData()),
);
Expand All @@ -42,13 +42,13 @@ export function createOptionDefaults(promptedOptions?: PromptedOptions) {
.split(":")[1]
?.trim(),
),
guide: readGuide,
guide: async () => readGuide("."),
owner: async () =>
(await gitDefaults())?.organization ?? (await packageAuthor()).author,
repository: async () =>
promptedOptions?.repository ??
(await gitDefaults())?.name ??
(await packageData()).name,
...readDefaultsFromReadme(),
...readDefaultsFromReadme("."),
};
}
Loading
Loading