GitRoot
craft your forge, build your project, grow your community freely
1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5import { JSON } from "json-as";
6import {
7 Commit,
8 File,
9 Plugin,
10 PluginRun,
11 pluginRunWhenAll,
12 PluginWrite,
13 PluginWriteRight,
14 pluginWriteRightCanAll,
15 Register,
16 ReportLevelError,
17 ReportLevelWarning,
18 Server,
19} from "plugin/server";
20import { getInitShContent } from "./embedded-data";
21
22export * from "plugin/exports";
23
24@json
25class Conf {
26 static default(): JSON.Raw {
27 return JSON.Raw.from(JSON.stringify<Conf>(new Conf()));
28 }
29}
30
31const defaultRun: PluginRun = PluginRun.build(
32 "**/*",
33 ["*"],
34 pluginRunWhenAll,
35 [],
36 PluginWrite.build(
37 [
38 PluginWriteRight.build(".gitroot/init.sh", pluginWriteRightCanAll),
39 PluginWriteRight.build(
40 ".gitroot/allowed_signers",
41 pluginWriteRightCanAll,
42 ),
43 ],
44 [],
45 [],
46 [],
47 ),
48 Conf.default(),
49);
50
51class Stigma implements Plugin {
52 private server: Server;
53 private conf: Conf | null = null;
54 private goodCommits: Array<Commit> = new Array<Commit>();
55 private notSignedCommits: Array<Commit> = new Array<Commit>();
56 private badCommits: Array<Commit> = new Array<Commit>();
57
58 constructor(server: Server) {
59 this.server = server;
60 }
61
62 init(
63 repoName: string,
64 confHasChanged: boolean,
65 serializedConf: string,
66 ): void {
67 this.conf = JSON.parse<Conf>(serializedConf);
68 if (!this.server.worktree().exists(".gitroot/init.sh")) {
69 this.server.modifyContent(".gitroot/init.sh", getInitShContent());
70 }
71 if (!this.server.worktree().exists(".gitroot/allowed_signers")) {
72 this.server.modifyContent(".gitroot/allowed_signers", "");
73 }
74 this.server.commitAllIfNeeded("init stigma plugin");
75 return;
76 }
77
78 startCommit(commit: Commit): void {
79 if (commit.isSigned && commit.isValidSignature) {
80 this.goodCommits.push(commit);
81 } else if (commit.isSigned === false) {
82 this.notSignedCommits.push(commit);
83 } else if (commit.isValidSignature === false) {
84 this.badCommits.push(commit);
85 }
86 return;
87 }
88
89 addFile(file: File): void {
90 return;
91 }
92
93 modFile(file: File): void {
94 return;
95 }
96
97 delFile(file: File): void {
98 return;
99 }
100
101 endCommit(commit: Commit): void {
102 return;
103 }
104
105 finish(): void {
106 if (this.badCommits.length > 0) {
107 this.server.report(ReportLevelError, ["Commits are bad signed!"]);
108 }
109 if (this.notSignedCommits.length > 0) {
110 this.server.report(ReportLevelWarning, ["Commits are not signed!"]);
111 }
112 if (this.goodCommits.length > 0) {
113 let content =
114 this.server.worktree().readAll(".gitroot/allowed_signers") || "";
115 for (let i = 0; i < this.goodCommits.length; i++) {
116 const c = this.goodCommits[i];
117 if (!content.includes(c.signingKey)) {
118 content = content + `${c.committerEmail} ${c.signingKey}\n`;
119 }
120 }
121 this.server.worktree().writeContent(".gitroot/allowed_signers", content);
122 this.server.commitAllIfNeeded("added signers");
123 }
124 this.conf = null;
125 this.goodCommits = [];
126 this.notSignedCommits = [];
127 this.badCommits = [];
128 return;
129 }
130}
131
132function build(server: Server): Plugin {
133 return new Stigma(server);
134}
135
136export function install(): void {
137 Register([defaultRun], build);
138}