Skip to main content

Advanced usage

Once you've started using Cortex's Scaffolder, you may be looking for some advanced features of templates: customizing the autogenerated cortex.yaml, building complex templates that use all the inputs collected by Cortex, building templates that add to existing services, and more!

Templates that add to existing services

A powerful workflow that the Scaffolder unlocks is the ability to templatize code to be added to existing services – for example, you may want templates that developers can use to add Terraform modules, CI pipelines, or build/deploy scripts to a service that's already been built.

To do so, you'll want to create a template that generates the file or directory you'd like to add to your existing service, and enable the PR-only toggle when adding the template to your workspace.

Keep in mind that your template always needs to generate a directory as expected by cookiecutter.

The Scaffolder merges files with a recursive copy

If your template generates files or directories that already exist in the target repo, Cortex will not attempt to merge the content of the files. Instead, we do a recursive copy, replacing any files that exist in the target.

Root-level files

If you're trying to add files to the root of an existing service, your template should generate the files at the root of the generated directory. Keep in mind that you can use template variables for the filenames as well.

The expected file structure of your template would be:

(template-folder)$ ls -l
cookiecutter.json
{{ cookiecutter.project_slug }}/
{{ cookiecutter.user_input }}-generated-file.js
my-file-to-generate.yml

Note that the files live under a nested, templated directory. When we open a PR, the PR will still only contain the two nested files at the root of the PR. Essentially, the PR will be:

{{ cookiecutter.user_input }}-generated-file.js
my-file-to-generate.yml

Directories

If you're looking to generate a directory, the template should generate the desired directory as the output. Although cookiecutter by default generates the output of the entire template into a directory, the PR feature copies output of the template out of the generated directory into the root (recursively).

For example, if you want your template to generate the directory infra-files-{{variable}} (or generate a file into this directory), your structure should look like:

(template folder)$ ls -l
cookiecutter.json
{{ cookiecutter.project_slug }}/
infra-files-{{cookiecutter.my_variable}}/
my-generated-file.json

Note that you can use templated variables when generating the directory name or files in the directory.

Overriding the autogenerated Service Descriptor

By default, the Scaffolder generates an entity descriptor file named cortex.yaml in the root of the repository when used to create a service. This file contains some basic information which the user inputs during the flow such as service ownership, Groups, and external docs.

In some cases, you may want to generate your own Service Descriptor. This may because you want to add Custom Data, auto-generate groups based on the template they've selected, add placeholder links, or other such advanced use cases.

To override the Service Descriptor generated by Cortex, just include a cortex.yaml or cortex.yml file in the template. If Cortex detects that the template has generated either one of these files, the Scaffolder will skip the Service Descriptor generation step.

Hiding template variables in the UI

As the complexity of your template grows, you may have variables in your cookiecutter.json that you want to hide from the UI, for example if they're autogenerated using Python or they're not something you want users to override at all.

To hide fields, just add a @cortex_hidden_keys field containing a list of variables you want to hide.

cookiecutter.json
{
"project_name": "My Project",
"project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|trim() }}",
"@cortex_hidden_keys": ["project_slug"],
"version": "0.0.1"
}

We hide the version key by default, so you don't need to add that to the list!

If you have "private variables" (variables starting with _, such as "_my_secret_variable"), they will automatically be hidden from the UI.

Consuming Cortex fields in a template

The Cortex Scaffolder flow requires users to input additional information aside from the fields defined in the cookiecutter.json file when creating a service. This includes:

  • Git repository details
  • Ownership
  • Cortex entity tag
  • Groups
  • Dependencies
  • Template details
  • etc

You may want your template to use this information, without duplicating these fields in your cookiecutter.json and having developers fill out the information twice! For example, you may want to use the name of the git repo that's created while generating a deployment script in the template.

These fields are available to your template in a magic _@cortex_inputs variable.

The _@cortex_inputs variable

The structure of _@cortex_inputs is a dictionary that will always contain the following:

{
"git": { ... }, // varies based on git provider, see below
"user": {
"name": "Full Name",
"email": "user@email.com"
},
"template_details": {
"name": "My template name",
"description": "My template description",
"tags": ["list", "of", "tags"]
}
}

See below for details on the git object, which contains information on the repository that Cortex is creating for the new project.

Using it in a template

You can access this variable in your template the same way you do for any of your own variables: {{ cookiecutter['_@cortex_inputs'] }}.

If your template has the "Requires a new service" flag enabled, _@cortex_inputs will additionally contain the following:

{
"service_details": {
"tag": "my-service",
"name": "My Service",
"description": "Optional description if defined",
"owners": [
{"type": "SLACK", "channel": "my-channel"},
{"type":"GROUP", "name": "my-group"},
{"type": "EMAIL", "email": "user@cortex.io"}
],
"serviceGroups": ["list", "of", "groups"],
"dependencies": [
{"type": "service", "tag": "service-dependency", "method": "GET", "path": "/test-path", "description": "This is an service dependency" },
{"type": "rds", "tag": "rds-dependency", "description": "This is an rds dependency" },
{"type": "s3", "tag": "s3-dependency", "description": "This is an s3 dependency" }
]
}
}

Git details

The git object shown above is different based on the provider you're using. The structure is shown below.

GitHub
{
"git": {
"repository": {
"org": "cortexapps",
"repoName": "my-repo"
}
}
}
GitLab
{
"git": {
"repository": {
"namespace": "cortexapps",
"projectName": "my-repo"
}
}
}
Bitbucket
{
"git": {
"repository": {
"workspace": "cortexapps",
"repoSlug": "my-repo"
}
}
}
Azure DevOps
{
"git": {
"repository": {
"projectName": "cortexapps",
"repoName": "my-repo"
}
}
}

Extending Cookiecutter: Cortex field configurations

You can add metadata to each variable that Cortex will display during the scaffolding process.

Adding descriptions to inputs

You can tell the Scaffolder to display description of each input field by specifying the description key within the variables configuration. For text inputs, you can also surface the default values from your cookiecutter.json by marking use_default as true.

Here's an example cookiecutter.json, where full_name is an input consumed by the template. We've defined the _cortex_field_configurations object that configures a description to be displayed in the UI for the full_name variable.

We've also enabled the use of default values for this input.

{
"full_name": "Albert Einstein",
"_cortex_field_configurations": {
"full_name": {
"description": "The author's full name.",
"use_default": true
}
}
}

Field description during variable input