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 Plugin,
9 PluginRun,
10 pluginRunWhenAll,
11 PluginWrite,
12 PluginWriteRight,
13 PluginWriteRightCanAdd,
14 PluginWriteRightCanMod,
15 Register,
16 Server,
17} from "plugin/server";
18
19export * from "plugin/exports";
20
21@json
22class FluxConf {
23 constructor(
24 public link: string,
25 public path: string,
26 public title: string,
27 public description: string,
28 public language: string
29 ) {}
30
31 static build(
32 link: string,
33 path: string,
34 title: string,
35 description: string,
36 language: string
37 ): FluxConf {
38 return new FluxConf(link, path, title, description, language);
39 }
40
41 generateEmptyRss(): string {
42 return `<rss version="2.0">
43 <channel>
44 <title>${this.title}</title>
45 <link>${this.link}</link>
46 <description>${this.description}</description>
47 <language>${this.language}</language>
48 <generator>Pollen plugin - GitRoot</generator>
49 <!-- next -->
50 </channel>
51</rss>`;
52 }
53}
54
55@json
56class Conf {
57 flux: Array<FluxConf> = [
58 FluxConf.build(
59 "https://YOURINSTANCE.com/YOURPROJECT/",
60 "rss/all.xml",
61 "All push",
62 "Every push made in your forge",
63 "en"
64 ),
65 ];
66
67 static default(): JSON.Raw {
68 return JSON.Raw.from(JSON.stringify<Conf>(new Conf()));
69 }
70}
71
72const defaultRun: PluginRun = PluginRun.build(
73 "*",
74 ["*"],
75 pluginRunWhenAll,
76 PluginWrite.build(
77 [],
78 [
79 PluginWriteRight.build("rss/*", [
80 PluginWriteRightCanAdd,
81 PluginWriteRightCanMod,
82 ]),
83 ]
84 ),
85 Conf.default()
86);
87
88function generateItem(conf: FluxConf, commits: Array<Commit>): string {
89 const before = `<item><author>${commits[0].committer}</author><link>${conf.link}</link><title>Has pushed ${commits.length} commits on ${commits[0].branch}</title><description>`;
90 const beforeDesc = `<![CDATA[<p>All commits:</p><ul>`;
91 const afterDesc = `</ul>]]>`;
92 const after = `</description><pubDate>${commits[0].date.toUTCString()}</pubDate><guid>${
93 commits[0].hash
94 }</guid></item>`;
95 const html =
96 beforeDesc +
97 commits
98 .sort((a, b) => a.date.getUTCMilliseconds() - b.date.getUTCMilliseconds())
99 .reduce((acc, item) => {
100 return `${acc}<li>${item.message.replaceAll("\\n", "<br/>")}</li>`;
101 }, "") +
102 afterDesc;
103 return before + html + after;
104}
105
106class Pollen implements Plugin {
107 private server: Server;
108 private conf: Conf | null = null;
109 private commits: Array<Commit> = new Array<Commit>();
110
111 constructor(server: Server) {
112 this.server = server;
113 }
114
115 init(
116 repoName: string,
117 confHasChanged: boolean,
118 serializedConf: string
119 ): void {
120 this.conf = JSON.parse<Conf>(serializedConf);
121
122 for (let i = 0; i < this.conf!.flux.length; ++i) {
123 const flux = this.conf!.flux[i];
124 if (!this.server.webcontent().exists(flux.path)) {
125 this.server.modifyWebContent(flux.path, flux.generateEmptyRss());
126 }
127 }
128 return;
129 }
130
131 startCommit(commit: Commit): void {
132 this.commits.push(commit);
133 return;
134 }
135
136 addFile(path: string): void {
137 return;
138 }
139
140 modFile(fromPath: string, toPath: string): void {
141 return;
142 }
143
144 delFile(path: string): void {
145 return;
146 }
147
148 endCommit(commit: Commit): void {
149 return;
150 }
151
152 finish(): void {
153 if (this.conf != null && this.commits.length > 0) {
154 for (let i = 0; i < this.conf!.flux.length; i++) {
155 this.server.replaceWebContent(
156 this.conf!.flux[i].path,
157 "<!-- next -->",
158 "<!-- next -->\n" + generateItem(this.conf!.flux[i], this.commits)
159 );
160 }
161 }
162 this.conf = null;
163 this.commits = [];
164 return;
165 }
166}
167
168function build(server: Server): Plugin {
169 return new Pollen(server);
170}
171
172export function install(): void {
173 Register([defaultRun], build);
174}