userguides

package module
v0.45.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 8 Imported by: 0

README

Spacelift User Guides Library

A library of user guides for Spacelift, stored as code and embedded into the Spacelift backend.

Purpose

This repository serves as the source of truth for Spacelift user guides content. By storing guides as structured YAML files in version control, content can be managed independently from the backend code. The library embeds this content at compile time using Go's embed.FS, providing type safety and compile-time validation that ensures invalid guides are caught during build rather than at runtime. This approach enables content contributors to add or modify guides through standard pull request workflows while maintaining consistency and reliability.

Directory Structure

The repository uses a hierarchical directory structure that mirrors the GraphQL schema (Group → Chapter → Guide):

guides/
├── {group-slug}/
│   ├── group.yaml              # Group metadata
│   ├── {chapter-slug}/
│   │   ├── chapter.yaml       # Chapter metadata
│   │   ├── {guide-slug}.yaml  # Individual guide
│   │   └── ...
│   └── ...
└── ...
Example Structure
guides/
├── getting-started/
│   ├── group.yaml
│   ├── first-stack/
│   │   ├── chapter.yaml
│   │   ├── create-stack.yaml
│   │   ├── configure-vcs.yaml
│   │   └── add-policy.yaml
│   └── collaboration/
│       ├── chapter.yaml
│       ├── invite-users.yaml
│       └── manage-teams.yaml
└── advanced-workflows/
    ├── group.yaml
    ├── drift-detection/
    │   ├── chapter.yaml
    │   └── enable-drift.yaml
    └── custom-inputs/
        ├── chapter.yaml
        └── terraform-variables.yaml

Slug Generation

Slugs are automatically derived from the directory structure:

  • Group Slug: Directory name (e.g., getting-started)
  • Chapter Slug: Directory name (e.g., first-stack)
  • Guide Slug: Filename without extension (e.g., create-stack)

Slugs provide human-readable identifiers at each level of the hierarchy. The hierarchical relationships (Group → Chapter → Guide) are maintained through the nested data structure. This approach eliminates manual ID management and ensures uniqueness within each level.

File Formats

group.yaml

Defines metadata for a group of guides.

name: "Getting Started"
description: "Learn the basics of Spacelift"
skillLevel: BEGINNER  # BEGINNER, ENABLER, COMMANDER, or GUARDIAN
ordering: 1
requiredEntitlements:    # optional - Spacelift entitlements the account must have for this group
  - NOTIFICATION_POLICIES

Required Fields:

  • name (string): Display name of the group
  • description (string): Brief description of the group
  • skillLevel (string): One of BEGINNER, ENABLER, COMMANDER, or GUARDIAN
  • ordering (int): Display order (lower numbers appear first)

Optional Fields:

  • requiredEntitlements ([]string): Spacelift product entitlements the user's account must have to complete the guides in this group. Each entry must be one of NOTIFICATION_POLICIES, RUN_STATE_CHANGE_WEBHOOKS. Values mirror the backend Entitlement GraphQL enum (1:1 string match) — the backend maps each entitlement to its own billing / feature-flag model. Omit (or leave empty) when the group is available on every plan (including Free). The recognised set is intentionally small — when adding a group that depends on a new gated feature (e.g. private workers, blueprints, drift detection, push policies), extend the Entitlement constants in library.go, the validEntitlements map, and the enum in schema/group_schema.json, and make sure the new value exists verbatim in the backend Entitlement enum.
chapter.yaml

Defines metadata for a chapter within a group.

name: "Your First Stack"
description: "Create and manage your first stack"
ordering: 1

Required Fields:

  • name (string): Display name of the chapter
  • description (string): Brief description of the chapter
  • ordering (int): Display order within the group (lower numbers appear first)
{guide-slug}.yaml

Defines an individual guide with metadata, steps, and completion information.

ordering: 1
metadata:
  title: "Create Your First Stack"
  description: "Learn how to create a stack in Spacelift"
  labels: ["terraform", "basics"]
  difficulty: "easy"
  minutesToComplete: 10

steps:
  - order: 1
    title: "Navigate to Stacks"
    instruction: "Click on **Stacks** in the left sidebar to open the stacks page."
    hint: "If you don't see the sidebar, click the menu icon in the top-left corner."
    docs:
      - title: "What is a Stack?"
        url: "https://docs.spacelift.io/concepts/stack"

  - order: 2
    title: "Create New Stack"
    instruction: "Click the **Add Stack** button in the top-right corner."
    docs:
      - title: "Stack Creation Guide"
        url: "https://docs.spacelift.io/concepts/stack/creating-a-stack"

completion:
  successMessage: "Congratulations! You've created your first stack. Next, learn how to connect it to your VCS."
  recommendedGuideIds:
    - "getting-started/first-stack/configure-vcs"
    - "getting-started/first-stack/add-policy"

Required Fields:

  • ordering (int): Display order within the chapter (lower numbers appear first)

Optional Top-Level Fields:

  • prerequisiteGuideSlugs ([]string): Slugs of guides that must be completed first

metadata:

  • title (string): Display title of the guide
  • description (string): Brief description
  • labels ([]string): Tags for categorization
  • difficulty (string): Difficulty level (e.g., "easy", "medium", "hard")
  • minutesToComplete (int): Estimated time to complete (must be >= 0)

steps:

  • order (int): Step number (must be > 0, unique within guide)
  • title (string): Step title
  • instruction (string): What the user should do (supports Markdown)
  • hint (string, optional): Additional help text
  • docs ([]object, optional): Related documentation links
    • title (string): Link text
    • url (string): Documentation URL

completion:

  • successMessage (string): Message shown when guide is completed
  • recommendedGuideIds ([]string): IDs of guides to suggest next

Content Style Conventions

Guide instructions, hints, and other user-facing strings support Markdown. To keep the experience consistent across the product, follow these formatting rules.

Actions — bold

Use bold for any UI element the user is acting on — clicking, toggling, enabling, disabling, checking, typing into, or selecting from. That includes buttons, links, toggles, checkboxes, and values being entered into a field (including code identifiers like TF_VAR_env or subnet_id — when the user is typing them in, the action wins over the code style).

instruction: "Click **Create stack**, set Workflow tool to **OpenTofu**, then click **Continue**."
instruction: "Enable **Autodeploy** and click **Save**."
instruction: "Enable both **Read** and **Write** permissions."
instruction: "In the `Output name` field, enter **subnet_id**."

The rule is acting vs. referring. If the user is doing something to a toggle, its label is bold. If the user is checking that a toggle is in a certain state, or the toggle is just being referenced in prose, its label is backticked (see the next section).

User-named entities — references to a stack, policy, context, or space the user created earlier — are also bold, because they behave like proper nouns of UI entities:

instruction: "Open your stack **${main_stack_name}** and click **Trigger**."
Titles — code style (Inconsolata)

Use backticks for anything that names a UI location, label, or literal code identifier — the "where you are" rather than "what you click":

  • Page/screen names, tabs, menu paths and breadcrumbs, sections within a page
  • Field labels (when used as locators, e.g. "the Role ARN field")
  • Setting/toggle names when referenced or verified, not acted on (e.g. "make sure Autodeploy is disabled" or "the Autodeploy setting controls…")
  • File names (main.tf)
  • Code identifiers when referenced in descriptive prose — variable names, resource types, output names, function names, etc.
  • Label literals (env:production, autoattach:*) when referenced in descriptive prose
instruction: "Navigate to `Ship Infra > Stacks` and open the `Stacks` page."
instruction: "Go to `Settings > Behavior` and verify `Autodeploy` is enabled."
instruction: "Edit `main.tf` to add the `aws_s3_bucket` resource."
hint: "The `data_store_bucket` output should show the bucket name."

Note: when a UI element is being acted on, or a code identifier/label literal is the value being entered into a field, it's bold, not code — see the actions rule above.

Reserve bold for the actual action within that location (e.g. "In the Policies tab, click Attach policy.").

Menu paths and breadcrumbs

When the user navigates down a UI hierarchy (menus, sub-menus, settings paths), write the path as a single breadcrumb inside one backtick span using > as the separator. Do not break it up with words like "then" or with multiple backtick spans.

# Good
instruction: "Open the three-dot menu and go to `Settings > Behavior`."
instruction: "Navigate to `Ship Infra > Stacks` and click **Create stack**."
instruction: "Go to `Settings > Integrations > Cloud integrations`."

# Bad
instruction: "Go to the `Settings` tab, then `Integrations`."
instruction: "Go to `Settings`, then `Behavior`."

If the entry point isn't obvious (e.g. behind a kebab menu or context menu), state it before the breadcrumb: "Open the three-dot menu and go to Settings > Behavior."

For acting on a section that's already visible within the current page (not a deeper menu hop), use "In the X section/tab" instead of a breadcrumb:

instruction: "In the `Depends on` section, click **Add dependencies**."
instruction: "In the `Policies` tab, click **Attach policy**."

For top-level Spacelift pages, the breadcrumb format is also acceptable inside a Markdown link: [Ship Infra > Stacks](/stacks).

Statuses — ALL CAPS, no formatting

Run states, phases, and similar status values are written in ALL CAPS without bold or backticks. They're already visually distinct and match how Spacelift renders them in the UI.

instruction: "Watch the run progress through INITIALIZING → PLANNING → UNCONFIRMED → APPLYING → FINISHED."
hint: "If the run enters the FAILED state, check the logs for details."
System-returned text — italic in double quotes, after a colon

When you quote a string the system produces — a policy denial message, an error notification, a log line the user should look for — wrap it in double quotes and italicize the whole quote. Introduce it with a colon, not a dash, so the quote reads as the thing the system says.

Code identifiers that appear inside the quoted system message (tag names, resource names, etc.) stay as plain text inside the italics — the italic styling already marks the whole span as system output, so don't re-format pieces of it with backticks or bold.

# Good
instruction: "The run should FAIL with a policy denial: *\"your S3 bucket is missing the cost-center tag.\"*"
instruction: "You should see a deny message: *\"S3 bucket ... is missing required 'cost-center' tag.\"*"

# Bad
instruction: "The run should FAIL with a policy denial - your S3 bucket is missing the `cost-center` tag."
instruction: "You should see a deny message: `S3 bucket ... is missing required 'cost-center' tag`."

This is for quoted system text. When you're describing the message in your own words ("a policy denial about the missing cost-center tag"), keep the existing rules — backticks for the code identifier, no italics.

Quick reference
Element Style Example
Buttons, links, toggles, checkboxes, values being entered (including code identifiers entered as field values) Bold Click Trigger; Enable Autodeploy; enter subnet_id
User-named entities (${stack_name} etc.) Bold Open your stack ${main_stack_name}
Page names, tabs, menu paths, sections, field labels Code Open Settings > Behavior; in the Policies tab
Menu paths / breadcrumbs (full hierarchy in one span, > as separator — never split with "then") Code Settings > Integrations > Cloud integrations
Setting/toggle names when referenced, not acted on Code Make sure Autodeploy is enabled
File names Code Edit main.tf
Code identifiers referenced in prose Code The aws_s3_bucket resource; the TF_VAR_ prefix
Label literals referenced in prose Code Stacks with the env:production label
Run states, phases, statuses ALL CAPS, no formatting Wait for FINISHED
System-returned text (policy messages, errors, log lines) Italic in double quotes, after a colon … policy denial: "your S3 bucket is missing the cost-center tag."

How to Add New Guides

1. Create or Navigate to a Group

If the group doesn't exist, create a new directory under guides/:

mkdir -p guides/my-group

Create group.yaml with the group metadata:

name: "My Group"
description: "Description of this group"
skillLevel: BEGINNER
2. Create a Chapter

Create a chapter directory within the group:

mkdir -p guides/my-group/my-chapter

Create chapter.yaml with the chapter metadata:

name: "My Chapter"
description: "Description of this chapter"
3. Create a Guide

Create a YAML file for your guide in the chapter directory:

touch guides/my-group/my-chapter/my-guide.yaml

Fill in the guide content following the format described above. The filename (without extension) will become the guide's slug.

4. Test Your Changes

Run the tests to validate your guide structure:

go test -v

The library will validate:

  • YAML syntax: All YAML files must be valid
  • Required fields: All mandatory fields must be present
  • Unique slugs: No duplicate slugs within groups, chapters, or guides
  • Skill level enum: Must be BEGINNER, ENABLER, COMMANDER, or GUARDIAN
  • Difficulty enum: Must be easy, medium, or hard (if specified)
  • Step ordering: Steps must be sequentially ordered (1, 2, 3...) with no gaps
  • URL validation: Documentation URLs must use http or https schemes
  • Label validation: Labels must be non-empty strings
  • Referential integrity: RecommendedGuideIds must reference existing guides
  • Non-negative values: MinutesToComplete must be >= 0
5. Submit a Pull Request

Once tests pass, commit your changes and create a pull request. The CI pipeline will run validation automatically.

Validation and Testing

Compile-Time Validation

The library validates all content at build time by calling Guides(). Invalid content causes a panic, ensuring problems are caught before deployment:

func Guides() (*Library, error) {
    lib, err := parse(guidesFS)
    if err != nil {
        panic("userguides: " + err.Error())
    }
    return lib, nil
}
Validation Rules

Structural Validation:

  • Required YAML files must exist (group.yaml, chapter.yaml)
  • All required fields must be present
  • No duplicate slugs within groups, chapters, or guides
  • Steps must be sequentially ordered (1, 2, 3...) with no gaps or duplicates

Type Validation:

  • SkillLevel: Must be one of BEGINNER, ENABLER, COMMANDER, GUARDIAN
  • Difficulty: Must be one of easy, medium, hard (if specified)
  • RequiredEntitlements (group-level): If specified, each entry must be a valid entitlement (NOTIFICATION_POLICIES, RUN_STATE_CHANGE_WEBHOOKS); duplicates and empty values are rejected
  • MinutesToComplete: Must be >= 0
  • Labels: Must be non-empty strings (no whitespace-only labels)
  • URLs: Must use http or https scheme and be well-formed

Referential Integrity:

  • RecommendedGuideIds must reference existing guides
  • Full guide paths are validated (group/chapter/guide)

Error Handling:

  • Validation failures cause a panic at build time (not runtime)
  • Error messages include file paths and specific validation failures
  • This ensures invalid content breaks the build, not production
Running Tests
# Run all tests
go test -v

# Run specific test
go test -v -run TestGuidesLoad

# Check test coverage
go test -v -cover

CI/CD Pipeline

Automated Validation

The repository uses GitHub Actions to automatically validate all content changes. The CI pipeline runs on:

  • Every push to the main branch
  • Every pull request targeting main
Workflow Steps

The validation workflow (.github/workflows/validate.yml) performs the following checks:

  1. Checkout Code: Retrieves the repository code
  2. Setup Go: Installs Go using the version specified in go.mod
  3. Download Dependencies: Fetches all required Go modules
  4. Run Tests: Executes all tests with go test -v ./...
    • Triggers Guides() which validates all content
    • Runs comprehensive validation tests
    • Panics on any validation errors
  5. Verify go.mod: Ensures dependencies are correctly tracked
  6. Build Library: Compiles the library to catch any build errors
What Gets Validated

Every commit automatically checks:

  • ✅ YAML syntax correctness
  • ✅ Required fields presence
  • ✅ Unique slugs (no duplicates)
  • ✅ Valid enums (skill levels, difficulty)
  • ✅ Sequential step ordering
  • ✅ URL format validation
  • ✅ Referential integrity (recommendedGuideIds)
  • ✅ Label and field constraints
Pull Request Requirements
  • All CI checks must pass before merging
  • Any validation failure will block the PR
  • Review the CI output for specific error messages
  • Fix validation errors and push again to re-trigger CI

Integration with Backend

The Spacelift backend imports this library as a Go module:

import userguidelib "github.com/spacelift-io/spacelift-user-guides-library"

// Load guides
lib, err := userguidelib.Guides()

Content is synced to the database during migrations, similar to policy templates. See the design document for full integration details.

Development Workflow

  1. Make changes to guides in this repository
  2. Test locally with go test -v
  3. Submit PR for review
  4. Merge to main branch
  5. Backend update: Update go.mod in backend to new library version
  6. Deploy: Backend deployment syncs new content to database

Schema Versioning

Guide content is stored as JSONB in the database. If the schema needs to evolve, use the schema_version approach:

switch schema_version {
  case 1:
    json.Decode(content, &v1Guide)
  case 2:
    json.Decode(content, &v2Guide)
}

Design Document

For architectural details and implementation phases, see the full design document: User Guides Library - Design Document

License

Copyright © Spacelift, Inc.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Chapter

type Chapter struct {
	Slug        string
	Name        string
	Description string
	Ordering    int
	Variables   []GuideVariable
	Guides      []Guide
}

func (Chapter) Validate

func (c Chapter) Validate() error

type Entitlement added in v0.41.0

type Entitlement string

Entitlement is a Spacelift product feature a guide group requires the user's account to have. Values mirror the backend `Entitlement` GraphQL enum (1:1 string match) — the backend then maps each entitlement to its own billing / feature-flag model and decides whether a given account has it. When a group's `RequiredEntitlements` is empty the group has no feature requirements and is available on every plan (including Free).

The set of recognised entitlements below is intentionally small — only the features actually referenced by current guides are listed. Extend this enum (the constant, the validEntitlements map, and the matching enum in schema/group_schema.json) as new guide groups are added that depend on additional gated features, e.g. private workers, blueprints, drift detection, push policies, etc. Every value added here must exist in the backend `Entitlement` enum with the exact same string.

const (
	EntitlementNotificationPolicies   Entitlement = "NOTIFICATION_POLICIES"
	EntitlementRunStateChangeWebhooks Entitlement = "RUN_STATE_CHANGE_WEBHOOKS"
)

type Group

type Group struct {
	Slug                 string
	Name                 string
	Description          string
	SkillLevel           string
	Ordering             int
	RequiredEntitlements []Entitlement
	Chapters             []Chapter
}

func (Group) Validate

func (g Group) Validate() error

type Guide

type Guide struct {
	Slug                   string
	Ordering               int
	PrerequisiteGuideSlugs []string
	Metadata               GuideMetadata
	Steps                  []GuideStep
	Completion             GuideCompletion
}

func (Guide) Validate

func (g Guide) Validate() error

type GuideCompletion

type GuideCompletion struct {
	SuccessMessage      string   `yaml:"successMessage"`
	RecommendedGuideIDs []string `yaml:"recommendedGuideIds"`
}

type GuideDoc

type GuideDoc struct {
	Title string `yaml:"title"`
	URL   string `yaml:"url"`
}

type GuideMetadata

type GuideMetadata struct {
	Title             string   `yaml:"title"`
	Description       string   `yaml:"description"`
	Labels            []string `yaml:"labels"`
	Difficulty        string   `yaml:"difficulty"`
	MinutesToComplete int      `yaml:"minutesToComplete"`
	Prerequisites     []string `yaml:"prerequisites"`
}

type GuideStep

type GuideStep struct {
	Order          int        `yaml:"order"`
	Title          string     `yaml:"title"`
	Instruction    string     `yaml:"instruction"`
	Hint           string     `yaml:"hint"`
	ValidationHint string     `yaml:"validationHint"`
	Validation     string     `yaml:"validation"`
	Docs           []GuideDoc `yaml:"docs"`
}

type GuideVariable added in v0.9.0

type GuideVariable struct {
	Name         string               `yaml:"name"`
	Description  string               `yaml:"description"`
	ResourceType VariableResourceType `yaml:"resourceType"`
}

type Library

type Library struct {
	Groups []Group
}

func Guides

func Guides() (*Library, error)

type VariableResourceType added in v0.9.0

type VariableResourceType string
const (
	VariableResourceTypeStack          VariableResourceType = "stack"
	VariableResourceTypePolicy         VariableResourceType = "policy"
	VariableResourceTypeAWSIntegration VariableResourceType = "aws_integration"
	VariableResourceTypeContext        VariableResourceType = "context"
	VariableResourceTypeSpace          VariableResourceType = "space"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL