npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@thecodingsheikh/backstage-plugin-entity-scaffolder

v0.4.2

Published

This plugin embeds a Backstage Scaffolder workflow into an entity's page, allowing to update catalog entities with the same template workflow

Readme

backstage-plugin-entity-scaffolder

This plugin embeds a Backstage Scaffolder workflow into an entity's page, allowing to update catalog entities with the same template workflow

screenshow

Installation

  1. Install the package in your Backstage app:

    yarn --cwd packages/app add @thecodingsheikh/backstage-plugin-entity-scaffolder
  2. Add the scaffolder tab to your entity page in packages/app/src/components/catalog/EntityPage.tsx:

    // In packages/app/src/components/catalog/EntityPage.tsx
    
    import {
      EntityScaffolderContent,
      isEntityScaffolderAvailable,
    } from '@thecodingsheikh/backstage-plugin-entity-scaffolder';
    
    
    // ...
    const websiteEntityPage = ( // Or any other EntitPage
      <EntityLayout>
        {/* ... other routes */}
    
        <EntityLayout.Route
          path="/entity-scaffolder"
          title="manage"
          if={isEntityScaffolderAvailable}
        >
          <EntityScaffolderContent/>
        </EntityLayout.Route>
    
        {/* ... other routes */}
      </EntityLayout>
    );

RBAC for edit

This plugin integrates with the Backstage Permission Framework. Who may use the embedded workflow on any given entity is decided by your permission policy — when denied, the tab stays visible but the panel renders a Not authorized message instead of the workflow.

The common package @thecodingsheikh/backstage-plugin-entity-scaffolder-common exports:

  • entityScaffolderEditPermission — permission name entity-scaffolder.edit, action update, resourceType: catalog-entity.

Because the permission's resource type is catalog-entity, your existing catalog conditional rules (IS_ENTITY_OWNER, HAS_ANNOTATION, IS_ENTITY_KIND, …) apply to it directly — no custom rule code is needed.

Installation

  1. Install the common package (it's a transitive dep of the frontend plugin, but your permission backend or policy provider may need it too):

    yarn --cwd packages/backend add @thecodingsheikh/backstage-plugin-entity-scaffolder-common
  2. Make sure @backstage/plugin-permission-backend (and a policy provider, e.g. the RHDH RBAC plugin) is installed and wired up. The frontend already calls usePermission against entity-scaffolder.edit — without a permission backend, the result defaults to allowed.

RHDH policy examples

Deny by default, allow platform admins outright

rbac-policy.csv: |
  p, role:default/all_users, entity-scaffolder.edit, update, deny
  p, role:default/platform_admins, entity-scaffolder.edit, update, allow

  g, group:default/user, role:default/all_users
  g, group:default/platform, role:default/platform_admins

Owners of the entity are allowed (via IS_ENTITY_OWNER)

conditional-policies.yaml: |
  ---
  result: CONDITIONAL
  roleEntityRef: role:default/all_users
  pluginId: catalog
  resourceType: catalog-entity
  permissionMapping:
    - entity-scaffolder.edit
  conditions:
    rule: IS_ENTITY_OWNER
    resourceType: catalog-entity
    params:
      claims: ["$ownerRefs"]

IS_ENTITY_OWNER already works with entities using @thecodingsheikh/backstage-plugin-multi-owner, because the multi-owner catalog processor emits an ownedBy relation for every entry in spec.owners — regardless of the owner's role.

Role-aware owner checks (requires the multi-owner permission rule)

If you want backstage.io/scaffolder-edit-roles: 'admin' to mean "only owners whose role: admin may edit", install @thecodingsheikh/backstage-plugin-catalog-backend-module-multi-owner-processor — it registers the IS_ENTITY_MULTI_OWNER_WITH_ANNOTATION_ROLE rule used below. The resulting policy looks like:

conditional-policies.yaml: |
  ---
  result: CONDITIONAL
  roleEntityRef: role:default/all_users
  pluginId: catalog
  resourceType: catalog-entity
  permissionMapping:
    - entity-scaffolder.edit
  conditions:
    anyOf:
      # No edit-roles annotation → fall back to plain owner check
      - allOf:
          - not:
              rule: HAS_ANNOTATION
              resourceType: catalog-entity
              params: { annotation: backstage.io/scaffolder-edit-roles }
          - rule: IS_ENTITY_OWNER
            resourceType: catalog-entity
            params: { claims: ["$ownerRefs"] }
      # Annotation present → only owners whose role matches the CSV in it
      - rule: IS_ENTITY_MULTI_OWNER_WITH_ANNOTATION_ROLE
        resourceType: catalog-entity
        params: { annotation: backstage.io/scaffolder-edit-roles }

With that rule in place, per-entity annotations drive the role check:

# only owners with role: admin may edit
metadata:
  annotations:
    backstage.io/scaffolder-edit-roles: 'admin'
spec:
  owners:
    - { name: group:default/team, role: edit }       # denied
    - { name: group:default/platform, role: admin }  # allowed

Redhat Developer Hub (RHDH)

This plugin can be installed as a dynamic plugin, Check here

Usage

To enable the Scaffolder tab on an entity page, add the following annotations to the entity. The tab will only appear if both annotations are present.

  • backstage.io/scaffolder-template: The entity reference for the Scaffolder template to use.
  • backstage.io/last-applied-configuration: A JSON object string representing the template parameter values to to pass to the Scaffolder workflow.
  • backstage.io/immutable-fields (optional): A comma-separated list of field names that should be disabled (non-editable) when the form is rendered from an entity page. This is useful for fields like name or repoUrl that should not change after initial creation.

Example

Here is an example of how to configure a Component entity to use the Scaffolder plugin.

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    backstage.io/last-applied-configuration: '{"name":"my-service","repoUrl":"github.com?owner=thecodingsheikh&repo=backstage-plugins","firstRun":false}'
    backstage.io/scaffolder-template: template:default/entity-scaffolder-template
    backstage.io/immutable-fields: 'name,repoUrl'
spec:
  type: service
  lifecycle: experimental
  owner: team-a

Note: The immutable-fields annotation applies ui:disabled to the specified fields, which works with both standard form fields and custom field extensions like RepoUrlPicker, OwnerPicker, etc. It is best to add them automatically from a scaffolder template, for example

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: entity-scaffolder-template
spec:
  # ...
  steps:
    - id: fetch-base
      name: Fetch Base
      action: fetch:template
      input:
        url: ./content
        values:
          # ...
          params: ${{ parameters }}

and in the template you can do

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ values.name | dump }}
  annotations:
    backstage.io/last-applied-configuration: '${{ values.params | dump }}'
    backstage.io/scaffolder-template: template:entity-scaffolder-template

or you can use the catalog:annotate action instead, with conditional step (Example below)

Conditional Workflow

there is a special template parameter firstRun that is added with the value false in any scaffolder template initiated from an entity's page, this offers conditional steps, for example

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: entity-scaffolder-template
  title: Example Entity Scaffolder
  description: An example template
spec:
  owner: user:guest
  type: service

  parameters:
    - title: Fill in some steps
      required:
        - name
      properties:
        name:
          title: Name
          type: string
    - title: Choose a location
      required:
        - repoUrl
      properties:
        repoUrl:
          title: Repository Location
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts:
              - github.com

  steps:
    - id: fetch-base
      name: Fetch Base
      action: fetch:template
      input:
        url: ./content
        values:
          name: ${{ parameters.name }}
          params: ${{ parameters }}

    - id: publish
      name: Publish
      # This means if the value of firstRun is true or doesn't exist, execute this step, so it will only run when executed first time from the self service page, and will be skipped if executed from the entity page
      if: ${{ parameters.firstRun != false }}
      action: publish:github
      input:
        description: This is ${{ parameters.name }}
        repoUrl: ${{ parameters.repoUrl }}
        defaultBranch: 'main'

    - id: register
      name: Register
      # Same as above
      if: ${{ parameters.firstRun != false }}
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
        catalogInfoPath: '/catalog-info.yaml'

    - id: pull
      name: pull
      # This step will execute only when executed from an enity's page
      if: ${{ parameters.firstRun == false }}
      action: publish:github:pull-request
      input:
        title: test
        description: This is ${{ parameters.name }}
        repoUrl: ${{ parameters.repoUrl }}
        branchName: 'test'