Skip to content

Commit a7916d6

Browse files
APP-7171: Add README.md to module generate CLI (viamrobotics#4731)
1 parent 2ccf232 commit a7916d6

File tree

5 files changed

+120
-4
lines changed

5 files changed

+120
-4
lines changed

cli/module_generate.go

+43-2
Original file line numberDiff line numberDiff line change
@@ -378,9 +378,12 @@ func populateAdditionalInfo(newModule *modulegen.ModuleInputs) {
378378
}
379379
newModule.ResourceTypePascal = spaceReplacer.Replace(titleCaser.String(replacer.Replace(newModule.ResourceType)))
380380
newModule.ModelPascal = spaceReplacer.Replace(titleCaser.String(replacer.Replace(newModule.ModelName)))
381-
newModule.ModelTriple = fmt.Sprintf("%s:%s:%s", newModule.Namespace, newModule.ModuleName, newModule.ModelName)
382381
newModule.ModelCamel = strings.ToLower(string(newModule.ModelPascal[0])) + newModule.ModelPascal[1:]
383382
newModule.ModelLowercase = strings.ToLower(newModule.ModelPascal)
383+
384+
modelTriple := fmt.Sprintf("%s:%s:%s", newModule.Namespace, newModule.ModuleName, newModule.ModelName)
385+
newModule.ModelTriple = modelTriple
386+
newModule.ModelReadmeLink = "README.md#" + generateAnchor(fmt.Sprintf("Model %s", modelTriple))
384387
}
385388

386389
// Creates a new directory with moduleName.
@@ -415,6 +418,11 @@ func renderCommonFiles(c *cli.Context, module modulegen.ModuleInputs, globalArgs
415418
return errors.Wrapf(err, "failed to write generator info to %s", infoFilePath)
416419
}
417420

421+
// Render README.md
422+
if err := renderReadme(module); err != nil {
423+
return errors.Wrap(err, "failed to render README.md")
424+
}
425+
418426
// Render workflows for cloud build
419427
if module.EnableCloudBuild {
420428
debugf(c.App.Writer, globalArgs.Debug, "\tCreating cloud build workflow")
@@ -784,6 +792,38 @@ func createModuleAndManifest(cCtx *cli.Context, c *viamClient, module modulegen.
784792
return nil
785793
}
786794

795+
// Create the README.md file.
796+
func renderReadme(module modulegen.ModuleInputs) error {
797+
readmeTemplatePath, err := templates.Open(filepath.Join(templatesPath, defaultReadmeFilename))
798+
readmeDest := filepath.Join(module.ModuleName, defaultReadmeFilename)
799+
if err != nil {
800+
return err
801+
}
802+
defer utils.UncheckedErrorFunc(readmeTemplatePath.Close)
803+
tBytes, err := io.ReadAll(readmeTemplatePath)
804+
if err != nil {
805+
return err
806+
}
807+
808+
tmpl, err := template.New(defaultReadmeFilename).Parse(string(tBytes))
809+
if err != nil {
810+
return err
811+
}
812+
813+
//nolint:gosec
814+
destFile, err := os.Create(readmeDest)
815+
if err != nil {
816+
return err
817+
}
818+
defer utils.UncheckedErrorFunc(destFile.Close)
819+
820+
err = tmpl.Execute(destFile, module)
821+
if err != nil {
822+
return err
823+
}
824+
return nil
825+
}
826+
787827
// Create the meta.json manifest.
788828
func renderManifest(c *cli.Context, moduleID string, module modulegen.ModuleInputs, globalArgs globalArgs) error {
789829
debugf(c.App.Writer, globalArgs.Debug, "Rendering module manifest")
@@ -793,13 +833,14 @@ func renderManifest(c *cli.Context, moduleID string, module modulegen.ModuleInpu
793833
visibility = moduleVisibilityPublic
794834
}
795835

836+
modelDescription := "Provide a short (100 characters or less) description of this model here"
796837
manifest := moduleManifest{
797838
Schema: "https://dl.viam.dev/module.schema.json",
798839
ModuleID: moduleID,
799840
Visibility: visibility,
800841
Description: fmt.Sprintf("Modular %s %s: %s", module.ResourceSubtype, module.ResourceType, module.ModelName),
801842
Models: []ModuleComponent{
802-
{API: module.API, Model: module.ModelTriple},
843+
{API: module.API, Model: module.ModelTriple, MarkdownLink: &module.ModelReadmeLink, Description: &modelDescription},
803844
},
804845
}
805846
switch module.Language {
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Module {{.ModuleName}}
2+
3+
Provide a description of the purpose of the module and any relevant information.
4+
5+
## Model {{.ModelTriple}}
6+
7+
Provide a description of the model and any relevant information.
8+
9+
### Configuration
10+
The following attribute template can be used to configure this model:
11+
12+
```json
13+
{
14+
"attribute_1": <float>,
15+
"attribute_2": <string>
16+
}
17+
```
18+
19+
#### Attributes
20+
21+
The following attributes are available for this model:
22+
23+
| Name | Type | Inclusion | Description |
24+
|---------------|--------|-----------|----------------------------|
25+
| `attribute_1` | float | Required | Description of attribute 1 |
26+
| `attribute_2` | string | Optional | Description of attribute 2 |
27+
28+
#### Example Configuration
29+
30+
```json
31+
{
32+
"attribute_1": 1.0,
33+
"attribute_2": "foo"
34+
}
35+
```
36+
37+
### DoCommand
38+
39+
If your model implements DoCommand, provide an example payload of each command that is supported and the arguments that can be used. If your model does not implement DoCommand, remove this section.
40+
41+
#### Example DoCommand
42+
43+
```json
44+
{
45+
"command_name": {
46+
"arg1": "foo",
47+
"arg2": 1
48+
}
49+
}
50+
```

cli/module_generate/modulegen/inputs.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ type ModuleInputs struct {
3535
ModelCamel string `json:"-"`
3636
ModelTriple string `json:"-"`
3737
ModelLowercase string `json:"-"`
38-
39-
SDKVersion string `json:"-"`
38+
ModelReadmeLink string `json:"-"`
39+
SDKVersion string `json:"-"`
4040
}
4141

4242
// Resources is a list of all the available resources in Viam.

cli/module_generate_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func TestGenerateModuleAction(t *testing.T) {
3636
ResourceSubtypePascal: "Arm",
3737
ModelPascal: "MyModel",
3838
ModelTriple: "my-org:my-module:my-model",
39+
ModelReadmeLink: "model-readme-link",
3940

4041
SDKVersion: "0.0.0",
4142
}
@@ -74,6 +75,17 @@ func TestGenerateModuleAction(t *testing.T) {
7475
test.That(t, err, test.ShouldBeNil)
7576
test.That(t, module.ModuleName, test.ShouldEqual, testModule.ModuleName)
7677

78+
_, err = os.Stat(filepath.Join(modulePath, "README.md"))
79+
test.That(t, err, test.ShouldBeNil)
80+
81+
readme, err := os.Open(filepath.Join(modulePath, "README.md"))
82+
test.That(t, err, test.ShouldBeNil)
83+
defer readme.Close()
84+
bytes, err = io.ReadAll(readme)
85+
test.That(t, err, test.ShouldBeNil)
86+
test.That(t, string(bytes), test.ShouldContainSubstring, "Module "+testModule.ModuleName)
87+
test.That(t, string(bytes), test.ShouldContainSubstring, "Model "+testModule.ModelTriple)
88+
7789
// cloud build enabled
7890
_, err = os.Stat(filepath.Join(modulePath, ".github"))
7991
test.That(t, err, test.ShouldBeNil)
@@ -170,5 +182,17 @@ func TestGenerateModuleAction(t *testing.T) {
170182
test.That(t, err, test.ShouldBeNil)
171183
_, err = os.Stat(filepath.Join(testDir, testModule.ModuleName, "meta.json"))
172184
test.That(t, err, test.ShouldBeNil)
185+
186+
manifestFile, err := os.Open(filepath.Join(testDir, testModule.ModuleName, "meta.json"))
187+
test.That(t, err, test.ShouldBeNil)
188+
defer manifestFile.Close()
189+
bytes, err := io.ReadAll(manifestFile)
190+
test.That(t, err, test.ShouldBeNil)
191+
var manifest moduleManifest
192+
err = json.Unmarshal(bytes, &manifest)
193+
test.That(t, err, test.ShouldBeNil)
194+
test.That(t, len(manifest.Models), test.ShouldEqual, 1)
195+
test.That(t, manifest.Models[0].Model, test.ShouldEqual, testModule.ModelTriple)
196+
test.That(t, *manifest.Models[0].MarkdownLink, test.ShouldEqual, testModule.ModelReadmeLink)
173197
})
174198
}

cli/module_registry.go

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ type moduleManifest struct {
106106

107107
const (
108108
defaultManifestFilename = "meta.json"
109+
defaultReadmeFilename = "README.md"
109110
)
110111

111112
type createModuleActionArgs struct {

0 commit comments

Comments
 (0)