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
54 changes: 54 additions & 0 deletions .github/workflows/build-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Build & Deploy Registry

on:
push:
branches: [main]
repository_dispatch:
types: [plugin-release]
schedule:
- cron: '0 6 * * *'
workflow_dispatch: {}

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y jq

- name: Build static index
run: bash scripts/build-index.sh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build version data
run: bash scripts/build-versions.sh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: v1

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v1/
116 changes: 101 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
# workflow-registry

[![Validate Registry](https://github.com/GoCodeAlone/workflow-registry/actions/workflows/validate.yml/badge.svg)](https://github.com/GoCodeAlone/workflow-registry/actions/workflows/validate.yml)
[![Build & Deploy](https://github.com/GoCodeAlone/workflow-registry/actions/workflows/build-pages.yml/badge.svg)](https://github.com/GoCodeAlone/workflow-registry/actions/workflows/build-pages.yml)

The official plugin and template registry for the [GoCodeAlone/workflow](https://github.com/GoCodeAlone/workflow) engine.

**Registry API**: `https://gocodealone.github.io/workflow-registry/v1/`

This registry catalogs all built-in plugins, community extensions, and reusable templates that can be used with the workflow engine. It serves as the source of truth for the `wfctl` CLI's marketplace and `wfctl publish` command.

## Table of Contents

- [What is this?](#what-is-this)
- [Browsing Plugins](#browsing-plugins)
- [Usage via wfctl](#usage-via-wfctl)
- [Plugin Tiers](#plugin-tiers)
- [Built-in Plugins](#built-in-plugins)
- [External Plugins](#external-plugins)
- [Templates](#templates)
- [Schema](#schema)
- [Submitting a Community Plugin](#submitting-a-community-plugin)
- [Plugin Manifest Format](#plugin-manifest-format)
- [Submitting a Plugin](#submitting-a-plugin)
- [Automatic Version Tracking](#automatic-version-tracking)
- [Registry Structure](#registry-structure)

---

Expand All @@ -30,21 +35,28 @@ The registry is consumed by:
- `wfctl marketplace` — browse and search available plugins
- `wfctl publish` — submit your plugin to the registry
- The workflow UI Marketplace page
- The static JSON API at `https://gocodealone.github.io/workflow-registry/v1/`

---

## Browsing Plugins

Plugins are organized under `plugins/<name>/manifest.json`. Each manifest describes the plugin's capabilities, version, tier, and source location.

To search via CLI:
## Usage via wfctl

```bash
# Search for plugins by keyword
wfctl marketplace search http
wfctl marketplace info http
```

To browse manually, see the [`plugins/`](./plugins/) directory.
# Get details for a specific plugin
wfctl marketplace info payments

# Install a plugin into your project
wfctl install payments

# List all installed plugins
wfctl plugin list

# Update all plugins to latest versions
wfctl plugin update
```

---

Expand Down Expand Up @@ -154,25 +166,37 @@ Every pull request and push to `main` triggers the [Validate Registry](.github/w
1. Validates all `plugins/*/manifest.json` files against `schema/registry-schema.json` (JSON Schema draft 2020-12 via `ajv-cli`)
2. Checks that every plugin referenced in `templates/*.yaml` has a corresponding manifest

The [Build & Deploy](.github/workflows/build-pages.yml) workflow runs on every push to `main`, on a daily schedule, and whenever a plugin sends a `plugin-release` dispatch event. It:

1. Generates `v1/index.json` from all manifests
2. Queries GitHub Releases for each plugin to build `v1/plugins/<name>/versions.json`
3. Deploys the `v1/` directory to GitHub Pages

PRs that fail validation cannot be merged.

---

## Submitting a Community Plugin
## Submitting a Plugin

### Step-by-step PR Process

1. **Fork** this repository
2. **Create** a directory under `plugins/<your-plugin-name>/`
3. **Add** a `manifest.json` that conforms to the [registry schema](./schema/registry-schema.json)
4. **Validate** your manifest against the schema
5. **Open a PR** with a description of your plugin
4. **Validate** your manifest locally:
```bash
bash scripts/validate-manifests.sh
```
5. **Open a PR** with a description of your plugin, what it provides, and a link to the source repository

### Manifest Requirements

- `name`, `version`, `author`, `description`, `type`, `tier`, `license` are required
- `type` must be `"external"` for community plugins (only GoCodeAlone sets `"builtin"`)
- `tier` must be `"community"` for third-party submissions
- `source` should point to the public repository where the plugin lives
- `repository` should point to the public GitHub repository where the plugin lives
- `capabilities.moduleTypes`, `stepTypes`, `triggerTypes`, `workflowHandlers` must accurately reflect what the plugin registers
- `private: true` must be set for plugins that are not publicly installable

### Review Process

Expand All @@ -184,6 +208,67 @@ PRs are reviewed by maintainers for:

---

## Automatic Version Tracking

When you publish a new release of your plugin, you can automatically trigger a registry rebuild so that `v1/plugins/<name>/versions.json` and `v1/plugins/<name>/latest.json` are updated within minutes.

See [`templates/notify-registry.yml`](./templates/notify-registry.yml) for the reusable workflow snippet to add to your plugin's release workflow.

**Setup**:
1. Create a GitHub PAT with `repo` scope for `GoCodeAlone/workflow-registry`
2. Add it as a secret named `REGISTRY_PAT` in your plugin repo
3. Copy the `notify-registry` job from the template into your `.github/workflows/release.yml`

The registry rebuilds daily at 06:00 UTC as a fallback even without dispatch events.

---

## Registry Structure

```
workflow-registry/
├── plugins/ # Source of truth — one directory per plugin
│ └── <name>/
│ └── manifest.json # Plugin metadata and capabilities
├── templates/ # Reusable workflow config templates
│ ├── notify-registry.yml # Action snippet for plugin release notifications
│ └── *.yaml # Workflow starter templates
├── schema/
│ └── registry-schema.json # JSON Schema for manifest validation
├── scripts/
│ ├── build-index.sh # Generates v1/index.json
│ ├── build-versions.sh # Queries GitHub Releases → v1/plugins/*/versions.json
│ ├── validate-manifests.sh # CI manifest validation
│ └── validate-templates.sh # CI template validation
├── .github/workflows/
│ ├── validate.yml # PR validation gate
│ └── build-pages.yml # Build and deploy static registry to GitHub Pages
└── v1/ # Generated — served via GitHub Pages (not committed)
├── index.json # Array of all plugin summaries, sorted by name
└── plugins/
└── <name>/
├── manifest.json # Copy of source manifest
├── versions.json # Release history from GitHub
└── latest.json # Latest release entry only
```

### Static API Endpoints

| Endpoint | Description |
|----------|-------------|
| `GET /v1/index.json` | All plugin summaries (name, description, version, capabilities, ...) |
| `GET /v1/plugins/<name>/manifest.json` | Full manifest for a specific plugin |
| `GET /v1/plugins/<name>/versions.json` | All release versions with download URLs |
| `GET /v1/plugins/<name>/latest.json` | Latest release version only |
Comment on lines +257 to +262

---

## Plugin Authoring Guide

See the [Plugin Manifest Format](#plugin-manifest-format) section below and the [registry schema](./schema/registry-schema.json) for a complete reference on building, testing, and publishing a workflow engine plugin.

---

## Plugin Manifest Format

```json
Expand All @@ -198,6 +283,7 @@ PRs are reviewed by maintainers for:
"tier": "community",
"license": "MIT",
"minEngineVersion": "0.1.0",
"repository": "https://github.com/yourorg/my-plugin",
"keywords": ["tag1", "tag2"],
"capabilities": {
"moduleTypes": ["mymodule.type"],
Expand Down
74 changes: 74 additions & 0 deletions scripts/build-index.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# scripts/build-index.sh
#
# Generates v1/index.json — an array of plugin summaries sorted by name.
# Also copies each manifest to v1/plugins/<name>/manifest.json.
#
# Requires: jq

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
PLUGINS_DIR="${REPO_ROOT}/plugins"
OUT_DIR="${REPO_ROOT}/v1"

if ! command -v jq &>/dev/null; then
echo "error: jq is required but not found in PATH" >&2
exit 1
fi

echo "Building registry index..."

mkdir -p "${OUT_DIR}/plugins"

# Collect summaries from all plugin manifests, sorted by name
summaries="[]"

while IFS= read -r manifest; do
plugin_name="$(basename "$(dirname "${manifest}")")"

# Validate it's readable JSON
if ! jq empty "${manifest}" 2>/dev/null; then
echo "warning: skipping invalid JSON at ${manifest}" >&2
continue
fi

# Extract summary fields.
# Use the directory name as the canonical "name" so that it matches the
# v1/plugins/<name>/ API path, even if the manifest's "name" field differs.
summary="$(jq --arg dir_name "${plugin_name}" '{
name: $dir_name,
description: (.description // ""),
version: (.version // ""),
type: (.type // ""),
tier: (.tier // ""),
license: (.license // ""),
author: (.author // ""),
keywords: (.keywords // []),
private: (.private // false),
repository: (.repository // null),
minEngineVersion: (.minEngineVersion // null),
capabilities: {
moduleTypes: (.capabilities.moduleTypes // []),
stepTypes: (.capabilities.stepTypes // []),
triggerTypes: (.capabilities.triggerTypes // []),
workflowHandlers: (.capabilities.workflowHandlers // []),
wiringHooks: (.capabilities.wiringHooks // [])
}
}' "${manifest}")"

summaries="$(echo "${summaries}" | jq --argjson s "${summary}" '. + [$s]')"

# Copy manifest to v1/plugins/<name>/manifest.json
dest_dir="${OUT_DIR}/plugins/${plugin_name}"
mkdir -p "${dest_dir}"
cp "${manifest}" "${dest_dir}/manifest.json"
echo " copied plugins/${plugin_name}/manifest.json"
done < <(find "${PLUGINS_DIR}" -name "manifest.json" | sort)

# Sort summaries by name and write index
echo "${summaries}" | jq 'sort_by(.name)' > "${OUT_DIR}/index.json"

plugin_count="$(echo "${summaries}" | jq 'length')"
echo "Done. Generated v1/index.json with ${plugin_count} plugins."
Loading
Loading