Skip to content

Commit 201613e

Browse files
authored
chore(suggestion-bot): move package into rnx-kit (#4064)
1 parent 3d6c4aa commit 201613e

21 files changed

Lines changed: 2263 additions & 12 deletions

.changeset/clean-towns-leave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"suggestion-bot": patch
3+
---
4+
5+
Publish from rnx-kit

docsite/generate.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
const fs = require("node:fs");
33
const path = require("node:path");
44

5+
const UTF_8 = /** @type {const} */ ({ encoding: "utf-8" });
6+
57
const badges = [
68
"https://github\\.com/microsoft/rnx-kit/actions/workflows",
79
"https://img\\.shields\\.io",
@@ -12,6 +14,7 @@ const badgesRE = new RegExp(
1214
"g"
1315
);
1416

17+
const localImagesRE = /!\[(.*?)\]\(\.\/(.*?)\)/g;
1518
const titleRE = /# @rnx-kit\/(.*)/;
1619

1720
function copyContributing() {
@@ -33,19 +36,22 @@ function generateToolsSidebar() {
3336
continue;
3437
}
3538

36-
const content = fs.readFileSync(manifest, { encoding: "utf-8" });
37-
if (JSON.parse(content).private) {
39+
if (JSON.parse(fs.readFileSync(manifest, UTF_8)).private) {
3840
continue;
3941
}
4042

4143
const output = path.join("docs", "tools", `${pkg}.md`);
4244
if (!fs.existsSync(output)) {
43-
const content = fs.readFileSync(readme, { encoding: "utf-8" });
45+
const content = fs.readFileSync(readme, UTF_8);
4446
fs.writeFileSync(
4547
output,
4648
content
4749
.replace(titleRE, "# $1") // Remove scope from title
4850
.replace(badgesRE, "") // Remove badges
51+
.replace(
52+
localImagesRE,
53+
`![$1](${path.join("..", "..", path.dirname(readme), "$2")})`
54+
)
4955
);
5056
}
5157

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"nx": "patch:nx@npm%3A22.5.4#~/.yarn/patches/nx-npm-22.5.4-3d43ac9c10.patch",
5252
"oxfmt": "catalog:",
5353
"oxlint": "catalog:",
54-
"suggestion-bot": "^4.0.0",
54+
"suggestion-bot": "workspace:*",
5555
"typescript": "catalog:"
5656
},
5757
"resolutions": {

packages/suggestion-bot/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.d.ts
2+
*.d.ts.map

packages/suggestion-bot/README.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# suggestion-bot
2+
3+
[![Build](https://github.com/microsoft/rnx-kit/actions/workflows/build.yml/badge.svg)](https://github.com/microsoft/rnx-kit/actions/workflows/build.yml)
4+
[![npm version](https://img.shields.io/npm/v/suggestion-bot)](https://www.npmjs.com/package/suggestion-bot)
5+
6+
`suggestion-bot` submits code reviews with suggestions based on your diffs.
7+
8+
![screenshot of code review with suggestions](./docs/screenshot.png)
9+
10+
## Usage
11+
12+
```
13+
Usage: cli.js [options] [diff | file]
14+
15+
Submit code reviews with suggestions based on your diffs
16+
17+
Arguments:
18+
diff | file the diff or file containing diff to create suggestions from
19+
20+
Options:
21+
-h, --help display this help message
22+
-v, --version display version number
23+
-m, --message <msg> use the specified message as the PR comment
24+
-f, --fail fail if comments could not be posted
25+
26+
Examples:
27+
# Submit current changes as suggestions
28+
GITHUB_TOKEN=<secret> suggestion-bot "$(git diff)"
29+
30+
# Alternatively, pipe to suggestion-bot
31+
# to avoid escape character issues
32+
git diff | GITHUB_TOKEN=<secret> suggestion-bot
33+
```
34+
35+
If your CI is hosted by Azure DevOps, replace `GITHUB_TOKEN` with
36+
`AZURE_PERSONAL_ACCESS_TOKEN`.
37+
38+
## Requirements
39+
40+
- Host your code on [GitHub](https://github.com/)
41+
- `GITHUB_TOKEN` permissions:
42+
- `pull-requests: write` — required for creating code reviews
43+
- `issues: write` — fallback in case creating a review fails
44+
45+
-- or --
46+
47+
- Host your code on [Azure DevOps](https://dev.azure.com/)
48+
- An Azure DevOps [personal access token][ado-personal-access-token]
49+
50+
## Recipes
51+
52+
- [Using `suggestion-bot` with GitHub Actions](#using-suggestion-bot-with-github-actions)
53+
- [Using `suggestion-bot` with `clang-format`](#using-suggestion-bot-with-clang-format)
54+
- [Using `suggestion-bot` with Prettier](#using-suggestion-bot-with-prettier)
55+
56+
### Using `suggestion-bot` with GitHub Actions
57+
58+
1. Install `suggestion-bot` in your project
59+
60+
```sh
61+
yarn add suggestion-bot --dev
62+
```
63+
64+
2. Configure your GitHub workflow so `suggestion-bot` can access the
65+
`GITHUB_TOKEN` secret provided by GitHub via an environment variable with the
66+
same name:
67+
68+
```yaml
69+
# .github/workflows/build.yml
70+
jobs:
71+
lint:
72+
runs-on: ubuntu-latest
73+
steps:
74+
- name: Set up Node.js
75+
uses: actions/setup-node@v6
76+
with:
77+
node-version: 24
78+
- name: Checkout
79+
uses: actions/checkout@v6
80+
- name: Install
81+
run: yarn
82+
- name: ClangFormat
83+
if: ${{ github.event_name == 'pull_request' }}
84+
env:
85+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
86+
run: scripts/clang-format-diff.sh | yarn suggestion-bot
87+
```
88+
89+
### Using `suggestion-bot` with `clang-format`
90+
91+
Use [`clang-format-diff`][] to format only changed files:
92+
93+
```sh
94+
curl --silent --show-error --remote-name https://raw.githubusercontent.com/llvm/llvm-project/release/10.x/clang/tools/clang-format/clang-format-diff.py
95+
git diff --unified=0 --no-color @^ \
96+
| python clang-format-diff.py -p1 -regex '.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|m|mm|inc)' -sort-includes \
97+
| yarn suggestion-bot
98+
```
99+
100+
### Using `suggestion-bot` with Prettier
101+
102+
We must first write a script that pipes [Prettier][]'s output to `diff` so we
103+
can feed it to `suggestion-bot` later.
104+
105+
```js
106+
#!/usr/bin/env node
107+
108+
import { spawnSync } from "node:child_process";
109+
import * as fs from "node:fs";
110+
import * as prettier from "prettier";
111+
import suggest from "suggestion-bot";
112+
113+
const diff = process.argv.slice(2).reduce((diff, filepath) => {
114+
const source = fs.readFileSync(filepath, { encoding: "utf8" });
115+
const { stdout } = spawnSync("diff", ["--unified", filepath, "-"], {
116+
input: prettier.format(source, { filepath }),
117+
encoding: "utf-8",
118+
});
119+
return diff + stdout;
120+
}, "");
121+
122+
suggest(diff);
123+
```
124+
125+
Save the script somewhere, e.g. `scripts/prettier-diff.mjs`, then invoke it with
126+
Node:
127+
128+
```sh
129+
node scripts/prettier-diff.mjs $(git ls-files '*.js')
130+
```
131+
132+
<!-- References -->
133+
134+
[Prettier]: https://prettier.io/
135+
[`clang-format-diff`]: https://clang.llvm.org/docs/ClangFormat.html#script-for-patch-reformatting
136+
[ado-personal-access-token]: https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page

packages/suggestion-bot/cli.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env node
2+
3+
import * as fs from "node:fs";
4+
import * as path from "node:path";
5+
import { fileURLToPath, URL } from "node:url";
6+
import { parseArgs } from "node:util";
7+
import suggest from "./src/index.js";
8+
9+
function printHelp() {
10+
console.log(
11+
[
12+
`Usage: ${path.basename(process.argv[1])} [options] [diff | file]`,
13+
"",
14+
"Submit code reviews with suggestions based on your diffs",
15+
"",
16+
"Arguments:",
17+
" diff | file the diff or file containing diff to create suggestions from",
18+
"",
19+
"Options:",
20+
" -h, --help display this help message",
21+
" -v, --version display version number",
22+
" -m, --message <msg> use the specified message as the PR comment",
23+
" -f, --fail fail if comments could not be posted",
24+
"",
25+
"Examples:",
26+
" # Submit current changes as suggestions",
27+
' GITHUB_TOKEN=<secret> suggestion-bot "$(git diff)"',
28+
"",
29+
" # Alternatively, pipe to suggestion-bot",
30+
" # to avoid escape character issues",
31+
" git diff | GITHUB_TOKEN=<secret> suggestion-bot",
32+
"",
33+
"If your CI is hosted by Azure DevOps, replace `GITHUB_TOKEN` with `AZURE_PERSONAL_ACCESS_TOKEN`.",
34+
].join("\n")
35+
);
36+
}
37+
38+
const { values, positionals } = parseArgs({
39+
args: process.argv.slice(2),
40+
options: {
41+
help: {
42+
type: "boolean",
43+
short: "h",
44+
},
45+
version: {
46+
type: "boolean",
47+
short: "v",
48+
},
49+
message: {
50+
type: "string",
51+
short: "m",
52+
},
53+
fail: {
54+
type: "boolean",
55+
short: "f",
56+
},
57+
},
58+
allowPositionals: true,
59+
});
60+
61+
if (values.help) {
62+
printHelp();
63+
} else if (values.version) {
64+
const p = fileURLToPath(new URL("package.json", import.meta.url));
65+
const manifest = fs.readFileSync(p, { encoding: "utf-8" });
66+
const { name, version } = JSON.parse(manifest);
67+
console.log(name, version);
68+
} else {
69+
if (positionals.length > 0) {
70+
const diffOrFile = positionals[0];
71+
const diff = fs.existsSync(diffOrFile)
72+
? fs.readFileSync(diffOrFile, { encoding: "utf-8" })
73+
: diffOrFile;
74+
suggest(diff, values);
75+
} else if (!process.stdin.isTTY) {
76+
let data = "";
77+
const stdin = process.openStdin();
78+
stdin.setEncoding("utf8");
79+
stdin.on("data", (chunk) => (data += chunk));
80+
stdin.on("end", () => suggest(data, values));
81+
} else {
82+
process.exitCode = 1;
83+
printHelp();
84+
}
85+
}
63.2 KB
Loading
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "suggestion-bot",
3+
"version": "4.0.3",
4+
"description": "suggestion-bot submits code reviews with suggestions based on your diffs",
5+
"keywords": [
6+
"code review",
7+
"diff",
8+
"github",
9+
"pull request",
10+
"review",
11+
"suggestion"
12+
],
13+
"homepage": "https://github.com/microsoft/rnx-kit/tree/main/packages/suggestion-bot#readme",
14+
"license": "MIT",
15+
"author": {
16+
"name": "Microsoft Open Source",
17+
"email": "microsoftopensource@users.noreply.github.com"
18+
},
19+
"repository": {
20+
"type": "git",
21+
"url": "https://github.com/microsoft/rnx-kit",
22+
"directory": "packages/suggestion-bot"
23+
},
24+
"bin": "cli.js",
25+
"files": [
26+
"cli.js",
27+
"src"
28+
],
29+
"type": "module",
30+
"main": "src/index.js",
31+
"exports": {
32+
".": {
33+
"default": "./src/index.js"
34+
},
35+
"./cli.js": {
36+
"default": "./cli.js"
37+
},
38+
"./package.json": "./package.json"
39+
},
40+
"scripts": {
41+
"//build": "We run `tsgo` directly because we don't want to output to `lib`",
42+
"build": "yarn clean && tsgo",
43+
"clean": "git clean -dfqx -- src/ test/",
44+
"format": "rnx-kit-scripts format",
45+
"lint": "rnx-kit-scripts lint",
46+
"suggest": "suggestion-bot",
47+
"test": "rnx-kit-scripts test"
48+
},
49+
"dependencies": {
50+
"@octokit/core": "^7.0.0",
51+
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
52+
"azure-devops-node-api": "^15.0.0",
53+
"parse-diff": "^0.11.0"
54+
},
55+
"devDependencies": {
56+
"@rnx-kit/scripts": "*",
57+
"@rnx-kit/tsconfig": "*",
58+
"@types/node": "^24.0.0"
59+
},
60+
"engines": {
61+
"node": ">=20.9"
62+
}
63+
}

0 commit comments

Comments
 (0)