Skip to main content

Standardization (Idea)

Before we begin, I would like to emphasize that none of this is necessary, it is just an idea, but it also serves as a learning exercise.

Templates generate things, and the ideal is to generate something with a certain standard that makes it easy to find and relate things within Backstage.

To facilitate the search for templates, let's organize them by tags. We can define that a template needs to assume one of the following tags:

  • code: If it's a template that will generate a code skeleton for development.
  • infra: If it's a template that will generate some infrastructure as code to create a resource.
  • general: What doesn't fit into any of them.

We can start to observe a pattern here already.

  • Each template has a docs folder with an index.md inside explaining what that template does.
  • Each template is a repository and has its catalog-info.yaml that will define the template and must have the tags proposed above.
  • Each template will define the owner, which must be an existing group.
  • Each template has a template folder with the content of what will be modified to generate a code skeleton output.

To standardize this, I decided to create this template generator.

The idea is to create a template that generates new templates with a certain standard. Read through this repository quickly to understand the proposal.

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template

metadata:
name: template-generator
title: Template to create template
description: Generator using basic organizational rules

annotations:
backstage.io/techdocs-ref: dir:. # Inside the directory we will have documentation for this template
backstage.io/time-saved: PT4H
tags:
- general
spec:
owner: team-idp
type: ground
lifecycle: production

parameters:
- title: General Inputs
required:
- name
- owner
- principalTag
properties:
name:
# here we have the template name that can only use lowercase letters and -
title: Template Name
type: string
pattern: "^[a-z-]+$"
description: Template Name
ui:autofocus: true
ui:help: 'Only lowercase and dashes'
# the owner must exist in the system and must be of type team. We won't let a user be the owner of anything in the system, only teams.
owner:
title: Owner
type: string
description: Owner of the component
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
spec.type: team
# The choice of the principal tag that we mentioned above.
principalTag:
title: General Tag
type: string
description: Principal Tag
default: general
enum:
- code
- infra
- general
ui:help: 'Used to Group'
# A list of other tags that can be added besides the principal tag. Only lowercase letters.
otherTags:
description: List of other tags (only lowercase letters and hyphen allowed)
type: array
items:
type: string
pattern: "^[a-z]+$"
ui:field: MultiTagInputField
uniqueItems: true
errorMessage:
properties:
name: 'Only lowercase and dashes can be used'

# Here I simply limited everything to be in my own account.
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
description: The prefix backstage-template- will be added automatically
type: string
ui:field: RepoUrlPicker
ui:help: |
SAME NAME GIVEN TO THE TEMPLATE
ui:options:
allowedHosts:
- github.com
- gitlab.com
allowedOrganizations:
- davidpuziol
allowedOwners:
- davidpuziol


# A documentation entry if desired, but it's not necessary, this can be done later in the repository that will be generated.
- title: Documentation
properties:
description:
title: Write some description to be added to documentation
type: string
ui:widget: textarea
ui:options:
rows: 10
ui:placeholder: |
Do not forget write documentation!
steps:
- id: fetch-base
name: Fetch Base
action: fetch:template # This action is to populate the template with our input values
input:
url: ./template
values:
name: ${{parameters.name}}
owner: ${{parameters.owner}}
principalTag: ${{parameters.principalTag}}
otherTags: ${{parameters.otherTags}}
description: ${{parameters.description}}

- id: publish
name: Publish
action: publish:github # publish to the repository we chose, but we will add a backstage-template- prefix to the chosen repository name.
input:
allowedHosts: ['github.com']
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl.split("repo=")[0] }}repo=backstage-template-${{ parameters.repoUrl.split("repo=")[1] }}
repoVisibility: private
- id: register
name: Register
action: catalog:register # Notify backstage about this repository
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'
output:
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }} # show the repository output url.

Everything we will populate will be in the ./templates folder, which in this case is practically the same code as the generator template, but with some differences. This will force every template to have the structure below.

The ./template folder has the following:

❯ tree template
template
β”œβ”€β”€ catalog-info.yaml # The next template, based on the parent template.
β”œβ”€β”€ docs # documentation folder
β”‚ └── index.md # The documentation
└── template # template folder that it will generate
└── main # first file indicating that this is where you need to put the content of things.

2 directories, 3 files

Inside this template folder, the user needs to create their component, resource, or anything else. This is where everything will actually begin. We could copy files to this folder based on some other input that exists in the main template.

For example, we could already ask if it's a component, a service, a resource, etc. Depending on this input, we will already add a catalog-info.yaml there with this resource ready.

Sometimes centralizing, as we are doing to avoid many study repositories, can be a strategy if the organization only allows a finite group of people to create templates.