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.
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.
{
"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.
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
}
}
}