Scaffolder Backend Module Rails logo

Backstage Scaffolder Backend Module Rails Plugin

Created by Rogerio Angeliski

Scaffolder Backend Module Rails adds Rails support to Backstage Scaffolder. It lets your templates create a new Rails app with the defaults your team needs. You keep using Scaffolder. This module provides a Rails action so your template can call rails new behind the scenes.

At a high level it fits into the flow you already know. A template can fetch a base, generate a Rails app with your chosen options, write catalog info, publish a repo, then register the service in the catalog. Inputs can cover common choices like minimal setup, API only mode, database selection, skipping bundle, or using a custom template file. The result is a working Rails project that follows your standards from the first commit.

Typical use cases include spinning up a new Rails service, creating an API only app for a backend, or bootstrapping a service with a predefined structure. It also helps platform teams define one way to start a Rails project so every service begins with the same layout, linting, and dependencies. You can run it where Rails is available locally. You can also run it in a container image as part of your template. This reduces friction for teams and makes new service creation fast and repeatable.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the backend module

  1. Add the package to your backend
Copy
yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-rails
  1. Wire it in with the new backend system

Edit packages/backend/src/index.ts

Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-rails'));

backend.start();
  1. Wire it in with the old backend system

Edit packages/backend/src/plugins/scaffolder.ts

Copy
// packages/backend/src/plugins/scaffolder.ts
import { Router } from 'express';
import { CatalogClient } from '@backstage/catalog-client';
import {
  createRouter,
  createBuiltinActions,
  DockerContainerRunner,
} from '@backstage/plugin-scaffolder-backend';
import { ScmIntegrations } from '@backstage/integration';
import { createFetchRailsAction } from '@backstage/plugin-scaffolder-backend-module-rails';
import { PluginEnvironment } from '../types';

export default async function createPlugin(env: PluginEnvironment): Promise<Router> {
  const { logger, config, database, reader, discovery } = env;

  const catalogClient = new CatalogClient({ discoveryApi: discovery });
  const integrations = ScmIntegrations.fromConfig(config);

  const containerRunner = new DockerContainerRunner({ logger });

  const actions = [
    ...createBuiltinActions({
      integrations,
      config,
      reader,
      catalogClient,
      containerRunner,
      logger,
    }),
    createFetchRailsAction({
      logger,
      containerRunner,
    }),
  ];

  return await createRouter({
    logger,
    config,
    database,
    reader,
    catalogClient,
    actions,
  });
}

If you do not plan to use Docker, you can pass undefined for containerRunner in both places above

Add a template that uses the action

Place a Template YAML in your software templates location. Many apps keep them under a folder named templates or similar, then register them in the catalog

Copy
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: rails-demo
  title: Rails template
  description: scaffolder Rails app
spec:
  owner: backstage/techdocs-core
  type: service

  parameters:
    - title: Fill in some steps
      required:
        - name
        - owner
      properties:
        name:
          title: Name
          type: string
          description: Unique name of the component
          ui:autofocus: true
          ui:options:
            rows: 5
        owner:
          title: Owner
          type: string
          description: Owner of the component
          ui:field: OwnerPicker
          ui:options:
            catalogFilter:
              kind: Group
        system:
          title: System
          type: string
          description: System of the component
          ui:field: EntityPicker
          ui:options:
            catalogFilter:
              kind: System
            defaultKind: System

    - title: Choose a location
      required:
        - repoUrl
        - dryRun
      properties:
        repoUrl:
          title: Repository Location
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts:
              - github.com
        dryRun:
          title: Only perform a dry run, don't publish anything
          type: boolean
          default: false
        railsArguments:
          title: arguments to run the rails new command
          type: object
          properties:
            minimal:
              title: minimal
              description: Preconfigure a minimal rails app
              type: boolean
            skipBundle:
              title: skipBundle
              description: Don't run bundle install
              type: boolean
            skipWebpackInstall:
              title: skipWebpackInstall
              description: Don't run Webpack install
              type: boolean
            api:
              title: api
              description: Preconfigure smaller stack for API only apps
              type: boolean
            template:
              title: template
              description: Path to some application template can be a filesystem path or URL
              type: string
              default: './rails-template-file.rb'
            webpacker:
              title: webpacker
              description:
                Preconfigure Webpack with a particular framework options react
                vue
                angular
                elm
                stimulus
              type: string
              enum:
                - react
                - vue
                - angular
                - elm
                - stimulus
            database:
              title: database
              description: Preconfigure for selected database options mysql postgresql sqlite3 oracle sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc
              type: string
              enum:
                - mysql
                - postgresql
                - sqlite3
                - oracle
                - sqlserver
                - jdbcmysql
                - jdbcsqlite3
                - jdbcpostgresql
                - jdbc
            railsVersion:
              title: Rails version in Gemfile
              description:
                Set up the application with Gemfile pointing to a specific version
                options dev
                edge
                master
              type: string
              enum:
                - dev
                - edge
                - master

  steps:
    - id: fetch-base
      name: Fetch Base
      action: fetch:rails
      input:
        url: ./template
        values:
          name: ${{ parameters.name }}
          owner: ${{ parameters.owner }}
          system: ${{ parameters.system }}
          railsArguments: ${{ parameters.railsArguments }}

    - name: Write Catalog information
      action: catalog:write
      input:
        component:
          apiVersion: 'backstage.io/v1alpha1'
          kind: Component
          metadata:
            name: ${{ parameters.name }}
            annotations:
              github.com/project-slug: ${{ parameters.repoUrl | projectSlug }}
          spec:
            type: service
            lifecycle: production
            owner: ${{ parameters.owner }}

    - id: publish
      if: ${{ parameters.dryRun !== true }}
      name: Publish
      action: publish:github
      input:
        allowedHosts:
          - github.com
        description: This is ${{ parameters.name }}
        repoUrl: ${{ parameters.repoUrl }}

    - id: register
      if: ${{ parameters.dryRun !== true }}
      name: Register
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
        catalogInfoPath: '/catalog-info.yaml'

    - name: Results
      if: ${{ parameters.dryRun }}
      action: debug:log
      input:
        listWorkspace: true

  output:
    links:
      - title: Repository
        url: ${{ steps['publish'].output.remoteUrl }}
      - title: Open in catalog
        icon: catalog
        entityRef: ${{ steps['register'].output.entityRef }}

Choose the runtime for rails new

The action runs the rails new command. Your runner needs a rails install or you can run it in a container

If your runner has rails on the path no change is needed

If you want to run inside a container set imageName in the step

Copy
steps:
  - id: fetch-base
    name: Fetch Base
    action: fetch:rails
    input:
      url: ./template
      imageName: repository/rails:tag
      values:
        name: ${{ parameters.name }}
        owner: ${{ parameters.owner }}
        system: ${{ parameters.system }}
        railsArguments: ${{ parameters.railsArguments }}

Where you will see it in the app

This module does not add a frontend component. The Create page in your app will list any Template you register in the catalog. Use the Template above in your catalog so the action shows up there

Changelog

This changelog is produced from commits made to the Scaffolder Backend Module Rails plugin since a year ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.

Breaking changes

  • Remove old deprecated scaffolder types. If you rely on those types in custom code you need to migrate. #31235 Merged 12 days ago
  • Raise Node runtime to 20 and 22. Drop support for 18. #27286 Merged 11 months ago

Improvements

  • Migrate the Rails module actions to the new actions format. Existing templates should keep working. If you register actions by hand update to the new format. #30134 Merged 4 months ago
  • Promote backend feature exports to the main entry point. You no longer need alpha imports for features in this area. #27088 Merged 11 months ago

Deprecations

  • Deprecate createTemplateAction with the old JSONSchema and zod shapes. Use the new actions API. #28798 Merged 7 months ago

Dependencies

  • Bump TypeScript to 5.6. #29090 Merged 7 months ago
  • Update Ruby Docker tag to 3.4. #28548 Merged 7 months ago

Internal

  • Remove most backend common usages in scaffolder code. #29017 Merged 7 months ago

Set up Backstage in minutes with Roadie