Skip to content
Open
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
4 changes: 2 additions & 2 deletions .github/plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
},
"metadata": {
"description": "Copilot CLI plugins for migrating CI/CD pipelines to GitHub Actions",
"version": "1.3.0"
"version": "1.4.0"
},
"plugins": [
{
"name": "actions-migrator",
"description": "Migrate CI/CD pipelines from Jenkins, Azure DevOps, CircleCI, GitLab, Travis CI, Bamboo, Bitbucket Pipelines, and Drone CI to GitHub Actions. Includes a Reusable Workflow Builder that detects cross-platform patterns across multiple organizations.",
"version": "1.3.0",
"version": "1.4.0",
"source": "./plugin"
}
]
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/hooks-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Hook Contract Tests

# Runs the plugin hook regression suite on every change to the hooks so a
# schema or behavior change that would silently break the CLI, Cloud agent, or
# VS Code surface fails the PR instead of shipping unnoticed.

on:
push:
branches: [main]
paths:
- 'plugin/hooks.json'
- 'plugin/hooks.test.sh'
- '.github/workflows/hooks-test.yml'
pull_request:
paths:
- 'plugin/hooks.json'
- 'plugin/hooks.test.sh'
- '.github/workflows/hooks-test.yml'

permissions:
contents: read

jobs:
hook-contract-tests:
runs-on: ubuntu-latest
steps:
# actions/checkout v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683

- name: Validate hooks.json is parseable
run: jq empty plugin/hooks.json

- name: Run hook contract tests (CLI + VS Code schemas)
run: bash plugin/hooks.test.sh
5 changes: 5 additions & 0 deletions consumer-template/.github/copilot/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"enabledPlugins": {
"actions-migrator@actions-migrations-via-copilot": true
}
}
36 changes: 36 additions & 0 deletions consumer-template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Consumer Template

Drop the contents of this directory into the **root of any repo** that should
use the `actions-migrator` plugin.

It enables the plugin (and its hooks) automatically on three surfaces:

| Surface | What happens | Requires |
| --- | --- | --- |
| Copilot CLI | `enabledPlugins` auto-installs on next `copilot` startup | CLI ≥ 1.0.60 |
| Copilot cloud agent (github.com) | sweagentd loads plugin into the job sandbox; hooks fire | Repo, org, or enterprise scope. `CopilotSWEAgentPluginsEnabled` enabled. |
| VS Code Agent Plugins (preview) | Plugin shows up in `@agentPlugins` recommendations | `chat.plugins.enabled: true` (org policy) |

## Files

| Path | Purpose |
| --- | --- |
| `.github/copilot/settings.json` | Declares the plugin in `enabledPlugins`. The single line that wires up all three surfaces. |

## Optional: org-wide rollout

Put the same `.github/copilot/settings.json` in your org's `.github` or
`.github-private` repo. Every repo in the org inherits it. Enterprise admins
can do the same in the designated `.github-private` to enforce it across all
orgs.

Merge precedence in CCA is **enterprise > org > repo**.

## Sanity check after merging

```bash
# After a migration session, the consumer repo should have:
test -s .github/MIGRATION-SCORECARD.md && echo SCORECARD_OK
jq -r '.tool' .github/ci-archive/migration-audit.jsonl | sort -u
# Expect: bash, create, edit, view (NOT "null")
```
56 changes: 56 additions & 0 deletions plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,62 @@ This replaces the previous pattern of agents fetching `knowledge/*.md` files at

---

## Hooks — Deterministic Enforcement

The plugin includes hooks that run deterministic checks during migrations. Unlike skills and agent instructions (which the model can choose to ignore), hooks execute as shell commands at specific lifecycle points and can **block** operations or **inject warnings** into the agent's context.

### `hooks.json`

| Hook | Event | Matcher | What it does |
|------|-------|---------|-------------|
| Secret detection | `preToolUse` | `create\|edit` | Hard-denies file writes containing hardcoded secrets (passwords, tokens, API keys). Forces use of `${{ secrets.NAME }}`. Uses `permissionDecision: "deny"`. |
| File deletion guard | `preToolUse` | `bash` | Hard-denies `rm` operations outside `.github/ci-archive/`. Prevents accidental deletion of application source code. |
| Quality check + actionlint | `postToolUse` | `create\|edit` | After any workflow file write, injects `additionalContext` with: unpinned actions (tag vs SHA), placeholder text (TODO/FIXME), over-broad permissions (`write-all`), missing permissions block, and actionlint errors. The agent sees these on the same turn. |
| **Quality gate** | `agentStop` | — | Scans ALL workflow files when the agent finishes a turn. If any have issues, returns `decision: "block"` forcing the agent to take another turn to fix them. Safety valve releases after 3 attempts to prevent infinite loops. |
| **Migration scorecard** | `sessionEnd` (CLI) / `Stop` (VS Code) | — | Appends an entry to `.github/MIGRATION-SCORECARD.md` with session ID, timestamp, completion reason, and per-file workflow table (total / clean / with-issues). Multiple passes show quality progression. Audit artifact for migration quality tracking. |

The hooks run on all three Copilot surfaces — **CLI**, **Cloud agent**, and **VS Code Agent Plugins** — from this single `hooks.json`. The surfaces send different payload schemas (e.g. CLI `toolName`/`toolArgs`-string vs VS Code `tool_name`/`tool_input`-object, and CLI `sessionEnd` vs VS Code `Stop`); each hook normalizes its input and emits both output shapes so the same file works everywhere.

### Why hooks matter

The `migration-core` skill already contains guardrails as agent instructions. Hooks add a **deterministic layer** — the agent can't bypass them. This is the difference between "please don't delete files outside ci-archive" (instruction) and "the system will reject the tool call" (hook).

**actionlint** runs in three hooks: `postToolUse` (per-file, immediate feedback), `agentStop` (all files, blocks completion), and `sessionEnd` (final scorecard counts). The agent cannot skip or ignore lint errors — the quality gate blocks completion until they're fixed.

**The quality gate** (`agentStop`) is the key enforcement mechanism. Instead of just warning after each file write, it checks all workflows at the end of every agent turn and forces continuation until they pass. This works in both CLI interactive mode and cloud agent jobs.

### Enabling hooks

Hooks are installed automatically with the plugin. To verify:

```bash
copilot
/hooks list
```

To disable hooks temporarily (e.g., for debugging):

```bash
copilot --disable-hooks
```

### Testing the hooks

The hooks are covered by a contract test suite that pins their behavior on **both** the CLI and VS Code payload schemas, so a change that silently breaks one surface fails loudly instead of shipping unnoticed.

```bash
# from the repo root
bash plugin/hooks.test.sh
```

What it checks (22 cases): secret-detection deny/allow, destructive-op guard (rm/mv/git mv/find -delete, path traversal, CI-source archival, shell redirects), workflow quality flags, and scorecard generation including the VS Code `Stop` loop-guard — each exercised against both the CLI (`toolName`/`toolArgs`-string) and VS Code (`tool_name`/`tool_input`-object) shapes.

**Requirements:** `bash` and `jq` only. The suite is self-contained — it does **not** require `actionlint`, network access, `curl`, or `brew` (workflow-quality checks fall back to static `grep` analysis when `actionlint` is unavailable), it writes nothing to the working tree, and it cleans up its own temp files. If `jq` is missing it exits with a clear `FATAL: jq is required` message.

**CI:** [`.github/workflows/hooks-test.yml`](../.github/workflows/hooks-test.yml) runs this suite on every pull request that touches `plugin/hooks.json` or `plugin/hooks.test.sh`, and validates that `hooks.json` parses. A regression blocks the PR.

---

## Customizing Skills

Customizing skills is the CLI plugin's equivalent of editing the `knowledge/` knowledge base in the [cloud-agent deployment](../docs/deployment.md). Because the plugin ships content **locally**, your edits take effect on the next `copilot plugin install ./plugin`—no `.github-private` push, no MCP round-trip.
Expand Down
Loading
Loading