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