Repository navigation
fix(github): read text pull request templates #21449
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,10 +11,16 @@ const TRUNCATION_MARKER = "[truncated]"; | |
| const TEMPLATE_PATHS = [ | ||
| ".github/pull_request_template.md", | ||
| ".github/PULL_REQUEST_TEMPLATE.md", | ||
| ".github/pull_request_template.txt", | ||
| ".github/PULL_REQUEST_TEMPLATE.txt", | ||
|
Comment on lines
+14
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a repository commits Useful? React with 👍 / 👎. |
||
| "pull_request_template.md", | ||
| "PULL_REQUEST_TEMPLATE.md", | ||
| "pull_request_template.txt", | ||
| "PULL_REQUEST_TEMPLATE.txt", | ||
| "docs/pull_request_template.md", | ||
| "docs/PULL_REQUEST_TEMPLATE.md", | ||
| "docs/pull_request_template.txt", | ||
| "docs/PULL_REQUEST_TEMPLATE.txt", | ||
| ] as const; | ||
|
|
||
| const TEMPLATE_DIRECTORIES = [ | ||
|
|
@@ -23,7 +29,7 @@ const TEMPLATE_DIRECTORIES = [ | |
| "docs/PULL_REQUEST_TEMPLATE", | ||
| ] as const; | ||
|
|
||
| const TREE_PATHS = [...TEMPLATE_PATHS, ...TEMPLATE_DIRECTORIES] as const; | ||
| const TEMPLATE_LOCATIONS = [".github/", "", "docs/"] as const; | ||
|
|
||
| type ExecuteGit = SourceControlHost.SourceControlHost["Service"]["git"]["execute"]; | ||
|
|
||
|
|
@@ -105,7 +111,7 @@ function readTemplateDirectory(input: { | |
| return false; | ||
| } | ||
| const relativePath = entry.path.slice(prefix.length); | ||
| return !relativePath.includes("/") && relativePath.toLowerCase().endsWith(".md"); | ||
| return !relativePath.includes("/") && /\.(?:md|txt)$/i.test(relativePath); | ||
| }); | ||
|
|
||
| const templates: string[] = []; | ||
|
|
@@ -133,31 +139,51 @@ export const detect = Effect.fn("gitHubChangeRequestTemplate.detect")(function* | |
| return yield* Effect.gen(function* () { | ||
| // Worktree paths can be replaced between validation and open. Read regular blobs from the | ||
| // committed base tree so repository-controlled symlinks and path races never reach the host filesystem. | ||
| const result = yield* executeGit({ | ||
| operation: "gitHubChangeRequestTemplate.listTemplates", | ||
| cwd, | ||
| args: ["ls-tree", "-r", "-z", "--full-tree", treeish, "--", ...TREE_PATHS], | ||
| maxOutputBytes: TREE_LIST_MAX_BYTES, | ||
| appendTruncationMarker: true, | ||
| }); | ||
| if (result.stdoutTruncated) { | ||
| return Option.none(); | ||
| } | ||
| const listTemplates = (paths: ReadonlyArray<string>) => | ||
| executeGit({ | ||
| operation: "gitHubChangeRequestTemplate.listTemplates", | ||
| cwd, | ||
| args: ["ls-tree", "-z", "--full-tree", treeish, "--", ...paths], | ||
| maxOutputBytes: TREE_LIST_MAX_BYTES, | ||
| appendTruncationMarker: true, | ||
| }); | ||
|
|
||
| const entries = parseTemplateTreeEntries(result.stdout); | ||
| const entriesByPath = new Map(entries.map((entry) => [entry.path, entry])); | ||
| for (const templatePath of TEMPLATE_PATHS) { | ||
| const entry = entriesByPath.get(templatePath); | ||
| if (!entry) { | ||
| continue; | ||
| // A lower-priority directory must not exhaust an earlier template's listing budget. | ||
| for (const location of TEMPLATE_LOCATIONS) { | ||
| const templatePaths = TEMPLATE_PATHS.filter( | ||
| (path) => path.slice(0, path.lastIndexOf("/") + 1) === location, | ||
| ); | ||
| let result = yield* listTemplates([location || "."]); | ||
| if (result.stdoutTruncated) { | ||
| result = yield* listTemplates(templatePaths); | ||
| } | ||
| const template = yield* readTemplateBlob({ cwd, executeGit, entry }); | ||
| if (Option.isSome(template)) { | ||
| return template; | ||
| if (result.stdoutTruncated) { | ||
| return Option.none(); | ||
| } | ||
|
|
||
| const entries = parseTemplateTreeEntries(result.stdout); | ||
| for (const templatePath of templatePaths) { | ||
| const candidates = entries | ||
| .filter((entry) => entry.path.toLowerCase() === templatePath.toLowerCase()) | ||
| .toSorted( | ||
| (left, right) => | ||
| Number(right.path === templatePath) - Number(left.path === templatePath), | ||
| ); | ||
| for (const entry of candidates) { | ||
| const template = yield* readTemplateBlob({ cwd, executeGit, entry }); | ||
| if (Option.isSome(template)) { | ||
| return template; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for (const directory of TEMPLATE_DIRECTORIES) { | ||
| const result = yield* listTemplates([`${directory}/`]); | ||
| if (result.stdoutTruncated) { | ||
| return Option.none(); | ||
| } | ||
| const entries = parseTemplateTreeEntries(result.stdout); | ||
| const directoryTemplate = yield* readTemplateDirectory({ | ||
| cwd, | ||
| executeGit, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.