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

@ruivalim/shell-exec

v0.1.7

Published

A custom Backstage scaffolder action to execute shell commands.

Readme

Shell Exec for Backstage

A custom Backstage scaffolder action to execute shell commands and scripts during template scaffolding.

What

This action allows you to run shell commands, bash scripts, or any executable during your Backstage software template execution. It captures stdout/stderr and provides proper error handling.

Why

Use this action when you need to:

  • Run setup scripts (e.g., configure Azure DevOps, GitHub, cloud resources)
  • Execute build or deployment commands
  • Run custom automation during template scaffolding
  • Perform system operations that aren't covered by built-in actions

Installation

Add the package to your Backstage backend:

yarn add --cwd packages/backend @ruivalim/shell-exec

Register the action in your scaffolder module (packages/backend/src/modules/scaffolder/index.ts):

import { createBackendModule } from '@backstage/backend-plugin-api';
import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node';
import { shellExec } from '@ruivalim/shell-exec';

export default createBackendModule({
  pluginId: 'scaffolder',
  moduleId: 'custom-actions',
  register(env) {
    env.registerInit({
      deps: {
        scaffolder: scaffolderActionsExtensionPoint,
      },
      async init({ scaffolder }) {
        scaffolder.addActions(shellExec());
      },
    });
  },
});

Usage

Basic Example

Execute a simple command:

steps:
  - id: list-files
    name: List Files
    action: shell:exec
    input:
      command: ls
      args:
        - '-la'

Run a Script

Execute a bash script with arguments:

steps:
  - id: setup-infrastructure
    name: Setup Infrastructure
    action: shell:exec
    input:
      command: bash
      args:
        - './scripts/setup.sh'
        - '${{ parameters.environment }}'
        - '${{ parameters.region }}'

Custom Working Directory

Run a command in a specific directory:

steps:
  - id: build-app
    name: Build Application
    action: shell:exec
    input:
      command: npm
      args:
        - 'run'
        - 'build'
      cwd: './app'

Real-World Example: Azure DevOps Setup

steps:
  - id: fetch-script
    name: Fetch Azure DevOps Setup Script
    action: fetch:plain
    input:
      url: https://raw.githubusercontent.com/your-org/scripts/main/setup-azure-devops.sh
      targetPath: ./scripts/

  - id: setup-azure-devops
    name: Setup Azure DevOps (Pipeline & Branch Policies)
    action: shell:exec
    input:
      command: bash
      args:
        - './scripts/setup-azure-devops.sh'
        - '${{ parameters.organization }}'
        - '${{ parameters.project }}'
        - '${{ parameters.repositoryName }}'

Execute with Environment Variables

steps:
  - id: deploy
    name: Deploy to Cloud
    action: shell:exec
    input:
      command: sh
      args:
        - '-c'
        - 'export API_KEY=${{ secrets.apiKey }} && ./deploy.sh ${{ parameters.env }}'

Using Outputs from Scripts

Execute a script and use its output in subsequent steps:

steps:
  - id: setup-sonarcloud
    name: Setup SonarCloud
    action: shell:exec
    input:
      command: bash
      args:
        - './scripts/setup-sonarcloud.sh'
        - 'fairfax-brasil'
        - '${{ parameters.projectName }}'

  - id: create-readme
    name: Create README with SonarCloud Badge
    action: fetch:template
    input:
      url: ./templates
      values:
        projectName: '${{ parameters.projectName }}'
        sonarUrl: '${{ steps.setup-sonarcloud.output.value }}'

The script setup-sonarcloud.sh would output the SonarCloud URL:

#!/bin/bash
ORG=$1
PROJECT=$2

# Setup SonarCloud project...
SONAR_URL="https://sonarcloud.io/dashboard?id=${ORG}_${PROJECT}"

# Output the URL for use in other steps
echo "EXEC_SHELL_OUTPUT_VALUE=${SONAR_URL}"

Parameters

command (required)

The shell command or executable to run.

Type: string

Examples:

  • bash
  • sh
  • python
  • npm
  • ./custom-script.sh

args (optional)

Array of arguments to pass to the command.

Type: string[]

Default: []

Example:

args:
  - '--verbose'
  - '--output=json'
  - '${{ parameters.name }}'

cwd (optional)

Working directory where the command should be executed.

Type: string

Default: Template workspace path

Example:

cwd: './subfolder'

Outputs

The action provides the following outputs that can be used in subsequent steps:

value (optional)

Captured value from the EXEC_SHELL_OUTPUT_VALUE variable in the command output. Your script can echo this value to pass data to other steps.

Type: string

Example:

steps:
  - id: get-repo-url
    name: Get Repository URL
    action: shell:exec
    input:
      command: bash
      args:
        - './scripts/create-repo.sh'
        - '${{ parameters.projectName }}'

  - id: show-repo
    name: Show Repository Info
    action: debug:log
    input:
      message: 'Repository URL: ${{ steps.get-repo-url.output.value }}'

Your script should output the value like this:

#!/bin/bash
# scripts/create-repo.sh

PROJECT_NAME=$1
REPO_URL="https://github.com/myorg/${PROJECT_NAME}"

# ... do some work ...

# Output the value for Backstage
echo "EXEC_SHELL_OUTPUT_VALUE=${REPO_URL}"

stdout

The complete standard output from the executed command.

Type: string

Example:

message: 'Command output: ${{ steps.my-step.output.stdout }}'

stderr

The complete standard error output from the executed command.

Type: string

exitCode

The exit code returned by the command (always 0 for successful executions).

Type: number

Output and Logging

The action automatically captures and logs:

  • Command execution details (command, args, working directory)
  • Real-time stdout/stderr output
  • Exit codes and error messages

All output appears in the Backstage scaffolder task logs.

Error Handling

The action will:

  • Throw an error if the command exits with a non-zero exit code
  • Log detailed error information including command, args, and error message
  • Halt template execution on failure

Security Considerations

⚠️ Important Security Notes:

  1. Command Injection Risk: Be careful when using user input in commands. Always validate and sanitize parameters.

  2. File Permissions: Ensure scripts have execute permissions (chmod +x script.sh) before running them.

  3. Secrets: Use Backstage's secret management for sensitive data. Don't hardcode credentials.

  4. Sandboxing: The command runs in the context of the Backstage backend. Consider containerization for additional isolation.

  5. Audit: All command executions are logged for audit purposes.

Best Practices

Do:

  • Use explicit paths for scripts (e.g., ./scripts/setup.sh)
  • Validate user input before passing to commands
  • Use parameter substitution: ${{ parameters.name }}
  • Check script exit codes
  • Log important information

Don't:

  • Use user input directly in shell commands without validation
  • Execute commands from untrusted sources
  • Store secrets in template parameters
  • Use eval or dynamic command construction

Requirements

  • Backstage version: ^1.0.0
  • @backstage/plugin-scaffolder-node: ^0.12.1
  • Node.js: >=18.0.0

Troubleshooting

Script not found

Ensure the script path is relative to the workspace:

command: bash
args:
  - './scripts/setup.sh'  # ✅ Correct
  # Not: '/tmp/scripts/setup.sh'  # ❌ Wrong

Permission denied

Make scripts executable before running:

- id: make-executable
  action: shell:exec
  input:
    command: chmod
    args: ['+x', './scripts/setup.sh']

- id: run-script
  action: shell:exec
  input:
    command: './scripts/setup.sh'

Command not showing output

The action uses the logger parameter (not deprecated logStream) to capture output. Ensure you're using version >=0.1.5.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Apache-2.0

Author

Rui Valim

Links