Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/patch-add-actions-build-tooling.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
.github/aw/github-agentic-workflows.md linguist-generated=true merge=ours
pkg/cli/workflows/*.lock.yml linguist-generated=true merge=ours
pkg/workflow/js/*.js linguist-generated=true
actions/*/index.js linguist-generated=true
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
paths:
- '**.go'
- 'pkg/workflow/**'
- 'actions/**'
- '.github/workflows/ci.yml'
- '.github/workflows/**/*.md'
workflow_dispatch:
Expand Down Expand Up @@ -285,6 +286,42 @@ jobs:
- name: Lint error messages
run: make lint-errors

actions-build:
needs: [lint]
runs-on: ubuntu-latest
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}-actions-build
cancel-in-progress: true
steps:
- name: Checkout code
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5

- name: Set up Go
id: setup-go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version-file: go.mod
cache: true

- name: Report Go cache status
run: |
if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then
echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY
fi

- name: Verify dependencies
run: go mod verify

- name: Build actions
run: make actions-build

- name: Validate actions
run: make actions-validate

fuzz:
needs: [test]
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ gh-aw-test/
/package.json
/package-lock.json

# Action build artifacts - keep bundled index.js, exclude temp files
actions/**/node_modules/
actions/**/*.tmp
actions/**/.build/

pkg/cli/workflows/*.yml
.github/workflows/test-update.md
.github/workflows/test-update.lock.yml
Expand Down
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,22 @@ clean:
rm -f $(BINARY_NAME) $(BINARY_NAME)-* coverage.out coverage.html
go clean

# Actions management targets
.PHONY: actions-build
actions-build:
@echo "Building all actions..."
@go run ./internal/tools/actions-build build

.PHONY: actions-validate
actions-validate:
@echo "Validating action.yml files..."
@go run ./internal/tools/actions-build validate

.PHONY: actions-clean
actions-clean:
@echo "Cleaning action artifacts..."
@go run ./internal/tools/actions-build clean

# Check Node.js version
.PHONY: check-node-version
check-node-version:
Expand Down Expand Up @@ -419,6 +435,9 @@ help:
@echo " fuzz - Run fuzz tests for 30 seconds"
@echo " bundle-js - Build JavaScript bundler tool (./bundle-js <input> [output])"
@echo " clean - Clean build artifacts"
@echo " actions-build - Build all custom GitHub Actions from source"
@echo " actions-validate - Validate action.yml files"
@echo " actions-clean - Clean action build artifacts"
@echo " deps - Install dependencies"
@echo " check-node-version - Check Node.js version (20 or higher required)"
@echo " lint - Run linter"
Expand Down
238 changes: 238 additions & 0 deletions actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# GitHub Actions Directory

This directory contains custom GitHub Actions for the gh-aw project. These actions are used internally by compiled workflows to provide functionality such as MCP server file management.

## Directory Structure

Each action follows a standard structure:

```
actions/{action-name}/
├── action.yml # Action metadata and configuration
├── index.js # Bundled action code (generated, committed)
├── src/ # Source files
│ └── index.js # Main action source
└── README.md # Action-specific documentation
```

## Available Actions

### setup-safe-outputs

Copies safe-outputs MCP server files to the agent environment. This action embeds all necessary JavaScript files for the safe-outputs MCP server and copies them to a specified destination directory.

[Documentation](./setup-safe-outputs/README.md)

### setup-safe-inputs

Copies safe-inputs MCP server files to the agent environment. This action embeds all necessary JavaScript files for the safe-inputs MCP server and copies them to a specified destination directory.

[Documentation](./setup-safe-inputs/README.md)

## Building Actions

Actions are built using the Go-based build system that reuses the bundler and script registry infrastructure from the workflow compiler.

The build process:

1. Reads source files from `actions/{action-name}/src/`
2. Identifies and embeds required JavaScript dependencies from `pkg/workflow/js/`
3. Uses the same bundler infrastructure as workflow compilation
4. Bundles everything into `actions/{action-name}/index.js`
5. Validates `action.yml` files

### Build Commands

```bash
# Build all actions (generates index.js files)
make actions-build
# or
gh aw actions-build

# Validate action.yml files
make actions-validate
# or
gh aw actions-validate

# Clean generated files
# Clean generated files
make actions-clean
# or
gh aw actions-clean
```

The build system uses:
- **Bundler Infrastructure**: Same JavaScript bundler used for workflow compilation (`pkg/workflow/bundler.go`)
- **Script Registry**: Centralized registry for managing JavaScript sources (`pkg/workflow/script_registry.go`)
- **Embedded Sources**: All JavaScript files from `pkg/workflow/js/` via `GetJavaScriptSources()`

## Development Guidelines

### Creating a New Action

1. **Create the directory structure:**
```bash
mkdir -p actions/{action-name}/src
```

2. **Create action.yml:**
Define the action metadata, inputs, outputs, and runtime configuration.
```yaml
name: 'Action Name'
description: 'Action description'
author: 'GitHub Next'

inputs:
input-name:
description: 'Input description'
required: false
default: 'default-value'

outputs:
output-name:
description: 'Output description'

runs:
using: 'node20'
main: 'index.js'
```

3. **Create source file (src/index.js):**
Write the action logic using `@actions/core` and `@actions/github`:
```javascript
const core = require('@actions/core');

async function run() {
try {
const input = core.getInput('input-name');
core.info(`Processing: ${input}`);

// Action logic here

core.setOutput('output-name', 'result');
} catch (error) {
core.setFailed(`Action failed: ${error.message}`);
}
}

run();
```

4. **Update dependency mapping:**
Add the action to the dependency map in `pkg/cli/actions_build_command.go` to specify which JavaScript files from `pkg/workflow/js/` should be embedded.

5. **Build and test:**
```bash
make actions-build
make actions-validate
```

6. **Create README.md:**
Document the action's purpose, usage, inputs, outputs, and examples.

### Action Requirements

- **Runtime**: Actions must use Node.js 20 (`using: 'node20'`)
- **Dependencies**: Use `@actions/core` and `@actions/github` for GitHub Actions integration
- **Error Handling**: Always wrap main logic in try-catch and use `core.setFailed()` for errors
- **Logging**: Use `core.info()`, `core.warning()`, and `core.error()` for output
- **Outputs**: Set outputs using `core.setOutput(name, value)`

### Embedding Files

The build system supports embedding JavaScript files from `pkg/workflow/js/` into actions. To use this:

1. Define a `FILES` constant in your source file:
```javascript
const FILES = {
// This will be populated by the build script
};
```

2. Add the files to the dependency map in `scripts/build-actions.js`

3. The build script will replace the empty `FILES` object with the actual file contents

4. Use the embedded files in your action:
```javascript
for (const [filename, content] of Object.entries(FILES)) {
fs.writeFileSync(path.join(destination, filename), content, 'utf8');
}
```

## Action Types

This directory supports two types of actions:

### 1. Simple Actions (Single-file)

Actions with all logic in `src/index.js` without additional source files.

### 2. Complex Actions (Multi-file)

Actions that use multiple source files in the `src/` directory. The build system will bundle them together.

## Validation

The build system validates:

- ✅ `action.yml` exists and contains required fields
- ✅ `action.yml` uses `node20` runtime
- ✅ Source files exist in `src/` directory
- ✅ Required dependencies are available

## Git Management

### Files Committed to Git

- ✅ `action.yml` - Action metadata
- ✅ `index.js` - **Bundled action code (generated but committed)**
- ✅ `src/` - Source files
- ✅ `README.md` - Documentation

### Files Excluded from Git

- ❌ `node_modules/` - Dependencies (if any)
- ❌ `*.tmp` - Temporary files
- ❌ `.build/` - Build artifacts

**Note**: The bundled `index.js` files are committed to the repository because GitHub Actions requires the complete action code to be present when the action is used. This is standard practice for JavaScript actions.

## Testing

Test actions locally by:

1. Creating a test workflow in `.github/workflows/`
2. Using the action with a local path:
```yaml
- uses: ./actions/setup-safe-outputs
with:
destination: /tmp/test
```
3. Running the workflow on GitHub Actions

## Troubleshooting

### Build fails with "File not found"

Ensure the dependency files exist in `pkg/workflow/js/` and are listed correctly in the dependency map.

### Action fails at runtime

Check that:
- All required inputs are provided
- The bundled `index.js` is up to date (run `make actions-build`)
- The action has necessary permissions

### Validation fails

Ensure `action.yml` includes all required fields:
- `name`
- `description`
- `runs` with `using: 'node20'` and `main: 'index.js'`

## References

- [GitHub Actions - Creating JavaScript actions](https://docs.github.com/en/actions/creating-actions/creating-a-javascript-action)
- [GitHub Actions Toolkit](https://github.com/actions/toolkit)
- [@actions/core documentation](https://github.com/actions/toolkit/tree/main/packages/core)
Loading