Skip to content

Commit 3d36a0a

Browse files
committed
Merge branch 'add-the-action'
2 parents cfb828b + b8e8b52 commit 3d36a0a

File tree

9 files changed

+230
-175
lines changed

9 files changed

+230
-175
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/.env
33
/.test-dir/
44
/build/
5+
/dist/**/*.d.ts
56
/node_modules/
67
/npm-debug.log
78
/junit.xml
@@ -10,4 +11,3 @@
1011
/obj/**
1112
/*.sln*
1213
/*.njsproj*
13-
/*.tgz

eslint.config.mjs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import eslint from "@eslint/js";
2-
import stylistic from "@stylistic/eslint-plugin";
3-
import jsdoc from "eslint-plugin-jsdoc";
4-
import eslintConfigPrettier from "eslint-config-prettier";
5-
import eslintPluginPrettier from "eslint-plugin-prettier";
6-
import eslintPluginSecurity from "eslint-plugin-security";
7-
import globals from "globals";
8-
import tseslint from "typescript-eslint";
1+
import eslint from "@eslint/js"
2+
import stylistic from "@stylistic/eslint-plugin"
3+
import jsdoc from "eslint-plugin-jsdoc"
4+
import eslintConfigPrettier from "eslint-config-prettier"
5+
import eslintPluginPrettier from "eslint-plugin-prettier"
6+
import eslintPluginSecurity from "eslint-plugin-security"
7+
import globals from "globals"
8+
import tseslint from "typescript-eslint"
99

1010
export default tseslint.config(
1111
{
@@ -127,6 +127,18 @@ export default tseslint.config(
127127
"security/detect-object-injection": "off",
128128
},
129129
},
130+
{
131+
files: ["**/*.js", "**/*.mjs"],
132+
rules: {
133+
"@stylistic/semi": ["error", "never"],
134+
"prettier/prettier": [
135+
"warn",
136+
{
137+
semi: false,
138+
},
139+
],
140+
},
141+
},
130142
{
131143
files: ["tests/*.ts", "lib/*.ts", "script/*.ts"],
132144
extends: [...tseslint.configs.recommendedTypeChecked],
@@ -181,4 +193,4 @@ export default tseslint.config(
181193
},
182194
},
183195
// eslintPluginPrettierRecommended,
184-
);
196+
)

handle-pr-comment/action.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: 'Handle PR Comment'
2+
description: 'Handles slash commands such as /submit and /preview'
3+
author: 'Johannes Schindelin'
4+
inputs:
5+
gitgitgadget-git-access-token:
6+
description: 'The access token to work on gitgitgadget/git'
7+
required: true
8+
git-git-access-token:
9+
description: 'The access token to work on git/git'
10+
required: true
11+
dscho-git-access-token:
12+
description: 'The access token to work on dscho/git'
13+
required: true
14+
smtp-options:
15+
description: 'The SMTP options to use for sending emails'
16+
required: true
17+
pr-comment-url:
18+
description: 'The URL of the PR comment to add a reaction to'
19+
required: true
20+
runs:
21+
using: 'node20'
22+
main: './index.js'
23+
branding:
24+
icon: 'git-commit'
25+
color: 'orange'

handle-pr-comment/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
async function run() {
2+
const { CIHelper } = await import("../dist/index.js")
3+
4+
const ci = new CIHelper()
5+
const { owner, commentId } = ci.parsePRCommentURLInput()
6+
7+
await ci.setupGitHubAction()
8+
await ci.handleComment(owner, commentId)
9+
}
10+
11+
run()

lib/ci-helper.ts

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import * as core from "@actions/core";
12
import * as fs from "fs";
23
import * as util from "util";
34
import addressparser from "nodemailer/lib/addressparser/index.js";
5+
import path from "path";
46
import { ILintError, LintCommit } from "./commit-lint.js";
57
import { commitExists, git, emptyTreeName } from "./git.js";
68
import { GitNotes } from "./git-notes.js";
@@ -15,6 +17,7 @@ import { IPatchSeriesMetadata } from "./patch-series-metadata.js";
1517
import { IConfig, getExternalConfig, setConfig } from "./project-config.js";
1618
import { getPullRequestKeyFromURL, pullRequestKey } from "./pullRequestKey.js";
1719
import { ISMTPOptions } from "./send-mail.js";
20+
import { fileURLToPath } from "url";
1821

1922
const readFile = util.promisify(fs.readFile);
2023
type CommentFunction = (comment: string) => Promise<void>;
@@ -62,6 +65,84 @@ export class CIHelper {
6265
this.urlRepo = `${this.urlBase}${this.config.repo.name}/`;
6366
}
6467

68+
public async setupGitHubAction(): Promise<void> {
69+
// help dugite realize where `git` is...
70+
process.env.LOCAL_GIT_DIRECTORY = "/usr/";
71+
process.env.GIT_EXEC_PATH = "/usr/lib/git-core";
72+
73+
// get the access tokens via the inputs of the GitHub Action
74+
this.setAccessToken("gitgitgadget", core.getInput("gitgitgadget-git-access-token"));
75+
this.setAccessToken("git", core.getInput("git-git-access-token"));
76+
this.setAccessToken("dscho", core.getInput("dscho-git-access-token"));
77+
78+
// set the SMTP options
79+
try {
80+
const options = JSON.parse(core.getInput("smtp-options")) as {
81+
user?: string;
82+
host?: string;
83+
pass?: string;
84+
opts?: string;
85+
};
86+
if (typeof options === "object" && options.user && options.host && options.pass) {
87+
this.setSMTPOptions({
88+
smtpUser: options.user,
89+
smtpHost: options.host,
90+
smtpPass: options.pass,
91+
smtpOpts: options.opts,
92+
});
93+
}
94+
} catch (e) {
95+
// Ignore, for now
96+
}
97+
98+
// eslint-disable-next-line security/detect-non-literal-fs-filename
99+
if (!fs.existsSync(this.workDir)) await git(["init", "--bare", "--initial-branch", "main", this.workDir]);
100+
for (const [key, value] of [
101+
["remote.origin.url", `https://github.com/${this.config.repo.owner}/${this.config.repo.name}`],
102+
["remote.origin.promisor", "true"],
103+
["remote.origin.partialclonefilter", "blob:none"],
104+
]) {
105+
await git(["config", key, value], { workDir: this.workDir });
106+
}
107+
const notesRefs = [GitNotes.defaultNotesRef];
108+
// TODO Add `refs/notes/mail-to-commit` on demand
109+
await git(
110+
[
111+
"-c",
112+
"remote.origin.promisor=false", // let's fetch the notes with all of their blobs
113+
"fetch",
114+
"origin",
115+
"--depth=1",
116+
...notesRefs.map((ref) => `+${ref}:${ref}`),
117+
],
118+
{
119+
workDir: this.workDir,
120+
},
121+
);
122+
this.gggNotesUpdated = true;
123+
// TODO: determine on demand how many commits to fetch; This is contingent on the need
124+
// to fetch a PR branch (for handle-pr-push or handle-pr-comment), or whether the upstream branches
125+
// are needed (for update-commit-mappings and handle-open-prs).
126+
// await git(["fetch", "origin", "--depth=500", `${GitNotes.defaultNotesRef}:${GitNotes.defaultNotesRef}`], {
127+
// workDir: this.workDir,
128+
// });
129+
// "Un-shallow" the refs without fetching anything; Since this is a partial clone,
130+
// any missing commits will be fetched on demand (but when fetching a commit, you
131+
// get the entire commit history reachable from it, too, that's why we go through
132+
// the stunt of making a shallow-not-shallow fetch here).
133+
await fs.promises.rm(path.join(this.workDir, ".git", "shallow"), { force: true });
134+
}
135+
136+
public parsePRCommentURLInput(): { owner: string; repo: string; prNumber: number; commentId: number } {
137+
const prCommentUrl = core.getInput("pr-comment-url");
138+
const [, owner, repo, prNumber, commentId] =
139+
prCommentUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)#issuecomment-(\d+)/) || [];
140+
if (!this.config.repo.owners.includes(owner) || repo !== "git") {
141+
throw new Error(`Invalid PR comment URL: ${prCommentUrl}`);
142+
}
143+
return { owner, repo, prNumber: parseInt(prNumber, 10), commentId: parseInt(commentId, 10) };
144+
}
145+
65146
public setAccessToken(repositoryOwner: string, token: string): void {
66147
this.github.setAccessToken(repositoryOwner, token);
67148
if (this.config.repo.owner === repositoryOwner) {
@@ -73,6 +154,10 @@ export class CIHelper {
73154
this.smtpOptions = smtpOptions;
74155
}
75156

157+
public static getActionsCore(): typeof import("@actions/core") {
158+
return core;
159+
}
160+
76161
/*
77162
* Given a commit that was contributed as a patch via GitGitGadget (i.e.
78163
* a commit with a Message-ID recorded in `refs/notes/gitgitgadget`),
@@ -785,6 +870,11 @@ export class CIHelper {
785870
}
786871
}
787872

873+
public static async getWelcomeMessage(username: string): Promise<string> {
874+
const resPath = path.resolve(fileURLToPath(new URL(".", import.meta.url)), "..", "res", "WELCOME.md");
875+
return (await readFile(resPath)).toString().replace(/\${username}/g, username);
876+
}
877+
788878
public async handlePush(repositoryOwner: string, prNumber: number): Promise<void> {
789879
const prKey = {
790880
owner: repositoryOwner,
@@ -808,7 +898,7 @@ export class CIHelper {
808898
this.smtpOptions,
809899
);
810900
if (!pr.hasComments && !gitGitGadget.isUserAllowed(pr.author)) {
811-
const welcome = (await readFile("res/WELCOME.md")).toString().replace(/\${username}/g, pr.author);
901+
const welcome = await CIHelper.getWelcomeMessage(pr.author);
812902
await this.github.addPRComment(prKey, welcome);
813903

814904
await this.github.addPRLabels(prKey, ["new user"]);

lib/misc-action.ts

Lines changed: 0 additions & 84 deletions
This file was deleted.

lib/pull-action.ts

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)