1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2pub struct ToolchainPackageSpec {
3 pub crate_name: &'static str,
4 pub bins: &'static [&'static str],
5}
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct OciPackageSpec {
9 pub package: &'static str,
10}
11
12pub const GREENTIC_TOOLCHAIN_PACKAGES: &[ToolchainPackageSpec] = &[
13 ToolchainPackageSpec {
14 crate_name: "greentic-dev",
15 bins: &["greentic-dev"],
16 },
17 ToolchainPackageSpec {
18 crate_name: "greentic-operator",
19 bins: &["greentic-operator"],
20 },
21 ToolchainPackageSpec {
22 crate_name: "greentic-bundle",
23 bins: &["greentic-bundle"],
24 },
25 ToolchainPackageSpec {
26 crate_name: "greentic-setup",
27 bins: &["greentic-setup"],
28 },
29 ToolchainPackageSpec {
30 crate_name: "greentic-start",
31 bins: &["greentic-start"],
32 },
33 ToolchainPackageSpec {
34 crate_name: "greentic-deployer",
35 bins: &["greentic-deployer"],
36 },
37 ToolchainPackageSpec {
38 crate_name: "greentic-component",
39 bins: &["greentic-component"],
40 },
41 ToolchainPackageSpec {
42 crate_name: "greentic-flow",
43 bins: &["greentic-flow"],
44 },
45 ToolchainPackageSpec {
46 crate_name: "greentic-pack",
47 bins: &["greentic-pack"],
48 },
49 ToolchainPackageSpec {
50 crate_name: "greentic-runner",
51 bins: &["greentic-runner"],
52 },
53 ToolchainPackageSpec {
54 crate_name: "greentic-gui",
55 bins: &["greentic-gui"],
56 },
57 ToolchainPackageSpec {
58 crate_name: "greentic-secrets",
59 bins: &["greentic-secrets"],
60 },
61 ToolchainPackageSpec {
62 crate_name: "greentic-mcp",
63 bins: &["greentic-mcp"],
64 },
65];
66
67pub const GREENTIC_EXTERNAL_TOOL_PACKAGES: &[ToolchainPackageSpec] = &[ToolchainPackageSpec {
71 crate_name: "greentic-mcp-generator",
72 bins: &["greentic-mcp-gen"],
73}];
74
75pub const GREENTIC_EXTENSION_PACK_PACKAGES: &[OciPackageSpec] = &[
76 OciPackageSpec {
77 package: "packs/demos/cards-demo",
78 },
79 OciPackageSpec {
80 package: "packs/demos/cloud-deploy-demo-app",
81 },
82 OciPackageSpec {
83 package: "packs/demos/deep-research-demo",
84 },
85 OciPackageSpec {
86 package: "packs/demos/helpdesk-itsm",
87 },
88 OciPackageSpec {
89 package: "packs/demos/hr-onboarding",
90 },
91 OciPackageSpec {
92 package: "packs/demos/incident-demo",
93 },
94 OciPackageSpec {
95 package: "packs/demos/quickstart",
96 },
97 OciPackageSpec {
98 package: "packs/demos/quickstart-event",
99 },
100 OciPackageSpec {
101 package: "packs/demos/redbutton-demo",
102 },
103 OciPackageSpec {
104 package: "packs/demos/sales-crm",
105 },
106 OciPackageSpec {
107 package: "packs/demos/supply-chain",
108 },
109 OciPackageSpec {
110 package: "packs/demos/telco-x",
111 },
112 OciPackageSpec {
113 package: "packs/demos/weather-mcp-demo",
114 },
115 OciPackageSpec {
116 package: "packs/demos/weatherapi-pack",
117 },
118 OciPackageSpec {
119 package: "packs/deployer/greentic.deploy.gcp",
120 },
121 OciPackageSpec {
122 package: "packs/deployer/greentic.deploy.azure",
123 },
124 OciPackageSpec {
125 package: "packs/deployer/greentic.deploy.aws",
126 },
127 OciPackageSpec {
128 package: "packs/deployer/greentic.deploy.helm",
129 },
130 OciPackageSpec {
131 package: "packs/deployer/greentic.deploy.juju-k8s",
132 },
133 OciPackageSpec {
134 package: "packs/deployer/greentic.deploy.juju-machine",
135 },
136 OciPackageSpec {
137 package: "packs/deployer/greentic.deploy.k8s-raw",
138 },
139 OciPackageSpec {
140 package: "packs/deployer/greentic.deploy.serverless",
141 },
142 OciPackageSpec {
143 package: "packs/deployer/greentic.deploy.snap",
144 },
145 OciPackageSpec {
146 package: "packs/deployer/greentic.deploy.terraform",
147 },
148 OciPackageSpec {
149 package: "packs/dw/context/compressor-pack",
150 },
151 OciPackageSpec {
152 package: "packs/dw/context/retrieval-pack",
153 },
154 OciPackageSpec {
155 package: "packs/dw/context/static-pack",
156 },
157 OciPackageSpec {
158 package: "packs/dw/control/basic-policy-pack",
159 },
160 OciPackageSpec {
161 package: "packs/dw/control/delegation-guard-pack",
162 },
163 OciPackageSpec {
164 package: "packs/dw/delegation/capability-match-pack",
165 },
166 OciPackageSpec {
167 package: "packs/dw/delegation/static-router-pack",
168 },
169 OciPackageSpec {
170 package: "packs/dw/engine/default-pack",
171 },
172 OciPackageSpec {
173 package: "packs/dw/engine/router-lite-pack",
174 },
175 OciPackageSpec {
176 package: "packs/dw/memory/short-term-in-memory-pack",
177 },
178 OciPackageSpec {
179 package: "packs/dw/memory/short-term-redis-pack",
180 },
181 OciPackageSpec {
182 package: "packs/dw/observer/basic-audit-pack",
183 },
184 OciPackageSpec {
185 package: "packs/dw/observer/basic-metrics-pack",
186 },
187 OciPackageSpec {
188 package: "packs/dw/planning/llm-outline-pack",
189 },
190 OciPackageSpec {
191 package: "packs/dw/planning/static-pack",
192 },
193 OciPackageSpec {
194 package: "packs/dw/reflection/llm-critic-pack",
195 },
196 OciPackageSpec {
197 package: "packs/dw/reflection/rules-pack",
198 },
199 OciPackageSpec {
200 package: "packs/dw/reflection/schema-check-pack",
201 },
202 OciPackageSpec {
203 package: "packs/dw/state/task-store-in-memory-pack",
204 },
205 OciPackageSpec {
206 package: "packs/dw/state/task-store-redis-pack",
207 },
208 OciPackageSpec {
209 package: "packs/dw/tool/component-adapter-pack",
210 },
211 OciPackageSpec {
212 package: "packs/dw/tool/mcp-adapter-pack",
213 },
214 OciPackageSpec {
215 package: "packs/dw/workspace/fs-pack",
216 },
217 OciPackageSpec {
218 package: "packs/dw/workspace/in-memory-pack",
219 },
220 OciPackageSpec {
221 package: "packs/events/events-dummy",
222 },
223 OciPackageSpec {
224 package: "packs/events/events-email",
225 },
226 OciPackageSpec {
227 package: "packs/events/events-email-sendgrid",
228 },
229 OciPackageSpec {
230 package: "packs/events/events-sms",
231 },
232 OciPackageSpec {
233 package: "packs/events/events-sms-twilio",
234 },
235 OciPackageSpec {
236 package: "packs/events/events-timer",
237 },
238 OciPackageSpec {
239 package: "packs/events/events-webhook",
240 },
241 OciPackageSpec {
242 package: "packs/messaging/messaging-dummy",
243 },
244 OciPackageSpec {
245 package: "packs/messaging/messaging-email",
246 },
247 OciPackageSpec {
248 package: "packs/messaging/messaging-http",
249 },
250 OciPackageSpec {
251 package: "packs/messaging/messaging-slack",
252 },
253 OciPackageSpec {
254 package: "packs/messaging/messaging-teams",
255 },
256 OciPackageSpec {
257 package: "packs/messaging/messaging-telegram",
258 },
259 OciPackageSpec {
260 package: "packs/messaging/messaging-webchat",
261 },
262 OciPackageSpec {
263 package: "packs/messaging/messaging-webchat-gui",
264 },
265 OciPackageSpec {
266 package: "packs/messaging/messaging-webex",
267 },
268 OciPackageSpec {
269 package: "packs/messaging/messaging-websocket",
270 },
271 OciPackageSpec {
272 package: "packs/messaging/messaging-whatsapp",
273 },
274 OciPackageSpec {
275 package: "packs/oauth/oauth-github",
276 },
277 OciPackageSpec {
278 package: "packs/oauth/oauth-google",
279 },
280 OciPackageSpec {
281 package: "packs/oauth/oauth-microsoft-graph",
282 },
283 OciPackageSpec {
284 package: "packs/oauth/oauth-oidc-generic",
285 },
286 OciPackageSpec {
287 package: "packs/oauth/oauth-slack",
288 },
289 OciPackageSpec {
290 package: "packs/secret/greentic.secrets.aws-sm.gtpack",
291 },
292 OciPackageSpec {
293 package: "packs/secret/greentic.secrets.azure-kv.gtpack",
294 },
295 OciPackageSpec {
296 package: "packs/secret/greentic.secrets.gcp-sm.gtpack",
297 },
298 OciPackageSpec {
299 package: "packs/secret/greentic.secrets.k8s.gtpack",
300 },
301 OciPackageSpec {
302 package: "packs/secret/greentic.secrets.providers.gtpack",
303 },
304 OciPackageSpec {
305 package: "packs/secret/greentic.secrets.vault-kv.gtpack",
306 },
307 OciPackageSpec {
308 package: "packs/state-memory",
309 },
310 OciPackageSpec {
311 package: "packs/state-redis",
312 },
313 OciPackageSpec {
314 package: "packs/state/state-memory",
315 },
316 OciPackageSpec {
317 package: "packs/state/state-redis",
318 },
319];
320pub const GREENTIC_COMPONENT_PACKAGES: &[OciPackageSpec] = &[
321 OciPackageSpec {
322 package: "component/component-events2msg",
323 },
324 OciPackageSpec {
325 package: "component/component-http",
326 },
327 OciPackageSpec {
328 package: "component/component-llm-openai",
329 },
330 OciPackageSpec {
331 package: "component/component-msg2events",
332 },
333 OciPackageSpec {
334 package: "component/component-pack2flow",
335 },
336 OciPackageSpec {
337 package: "component/component-sorx-business",
338 },
339 OciPackageSpec {
340 package: "components/component-adaptive-card",
341 },
342 OciPackageSpec {
343 package: "components/component-qa",
344 },
345 OciPackageSpec {
346 package: "components/secrets-provider-inmemory",
347 },
348 OciPackageSpec {
349 package: "components/templates",
350 },
351];
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use std::collections::BTreeSet;
357
358 #[test]
359 fn external_packages_include_mcp_generator() {
360 let found = GREENTIC_EXTERNAL_TOOL_PACKAGES
361 .iter()
362 .find(|pkg| pkg.crate_name == "greentic-mcp-generator");
363 let pkg = found.expect("generator must be registered as an external tool");
364 assert_eq!(pkg.bins, &["greentic-mcp-gen"]);
365 }
366
367 #[test]
368 fn catalogue_contains_expected_public_toolchain() {
369 let expected = [
370 ("greentic-dev", "greentic-dev"),
371 ("greentic-operator", "greentic-operator"),
372 ("greentic-bundle", "greentic-bundle"),
373 ("greentic-setup", "greentic-setup"),
374 ("greentic-start", "greentic-start"),
375 ("greentic-deployer", "greentic-deployer"),
376 ("greentic-component", "greentic-component"),
377 ("greentic-flow", "greentic-flow"),
378 ("greentic-pack", "greentic-pack"),
379 ("greentic-runner", "greentic-runner"),
380 ("greentic-gui", "greentic-gui"),
381 ("greentic-secrets", "greentic-secrets"),
382 ("greentic-mcp", "greentic-mcp"),
383 ];
384
385 let actual = catalogue_pairs();
386 for pair in expected {
387 assert!(actual.contains(&pair), "missing {pair:?}");
388 }
389 }
390
391 #[test]
392 fn catalogue_has_no_duplicate_crate_bin_pairs() {
393 let mut seen = BTreeSet::new();
394 for package in GREENTIC_TOOLCHAIN_PACKAGES {
395 for bin in package.bins {
396 assert!(
397 seen.insert((package.crate_name, *bin)),
398 "duplicate crate/bin pair: {}/{}",
399 package.crate_name,
400 bin
401 );
402 }
403 }
404 }
405
406 #[test]
407 fn extension_pack_catalogue_tracks_github_packages() {
408 assert_eq!(GREENTIC_EXTENSION_PACK_PACKAGES.len(), 81);
409 assert_catalogue_has_no_duplicate_packages(GREENTIC_EXTENSION_PACK_PACKAGES);
410 assert!(GREENTIC_EXTENSION_PACK_PACKAGES.iter().all(|package| {
411 package.package.starts_with("packs/") || package.package.starts_with("greentic-bundle/")
412 }));
413 assert!(
414 GREENTIC_EXTENSION_PACK_PACKAGES
415 .iter()
416 .any(|package| package.package == "packs/deployer/greentic.deploy.aws")
417 );
418 assert!(
419 GREENTIC_EXTENSION_PACK_PACKAGES
420 .iter()
421 .any(|package| package.package == "packs/deployer/greentic.deploy.helm")
422 );
423 assert!(GREENTIC_EXTENSION_PACK_PACKAGES.iter().all(|package| {
424 !package
425 .package
426 .starts_with("packs/deployer/greentic.fixture.")
427 }));
428 assert!(
429 GREENTIC_EXTENSION_PACK_PACKAGES
430 .iter()
431 .any(|package| package.package == "packs/messaging/messaging-webchat-gui")
432 );
433 assert!(
434 GREENTIC_EXTENSION_PACK_PACKAGES
435 .iter()
436 .any(|package| package.package == "packs/messaging/messaging-http")
437 );
438 assert!(
439 GREENTIC_EXTENSION_PACK_PACKAGES
440 .iter()
441 .any(|package| package.package == "packs/messaging/messaging-websocket")
442 );
443 assert!(
444 GREENTIC_EXTENSION_PACK_PACKAGES
445 .iter()
446 .any(|package| package.package == "packs/oauth/oauth-microsoft-graph")
447 );
448 }
449
450 #[test]
451 fn component_catalogue_tracks_github_packages() {
452 assert_eq!(GREENTIC_COMPONENT_PACKAGES.len(), 10);
453 assert_catalogue_has_no_duplicate_packages(GREENTIC_COMPONENT_PACKAGES);
454 assert!(GREENTIC_COMPONENT_PACKAGES.iter().all(|package| {
455 package.package.starts_with("component/")
456 || package.package.starts_with("components/")
457 || package.package.starts_with("validators/")
458 }));
459 assert!(
460 GREENTIC_COMPONENT_PACKAGES
461 .iter()
462 .any(|package| package.package == "component/component-llm-openai")
463 );
464 assert!(
465 GREENTIC_COMPONENT_PACKAGES
466 .iter()
467 .any(|package| package.package == "component/component-sorx-business")
468 );
469 assert!(
470 GREENTIC_COMPONENT_PACKAGES
471 .iter()
472 .any(|package| package.package == "components/component-adaptive-card")
473 );
474 }
475
476 fn catalogue_pairs() -> BTreeSet<(&'static str, &'static str)> {
477 GREENTIC_TOOLCHAIN_PACKAGES
478 .iter()
479 .flat_map(|package| {
480 package
481 .bins
482 .iter()
483 .map(move |bin| (package.crate_name, *bin))
484 })
485 .collect()
486 }
487
488 fn assert_catalogue_has_no_duplicate_packages(catalogue: &[OciPackageSpec]) {
489 let mut seen = BTreeSet::new();
490 for package in catalogue {
491 assert!(
492 seen.insert(package.package),
493 "duplicate package: {}",
494 package.package
495 );
496 }
497 }
498}