mirror of
https://github.com/github/awesome-copilot.git
synced 2026-06-23 07:57:43 +00:00
Merge branch 'staged' into aaronpowell/comparing-vally-lint
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
---
|
||||
description: GitHub Agentic Workflows (gh-aw) - Create, debug, and upgrade AI-powered workflows with intelligent prompt routing
|
||||
name: Agentic Workflows
|
||||
description: GitHub Agentic Workflows (gh-aw) - Create, debug, and upgrade AI-powered workflows with intelligent prompt routing.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
@@ -13,13 +14,16 @@ This is a **dispatcher agent** that routes your request to the appropriate speci
|
||||
|
||||
- **Creating new workflows**: Routes to `create` prompt
|
||||
- **Updating existing workflows**: Routes to `update` prompt
|
||||
- **Debugging workflows**: Routes to `debug` prompt
|
||||
- **Debugging workflows**: Routes to `debug` prompt
|
||||
- **Upgrading workflows**: Routes to `upgrade-agentic-workflows` prompt
|
||||
- **Creating report-generating workflows**: Routes to `report` prompt — consult this whenever the workflow posts status updates, audits, analyses, or any structured output as issues, discussions, or comments
|
||||
- **Creating shared components**: Routes to `create-shared-agentic-workflow` prompt
|
||||
- **Fixing Dependabot PRs**: Routes to `dependabot` prompt — use this when Dependabot opens PRs that modify generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`). Never merge those PRs directly; instead update the source `.md` files and rerun `gh aw compile --dependabot` to bundle all fixes
|
||||
- **Analyzing test coverage**: Routes to `test-coverage` prompt — consult this whenever the workflow reads, analyzes, or reports on test coverage data from PRs or CI runs
|
||||
- **Rendering ASCII charts in markdown**: Routes to `asciicharts` guide — consult this whenever the workflow needs compact charts that render reliably in GitHub issues, comments, or discussions
|
||||
- **CLI commands and triggering workflows**: Routes to `cli-commands` guide — consult this whenever the user asks how to run, compile, debug, or manage workflows from the command line, or when they need the MCP tool equivalent of a `gh aw` command
|
||||
- **Reducing token consumption / cost optimization**: Routes to `token-optimization` guide — consult this whenever the user asks how to reduce token usage, lower costs, speed up workflows, or measure the impact of prompt changes with experiments
|
||||
- **Choosing workflow architectures and design patterns**: Routes to `patterns` guide — consult this whenever the user asks for strategy, architecture, operating models, or pattern selection for agentic workflows
|
||||
|
||||
Workflows may optionally include:
|
||||
|
||||
@@ -31,7 +35,7 @@ Workflows may optionally include:
|
||||
- Workflow files: `.github/workflows/*.md` and `.github/workflows/**/*.md`
|
||||
- Workflow lock files: `.github/workflows/*.lock.yml`
|
||||
- Shared components: `.github/workflows/shared/*.md`
|
||||
- Configuration: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/github-agentic-workflows.md
|
||||
- Configuration: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/github-agentic-workflows.md`
|
||||
|
||||
## Problems This Solves
|
||||
|
||||
@@ -50,30 +54,32 @@ When you interact with this agent, it will:
|
||||
|
||||
## Available Prompts
|
||||
|
||||
> **Note**: The prompt and reference files listed below are located in the [`github/gh-aw`](https://github.com/github/gh-aw) repository and are **not available locally** in this repository. Load them from their public URLs.
|
||||
|
||||
### Create New Workflow
|
||||
**Load when**: User wants to create a new workflow from scratch, add automation, or design a workflow that doesn't exist yet
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/create-agentic-workflow.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/create-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a workflow that triages issues"
|
||||
- "I need a workflow to label pull requests"
|
||||
- "Design a weekly research automation"
|
||||
|
||||
### Update Existing Workflow
|
||||
### Update Existing Workflow
|
||||
**Load when**: User wants to modify, improve, or refactor an existing workflow
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/update-agentic-workflow.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/update-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Add web-fetch tool to the issue-classifier workflow"
|
||||
- "Update the PR reviewer to use discussions instead of issues"
|
||||
- "Improve the prompt for the weekly-research workflow"
|
||||
|
||||
### Debug Workflow
|
||||
### Debug Workflow
|
||||
**Load when**: User needs to investigate, audit, debug, or understand a workflow, troubleshoot issues, analyze logs, or fix errors
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/debug-agentic-workflow.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/debug-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Why is this workflow failing?"
|
||||
@@ -83,7 +89,7 @@ When you interact with this agent, it will:
|
||||
### Upgrade Agentic Workflows
|
||||
**Load when**: User wants to upgrade workflows to a new gh-aw version or fix deprecations
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/upgrade-agentic-workflows.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/upgrade-agentic-workflows.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Upgrade all workflows to the latest version"
|
||||
@@ -93,7 +99,7 @@ When you interact with this agent, it will:
|
||||
### Create a Report-Generating Workflow
|
||||
**Load when**: The workflow being created or updated produces reports — recurring status updates, audit summaries, analyses, or any structured output posted as a GitHub issue, discussion, or comment
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/report.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/report.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a weekly CI health report"
|
||||
@@ -103,7 +109,7 @@ When you interact with this agent, it will:
|
||||
### Create Shared Agentic Workflow
|
||||
**Load when**: User wants to create a reusable workflow component or wrap an MCP server
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/create-shared-agentic-workflow.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/create-shared-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a shared component for Notion integration"
|
||||
@@ -113,7 +119,7 @@ When you interact with this agent, it will:
|
||||
### Fix Dependabot PRs
|
||||
**Load when**: User needs to close or fix open Dependabot PRs that update dependencies in generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`)
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/dependabot.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/dependabot.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Fix the open Dependabot PRs for npm dependencies"
|
||||
@@ -123,7 +129,7 @@ When you interact with this agent, it will:
|
||||
### Analyze Test Coverage
|
||||
**Load when**: The workflow reads, analyzes, or reports test coverage — whether triggered by a PR, a schedule, or a slash command. Always consult this prompt before designing the coverage data strategy.
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/test-coverage.md
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/test-coverage.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a workflow that comments coverage on PRs"
|
||||
@@ -133,7 +139,7 @@ When you interact with this agent, it will:
|
||||
### CLI Commands Reference
|
||||
**Load when**: The user asks how to run, compile, debug, or manage workflows from the command line; needs the MCP tool equivalent of a `gh aw` command; or is in a restricted environment (e.g., Copilot Cloud) without direct CLI access.
|
||||
|
||||
**Reference file**: https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/cli-commands.md
|
||||
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/cli-commands.md`
|
||||
|
||||
**Use cases**:
|
||||
- "How do I trigger workflow X on the main branch?"
|
||||
@@ -141,12 +147,36 @@ When you interact with this agent, it will:
|
||||
- "I'm in Copilot Cloud — how do I compile a workflow?"
|
||||
- "Show me all available gh aw commands"
|
||||
|
||||
### Token Consumption Optimization
|
||||
**Load when**: The user asks how to reduce token usage, lower workflow costs, make a workflow faster or cheaper, or measure the impact of prompt or configuration changes.
|
||||
|
||||
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/token-optimization.md`
|
||||
|
||||
**Use cases**:
|
||||
- "How do I reduce the token cost of this workflow?"
|
||||
- "My workflow is too expensive — how do I optimize it?"
|
||||
- "How do I compare token usage between two runs?"
|
||||
- "Should I use gh-proxy or the MCP server?"
|
||||
- "How do I use sub-agents to reduce costs?"
|
||||
- "How do I measure the impact of a prompt change?"
|
||||
|
||||
### Workflow Pattern Selection
|
||||
**Load when**: The user asks for architecture, strategy, operating model selection, or pattern recommendations for building agentic workflows.
|
||||
|
||||
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/patterns.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Which pattern should I use for multi-repo rollout?"
|
||||
- "How should I structure this workflow architecture?"
|
||||
- "What pattern fits slash-command triage?"
|
||||
- "Should this be DispatchOps or DailyOps?"
|
||||
|
||||
## Instructions
|
||||
|
||||
When a user interacts with you:
|
||||
|
||||
1. **Identify the task type** from the user's request
|
||||
2. **Load the appropriate prompt** from the GitHub repository URLs listed above
|
||||
2. **Load the appropriate prompt** from the URLs listed above
|
||||
3. **Follow the loaded prompt's instructions** exactly
|
||||
4. **If uncertain**, ask clarifying questions to determine the right prompt
|
||||
|
||||
@@ -185,12 +215,12 @@ gh aw compile --validate
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Always reference the instructions file at https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/github-agentic-workflows.md for complete documentation
|
||||
- Always reference the instructions file at `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/github-agentic-workflows.md` for complete documentation
|
||||
- Use the MCP tool `agentic-workflows` when running in GitHub Copilot Cloud
|
||||
- Workflows must be compiled to `.lock.yml` files before running in GitHub Actions
|
||||
- **Bash tools are enabled by default** - Don't restrict bash commands unnecessarily since workflows are sandboxed by the AWF
|
||||
- Follow security best practices: minimal permissions, explicit network access, no template injection
|
||||
- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/network.md for the full list of valid ecosystem identifiers and domain patterns.
|
||||
- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/network.md` for the full list of valid ecosystem identifiers and domain patterns.
|
||||
- **Single-file output**: When creating a workflow, produce exactly **one** workflow `.md` file. Do not create separate documentation files (architecture docs, runbooks, usage guides, etc.). If documentation is needed, add a brief `## Usage` section inside the workflow file itself.
|
||||
- **Triggering runs**: Always use `gh aw run <workflow-name>` to trigger a workflow on demand — not `gh workflow run <file>.lock.yml`. `gh aw run` handles workflow resolution by short name, input parsing and validation, and correct run-tracking for agentic workflows. Use `--ref <branch>` to run on a specific branch.
|
||||
- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see https://github.com/github/gh-aw/blob/v0.71.5/.github/aw/cli-commands.md
|
||||
- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/cli-commands.md`
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"entries": {
|
||||
"actions/checkout@v6.0.2": {
|
||||
"actions/checkout@v7.0.0": {
|
||||
"repo": "actions/checkout",
|
||||
"version": "v6.0.2",
|
||||
"sha": "de0fac2e4500dabe0009e67214ff5f5447ce83dd"
|
||||
"version": "v7.0.0",
|
||||
"sha": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"
|
||||
},
|
||||
"actions/download-artifact@v8.0.1": {
|
||||
"repo": "actions/download-artifact",
|
||||
@@ -20,15 +20,15 @@
|
||||
"version": "v7.0.1",
|
||||
"sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"
|
||||
},
|
||||
"github/gh-aw-actions/setup@v0.72.1": {
|
||||
"github/gh-aw-actions/setup@v0.80.9": {
|
||||
"repo": "github/gh-aw-actions/setup",
|
||||
"version": "v0.72.1",
|
||||
"sha": "bc56a0cad2f450c562810785ef38649c04db812a"
|
||||
"version": "v0.80.9",
|
||||
"sha": "8c7d04ebf1ece56cd381446125da3e0f6896294a"
|
||||
},
|
||||
"github/gh-aw/actions/setup@v0.72.1": {
|
||||
"github/gh-aw/actions/setup@v0.80.9": {
|
||||
"repo": "github/gh-aw/actions/setup",
|
||||
"version": "v0.72.1",
|
||||
"sha": "489dbab88cc78e35506b5ccbf08a4037166824ac"
|
||||
"version": "v0.80.9",
|
||||
"sha": "a3624368c4e7d877586ff2784b61de73405e2cdd"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,33 @@
|
||||
"description": "Drive Microsoft AgentRC from Copilot chat: assess AI readiness, generate Copilot instructions (flat or nested with applyTo globs for monorepos), and manage policies. Produces a self-contained static HTML dashboard at reports/index.html.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"name": "agent-council",
|
||||
"description": "A runtime-portable 5-agent quality gate that adjudicates text artifacts before they ship. Five role-conditioned LLM deliberators run a 2-round async protocol with cross-read rebuttal and return one verdict — SHIP, REVISE, or HOLD — plus a structured revision brief and a full audit transcript.",
|
||||
"version": "0.1.3",
|
||||
"author": {
|
||||
"name": "Parth Sangani",
|
||||
"url": "https://github.com/Avyayalaya"
|
||||
},
|
||||
"repository": "https://github.com/Avyayalaya/agent-council",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"quality-gate",
|
||||
"multi-agent",
|
||||
"adjudication",
|
||||
"council",
|
||||
"llm-as-judge",
|
||||
"agent-orchestration",
|
||||
"review",
|
||||
"claude-code",
|
||||
"mcp"
|
||||
],
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "Avyayalaya/agent-council",
|
||||
"ref": "v0.1.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ai-ready",
|
||||
"description": "Analyze any repository and generate AI-ready configuration — AGENTS.md, copilot-instructions.md, CI workflows, issue templates, and more. Mines your PR review patterns and creates files customized to your stack.",
|
||||
@@ -202,6 +229,30 @@
|
||||
"description": "Database administration, SQL optimization, and data management tools for PostgreSQL, SQL Server, and general database development best practices.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"name": "datadog",
|
||||
"description": "Use Datadog directly in Copilot / VS Code through a preconfigured Datadog MCP server. Query logs, metrics, traces, dashboards, and more through natural conversation.",
|
||||
"version": "0.7.15",
|
||||
"author": {
|
||||
"name": "Datadog",
|
||||
"url": "https://www.datadoghq.com/"
|
||||
},
|
||||
"repository": "https://github.com/datadog-labs/vscode-plugin",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"agent",
|
||||
"copilot",
|
||||
"datadog",
|
||||
"monitoring",
|
||||
"observability",
|
||||
"vscode"
|
||||
],
|
||||
"source": {
|
||||
"source": "github",
|
||||
"repo": "datadog-labs/vscode-plugin",
|
||||
"sha": "b003fcad48c3a935ffe04b6218f5cf58fe2b6760"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dataverse",
|
||||
"description": "Build and manage Microsoft Dataverse solutions using natural language. Includes table/column creation, solution lifecycle, data operations, and MCP server configuration.",
|
||||
@@ -561,7 +612,7 @@
|
||||
{
|
||||
"name": "modernize-dotnet",
|
||||
"description": "AI-powered .NET modernization and upgrade assistant. Helps upgrade .NET Framework and .NET applications to the latest versions of .NET.",
|
||||
"version": "1.0.1152-preview1",
|
||||
"version": "1.0.1157-preview1",
|
||||
"author": {
|
||||
"name": "Microsoft",
|
||||
"url": "https://www.microsoft.com"
|
||||
@@ -901,7 +952,7 @@
|
||||
{
|
||||
"name": "ui5",
|
||||
"description": "SAPUI5 / OpenUI5 plugin for GitHub CoPilot. Create and validate UI5 projects, access API documentation, run UI5 linter, get development guidelines and best practices for UI5 development.",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"author": {
|
||||
"name": "SAP SE",
|
||||
"url": "https://www.sap.com"
|
||||
@@ -922,14 +973,13 @@
|
||||
"source": "github",
|
||||
"repo": "UI5/plugins-coding-agents",
|
||||
"path": "plugins/ui5",
|
||||
"ref": "v0.1.3",
|
||||
"sha": "9b3d7d80356f687725f9584988e4038dbead0d53"
|
||||
"sha": "80f2d93287054f9d30dd990e842e15bcfca581c9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ui5-typescript-conversion",
|
||||
"description": "SAPUI5 / OpenUI5 plugin for GitHub CoPilot. Convert JavaScript based UI5 projects to TypeScript.",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"author": {
|
||||
"name": "SAP SE",
|
||||
"url": "https://www.sap.com"
|
||||
@@ -951,8 +1001,7 @@
|
||||
"source": "github",
|
||||
"repo": "UI5/plugins-coding-agents",
|
||||
"path": "plugins/ui5-typescript-conversion",
|
||||
"ref": "v0.1.3",
|
||||
"sha": "9b3d7d80356f687725f9584988e4038dbead0d53"
|
||||
"sha": "80f2d93287054f9d30dd990e842e15bcfca581c9"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
- [ ] I have read and followed the [CONTRIBUTING.md](https://github.com/github/awesome-copilot/blob/main/CONTRIBUTING.md) guidelines.
|
||||
- [ ] I have read and followed the [Guidance for submissions involving paid services](https://github.com/github/awesome-copilot/discussions/968).
|
||||
- [ ] My contribution adds a new instruction, prompt, agent, skill, or workflow file in the correct directory.
|
||||
- [ ] My contribution adds a new instruction, prompt, agent, skill, workflow, or canvas extension file in the correct directory.
|
||||
- [ ] The file follows the required naming convention.
|
||||
- [ ] The content is clearly structured and follows the example format.
|
||||
- [ ] I have tested my instructions, prompt, agent, skill, or workflow with GitHub Copilot.
|
||||
- [ ] I have tested my instructions, prompt, agent, skill, workflow, or canvas extension with GitHub Copilot.
|
||||
- [ ] I have run `npm start` and verified that `README.md` is up to date.
|
||||
- [ ] I am targeting the `staged` branch for this pull request.
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
- [ ] New plugin.
|
||||
- [ ] New skill file.
|
||||
- [ ] New agentic workflow.
|
||||
- [ ] Update to existing instruction, prompt, agent, plugin, skill, or workflow.
|
||||
- [ ] New canvas extension.
|
||||
- [ ] Update to existing instruction, prompt, agent, plugin, skill, workflow, or canvas extension.
|
||||
- [ ] Other (please specify):
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
name: agentic-workflows
|
||||
description: Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
|
||||
---
|
||||
|
||||
# Agentic Workflows Router
|
||||
|
||||
Use this skill when a user asks to design, create, update, debug, or upgrade GitHub Agentic Workflows in this repository.
|
||||
|
||||
This skill is a dispatcher: identify the task type, load the matching workflow prompt/skill file, and follow it directly. Keep responses concise and ask a clarifying question if the correct prompt is unclear.
|
||||
|
||||
Read only the files you need:
|
||||
Load these files from `github/gh-aw` (they are not available locally).
|
||||
- `.github/aw/agentic-chat.md`
|
||||
- `.github/aw/agentic-workflows-mcp.md`
|
||||
- `.github/aw/asciicharts.md`
|
||||
- `.github/aw/campaign.md`
|
||||
- `.github/aw/charts-trending.md`
|
||||
- `.github/aw/charts.md`
|
||||
- `.github/aw/cli-commands.md`
|
||||
- `.github/aw/context.md`
|
||||
- `.github/aw/create-agentic-workflow.md`
|
||||
- `.github/aw/create-shared-agentic-workflow.md`
|
||||
- `.github/aw/debug-agentic-workflow.md`
|
||||
- `.github/aw/dependabot.md`
|
||||
- `.github/aw/deployment-status.md`
|
||||
- `.github/aw/experiments.md`
|
||||
- `.github/aw/github-agentic-workflows.md`
|
||||
- `.github/aw/github-mcp-server.md`
|
||||
- `.github/aw/llms.md`
|
||||
- `.github/aw/mcp-clis.md`
|
||||
- `.github/aw/memory.md`
|
||||
- `.github/aw/messages.md`
|
||||
- `.github/aw/network.md`
|
||||
- `.github/aw/optimize-agentic-workflow.md`
|
||||
- `.github/aw/patterns.md`
|
||||
- `.github/aw/pr-reviewer.md`
|
||||
- `.github/aw/report.md`
|
||||
- `.github/aw/reuse.md`
|
||||
- `.github/aw/safe-outputs-automation.md`
|
||||
- `.github/aw/safe-outputs-content.md`
|
||||
- `.github/aw/safe-outputs-management.md`
|
||||
- `.github/aw/safe-outputs-runtime.md`
|
||||
- `.github/aw/safe-outputs.md`
|
||||
- `.github/aw/serena-tool.md`
|
||||
- `.github/aw/shared-safe-jobs.md`
|
||||
- `.github/aw/skills.md`
|
||||
- `.github/aw/subagents.md`
|
||||
- `.github/aw/syntax-agentic.md`
|
||||
- `.github/aw/syntax-core.md`
|
||||
- `.github/aw/syntax-tools-imports.md`
|
||||
- `.github/aw/syntax.md`
|
||||
- `.github/aw/test-coverage.md`
|
||||
- `.github/aw/test-expression.md`
|
||||
- `.github/aw/token-optimization.md`
|
||||
- `.github/aw/triggers.md`
|
||||
- `.github/aw/update-agentic-workflow.md`
|
||||
- `.github/aw/upgrade-agentic-workflows.md`
|
||||
- `.github/aw/visual-regression.md`
|
||||
- `.github/aw/workflow-constraints.md`
|
||||
- `.github/aw/workflow-editing.md`
|
||||
- `.github/aw/workflow-patterns.md`
|
||||
|
||||
- `.github/skills/agentic-workflow-designer/SKILL.md`
|
||||
After loading the matching workflow prompt or skill, follow it directly:
|
||||
- Design workflows from scratch via interview: `skills/agentic-workflow-designer/SKILL.md`
|
||||
- Create new workflows: `.github/aw/create-agentic-workflow.md`
|
||||
- Update existing workflows: `.github/aw/update-agentic-workflow.md`
|
||||
- Debug, audit, or investigate workflows: `.github/aw/debug-agentic-workflow.md`
|
||||
- Upgrade workflows and fix deprecations: `.github/aw/upgrade-agentic-workflows.md`
|
||||
- Create shared components or MCP wrappers: `.github/aw/create-shared-agentic-workflow.md`
|
||||
- Create report-generating workflows: `.github/aw/report.md`
|
||||
- Fix Dependabot manifest PRs: `.github/aw/dependabot.md`
|
||||
- Analyze coverage workflows: `.github/aw/test-coverage.md`
|
||||
- Render compact markdown charts: `.github/aw/asciicharts.md`
|
||||
- Map CLI commands to MCP usage: `.github/aw/cli-commands.md`
|
||||
- Choose workflow architecture and patterns: `.github/aw/patterns.md`
|
||||
- Optimize token usage and cost: `.github/aw/token-optimization.md`
|
||||
|
||||
When the task involves OTEL, OTLP, traces, observability backends, or telemetry-driven analysis, also read and follow `skills/otel-queries/SKILL.md` after loading the matching workflow prompt or skill.
|
||||
+455
-227
File diff suppressed because one or more lines are too long
+444
-222
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
||||
|
||||
- name: Check spelling with codespell
|
||||
uses: codespell-project/actions-codespell@406322ec52dd7b488e48c1c4b82e2a8b3a1bf630 # v2.1
|
||||
with:
|
||||
|
||||
@@ -21,6 +21,6 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Install gh-aw extension
|
||||
uses: github/gh-aw-actions/setup-cli@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
|
||||
uses: github/gh-aw-actions/setup-cli@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
|
||||
with:
|
||||
version: v0.71.5
|
||||
version: v0.80.9
|
||||
|
||||
+435
-182
File diff suppressed because one or more lines are too long
@@ -9,7 +9,8 @@ concurrency:
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync-merged-pr-labels:
|
||||
@@ -25,20 +26,6 @@ jobs:
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const staleLabels = ['awaiting-review', 'awaiting-approval', 'ready-for-review', 'rejected'];
|
||||
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: 'approved',
|
||||
color: '1D76DB',
|
||||
description: 'Submission was approved by a maintainer'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
|
||||
@@ -275,49 +275,6 @@ jobs:
|
||||
PR_NUMBER: ${{ steps.approval_pr.outputs.pr-number }}
|
||||
with:
|
||||
script: |
|
||||
const managedLabels = {
|
||||
'external-plugin': {
|
||||
color: 'FEF2C0',
|
||||
description: 'Public external plugin submission'
|
||||
},
|
||||
'awaiting-review': {
|
||||
color: 'FBCA04',
|
||||
description: 'Submission is waiting for automated intake validation'
|
||||
},
|
||||
'ready-for-review': {
|
||||
color: '0E8A16',
|
||||
description: 'Submission passed intake validation and is ready for maintainer review'
|
||||
},
|
||||
'requires-submitter-fixes': {
|
||||
color: 'D93F0B',
|
||||
description: 'Submission has quality-gate findings that submitter must fix before maintainer review'
|
||||
},
|
||||
'approved': {
|
||||
color: '1D76DB',
|
||||
description: 'Submission was approved by a maintainer'
|
||||
},
|
||||
'rejected': {
|
||||
color: 'B60205',
|
||||
description: 'Submission was rejected by a maintainer'
|
||||
}
|
||||
};
|
||||
|
||||
async function ensureLabel(name, config) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function removeLabel(issueNumber, name) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
@@ -334,7 +291,14 @@ jobs:
|
||||
}
|
||||
|
||||
async function syncIssueLabels(issueNumber, desiredLabels) {
|
||||
await Promise.all(Object.entries(managedLabels).map(([name, config]) => ensureLabel(name, config)));
|
||||
const managedLabels = {
|
||||
'external-plugin': true,
|
||||
'awaiting-review': true,
|
||||
'ready-for-review': true,
|
||||
'requires-submitter-fixes': true,
|
||||
'approved': true,
|
||||
'rejected': true
|
||||
};
|
||||
|
||||
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
owner: context.repo.owner,
|
||||
@@ -438,49 +402,6 @@ jobs:
|
||||
PLUGIN_NAME: ${{ steps.parse.outputs.plugin-name }}
|
||||
with:
|
||||
script: |
|
||||
const managedLabels = {
|
||||
'external-plugin': {
|
||||
color: 'FEF2C0',
|
||||
description: 'Public external plugin submission'
|
||||
},
|
||||
'awaiting-review': {
|
||||
color: 'FBCA04',
|
||||
description: 'Submission is waiting for automated intake validation'
|
||||
},
|
||||
'ready-for-review': {
|
||||
color: '0E8A16',
|
||||
description: 'Submission passed intake validation and is ready for maintainer review'
|
||||
},
|
||||
'requires-submitter-fixes': {
|
||||
color: 'D93F0B',
|
||||
description: 'Submission has quality-gate findings that submitter must fix before maintainer review'
|
||||
},
|
||||
'approved': {
|
||||
color: '1D76DB',
|
||||
description: 'Submission was approved by a maintainer'
|
||||
},
|
||||
'rejected': {
|
||||
color: 'B60205',
|
||||
description: 'Submission was rejected by a maintainer'
|
||||
}
|
||||
};
|
||||
|
||||
async function ensureLabel(name, config) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function removeLabel(name) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
@@ -496,7 +417,6 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(Object.entries(managedLabels).map(([name, config]) => ensureLabel(name, config)));
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
name: External Plugin PR Quality Gates
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
branches: [staged]
|
||||
paths:
|
||||
- "plugins/external.json"
|
||||
types: [opened, synchronize, reopened, edited, ready_for_review]
|
||||
|
||||
concurrency:
|
||||
group: external-plugin-pr-quality-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
detect-changed-plugins:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changed-plugins: ${{ steps.detect.outputs.changed-plugins }}
|
||||
changed-count: ${{ steps.detect.outputs.changed-count }}
|
||||
should-run: ${{ steps.detect.outputs.should-run }}
|
||||
steps:
|
||||
- name: Detect changed external plugins
|
||||
id: detect
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
with:
|
||||
script: |
|
||||
const filePath = 'plugins/external.json';
|
||||
const baseRef = context.payload.pull_request.base.sha;
|
||||
const headRef = context.payload.pull_request.head.sha;
|
||||
|
||||
function normalizePath(value) {
|
||||
if (!value || value === '/') {
|
||||
return '';
|
||||
}
|
||||
return String(value).trim().replace(/^\/+|\/+$/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
function toIdentity(plugin) {
|
||||
return [
|
||||
String(plugin?.name ?? '').trim().toLowerCase(),
|
||||
String(plugin?.source?.repo ?? '').trim().toLowerCase(),
|
||||
normalizePath(plugin?.source?.path),
|
||||
].join('|');
|
||||
}
|
||||
|
||||
async function readExternalJson(ref) {
|
||||
const response = await github.rest.repos.getContent({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
path: filePath,
|
||||
ref,
|
||||
});
|
||||
|
||||
const encoded = response.data?.content ?? '';
|
||||
const decoded = Buffer.from(encoded, 'base64').toString('utf8');
|
||||
return JSON.parse(decoded);
|
||||
}
|
||||
|
||||
const basePlugins = await readExternalJson(baseRef);
|
||||
const headPlugins = await readExternalJson(headRef);
|
||||
const baseByIdentity = new Map(basePlugins.map((plugin) => [toIdentity(plugin), plugin]));
|
||||
|
||||
const changedPlugins = headPlugins.filter((plugin) => {
|
||||
const identity = toIdentity(plugin);
|
||||
const basePlugin = baseByIdentity.get(identity);
|
||||
return !basePlugin || JSON.stringify(basePlugin) !== JSON.stringify(plugin);
|
||||
});
|
||||
|
||||
core.setOutput('changed-plugins', JSON.stringify(changedPlugins));
|
||||
core.setOutput('changed-count', String(changedPlugins.length));
|
||||
core.setOutput('should-run', changedPlugins.length > 0 ? 'true' : 'false');
|
||||
|
||||
run-quality-gates:
|
||||
runs-on: ubuntu-latest
|
||||
needs: detect-changed-plugins
|
||||
if: needs.detect-changed-plugins.outputs.should-run == 'true'
|
||||
outputs:
|
||||
quality-result: ${{ steps.quality.outputs.quality-result }}
|
||||
steps:
|
||||
- name: Checkout staged branch
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: staged
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install GitHub Copilot CLI
|
||||
run: npm install -g @github/copilot
|
||||
|
||||
- name: Run external plugin PR quality gates
|
||||
id: quality
|
||||
env:
|
||||
CHANGED_PLUGINS_JSON: ${{ needs.detect-changed-plugins.outputs.changed-plugins }}
|
||||
run: |
|
||||
result=$(node ./eng/external-plugin-pr-quality-gates.mjs --plugins-json "$CHANGED_PLUGINS_JSON")
|
||||
{
|
||||
echo 'quality-result<<EOF'
|
||||
echo "$result"
|
||||
echo 'EOF'
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
sync-pr-state:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [detect-changed-plugins, run-quality-gates]
|
||||
if: always()
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout staged branch
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: staged
|
||||
|
||||
- name: Sync labels and PR status comment
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
env:
|
||||
DETECT_JOB_RESULT: ${{ needs.detect-changed-plugins.result }}
|
||||
SHOULD_RUN: ${{ needs.detect-changed-plugins.outputs.should-run }}
|
||||
CHANGED_COUNT: ${{ needs.detect-changed-plugins.outputs.changed-count }}
|
||||
QUALITY_RESULT_JSON: ${{ needs.run-quality-gates.outputs.quality-result }}
|
||||
QUALITY_JOB_RESULT: ${{ needs.run-quality-gates.result }}
|
||||
with:
|
||||
script: |
|
||||
const path = require('path');
|
||||
const { pathToFileURL } = require('url');
|
||||
|
||||
const intakeState = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-intake-state.mjs')).href);
|
||||
const marker = '<!-- external-plugin-pr-quality -->';
|
||||
|
||||
const detectJobResult = process.env.DETECT_JOB_RESULT;
|
||||
const shouldRun = process.env.SHOULD_RUN === 'true';
|
||||
const changedCount = Number.parseInt(process.env.CHANGED_COUNT || '0', 10);
|
||||
const qualityJobResult = process.env.QUALITY_JOB_RESULT;
|
||||
|
||||
let qualityResult = {
|
||||
overall_status: 'not_run',
|
||||
failure_class: 'none',
|
||||
checked_plugins: [],
|
||||
summary: 'No changed external plugin entries were detected in this PR.',
|
||||
};
|
||||
|
||||
if (detectJobResult === 'failure' || detectJobResult === 'cancelled') {
|
||||
qualityResult = {
|
||||
overall_status: 'infra_error',
|
||||
failure_class: 'infra',
|
||||
checked_plugins: [],
|
||||
summary: 'External plugin PR change detection failed unexpectedly. Re-run this workflow.',
|
||||
};
|
||||
} else if (shouldRun) {
|
||||
if (qualityJobResult === 'failure' || qualityJobResult === 'cancelled') {
|
||||
qualityResult = {
|
||||
overall_status: 'infra_error',
|
||||
failure_class: 'infra',
|
||||
checked_plugins: [],
|
||||
summary: 'External plugin PR quality checks failed unexpectedly. Re-run this workflow.',
|
||||
};
|
||||
} else if (process.env.QUALITY_RESULT_JSON) {
|
||||
qualityResult = JSON.parse(process.env.QUALITY_RESULT_JSON);
|
||||
} else {
|
||||
qualityResult = {
|
||||
overall_status: 'infra_error',
|
||||
failure_class: 'infra',
|
||||
checked_plugins: [],
|
||||
summary: 'External plugin PR quality checks did not return a result payload.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const stateLabel = qualityResult.failure_class === 'submitter_fixes'
|
||||
? 'requires-submitter-fixes'
|
||||
: qualityResult.overall_status === 'pass' || !shouldRun
|
||||
? 'ready-for-review'
|
||||
: 'awaiting-review';
|
||||
|
||||
const desiredLabels = new Set(['external-plugin', stateLabel]);
|
||||
await intakeState.syncExternalPluginIntakeLabels({
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issueNumber: context.issue.number,
|
||||
desiredLabels,
|
||||
});
|
||||
|
||||
const checkedPlugins = Array.isArray(qualityResult.checked_plugins) ? qualityResult.checked_plugins : [];
|
||||
const header = qualityResult.failure_class === 'submitter_fixes'
|
||||
? '## ⚠️ External plugin PR checks require submitter fixes'
|
||||
: qualityResult.overall_status === 'pass' || !shouldRun
|
||||
? '## ✅ External plugin PR checks passed'
|
||||
: '## ⚠️ External plugin PR checks need maintainer follow-up';
|
||||
|
||||
const rows = checkedPlugins.length > 0
|
||||
? checkedPlugins.map((entry) => {
|
||||
const name = String(entry?.name || 'unknown');
|
||||
const quality = entry?.quality || {};
|
||||
const sourceUrl = String(entry?.source_tree_url || '');
|
||||
const locator = String(entry?.source?.sha || entry?.source?.ref || 'repository');
|
||||
const sourceCell = sourceUrl ? `[${locator}](${sourceUrl})` : locator;
|
||||
return `| ${name} | ${quality.skill_validator_status || 'not_run'} | ${quality.smoke_status || 'not_run'} | ${quality.overall_status || 'not_run'} | ${sourceCell} |`;
|
||||
})
|
||||
: ['| _none_ | not_run | not_run | not_run | _n/a_ |'];
|
||||
|
||||
const body = [
|
||||
marker,
|
||||
header,
|
||||
'',
|
||||
`- **Changed entries detected:** ${changedCount}`,
|
||||
`- **Workflow state label:** \`${stateLabel}\``,
|
||||
'',
|
||||
'### Per-plugin quality summary',
|
||||
'',
|
||||
'| Plugin | skill-validator | install smoke test | overall | source tree |',
|
||||
'|---|---|---|---|---|',
|
||||
...rows,
|
||||
'',
|
||||
String(qualityResult.summary || '').trim() || '_No summary provided._',
|
||||
].join('\n');
|
||||
|
||||
await intakeState.upsertExternalPluginIntakeComment({
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issueNumber: context.issue.number,
|
||||
marker,
|
||||
body,
|
||||
});
|
||||
@@ -180,34 +180,6 @@ jobs:
|
||||
PLUGIN_NAME: ${{ steps.parse.outputs.plugin-name }}
|
||||
with:
|
||||
script: |
|
||||
const managedLabels = {
|
||||
're-review-due': {
|
||||
color: 'FBCA04',
|
||||
description: 'Approved external plugin is due for six-month re-review'
|
||||
},
|
||||
're-review-follow-up': {
|
||||
color: 'D4C5F9',
|
||||
description: 'Six-month re-review needs maintainer follow-up before a final decision'
|
||||
}
|
||||
};
|
||||
|
||||
async function ensureLabel(name, config) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(Object.entries(managedLabels).map(([name, config]) => ensureLabel(name, config)));
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
|
||||
@@ -26,37 +26,6 @@ jobs:
|
||||
const rereview = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-rereview.mjs')).href);
|
||||
const validation = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-validation.mjs')).href);
|
||||
|
||||
const managedLabels = {
|
||||
[rereview.REREVIEW_LABELS.due]: {
|
||||
color: 'FBCA04',
|
||||
description: 'Approved external plugin is due for six-month re-review'
|
||||
},
|
||||
[rereview.REREVIEW_LABELS.followUp]: {
|
||||
color: 'D4C5F9',
|
||||
description: 'Six-month re-review needs maintainer follow-up before a final decision'
|
||||
},
|
||||
[rereview.REREVIEW_LABELS.removed]: {
|
||||
color: 'B60205',
|
||||
description: 'External plugin was removed from the marketplace after re-review'
|
||||
}
|
||||
};
|
||||
|
||||
async function ensureLabel(name, config) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function removeLabel(issueNumber, label) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
@@ -90,8 +59,6 @@ jobs:
|
||||
return Math.max(0, Math.floor(Math.abs(diff) / (1000 * 60 * 60 * 24)));
|
||||
}
|
||||
|
||||
await Promise.all(Object.entries(managedLabels).map(([name, config]) => ensureLabel(name, config)));
|
||||
|
||||
const { plugins, errors } = validation.readExternalPlugins({ policy: 'marketplace' });
|
||||
if (errors.length > 0) {
|
||||
core.setFailed(errors.join('\n'));
|
||||
|
||||
@@ -20,50 +20,18 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const managedLabels = {
|
||||
'targets-main': {
|
||||
color: 'B60205',
|
||||
description: 'PR targets main instead of staged'
|
||||
},
|
||||
'branched-main': {
|
||||
color: 'D93F0B',
|
||||
description: 'PR appears to include plugin files materialized from main'
|
||||
},
|
||||
'skills': {
|
||||
color: '1D76DB',
|
||||
description: 'PR touches skills'
|
||||
},
|
||||
'plugin': {
|
||||
color: '5319E7',
|
||||
description: 'PR touches plugins'
|
||||
},
|
||||
'agent': {
|
||||
color: '0E8A16',
|
||||
description: 'PR touches agents'
|
||||
},
|
||||
'instructions': {
|
||||
color: 'FBCA04',
|
||||
description: 'PR touches instructions'
|
||||
},
|
||||
'new-submission': {
|
||||
color: '006B75',
|
||||
description: 'PR adds at least one new contribution'
|
||||
},
|
||||
'website-update': {
|
||||
color: '0052CC',
|
||||
description: 'PR touches website content or code'
|
||||
},
|
||||
'external-plugin': {
|
||||
color: 'FEF2C0',
|
||||
description: 'PR updates plugins/external.json'
|
||||
},
|
||||
'hooks': {
|
||||
color: 'C2E0C6',
|
||||
description: 'PR touches hooks'
|
||||
},
|
||||
'workflow': {
|
||||
color: 'BFD4F2',
|
||||
description: 'PR touches workflow automation'
|
||||
}
|
||||
'targets-main': true,
|
||||
'branched-main': true,
|
||||
'skills': true,
|
||||
'plugin': true,
|
||||
'agent': true,
|
||||
'instructions': true,
|
||||
'new-submission': true,
|
||||
'website-update': true,
|
||||
'external-plugin': true,
|
||||
'hooks': true,
|
||||
'workflow': true,
|
||||
'canvas-extension': true
|
||||
};
|
||||
|
||||
const matchesAny = (filename, patterns) => patterns.some((pattern) => pattern.test(filename));
|
||||
@@ -91,22 +59,6 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureLabel(name, { color, description }) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color,
|
||||
description
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const files = await listAllFiles();
|
||||
const filenames = files.map((file) => file.filename);
|
||||
|
||||
@@ -139,12 +91,16 @@ jobs:
|
||||
/^workflows\/.+\.md$/,
|
||||
/^\.github\/workflows\/.+\.(?:ya?ml|md)$/
|
||||
],
|
||||
canvasExtension: [
|
||||
/^extensions\/[^/]+\//
|
||||
],
|
||||
newSubmission: [
|
||||
/^agents\/.+\.agent\.md$/,
|
||||
/^instructions\/.+\.instructions\.md$/,
|
||||
/^skills\/[^/]+\/SKILL\.md$/,
|
||||
/^hooks\/[^/]+\/(?:README\.md|hooks\.json)$/,
|
||||
/^plugins\/[^/]+\/\.github\/plugin\/plugin\.json$/,
|
||||
/^extensions\/[^/]+\/extension\.mjs$/,
|
||||
/^workflows\/.+\.md$/,
|
||||
/^\.github\/workflows\/.+\.(?:ya?ml|md)$/,
|
||||
/^website\//
|
||||
@@ -197,15 +153,15 @@ jobs:
|
||||
desiredLabels.add('workflow');
|
||||
}
|
||||
|
||||
if (filenames.some((filename) => matchesAny(filename, patterns.canvasExtension))) {
|
||||
desiredLabels.add('canvas-extension');
|
||||
}
|
||||
|
||||
if (hasNewSubmission) {
|
||||
desiredLabels.add('new-submission');
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(managedLabels).map(([name, config]) => ensureLabel(name, config))
|
||||
);
|
||||
|
||||
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
|
||||
+440
-222
File diff suppressed because one or more lines are too long
@@ -4,6 +4,8 @@ description: "Daily check for new GitHub Copilot features and updates. Opens a P
|
||||
on:
|
||||
schedule: daily
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
tools:
|
||||
bash: ["curl", "gh"]
|
||||
edit:
|
||||
@@ -83,4 +85,4 @@ Create a pull request with your changes, using the `staged` branch as the base b
|
||||
2. What sections of the guide were updated
|
||||
3. Links to the source announcements
|
||||
|
||||
The PR should target the `staged` branch and include the labels `automated-update` and `copilot-updates`.
|
||||
The PR should target the `staged` branch and include the labels `automated-update` and `copilot-updates`.
|
||||
+435
-181
File diff suppressed because one or more lines are too long
@@ -133,6 +133,14 @@ jobs:
|
||||
"${main_publish_ref}:${LEGACY_PUBLISHED_BRANCH}" \
|
||||
"${marketplace_publish_ref}:${MARKETPLACE_BRANCH}"
|
||||
|
||||
git fetch origin "${LEGACY_PUBLISHED_BRANCH}" "${MARKETPLACE_BRANCH}"
|
||||
if ! git diff --quiet "origin/${LEGACY_PUBLISHED_BRANCH}" "origin/${MARKETPLACE_BRANCH}"; then
|
||||
echo "Published branch mismatch detected between ${LEGACY_PUBLISHED_BRANCH} and ${MARKETPLACE_BRANCH}"
|
||||
git --no-pager diff --stat "origin/${LEGACY_PUBLISHED_BRANCH}" "origin/${MARKETPLACE_BRANCH}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Verified published outputs are in sync across ${LEGACY_PUBLISHED_BRANCH} and ${MARKETPLACE_BRANCH}"
|
||||
|
||||
- name: Dispatch website deployment
|
||||
run: gh workflow run deploy-website.yml --ref "${WEBSITE_DEPLOY_REF}"
|
||||
env:
|
||||
|
||||
+435
-182
File diff suppressed because one or more lines are too long
@@ -0,0 +1,148 @@
|
||||
name: Setup Repository Labels
|
||||
|
||||
on:
|
||||
workflow_dispatch
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
setup-labels:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create or update labels
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
with:
|
||||
script: |
|
||||
const labels = {
|
||||
// Intent labels for PR categorization
|
||||
'targets-main': {
|
||||
color: 'B60205',
|
||||
description: 'PR targets main instead of staged'
|
||||
},
|
||||
'branched-main': {
|
||||
color: 'D93F0B',
|
||||
description: 'PR appears to include plugin files materialized from main'
|
||||
},
|
||||
'skills': {
|
||||
color: '1D76DB',
|
||||
description: 'PR touches skills'
|
||||
},
|
||||
'plugin': {
|
||||
color: '5319E7',
|
||||
description: 'PR touches plugins'
|
||||
},
|
||||
'agent': {
|
||||
color: '0E8A16',
|
||||
description: 'PR touches agents'
|
||||
},
|
||||
'instructions': {
|
||||
color: 'FBCA04',
|
||||
description: 'PR touches instructions'
|
||||
},
|
||||
'new-submission': {
|
||||
color: '006B75',
|
||||
description: 'PR adds at least one new contribution'
|
||||
},
|
||||
'website-update': {
|
||||
color: '0052CC',
|
||||
description: 'PR touches website content or code'
|
||||
},
|
||||
'external-plugin': {
|
||||
color: 'FEF2C0',
|
||||
description: 'Public external plugin submission'
|
||||
},
|
||||
'hooks': {
|
||||
color: 'C2E0C6',
|
||||
description: 'PR touches hooks'
|
||||
},
|
||||
'workflow': {
|
||||
color: 'BFD4F2',
|
||||
description: 'PR touches workflow automation'
|
||||
},
|
||||
// External plugin intake state labels
|
||||
'awaiting-review': {
|
||||
color: 'FBCA04',
|
||||
description: 'Submission is waiting for automated intake validation'
|
||||
},
|
||||
'ready-for-review': {
|
||||
color: '0E8A16',
|
||||
description: 'Submission passed intake validation and is ready for maintainer review'
|
||||
},
|
||||
'requires-submitter-fixes': {
|
||||
color: 'D93F0B',
|
||||
description: 'Submission has quality-gate findings that submitter must fix before maintainer review'
|
||||
},
|
||||
'approved': {
|
||||
color: '1D76DB',
|
||||
description: 'Submission was approved by a maintainer'
|
||||
},
|
||||
'rejected': {
|
||||
color: 'B60205',
|
||||
description: 'Submission was rejected by a maintainer'
|
||||
},
|
||||
// Re-review labels
|
||||
'removed': {
|
||||
color: 'B60205',
|
||||
description: 'External plugin was removed from the marketplace after re-review'
|
||||
},
|
||||
're-review-follow-up': {
|
||||
color: 'D4C5F9',
|
||||
description: 'Six-month re-review needs maintainer follow-up before a final decision'
|
||||
},
|
||||
'awaiting-approval': {
|
||||
color: 'FBCA04',
|
||||
description: 'External plugin awaiting maintainer approval'
|
||||
}
|
||||
};
|
||||
|
||||
let created = 0;
|
||||
let updated = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const [name, config] of Object.entries(labels)) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description
|
||||
});
|
||||
created++;
|
||||
core.info(`✓ Created label: ${name}`);
|
||||
} catch (error) {
|
||||
if (error.status === 422) {
|
||||
// Label already exists, try to update it
|
||||
try {
|
||||
await github.rest.issues.updateLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description
|
||||
});
|
||||
updated++;
|
||||
core.info(`✓ Updated label: ${name}`);
|
||||
} catch (updateError) {
|
||||
failed++;
|
||||
core.error(`✗ Failed to update label ${name}: ${updateError.message}`);
|
||||
}
|
||||
} else {
|
||||
failed++;
|
||||
core.error(`✗ Failed to create label ${name}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
core.info(`
|
||||
Label setup complete:
|
||||
- Created: ${created}
|
||||
- Updated: ${updated}
|
||||
- Failed: ${failed}
|
||||
- Total: ${Object.keys(labels).length}
|
||||
`);
|
||||
|
||||
if (failed > 0) {
|
||||
throw new Error(`Failed to setup ${failed} label(s)`);
|
||||
}
|
||||
@@ -42,27 +42,7 @@ jobs:
|
||||
}
|
||||
};
|
||||
|
||||
async function ensureLabel(name, { color, description }) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color,
|
||||
description
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function syncManagedLabels(issueNumber, desiredLabels) {
|
||||
await Promise.all(
|
||||
Object.entries(managedLabels).map(([name, config]) => ensureLabel(name, config))
|
||||
);
|
||||
|
||||
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
name: Validate Canvas Extensions
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [staged]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "extensions/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Validate changed canvas extensions
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Collect changed extension directories from the PR diff
|
||||
const { execSync } = require('child_process');
|
||||
const changedFiles = execSync(
|
||||
`git diff --name-only origin/${{ github.base_ref }}...HEAD`
|
||||
).toString().trim().split('\n').filter(Boolean);
|
||||
|
||||
const EXTENSIONS_DIR = 'extensions';
|
||||
const EXTERNAL_ASSETS_DIR = 'external-assets';
|
||||
|
||||
const changedExtDirs = new Set();
|
||||
for (const file of changedFiles) {
|
||||
const parts = file.split('/');
|
||||
if (parts[0] === EXTENSIONS_DIR && parts.length >= 2) {
|
||||
const extName = parts[1];
|
||||
// Skip the external-assets directory — it's not a canvas extension
|
||||
// Also skip external.json and other files at extensions root level
|
||||
if (extName !== EXTERNAL_ASSETS_DIR && !extName.includes('.')) {
|
||||
changedExtDirs.add(path.join(EXTENSIONS_DIR, extName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changedExtDirs.size === 0) {
|
||||
console.log('No canvas extension directories changed — skipping validation.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Validating ${changedExtDirs.size} extension(s): ${[...changedExtDirs].join(', ')}`);
|
||||
|
||||
const errors = [];
|
||||
|
||||
for (const extDir of changedExtDirs) {
|
||||
if (!fs.existsSync(extDir)) {
|
||||
// Directory was deleted — skip
|
||||
console.log(`${extDir} no longer exists (deleted?), skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const extName = path.basename(extDir);
|
||||
|
||||
// Rule 1: must contain extension.mjs
|
||||
const mainFile = path.join(extDir, 'extension.mjs');
|
||||
if (!fs.existsSync(mainFile)) {
|
||||
errors.push(
|
||||
`**\`${extDir}\`**: missing required \`extension.mjs\`. ` +
|
||||
`Canvas extensions must have their entry point named \`extension.mjs\`.`
|
||||
);
|
||||
}
|
||||
|
||||
// Rule 2: must contain assets/preview.png
|
||||
const previewFile = path.join(extDir, 'assets', 'preview.png');
|
||||
if (!fs.existsSync(previewFile)) {
|
||||
errors.push(
|
||||
`**\`${extDir}\`**: missing required \`assets/preview.png\`. ` +
|
||||
`Canvas extensions must include a screenshot at \`assets/preview.png\` ` +
|
||||
`so reviewers and users can preview the extension before installing it.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length === 0) {
|
||||
console.log('✅ All changed canvas extensions pass validation.');
|
||||
return;
|
||||
}
|
||||
|
||||
const isFork = context.payload.pull_request.head.repo.fork;
|
||||
const body = [
|
||||
'❌ **Canvas extension validation failed**',
|
||||
'',
|
||||
'The following issue(s) were found in changed canvas extension(s):',
|
||||
'',
|
||||
...errors.map(e => `- ${e}`),
|
||||
'',
|
||||
'---',
|
||||
'',
|
||||
'### Required structure for canvas extensions',
|
||||
'',
|
||||
'Each extension folder under `extensions/` must contain:',
|
||||
'',
|
||||
'| Path | Required | Description |',
|
||||
'|------|----------|-------------|',
|
||||
'| `extension.mjs` | ✅ | Entry point for the canvas extension |',
|
||||
'| `assets/preview.png` | ✅ | Screenshot shown on the website and in the marketplace |',
|
||||
'',
|
||||
'Please add the missing file(s) and push an update to this PR.',
|
||||
].join('\n');
|
||||
|
||||
if (!isFork) {
|
||||
try {
|
||||
await github.rest.pulls.createReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
event: 'REQUEST_CHANGES',
|
||||
body
|
||||
});
|
||||
} catch (error) {
|
||||
core.warning(`Could not post PR review: ${error.message}`);
|
||||
core.warning(body);
|
||||
}
|
||||
} else {
|
||||
core.warning('PR is from a fork — skipping createReview to avoid permission errors.');
|
||||
core.warning(body);
|
||||
}
|
||||
|
||||
core.setFailed(`Canvas extension validation failed with ${errors.length} error(s).`);
|
||||
Reference in New Issue
Block a user