PlatformEngr® Backstage course

Cheat notes · Module 06 · Golden paths and docs

Golden paths with software templates

A five-minute review for when you have done this module and want it back in your head. Open the full module for the steps and the lab, or the full text to print.

The idea

A template makes the right way the easy way: a form (parameters), actions run on the backend (steps), and links (output). The service starts with an owner, a system, docs and a catalog entry from its first commit.

Remember

  • Parameters: JSON Schema with ui: hints, one form page per item. Each property becomes parameters.<name>. Pickers (OwnerPicker, EntityPicker, RepoUrlPicker) read the catalog.
  • Steps: each runs one action on the backend with the backend's credentials: fetch:template renders the skeleton, publish:github creates the repo, catalog:register adds it. Outputs feed later steps: steps['publish'].output.repoContentsUrl.
  • Skeleton: files with ${{ values.x }} placeholders, rendered with Nunjucks by fetch:template.
  • Templates are entities: registered through a location that allows Template, owned by the platform team.
  • Dry run: the template editor (Create, ⋮, Template Editor) runs steps without side effects and shows files, log and output. Actions without dry-run support are skipped.

Pitfalls

  • Removing a value from the fetch step that "looks unused": the template still runs, and the rendered file has system: with nothing after it. Dry-run every template in CI and assert key fields.
  • Shipping a clever first template nobody needs. Start with the service type teams create most often.

Commands

curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" "localhost:7007/api/catalog/entities?filter=kind=template" | jq -r '.[].metadata.name'Which templates the catalog has
gh repo view OWNER/cart --json name,descriptionThe repo the template created

The skeleton of a template

spec:
  parameters:
    - title: About the service
      required: [name, owner]
      properties:
        owner:
          type: string
          ui:field: OwnerPicker
          ui:options:
            catalogFilter: { kind: Group, spec.type: team }
  steps:
    - id: fetch
      action: fetch:template
      input:
        url: ./skeleton
        values:
          owner: ${{ parameters.owner }}
    - id: publish
      action: publish:github
    - id: register
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}

Test yourself

Answer in your head first, then open the answer.

Where do template steps run?

On the backend, with the backend's credentials, one action per step.

What does a dry run in the template editor do?

Runs the steps without side effects and shows the rendered files, the log and the output.

Why does the owner field use OwnerPicker with a catalogFilter?

It only offers teams that exist, so an owner typo is impossible.

Where module 06 leaves you: The portal offers one template, Node.js service. A cart repo exists on GitHub, created by the template and already in the catalog, owned by team-checkout.