Software Templates
É uma ferramenta que pode ajudar você a criar qualquer coisa dentro do Backstage. Por padrão, ele tem a capacidade de carregar esquemas de código, template em algumas variáveis e então publicar o código preenchido em algum repositório.
- São templates pré-definidos que facilitam a criação de novos objetos no Backstage, como, componentes, APIs, serviços, frontends ou qualquer outro tipo de software e infra as a code.
- Esses templates podem incluir estruturas de pastas, configurações iniciais, código boilerplate e integrações básicas (como CI/CD ou monitoramento).
- A ideia é economizar tempo e garantir que novos projetos sigam os padrões de arquitetura e segurança definidos pela organização.
Gosto desse recurso porque ele permite a padronização por facilidade ao invés da padronização por restrição, quando em algum da organização algum política impõe regras sem dizer como implementar o seu código.
Essa é parte do Backstage que trará o self-service para os desenvolvedores. A maioria do trabalho consiste em criar templates!
Scafffolder
- É o motor responsável por processar os templates e gerar novos projetos/códigos.
- Utiliza arquivos de configuração yaml para definir as entrada (parameters) e os passos (actions) necessários na criação de um novo componente ou recurso.
- Novas actions são disponibilizadas através de plugins.
De acordo com a documentação, usando o node a partir da versão 20 é necessário rodar com o parâmetro --no-node-snapshot (NODE_OPTIONS=--no-node-snapshot) para que o scaffolder funcione.
#podemos colocar os parâmetros que o node usa direto em um variável de ambiente. Obviamente usaremos isso mais tarde quando rodarmos o Backstage em container.
export NODE_OPTIONS=--no-node-snapshot
Vantagens
- Automação: Diminui a necessidade de criar projetos manualmente, evitando erros.
- Consistência: Garante que todos os projetos sigam as mesmas diretrizes organizacionais.
- Escalabilidade: Ajuda a adotar boas práticas rapidamente em equipes de todos os tamanhos.
- Onboarding facilitado: Novos desenvolvedores conseguem começar rapidamente com uma base sólida de projeto.
Se um desenvolvedor quer começar a construir um novo serviço, uma aplicação, um componente, um recurso, seja lá o que for, ele pode ir na parte /create
e preencher um formulário de um template pronto.
Básico de Templates
O template consiste em criar um formulário (definir valores) que serão passados como parâmetros e a partir destes executamos actions sequenciais. Sim sim.. é bem parecido com uma pipeline.
Diga o que você quer escolhendo um template, passe os dados necessário e lhe dou o schema pronto.
Existe um exemplo que vem com o próprio Backstage para a criação que cria um serviço nodejs.
Rodei sem o parâmetro --no-node-snapshot para ver o que acontece.
Agora com o parâmetro.
Agora temos outro problema. A idéia era criar um repositório com um modelo de desenvolvimento em nodejs e obviamente ele irá gerar código para publicar no repositório escolhido. O problema é que neste Backstage esta faltando instalar o plugin que executa a action publish, logo precisamos adicionar.
yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend
# Se quiser já adiciona do gitlab se estiver configurado nas integrações.
E adicionar para que seja carregado em packages/backend/src/index.ts
.
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
//Se for utilizar o gitlab adicione também abaixo
backend.add(import('@backstage/plugin-scaffolder-backend-module-gitlab'));
Podemos definir qual será a mensagem do commit inicial, o autor o email, etc.
E reinicie o Backstage. Depois tente novamente. Se olharmos o código deste template. Poderemos entender o que irá acontecer.
scaffolder:
defaultAuthor:
name: Backstage Scaffolder # Defaults é Scaffolder
#email: [email protected] # Este é o default se não for definido
#defaultCommitMessage: "Initial commit" # Este também é o default
apiVersion: scaffolder.backstage.io/v1beta3
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-template
kind: Template # O tipo
metadata:
name: example-nodejs-template # nome do template
title: Example Node.js Template
description: An example template for the scaffolder that creates a simple Node.js service
spec:
# Será usado no spec do componente criado
owner: user:guest
type: service
# These parameters are used to generate the input form in the frontend, and are
# used to gather input data for the execution of the template.
# OS PARAMETROS QUE SERÃO NECESSÁRIOS NAS AÇÕES QUE VAMOS UTILIZAR NOS STEPS.
parameters:
- title: Fill in some steps
required:
- name
properties:
name:
title: Name
type: string
description: Unique name of the component
ui:autofocus: true
ui:options:
rows: 5
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options: # As opções que poderemos escolher
allowedHosts:
- github.com
- gitlab.bom # Adicionei
steps:
- id: fetch-base
name: Fetch Base
action: fetch:template
input:
url: ./content # Onde está o modelo de preenchimento
values:
name: ${{ parameters.name }}
- id: publish
name: Publish
action: publish:github # Aqui aconteceu o problema, não tinhamos essa action disponível.
input:
allowedHosts: ['github.com']
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl }}
#repoVisibility: private # or 'internal' or 'public'. private é o default.
# The final step is to register our new component in the catalog.
- id: register
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'
# Saídas geradas
output:
links:
- title: Repository # O link para o repositório vindo do step publish
url: ${{ steps['publish'].output.remoteUrl }}
- title: Open in catalog # link do catalog para esta entidade.
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
Veja as saídas.
No repositório que ele criou já temos o catalog-info.yaml na raíz e alguns outros arquivos.
Nos aprofundando ainda mais, esses template foi incluindo diretamente no app-config.yaml, jájá vamos remover, mas vamos olhar primeiro o que temos.
tree
.
├── entities.yaml # Alguns exemplos de entidades
├── org.yaml # User e group
└── template # nesta pasta é o que nos interessa.
├── content
│ ├── catalog-info.yaml
│ ├── index.js
│ └── package.json
└── template.yaml
3 directories, 6 files
Na definição do template podemos observar que a ação fetch-base usar os arquivos em ./content e todos os lugares que tivermos name receberão o parâmetro name que foi passado durante chamada do template.
parameters:
- title: Fill in some steps
required:
- name
properties:
name: ## <<<
title: Name
type: string
description: Unique name of the component
ui:autofocus: true
ui:options:
rows: 5
- id: fetch-base
name: Fetch Base
action: fetch:template
input:
url: ./content # Onde está o modelo de preenchimento
values:
name: ${{ parameters.name }}
Pegando um dos arquivos, temos isso.
console.log('Hello from ${{ values.name }}!'); //Será preenchido
E no repositório ficou isso console.log('Hello from teste-scaffolder!');
Uma coisa interessante na criação dos templates é agrupá-los por tag na página create que mostram os templastes. Quando criar os templates procure usar as tags.
const routes = (
<FlatRoutes>
{/* ... */}
{/* <Route path="/create" element={<ScaffolderPage />} /> */}
<Route
path="/create"
element={
<ScaffolderPage
groups={[
{
title: "infra",
filter: entity =>
entity?.metadata?.tags?.includes('infra') ?? false,
},
{
title: "code",
filter: entity =>
entity?.metadata?.tags?.includes('code') ?? false,
},
]}
/>
}
/>
{/* ... */}
</FlatRoutes>
);
Entendendo o processo vamos então deletar essas pastas e remover os locations do app-config.yaml referentes a estas pois não precisaremos mais.
Curiosidade, o que acontece se apagarmos este recurso? Existe o processo de deletar a entidade e de desregistrar.
Se deletarmos a entidade ela irá reaparecer pois o catalog foi configurado para sincronizar com o que temos no repositório de tempos em tempos.
Desregistrar evitará que o respositório seja lido novamente e todas as entidades do repositório irão ser deletadas.
fetch:template vs fetch:cookiecutter
O Backstage decidiu simplificar a criação de templates ao substituir o Handlebars e o Cookiecutter por Nunjucks:
Antes, havia várias linguagens de template (Handlebars e Cookiecutter), o que tornava tudo mais confuso e complicado de manter. Agora, Nunjucks é a linguagem padrão para unificar tudo. Por que mudar?
- Menos dependências externas.
- Mais recursos integrados e flexíveis (filtros avançados, por exemplo).
- Sintaxe mais consistente e semelhante ao Jinja2, facilitando o uso.
Templates antigos precisarão ser atualizados, apesar de ser simples. Se hoje for começar no Backstage é bom partir direto pro fetch:template, mesmo que veja alguns tutoriais usando o cookiecutter.
O cookiecutter ainda é mais poderoso do que o templates, mas será por pouco tempo.