Skip to content

Commit

Permalink
APP-7171: Add README.md to module generate CLI (#4731)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaellee1019 authored Jan 27, 2025
1 parent 2ccf232 commit a7916d6
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 4 deletions.
45 changes: 43 additions & 2 deletions cli/module_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,12 @@ func populateAdditionalInfo(newModule *modulegen.ModuleInputs) {
}
newModule.ResourceTypePascal = spaceReplacer.Replace(titleCaser.String(replacer.Replace(newModule.ResourceType)))
newModule.ModelPascal = spaceReplacer.Replace(titleCaser.String(replacer.Replace(newModule.ModelName)))
newModule.ModelTriple = fmt.Sprintf("%s:%s:%s", newModule.Namespace, newModule.ModuleName, newModule.ModelName)
newModule.ModelCamel = strings.ToLower(string(newModule.ModelPascal[0])) + newModule.ModelPascal[1:]
newModule.ModelLowercase = strings.ToLower(newModule.ModelPascal)

modelTriple := fmt.Sprintf("%s:%s:%s", newModule.Namespace, newModule.ModuleName, newModule.ModelName)
newModule.ModelTriple = modelTriple
newModule.ModelReadmeLink = "README.md#" + generateAnchor(fmt.Sprintf("Model %s", modelTriple))
}

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

// Render README.md
if err := renderReadme(module); err != nil {
return errors.Wrap(err, "failed to render README.md")
}

// Render workflows for cloud build
if module.EnableCloudBuild {
debugf(c.App.Writer, globalArgs.Debug, "\tCreating cloud build workflow")
Expand Down Expand Up @@ -784,6 +792,38 @@ func createModuleAndManifest(cCtx *cli.Context, c *viamClient, module modulegen.
return nil
}

// Create the README.md file.
func renderReadme(module modulegen.ModuleInputs) error {
readmeTemplatePath, err := templates.Open(filepath.Join(templatesPath, defaultReadmeFilename))
readmeDest := filepath.Join(module.ModuleName, defaultReadmeFilename)
if err != nil {
return err
}
defer utils.UncheckedErrorFunc(readmeTemplatePath.Close)
tBytes, err := io.ReadAll(readmeTemplatePath)
if err != nil {
return err
}

tmpl, err := template.New(defaultReadmeFilename).Parse(string(tBytes))
if err != nil {
return err
}

//nolint:gosec
destFile, err := os.Create(readmeDest)
if err != nil {
return err
}
defer utils.UncheckedErrorFunc(destFile.Close)

err = tmpl.Execute(destFile, module)
if err != nil {
return err
}
return nil
}

// Create the meta.json manifest.
func renderManifest(c *cli.Context, moduleID string, module modulegen.ModuleInputs, globalArgs globalArgs) error {
debugf(c.App.Writer, globalArgs.Debug, "Rendering module manifest")
Expand All @@ -793,13 +833,14 @@ func renderManifest(c *cli.Context, moduleID string, module modulegen.ModuleInpu
visibility = moduleVisibilityPublic
}

modelDescription := "Provide a short (100 characters or less) description of this model here"
manifest := moduleManifest{
Schema: "https://dl.viam.dev/module.schema.json",
ModuleID: moduleID,
Visibility: visibility,
Description: fmt.Sprintf("Modular %s %s: %s", module.ResourceSubtype, module.ResourceType, module.ModelName),
Models: []ModuleComponent{
{API: module.API, Model: module.ModelTriple},
{API: module.API, Model: module.ModelTriple, MarkdownLink: &module.ModelReadmeLink, Description: &modelDescription},
},
}
switch module.Language {
Expand Down
50 changes: 50 additions & 0 deletions cli/module_generate/_templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Module {{.ModuleName}}

Provide a description of the purpose of the module and any relevant information.

## Model {{.ModelTriple}}

Provide a description of the model and any relevant information.

### Configuration
The following attribute template can be used to configure this model:

```json
{
"attribute_1": <float>,
"attribute_2": <string>
}
```

#### Attributes

The following attributes are available for this model:

| Name | Type | Inclusion | Description |
|---------------|--------|-----------|----------------------------|
| `attribute_1` | float | Required | Description of attribute 1 |
| `attribute_2` | string | Optional | Description of attribute 2 |

#### Example Configuration

```json
{
"attribute_1": 1.0,
"attribute_2": "foo"
}
```

### DoCommand

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.

#### Example DoCommand

```json
{
"command_name": {
"arg1": "foo",
"arg2": 1
}
}
```
4 changes: 2 additions & 2 deletions cli/module_generate/modulegen/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ type ModuleInputs struct {
ModelCamel string `json:"-"`
ModelTriple string `json:"-"`
ModelLowercase string `json:"-"`

SDKVersion string `json:"-"`
ModelReadmeLink string `json:"-"`
SDKVersion string `json:"-"`
}

// Resources is a list of all the available resources in Viam.
Expand Down
24 changes: 24 additions & 0 deletions cli/module_generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func TestGenerateModuleAction(t *testing.T) {
ResourceSubtypePascal: "Arm",
ModelPascal: "MyModel",
ModelTriple: "my-org:my-module:my-model",
ModelReadmeLink: "model-readme-link",

SDKVersion: "0.0.0",
}
Expand Down Expand Up @@ -74,6 +75,17 @@ func TestGenerateModuleAction(t *testing.T) {
test.That(t, err, test.ShouldBeNil)
test.That(t, module.ModuleName, test.ShouldEqual, testModule.ModuleName)

_, err = os.Stat(filepath.Join(modulePath, "README.md"))
test.That(t, err, test.ShouldBeNil)

readme, err := os.Open(filepath.Join(modulePath, "README.md"))
test.That(t, err, test.ShouldBeNil)
defer readme.Close()
bytes, err = io.ReadAll(readme)
test.That(t, err, test.ShouldBeNil)
test.That(t, string(bytes), test.ShouldContainSubstring, "Module "+testModule.ModuleName)
test.That(t, string(bytes), test.ShouldContainSubstring, "Model "+testModule.ModelTriple)

// cloud build enabled
_, err = os.Stat(filepath.Join(modulePath, ".github"))
test.That(t, err, test.ShouldBeNil)
Expand Down Expand Up @@ -170,5 +182,17 @@ func TestGenerateModuleAction(t *testing.T) {
test.That(t, err, test.ShouldBeNil)
_, err = os.Stat(filepath.Join(testDir, testModule.ModuleName, "meta.json"))
test.That(t, err, test.ShouldBeNil)

manifestFile, err := os.Open(filepath.Join(testDir, testModule.ModuleName, "meta.json"))
test.That(t, err, test.ShouldBeNil)
defer manifestFile.Close()
bytes, err := io.ReadAll(manifestFile)
test.That(t, err, test.ShouldBeNil)
var manifest moduleManifest
err = json.Unmarshal(bytes, &manifest)
test.That(t, err, test.ShouldBeNil)
test.That(t, len(manifest.Models), test.ShouldEqual, 1)
test.That(t, manifest.Models[0].Model, test.ShouldEqual, testModule.ModelTriple)
test.That(t, *manifest.Models[0].MarkdownLink, test.ShouldEqual, testModule.ModelReadmeLink)
})
}
1 change: 1 addition & 0 deletions cli/module_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ type moduleManifest struct {

const (
defaultManifestFilename = "meta.json"
defaultReadmeFilename = "README.md"
)

type createModuleActionArgs struct {
Expand Down

0 comments on commit a7916d6

Please sign in to comment.