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    if (!this.server.worktree().exists(".gitroot/init.sh")) {
 71      this.server
 72        .worktree()
 73        .writeContent(".gitroot/init.sh", getInitShContent());
 74    }
 75    if (!this.server.worktree().exists(".gitroot/allowed_signers")) {
 76      this.server.worktree().writeContent(".gitroot/allowed_signers", "");
 77    }
 78    this.server.commitAllIfNeeded("init stigma plugin");
 79    return;
 80  }
 81
 82  startCommit(commit: Commit): void {
 83    if (commit.isSigned && commit.isValidSignature) {
 84      this.goodCommits.push(commit);
 85    } else if (commit.isSigned === false) {
 86      this.notSignedCommits.push(commit);
 87    } else if (commit.isValidSignature === false) {
 88      this.badCommits.push(commit);
 89    }
 90    return;
 91  }
 92
 93  addFile(file: File): void {
 94    return;
 95  }
 96
 97  modFile(file: File): void {
 98    return;
 99  }
100
101  delFile(file: File): void {
102    return;
103  }
104
105  endCommit(commit: Commit): void {
106    return;
107  }
108
109  finish(): void {
110    if (this.badCommits.length > 0) {
111      this.server.report(ReportLevel.Error, ["Commits are bad signed!"]);
112    }
113    if (this.notSignedCommits.length > 0) {
114      this.server.report(ReportLevel.Warning, ["Commits are not signed!"]);
115    }
116    if (this.goodCommits.length > 0) {
117      let content =
118        this.server.worktree().readAll(".gitroot/allowed_signers") || "";
119      for (let i = 0; i < this.goodCommits.length; i++) {
120        const c = this.goodCommits[i];
121        if (!content.includes(c.signingKey)) {
122          content = content + `${c.committerEmail} ${c.signingKey}\n`;
123        }
124      }
125      this.server.worktree().writeContent(".gitroot/allowed_signers", content);
126      this.server.commitAllIfNeeded("added signers");
127    }
128    this.conf = null;
129    this.goodCommits = [];
130    this.notSignedCommits = [];
131    this.badCommits = [];
132    return;
133  }
134}
135
136function build(server: Server): Plugin {
137  return new Stigma(server);
138}
139
140export function install(): void {
141  Register([defaultRun], build);
142}