Set up your first GitHub Action with @savvy-web/github-action-builder.
- Node.js 24 or later
- npm, pnpm, or yarn
- A GitHub repository for your action
Scaffold a new GitHub Action project, then build it:
npx @savvy-web/github-action-builder init my-action
cd my-action
npm install
npm run buildThe init command generates a project that builds without further setup. Put your action logic in src/main.ts and run npm run build again.
The init command creates this structure:
my-action/
├── src/
│ ├── main.ts # Main action entry point
│ ├── pre.ts # Pre-action hook (runs before main)
│ └── post.ts # Post-action cleanup (runs after main)
├── action.yml # GitHub Action metadata
├── action.config.ts # Build configuration
├── package.json # Dependencies and scripts
└── tsconfig.json # TypeScript configuration
The build configuration file:
import { GitHubAction } from "@savvy-web/github-action-builder";
export default GitHubAction.create({
// Entry points are auto-detected from src/main.ts, src/pre.ts, src/post.ts
// Uncomment to customize:
// entries: {
// main: "src/main.ts",
// pre: "src/pre.ts",
// post: "src/post.ts",
// },
// Build options
// build: {
// minify: true,
// sourceMap: false,
// target: "es2022",
// },
});The GitHub Action metadata. The runs.using field must be node24:
name: "my-action"
description: "A GitHub Action built with @savvy-web/github-action-builder"
inputs:
example-input:
description: "An example input"
required: false
default: "hello"
outputs:
example-output:
description: "An example output"
runs:
using: "node24"
main: "dist/main.js"
pre: "dist/pre.js"
post: "dist/post.js"
branding:
icon: "zap"
color: "blue"The generated tsconfig.json extends the shared base configuration:
{
"extends": ["@savvy-web/github-action-builder/tsconfig/action.json"]
}This provides ES2022 target, strict mode, bundler module resolution, and includes patterns for src/, lib/, __test__/, and root-level TypeScript files like action.config.ts. You can override any setting in your project's tsconfig.json.
The main action entry point:
import * as core from "@actions/core";
async function run(): Promise<void> {
try {
const input = core.getInput("example-input");
core.info(`Running main action with input: ${input}`);
// Your main action logic goes here
core.setOutput("example-output", "success");
} catch (error) {
if (error instanceof Error) {
core.setFailed(error.message);
} else {
core.setFailed("An unexpected error occurred");
}
}
}
run();Install globally to use the github-action-builder command anywhere:
npm install -g @savvy-web/github-action-builderInstall as a dev dependency (already included in generated projects):
npm install --save-dev @savvy-web/github-action-builderRun any command without installing:
npx @savvy-web/github-action-builder build
npx @savvy-web/github-action-builder validateRun the build command:
npm run buildYou will see output like:
Loading configuration...
Found action.config.ts
Validating...
All checks passed
Building...
Build Summary:
✓ main: <size> (<time>) → dist/main.js
✓ pre: <size> (<time>) → dist/pre.js
✓ post: <size> (<time>) → dist/post.js
Total time: <total>
Build completed successfully!
After building, your dist/ directory contains:
dist/
├── main.js # Bundled main action
├── pre.js # Bundled pre-action hook
├── post.js # Bundled post-action cleanup
└── package.json # ESM module marker { "type": "module" }
Each bundled file holds your code and its dependencies in one file, minified by default, ready to run on Node.js 24 in GitHub Actions.
Check your action.yml and configuration without building:
npm run validateThis verifies:
action.ymlexists and is valid YAMLaction.ymlschema matches GitHub's specificationruns.usingis set tonode24- Required entry points exist
Run TypeScript type checking:
npm run typecheckThe builder treats validation issues differently depending on where it runs.
Local development: validation issues show as warnings and the build continues, so you keep working while you fix them.
CI environment (GitHub Actions): validation issues become errors and the build fails, so a broken action never gets past the pipeline.
The builder treats itself as running in CI when CI=true or GITHUB_ACTIONS=true is set.
GitHub Actions runs the bundled code directly, so commit your dist/ folder:
git add dist/
git commit -m "Build action"
git pushUse your action in a workflow:
# .github/workflows/test.yml
name: Test Action
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Run my action
uses: ./
with:
name: "GitHub Actions"To publish on the GitHub Marketplace:
- Create a release tag (e.g.,
v1.0.0) - Ensure
action.ymlhasbrandingconfigured - Follow GitHub's publishing guide
- Configuration - Customize build options
- CLI reference - All available commands
- Troubleshooting - Common issues and solutions