| name | github-actions |
| description | Create, configure, and optimize GitHub Actions including action types, triggers, runners, security practices, and marketplace integration |
GitHub Actions
Activate when creating, modifying, troubleshooting, or optimizing GitHub Actions components. This skill covers action development, marketplace integration, and best practices.
When to Use This Skill
Activate when:
- Creating custom GitHub Actions (JavaScript, Docker, or composite)
- Publishing actions to GitHub Marketplace
- Configuring action metadata and inputs/outputs
- Implementing action security and permissions
- Troubleshooting action execution
- Selecting or evaluating marketplace actions
- Optimizing action performance and reliability
Action Types
JavaScript Actions
Execute directly on runners with fast startup and cross-platform compatibility.
Structure:
my-action/
├── action.yml # Metadata and interface
├── index.js # Entry point
├── package.json # Dependencies
└── node_modules/ # Bundled dependencies
Key Requirements:
- Use
@actions/corefor inputs/outputs - Use
@actions/githubfor GitHub API access - Bundle all dependencies (use @vercel/ncc)
- Support Node.js LTS versions
Example action.yml:
name: 'My JavaScript Action'
description: 'Performs custom task'
inputs:
token:
description: 'GitHub token'
required: true
config:
description: 'Configuration file path'
required: false
default: 'config.yml'
outputs:
result:
description: 'Action result'
runs:
using: 'node20'
main: 'dist/index.js'
Docker Container Actions
Provide consistent execution environment with all dependencies packaged.
Structure:
my-action/
├── action.yml
├── Dockerfile
├── entrypoint.sh
└── src/
Key Requirements:
- Use lightweight base images (Alpine when possible)
- Set proper file permissions
- Handle signals gracefully
- Output to STDOUT/STDERR correctly
Example Dockerfile:
FROM alpine:3.18
RUN apk add --no-cache bash curl jq
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
Composite Actions
Combine multiple steps and actions into reusable units.
Structure:
name: 'Setup Environment'
description: 'Configure development environment'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '20'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
shell: bash
- run: npm run build
shell: bash
Action Metadata (action.yml)
Required Fields
name: 'Action Name' # Marketplace display name
description: 'What it does' # Clear, concise purpose
runs: # Execution configuration
using: 'node20' # or 'docker' or 'composite'
Optional Fields
author: 'Your Name'
branding: # Marketplace icon/color
icon: 'activity'
color: 'blue'
inputs: # Define all inputs
input-name:
description: 'Purpose'
required: true
default: 'value'
outputs: # Define all outputs
output-name:
description: 'What it contains'
Inputs and Outputs
Reading Inputs
JavaScript:
const core = require('@actions/core');
const token = core.getInput('token', { required: true });
const config = core.getInput('config') || 'default.yml';
Shell:
TOKEN="${{ inputs.token }}"
CONFIG="${{ inputs.config }}"
Setting Outputs
JavaScript:
core.setOutput('result', 'success');
core.setOutput('artifact-url', artifactUrl);
Shell:
echo "result=success" >> $GITHUB_OUTPUT
echo "artifact-url=$ARTIFACT_URL" >> $GITHUB_OUTPUT
GitHub Actions Toolkit
Essential npm packages for JavaScript actions:
@actions/core
const core = require('@actions/core');
// Inputs/Outputs
const input = core.getInput('name');
core.setOutput('name', value);
// Logging
core.info('Information message');
core.warning('Warning message');
core.error('Error message');
core.debug('Debug message');
// Grouping
core.startGroup('Group name');
// ... operations
core.endGroup();
// Failure
core.setFailed('Action failed: reason');
// Secrets
core.setSecret('sensitive-value'); // Masks in logs
// Environment
core.exportVariable('VAR_NAME', 'value');
@actions/github
const github = require('@actions/github');
// Context
const context = github.context;
console.log(context.repo); // { owner, repo }
console.log(context.sha); // Commit SHA
console.log(context.ref); // Branch/tag ref
console.log(context.actor); // Triggering user
console.log(context.payload); // Webhook payload
// Octokit client
const token = core.getInput('token');
const octokit = github.getOctokit(token);
// API operations
const { data: issues } = await octokit.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open'
});
@actions/exec
const exec = require('@actions/exec');
// Execute commands
await exec.exec('npm', ['install']);
// Capture output
let output = '';
await exec.exec('git', ['log', '--oneline'], {
listeners: {
stdout: (data) => { output += data.toString(); }
}
});
Security Best Practices
Input Validation
Always validate and sanitize inputs:
const core = require('@actions/core');
function validateInput(input) {
// Check for command injection
if (/[;&|`$()]/.test(input)) {
throw new Error('Invalid characters in input');
}
return input;
}
const userInput = core.getInput('user-input');
const safeInput = validateInput(userInput);
Token Permissions
Request minimal required permissions:
permissions:
contents: read # Read repository
pull-requests: write # Comment on PRs
issues: write # Create issues
Secret Handling
// Mask secrets in logs
core.setSecret(sensitiveValue);
// Never log tokens
core.debug(`Token: ${token}`); // ❌ WRONG
core.debug('Token received'); // ✅ CORRECT
// Secure token usage
const octokit = github.getOctokit(token);
// Token automatically included in requests
Dependency Security
# Audit dependencies
npm audit
# Use specific versions
npm install @actions/core@1.10.0
# Bundle dependencies
npm install -g @vercel/ncc
ncc build index.js -o dist
Marketplace Publishing
Prerequisites
- Public repository
- action.yml in repository root
- README.md with usage examples
- LICENSE file
- Repository topics (optional)
Publishing Process
- Create release with semantic version tag:
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
- Create GitHub Release from tag
- Check "Publish this Action to GitHub Marketplace"
- Select primary category
- Verify branding icon/color
Version Management
Use semantic versioning with major version tags:
# Release v1.2.3
git tag -a v1.2.3 -m "Release v1.2.3"
git tag -fa v1 -m "Update v1 to v1.2.3"
git push origin v1.2.3 v1 --force
Users reference by major version:
- uses: owner/action@v1 # Tracks latest v1.x.x
Testing Actions Locally
Use act for local testing (see act skill):
# Test action in current directory
act -j test
# Test with specific event
act push
# Test with secrets
act -s GITHUB_TOKEN=ghp_xxx
Common Patterns
Matrix Testing Action
# action.yml
name: 'Matrix Test Runner'
description: 'Run tests across multiple configurations'
inputs:
matrix-config:
description: 'JSON matrix configuration'
required: true
runs:
using: 'composite'
steps:
- run: |
echo "Testing with config: ${{ inputs.matrix-config }}"
# Parse and execute tests
shell: bash
Cache Management Action
const core = require('@actions/core');
const cache = require('@actions/cache');
async function run() {
const paths = [
'node_modules',
'.npm'
];
const key = `deps-${process.platform}-${hashFiles('package-lock.json')}`;
// Restore cache
const cacheKey = await cache.restoreCache(paths, key);
if (!cacheKey) {
core.info('Cache miss, installing dependencies');
await exec.exec('npm', ['ci']);
await cache.saveCache(paths, key);
} else {
core.info(`Cache hit: ${cacheKey}`);
}
}
Artifact Upload Action
const artifact = require('@actions/artifact');
async function uploadArtifact() {
const artifactClient = artifact.create();
const files = [
'dist/bundle.js',
'dist/styles.css'
];
const rootDirectory = 'dist';
const options = {
continueOnError: false
};
const uploadResponse = await artifactClient.uploadArtifact(
'build-artifacts',
files,
rootDirectory,
options
);
core.setOutput('artifact-id', uploadResponse.artifactId);
}
Troubleshooting
Action Not Found
- Verify repository is public or accessible
- Check action.yml exists in repository root
- Confirm version tag exists
Permission Denied
# Add required permissions to workflow
permissions:
contents: write
pull-requests: write
Node Modules Missing
- Bundle dependencies with ncc
- Check dist/ folder is committed
- Verify node_modules excluded from .gitignore for dist/
Docker Action Fails
- Check Dockerfile syntax
- Verify entrypoint has execute permissions
- Test container locally:
docker build -t test . && docker run test
Anti-Fabrication Requirements
- Execute Read or Glob tools to verify action files exist before claiming structure
- Use Bash to test commands before documenting syntax
- Validate action.yml schema against actual files using tool analysis
- Execute actual API calls with @actions/github before documenting responses
- Test permission configurations in real workflows before recommending settings
- Never claim action capabilities without reading actual implementation code
- Report actual npm audit results when discussing security, not fabricated vulnerability counts