Skip to main content

greentic_dev/
toolchain_catalogue.rs

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
67/// External tools distributed as their own single, unsuffixed binary
68/// (not part of the Greentic channel-suffixed toolchain). Resolved and
69/// installed by plain name — never with a `-dev`/`-rnd` suffix.
70pub 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}