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