Scaffolding CLI
The ggcommons scaffolding CLI is the fastest way to start a new Greengrass v2 component. It
generates a ready-to-build project for any of the four languages, wires up the ggcommons
dependency, and gives you commands to validate, build, publish, deploy, and upgrade the result.
The CLI is a single Python package (ggcommons-cli) that ships the templates for all four
languages inside the wheel, so once installed it scaffolds offline — no network needed to
generate a project.
Install
Section titled “Install”The CLI installs from the cli/ directory of the monorepo. pipx is recommended (isolated global
install); plain pip also works.
pipx install ./cli # recommended: isolated global `ggcommons`# or:python -m pip install ./cli # plain installpython -m pip install -e ./cli # editable (for developing the CLI itself)pipx install ./cli # recommended: isolated global `ggcommons`# or:python -m pip install ./cli # plain installpython -m pip install -e ./cli # editable (for developing the CLI itself)Two identical console entry points are installed — ggcommons and ggcommons-cli — so use
whichever you prefer. The examples below use ggcommons.
ggcommons --version # print the installed CLI versionggcommons # no subcommand → prints top-level helpggcommons <unknown> # → "Unknown command: <unknown>" (exit 2)When a command fails it prints error: <message> (no Python traceback) and exits non-zero.
doctor — check your toolchain
Section titled “doctor — check your toolchain”ggcommons doctor checks for the external tools needed to build and publish components and prints
where each one was found. It takes no flags and reports status only — it does not exit non-zero
when something is missing, so it is safe to run anytime.
ggcommons doctorggcommons doctorIt checks eight tools, in this order:
| Tool | Needed to |
|---|---|
git |
clone component templates |
gdk |
build/publish components (Greengrass Development Kit) |
cargo |
build Rust components |
mvn |
build Java components |
python3 (or python) |
run/build Python components |
node |
run/build TypeScript components |
npm |
install TypeScript dependencies |
aws |
publish to AWS / deploy |
Each line prints [ok] <name> -> <path> or [missing] <name> -- needed to …. You only need the
tools for the language and workflow you actually target.
create-component — scaffold a project
Section titled “create-component — scaffold a project”This is the core command. The minimal non-interactive form requires a fully-qualified component
name (-n) and a language (-l):
# pick one language with -l (JAVA | PYTHON | RUST | TYPESCRIPT)ggcommons create-component -n com.example.MyComponent -l JAVAggcommons create-component -n com.example.MyComponent -l PYTHONggcommons create-component -n com.example.MyComponent -l RUSTggcommons create-component -n com.example.MyComponent -l TYPESCRIPT# pick one language with -l (JAVA | PYTHON | RUST | TYPESCRIPT)ggcommons create-component -n com.example.MyComponent -l JAVAggcommons create-component -n com.example.MyComponent -l PYTHONggcommons create-component -n com.example.MyComponent -l RUSTggcommons create-component -n com.example.MyComponent -l TYPESCRIPTThe output directory is <path>/<ComponentName>, where ComponentName is the last dot-segment
of --name (so com.example.MyComponent generates into ./MyComponent). On success the CLI prints
Done. Component generated at: <dir>.
After generation it runs two guards: it fails fast if any <<TOKEN>> placeholder was left
unsubstituted, and it lints the generated recipe.yaml, printing WARNING (recipe): … for any
issues it finds.
| Flag | Short | Default | Notes |
|---|---|---|---|
--name |
-n |
(required non-interactively) | Fully-qualified component name, e.g. com.example.MyComponent. |
--language |
-l |
(required non-interactively) | One of JAVA, PYTHON, RUST, TYPESCRIPT. |
--description |
-d |
This is a Greengrass v2 component |
Short component description. |
--path |
-p |
. |
Parent directory for the generated project. |
--jar |
-j |
the component name | Jar file name (Java only). |
--author |
-a |
Amazon Web Services |
Component author. |
--bucket |
-b |
greengrass-component-artifacts-us-east-1 |
S3 artifact bucket (written into gdk-config.json). |
--region |
-r |
us-east-1 |
AWS region (written into gdk-config.json). |
--ggcommons-path |
-g |
libs/rust or libs/ts |
Absolute path to the local ggcommons library. Rust/TypeScript only (see below). |
--template-url |
-u |
built-in template | Override the template source: a git URL (cloned) or a local directory (copied). |
--template-ref |
(none) | repo default branch | Git branch/tag to clone. Only applies to a git --template-url; ignored for local dirs. |
--force |
-f |
off | Overwrite the target directory if it already exists and is non-empty. |
--interactive |
-i |
off | Run the guided wizard (see below). |
--platforms |
(none) | GREENGRASS,HOST,KUBERNETES |
Comma-separated target platforms; gates which platform-specific artifacts are emitted. |
--dep-source |
(none) | local |
How the component depends on ggcommons: local (path/file dep) or registry (published artifact). |
Interactive wizard
Section titled “Interactive wizard”The wizard auto-enables when you pass -i/--interactive, or when you omit -n/--name while
running on a terminal (a TTY). It collects the same inputs the flags do, then runs the identical
generation flow. Press Enter to accept the [default] shown in each prompt.
The prompts, in order, are: language, fully-qualified name (required), description,
target platform(s), dependency source, local ggcommons path (only asked for a Rust or
TypeScript component with --dep-source local), and author.
$ ggcommons create-component -iInteractive component scaffolding — press Enter to accept the [default].
Language (JAVA/PYTHON/RUST/TYPESCRIPT): PYTHONFully-qualified component name (e.g. com.example.MyComponent): com.example.MyComponentDescription [This is a Greengrass v2 component]:Target platform(s) — comma-separated from GREENGRASS,HOST,KUBERNETES [GREENGRASS,HOST,KUBERNETES]: HOSTggcommons dependency source (registry/local) [local]:Author [Amazon Web Services]: MarcPS> ggcommons create-component -iInteractive component scaffolding — press Enter to accept the [default].
Language (JAVA/PYTHON/RUST/TYPESCRIPT): PYTHONFully-qualified component name (e.g. com.example.MyComponent): com.example.MyComponentDescription [This is a Greengrass v2 component]:Target platform(s) — comma-separated from GREENGRASS,HOST,KUBERNETES [GREENGRASS,HOST,KUBERNETES]: HOSTggcommons dependency source (registry/local) [local]:Author [Amazon Web Services]: MarcPlatform gating (--platforms)
Section titled “Platform gating (--platforms)”--platforms controls which platform-specific artifacts the template emits. The three valid
values are GREENGRASS, HOST, and KUBERNETES; the default selects all three. Selecting a
subset prunes the artifacts only that platform needs.
The headline case: the Kubernetes Dockerfile and k8s/ manifests are emitted only when
KUBERNETES is among the selected platforms.
# Greengrass + HOST only → no Dockerfile, no k8s/ manifestsggcommons create-component -n com.example.MyComponent -l RUST --platforms GREENGRASS,HOST
# include KUBERNETES → also emits Dockerfile + k8s/ (configmap.yaml, deployment.yaml)ggcommons create-component -n com.example.MyComponent -l RUST --platforms KUBERNETES# Greengrass + HOST only → no Dockerfile, no k8s/ manifestsggcommons create-component -n com.example.MyComponent -l RUST --platforms GREENGRASS,HOST
# include KUBERNETES → also emits Dockerfile + k8s/ (configmap.yaml, deployment.yaml)ggcommons create-component -n com.example.MyComponent -l RUST --platforms KUBERNETESDependency source (--dep-source)
Section titled “Dependency source (--dep-source)”--dep-source decides how the generated component resolves the ggcommons library:
local(the default) — a path/file dependency on a monorepo checkout, ideal for developing against an unpublished library.registry— the published artifact (GitHub Packages / git tag).
This flag (and --ggcommons-path) only affect Rust and TypeScript — the two languages whose
generated dependency is a path/file reference. For Java and Python the dependency is always the
named coordinate (com.mbreissi:ggcommons in pom.xml, greengrass-commons in
requirements.txt), so --dep-source is a no-op there.
Here is the dependency declaration each language ends up with:
<!-- pom.xml — always this coordinate; --dep-source has no effect for Java --><dependency> <groupId>com.mbreissi</groupId> <artifactId>ggcommons</artifactId> <version>0.1.0</version></dependency># requirements.txt — always the named package; --dep-source has no effect for Pythongreengrass-commons# Cargo.toml — local (default): path dependency on your --ggcommons-pathggcommons = { path = "/abs/path/to/libs/rust", default-features = false }
# Cargo.toml — registry: git tagggcommons = { git = "https://github.com/mbreissi/ggcommons", tag = "rust-lib/v0.1.0", default-features = false }// package.json — local (default): file: dependency on your --ggcommons-path"@mbreissi/ggcommons": "file:/abs/path/to/libs/ts"
// package.json — registry: version range (plus a generated .npmrc pointing at GitHub Packages)"@mbreissi/ggcommons": "^0.1.0"For a Rust or TypeScript component with --dep-source local, the CLI requires a valid
--ggcommons-path (it defaults to libs/rust / libs/ts in this monorepo and is validated to
exist before any files are written).
What you get
Section titled “What you get”A generated project is a complete, buildable component: the language entry point, the ggcommons
dependency wired in, a recipe.yaml and gdk-config.json for GDK packaging, and sample
test-configs/. The exact layout is language-specific (for example pom.xml for Java, main.py
for Python, Cargo.toml + build.sh for Rust, package.json + tsconfig.json for TypeScript).
The generated entry point constructs the ggcommons runtime and reads the config manager off it:
package com.example.mycomponent;
import com.mbreissi.ggcommons.GGCommons;import com.mbreissi.ggcommons.config.ConfigManager;
public class MyComponent { public static void main(String[] args) { new MyComponent(args); }
public MyComponent(String[] args) { GGCommons ggCommons = new GGCommons("com.example.MyComponent", args); ConfigManager configManager = ggCommons.getConfigManager(); // ... business logic ... }}import sys, argparsefrom ggcommons import GGCommonsBuilder
def main(): arg_parser = argparse.ArgumentParser() gg = (GGCommonsBuilder.create("MyComponent") # short name; see note below .with_args(sys.argv[1:]) .with_app_options(arg_parser) .receive_own_messages(True) .build()) config_manager = gg.get_config_manager() try: ... # business logic finally: gg.shutdown()
if __name__ == "__main__": main()use ggcommons::prelude::*;
const COMPONENT_NAME: &str = "com.example.MyComponent";
#[tokio::main]async fn main() -> anyhow::Result<()> { let gg = GgCommonsBuilder::new(COMPONENT_NAME) .args(std::env::args_os()) .build() .await?; tracing::info!(thing = %gg.config().thing_name, "starting"); // ... business logic; dropping `gg` releases all resources (RAII) ... Ok(())}import { GGCommonsBuilder, logger } from "@mbreissi/ggcommons";
const COMPONENT_NAME = "com.example.MyComponent";
async function main(): Promise<void> { const gg = await new GGCommonsBuilder(COMPONENT_NAME).args(process.argv.slice(2)).build(); logger.info(`starting: thing=${gg.config().thingName}`); process.on("SIGTERM", () => void gg.close().then(() => process.exit(0))); // ... business logic ...}
main().catch((err) => { console.error("fatal:", err); process.exit(1); });For building and running a generated component against a local MQTT broker, see the Quickstart.
list-templates — see available languages
Section titled “list-templates — see available languages”Prints each language and the template source it will use — a local filesystem path (the bundled
wheel copy, or the in-repo templates/<lang> directory). It takes no flags.
ggcommons list-templatesggcommons list-templatesvalidate — lint the recipe
Section titled “validate — lint the recipe”Checks a generated component’s recipe.yaml for problems that would break
gdk component publish. Pass -p/--path to the project directory (or directly to a recipe.yaml);
it defaults to the current directory. It prints OK: … when clean, or lists the issues and exits
non-zero.
ggcommons validate -p ./MyComponentggcommons validate -p ./MyComponentdeploy — build, publish, deploy
Section titled “deploy — build, publish, deploy”Wraps the GDK (gdk component build, then optionally gdk component publish) and can create a
cloud deployment with the AWS CLI.
| Flag | Short | Default | Notes |
|---|---|---|---|
--path |
-p |
. |
Component project directory. |
--publish |
(none) | off | Publish to the cloud after building. |
--target |
-t |
(none) | Deployment target ARN (thing or thing group). Implies --publish and creates a cloud deployment. |
--region |
-r |
us-east-1 |
AWS region for the deployment. |
ggcommons deploy -p ./MyComponent # build onlyggcommons deploy -p ./MyComponent --publish # build + publishggcommons deploy -p ./MyComponent -t <target-arn> # build + publish + create deploymentggcommons deploy -p ./MyComponent # build onlyggcommons deploy -p ./MyComponent --publish # build + publishggcommons deploy -p ./MyComponent -t <target-arn> # build + publish + create deploymentdeploy requires gdk on PATH (and AWS credentials for publish/deploy). When deploying with
--target, the project’s gdk-config.json must use a concrete version — a NEXT_PATCH
version is rejected so the exact version to deploy is unambiguous. On-device development with
greengrass-cli is out of scope for this command.
upgrade — bump the ggcommons dependency
Section titled “upgrade — bump the ggcommons dependency”Updates the ggcommons dependency version in whichever manifest is present — pom.xml (Java),
requirements.txt (Python), Cargo.toml (Rust), or package.json (TypeScript). The target
version is required via -t/--to; the project path defaults to the current directory.
ggcommons upgrade -p ./MyComponent --to 1.2.3ggcommons upgrade -p ./MyComponent --to 1.2.3A Rust path dependency is deliberately left untouched: upgrade reports it as “nothing to
version-bump” rather than rewriting it.
For TypeScript, upgrade keys on the bare dependency name ggcommons and does not match the
scoped @mbreissi/ggcommons key the TS template generates. As a result, for a generated TS
component upgrade reports package.json: no ggcommons dependency found — for both a local
(file:) and a registry (^0.1.0) dependency — and the file:/link: “nothing to version-bump”
branch is never reached.
Manifest-driven templates
Section titled “Manifest-driven templates”Each template directory ships a ggcommons-template.json manifest that fully describes how the CLI
turns it into a project — adding a new language (or a forked template) needs only a template, not
changes to the CLI. The manifest has these sections:
substitutions— maps each file to the list of<<TOKEN>>placeholders to replace in it (for exampleCOMPONENTFULLNAME,COMPONENTNAME,GGCOMMONS_VERSION).renames— file/directory renames, including templated targets such assrc/main/java/{PACKAGEPATH}/{COMPONENTNAME}.java.required— substitution keys that must be provided.conditional— artifacts gated by an active condition flag.
The active condition flags are derived from your create-component inputs: one platform:<P> flag
per selected --platforms value, plus one dep:<source> flag for --dep-source. A conditional
entry’s paths are emitted only when its when flag is active. For example, the Java manifest
gates the Kubernetes artifacts:
{ "conditional": [ { "when": "platform:KUBERNETES", "paths": ["Dockerfile", "k8s"] } ]}and the TypeScript manifest additionally gates .npmrc on the registry dependency source:
{ "conditional": [ { "when": "platform:KUBERNETES", "paths": ["Dockerfile", "k8s"] }, { "when": "dep:registry", "paths": [".npmrc"] } ]}That is why --platforms KUBERNETES is what produces the Dockerfile and k8s/ manifests, and
--dep-source registry is what produces the TypeScript .npmrc.