From ffd907ca7c67ef2941381d9c35230fd7efb20933 Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Fri, 10 Jul 2026 09:24:16 -0500 Subject: [PATCH 1/4] Replace Integration Data Updater agentic workflow with deterministic scripts The gh-aw agentic Integration Data Updater spent Copilot inference on work that is entirely deterministic, and its 20-minute agent-step cap truncated the API-reference regeneration on catch-up runs. Replace it with an OS/CI-aware pwsh orchestrator (src/frontend/scripts/update-integration-data.ps1) that runs pnpm update:all, detects package version changes, conditionally regenerates the C#/TS API reference JSON and twoslash bundle, verifies the diff is well-scoped, and emits a PR title/body. A thin standard workflow (.github/workflows/update-integration-data.yml) runs it and opens a PR via the Aspire bot GitHub App, mirroring update-release-branch.yml. Removes update-integration-data.md and its generated .lock.yml; updates the update-integrations skill automation note. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .agents/skills/update-integrations/SKILL.md | 2 +- .../update-integration-data.lock.yml | 1665 ----------------- .github/workflows/update-integration-data.md | 258 --- .github/workflows/update-integration-data.yml | 177 ++ .../scripts/update-integration-data.ps1 | 391 ++++ 5 files changed, 569 insertions(+), 1924 deletions(-) delete mode 100644 .github/workflows/update-integration-data.lock.yml delete mode 100644 .github/workflows/update-integration-data.md create mode 100644 .github/workflows/update-integration-data.yml create mode 100644 src/frontend/scripts/update-integration-data.ps1 diff --git a/.agents/skills/update-integrations/SKILL.md b/.agents/skills/update-integrations/SKILL.md index 6d85dec3a..38f2d8f7c 100644 --- a/.agents/skills/update-integrations/SKILL.md +++ b/.agents/skills/update-integrations/SKILL.md @@ -7,7 +7,7 @@ description: Update integration documentation links and API reference data by sy This skill synchronizes the integration package catalog with documentation URL mappings and generates per-package API reference schemas. It ensures every NuGet package listed in the integrations data file has a corresponding documentation link and an up-to-date API reference JSON file. -> **Automation note:** The scheduled `update-integration-data` agentic workflow (`.github/workflows/update-integration-data.md`) now runs the C# and TypeScript API regeneration automatically whenever it detects a `"version":` change in `src/frontend/src/data/aspire-integrations.json`. Use this skill for **manual or one-off runs** — for example: adding a new integration, validating release-branch feed resolution, troubleshooting a regen failure flagged by the workflow's PR, or regenerating data outside the scheduled cadence. +> **Automation note:** The scheduled `update-integration-data` workflow (`.github/workflows/update-integration-data.yml`) runs this update deterministically — no agent/LLM. It executes `src/frontend/scripts/update-integration-data.ps1`, which runs `pnpm update:all`, detects `"version":` changes in `src/frontend/src/data/aspire-integrations.json`, and — only when a version actually moves — runs the C# and TypeScript API regeneration (and the twoslash bundle) before opening a PR via the Aspire bot GitHub App. You can reproduce the full flow locally with `pwsh src/frontend/scripts/update-integration-data.ps1` (add `-SkipRegen` for a fast data-only run). Use this skill for **manual or one-off runs that need judgment** — for example: adding a new integration, reconciling `integration-docs.json`, validating release-branch feed resolution, troubleshooting a regen failure flagged by the workflow's PR, or regenerating data outside the scheduled cadence. ## Overview diff --git a/.github/workflows/update-integration-data.lock.yml b/.github/workflows/update-integration-data.lock.yml deleted file mode 100644 index e91db9b5b..000000000 --- a/.github/workflows/update-integration-data.lock.yml +++ /dev/null @@ -1,1665 +0,0 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"2f153a94ea7e6bfa3244acb5d646150c2368f10d0de50d5dca58bd21e21b9022","body_hash":"b99350e37eea120c906faa1bbce71d8da812ba6eabf9b35d1a9cdfe751972a1c","compiler_version":"v0.81.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65"}} -# gh-aw-manifest: {"version":1,"secrets":["ASPIRE_BOT_APP_ID","ASPIRE_BOT_PRIVATE_KEY","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/create-github-app-token","sha":"bcd2ba49218906704ab6c1aa796996da409d3eb1","version":"v3.2.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"9a946fdbd5fb07b82b2f5a4466058b876ab72bb2","version":"v5.3.0 (source v5)"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"ba6380cc6e5be5d21677bebe04d52fb48e3abec7","version":"v0.81.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} -# This file was automatically generated by gh-aw (v0.81.6). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md -# -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ -# | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ -# \_| |_/\__, |\___|_| |_|\__|_|\___| -# __/ | -# _ _ |___/ -# | | | | / _| | -# | | | | ___ _ __ _ __| |_| | _____ ____ -# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| -# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ -# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ -# -# -# To update this file, edit the corresponding .md file and run: -# gh aw compile -# Not all edits will cause changes to this file. -# -# For more information: https://github.github.com/gh-aw/introduction/overview/ -# -# Daily update of integration data, sample metadata, and GitHub statistics by running pnpm update:all and creating a PR with the changes. When integration package versions change, also regenerates the C# and TypeScript API reference JSON files (and the twoslash bundle) and includes them in the same PR. -# -# Secrets used: -# - ASPIRE_BOT_APP_ID -# - ASPIRE_BOT_PRIVATE_KEY -# - GH_AW_CI_TRIGGER_TOKEN -# - GH_AW_GITHUB_MCP_SERVER_TOKEN -# - GH_AW_GITHUB_TOKEN -# - GITHUB_TOKEN -# -# Custom actions used: -# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 -# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 -# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 -# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 -# - actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 -# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 -# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 -# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) -# - actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 (source v5) -# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 -# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6 -# -# Container images used: -# - ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 -# - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d -# - ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d -# - ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be -# - ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b -# - ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 - -name: "Integration Data Updater" -on: - schedule: - - cron: "10 21 * * 1-5" - # Friendly format: daily on weekdays (scattered) - workflow_dispatch: - inputs: - aw_context: - default: "" - description: "Agent caller context (used internally by Agentic Workflows)." - required: false - type: string - -permissions: {} - -concurrency: - group: "gh-aw-${{ github.workflow }}" - -run-name: "Integration Data Updater" - -jobs: - activation: - runs-on: ubuntu-slim - permissions: - actions: read - contents: read - env: - GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} - GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} - outputs: - comment_id: "" - comment_repo: "" - daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }} - daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} - daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} - engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} - lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} - model: ${{ steps.generate_aw_info.outputs.model }} - setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} - setup-span-id: ${{ steps.setup.outputs.span-id }} - setup-trace-id: ${{ steps.setup.outputs.trace-id }} - stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} - steps: - - name: Setup Scripts - id: setup - uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6 - with: - destination: ${{ runner.temp }}/gh-aw/actions - job-name: ${{ github.job }} - safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} - env: - GH_AW_SETUP_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/update-integration-data.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" - - name: Generate agentic run info - id: generate_aw_info - env: - GH_AW_INFO_ENGINE_ID: "copilot" - GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" - GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AGENT_VERSION: "1.0.65" - GH_AW_INFO_CLI_VERSION: "v0.81.6" - GH_AW_INFO_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_INFO_EXPERIMENTAL: "false" - GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" - GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","containers","node","dotnet","aka.ms","aspire.dev","github"]' - GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_AWMG_VERSION: "" - GH_AW_INFO_FIREWALL_TYPE: "squid" - GH_AW_COMPILED_STRICT: "true" - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); - await main(core, context); - - name: Restore daily AIC usage cache - id: restore-daily-aic-cache - if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} - continue-on-error: true - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - key: agentic-workflow-usage-updateintegrationdata-${{ github.run_id }} - restore-keys: agentic-workflow-usage-updateintegrationdata- - path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl - - name: Restore daily AIC usage cache (artifact fallback) - id: restore-daily-aic-cache-fallback - if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} - continue-on-error: true - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }} - GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/restore_aic_usage_cache_fallback.cjs'); - await main(); - - name: Check daily workflow token guardrail - id: daily-effective-workflow-guardrail - if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_WORKFLOW_ID: "update-integration-data" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }} - GH_AW_HAS_SLASH_COMMAND: "false" - GH_AW_HAS_LABEL_COMMAND: "false" - GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_daily_aic_workflow_guardrail.cjs'); - await main(); - - name: Checkout .github and .agents folders - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - sparse-checkout: | - .github - .agents - .antigravity - .claude - .codex - .crush - .gemini - .opencode - .pi - sparse-checkout-cone-mode: true - fetch-depth: 1 - - name: Save agent config folders for base branch restoration - env: - GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" - GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" - # poutine:ignore untrusted_checkout_exec - run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" - - name: Check workflow lock file - id: check-lock-file - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_WORKFLOW_FILE: "update-integration-data.lock.yml" - GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); - await main(); - - name: Check compile-agentic version - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_COMPILED_VERSION: "v0.81.6" - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); - await main(); - - name: Log runtime features - if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} - run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh" - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl - GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} - GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} - GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} - GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - # poutine:ignore untrusted_checkout_exec - run: | - bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" - { - cat << 'GH_AW_PROMPT_7e46329d355d7bb6_EOF' - - GH_AW_PROMPT_7e46329d355d7bb6_EOF - cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" - cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" - cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" - cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_7e46329d355d7bb6_EOF' - - Tools: create_pull_request, close_pull_request(max:5), missing_tool, missing_data, noop - GH_AW_PROMPT_7e46329d355d7bb6_EOF - cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" - cat << 'GH_AW_PROMPT_7e46329d355d7bb6_EOF' - - GH_AW_PROMPT_7e46329d355d7bb6_EOF - cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_7e46329d355d7bb6_EOF' - - The following GitHub context information is available for this workflow: - {{#if github.actor}} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if github.repository}} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if github.workspace}} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}} - - **issue-number**: #__GH_AW_EXPR_802A9F6A__ - {{/if}} - {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}} - - **discussion-number**: #__GH_AW_EXPR_1A3A194A__ - {{/if}} - {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}} - - **pull-request-number**: #__GH_AW_EXPR_463A214A__ - {{/if}} - {{#if github.event.comment.id || github.aw.context.comment_id}} - - **comment-id**: __GH_AW_EXPR_FF1D34CE__ - {{/if}} - {{#if github.run_id}} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - GH_AW_PROMPT_7e46329d355d7bb6_EOF - cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_7e46329d355d7bb6_EOF' - - {{#runtime-import .github/workflows/update-integration-data.md}} - GH_AW_PROMPT_7e46329d355d7bb6_EOF - } > "$GH_AW_PROMPT" - - name: Interpolate variables and render templates - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_ENGINE_ID: "copilot" - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Substitute placeholders - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} - GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} - GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} - GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools' - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - - const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, - GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, - GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, - GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, - GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST - } - }); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - # poutine:ignore untrusted_checkout_exec - run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - # poutine:ignore untrusted_checkout_exec - run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" - - name: Upload activation artifact - if: success() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: activation - include-hidden-files: true - path: | - /tmp/gh-aw/aw_info.json - /tmp/gh-aw/models.json - /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/aw-prompts/prompt-template.txt - /tmp/gh-aw/aw-prompts/prompt-import-tree.json - /tmp/gh-aw/github_rate_limits.jsonl - /tmp/gh-aw/base - /tmp/gh-aw/.github/agents - /tmp/gh-aw/.github/skills - if-no-files-found: ignore - retention-days: 1 - - agent: - needs: activation - if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - copilot-requests: write - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}" - queue: max - env: - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - GH_AW_ASSETS_ALLOWED_EXTS: "" - GH_AW_ASSETS_BRANCH: "" - GH_AW_ASSETS_MAX_SIZE_KB: 0 - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} - GH_AW_WORKFLOW_ID_SANITIZED: updateintegrationdata - outputs: - agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} - ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} - aic: ${{ steps.parse-mcp-gateway.outputs.aic }} - ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }} - checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} - effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} - has_patch: ${{ steps.collect_output.outputs.has_patch }} - inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} - mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} - model: ${{ needs.activation.outputs.model }} - model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} - output: ${{ steps.collect_output.outputs.output }} - output_types: ${{ steps.collect_output.outputs.output_types }} - setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} - setup-span-id: ${{ steps.setup.outputs.span-id }} - setup-trace-id: ${{ steps.setup.outputs.trace-id }} - unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} - steps: - - name: Setup Scripts - id: setup - uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6 - with: - destination: ${{ runner.temp }}/gh-aw/actions - job-name: ${{ github.job }} - trace-id: ${{ needs.activation.outputs.setup-trace-id }} - parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} - env: - GH_AW_SETUP_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/update-integration-data.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" - - name: Set runtime paths - id: set-runtime-paths - run: | - { - echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" - echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" - echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" - } >> "$GITHUB_OUTPUT" - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Setup Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: '24' - package-manager-cache: false - - name: Create gh-aw temp directory - run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" - - name: Configure gh CLI for GitHub Enterprise - run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" - env: - GH_TOKEN: ${{ github.token }} - - name: Setup .NET SDK - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 (source v5) - with: - global-json-file: global.json - - name: Install Aspire CLI - run: "set -euo pipefail\naspire_bin=\"${RUNNER_TEMP}/gh-aw/aspire/bin\"\ncurl -sSL https://aspire.dev/install.sh | bash -s -- --install-path \"$aspire_bin\" --skip-path\necho \"ASPIRE_CLI_PATH=$aspire_bin/aspire\" >> \"$GITHUB_ENV\"\necho \"$aspire_bin\" >> \"$GITHUB_PATH\"\nexport PATH=\"$aspire_bin:$PATH\"\n\"$aspire_bin/aspire\" --version --banner\n" - - name: Setup pnpm - run: corepack enable && corepack install - working-directory: src/frontend - - name: Install dependencies - run: pnpm install --frozen-lockfile - working-directory: src/frontend - - - name: Configure Git credentials - env: - GITHUB_REPOSITORY: ${{ github.repository }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_TOKEN: ${{ github.token }} - run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - - name: Checkout PR branch - id: checkout-pr - if: | - github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request' - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); - await main(); - - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 - env: - GH_HOST: github.com - - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 - - name: Determine automatic lockdown mode for GitHub MCP Server - id: determine-automatic-lockdown - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) - env: - GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - with: - script: | - const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs'); - await determineAutomaticLockdown(github, context, core); - - name: Download activation artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: activation - path: /tmp/gh-aw - - name: Restore agent config folders from base branch - if: steps.checkout-pr.outcome == 'success' - env: - GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" - GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" - run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" - - name: Restore inline sub-agents from activation artifact - env: - GH_AW_SUB_AGENT_DIR: ".github/agents" - GH_AW_SUB_AGENT_EXT: ".agent.md" - run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" - - name: Restore inline skills from activation artifact - env: - GH_AW_SKILL_DIR: ".github/skills" - run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 - - name: Generate Safe Outputs Config - run: | - mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" - mkdir -p /tmp/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_f12cc567502dc821_EOF' - {"close_pull_request":{"max":5,"required_labels":[":octocat: auto-merge"],"required_title_prefix":"chore: Update integration data","target":"*"},"create_pull_request":{"allowed_files":["src/frontend/src/data/aspire-integrations.json","src/frontend/src/data/github-stats.json","src/frontend/src/data/samples.json","src/frontend/src/assets/samples/**","src/frontend/src/data/pkgs/**","src/frontend/src/data/ts-modules/**","src/frontend/src/data/twoslash/aspire.d.ts"],"draft":false,"fallback_as_issue":false,"labels":[":octocat: auto-merge"],"max":1,"max_patch_files":500,"max_patch_size":10240,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review","title_prefix":"chore: "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_f12cc567502dc821_EOF - - name: Generate Safe Outputs Tools - env: - GH_AW_TOOLS_META_JSON: | - { - "description_suffixes": { - "close_pull_request": " CONSTRAINTS: Maximum 5 pull request(s) can be closed. Target: *. Only PRs with labels [:octocat: auto-merge] can be closed. Only PRs with title prefix \"chore: Update integration data\" can be closed.", - "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"chore: \". Labels [\":octocat: auto-merge\"] will be automatically added." - }, - "repo_params": {}, - "dynamic_tools": [] - } - GH_AW_VALIDATION_JSON: | - { - "close_pull_request": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "pull_request_number": { - "optionalPositiveInteger": true - }, - "repo": { - "type": "string", - "maxLength": 256 - } - } - }, - "create_pull_request": { - "defaultMax": 1, - "fields": { - "base": { - "type": "string", - "sanitize": true, - "maxLength": 128 - }, - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "branch": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "draft": { - "type": "boolean" - }, - "labels": { - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 - }, - "repo": { - "type": "string", - "maxLength": 256 - }, - "title": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 128 - } - } - }, - "missing_data": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "context": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "data_type": { - "type": "string", - "sanitize": true, - "maxLength": 128 - }, - "reason": { - "type": "string", - "sanitize": true, - "maxLength": 256 - } - } - }, - "missing_tool": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 512 - }, - "reason": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "tool": { - "type": "string", - "sanitize": true, - "maxLength": 128 - } - } - }, - "noop": { - "defaultMax": 1, - "fields": { - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - } - } - }, - "report_incomplete": { - "defaultMax": 5, - "fields": { - "details": { - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "reason": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 1024 - } - } - } - } - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); - await main(); - - name: Start MCP Gateway - id: start-mcp-gateway - env: - GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} - GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} - GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }} - GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -eo pipefail - mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" - - # Export gateway environment variables for MCP config and gateway script - export MCP_GATEWAY_PORT="8080" - export MCP_GATEWAY_DOMAIN="host.docker.internal" - export MCP_GATEWAY_HOST_DOMAIN="localhost" - MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${MCP_GATEWAY_API_KEY}" - export MCP_GATEWAY_API_KEY - export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" - mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" - export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" - export DEBUG="*" - - export GH_AW_ENGINE="copilot" - MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') - MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') - case "${DOCKER_HOST:-}" in - unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; - /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; - * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; - esac - DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' - - mkdir -p "$HOME/.copilot" - GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_f014db59cae17bc3_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" - { - "mcpServers": { - "github": { - "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v1.4.0", - "env": { - "GITHUB_HOST": "${GITHUB_SERVER_URL}", - "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}", - "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" - }, - "guard-policies": { - "allow-only": { - "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY", - "repos": "$GITHUB_MCP_GUARD_REPOS" - } - } - }, - "safeoutputs": { - "type": "stdio", - "container": "ghcr.io/github/gh-aw-node", - "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["-w", "\${GITHUB_WORKSPACE}"], - "entrypoint": "sh", - "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"], - "env": { - "DEBUG": "*", - "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", - "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", - "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", - "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", - "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", - "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", - "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", - "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", - "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", - "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", - "GITHUB_TOKEN": "\${GITHUB_TOKEN}", - "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", - "RUNNER_TEMP": "\${RUNNER_TEMP}" - }, - "guard-policies": { - "write-sink": { - "accept": [ - "*" - ] - } - } - } - }, - "gateway": { - "port": $MCP_GATEWAY_PORT, - "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" - } - } - GH_AW_MCP_CONFIG_f014db59cae17bc3_EOF - - name: Mount MCP servers as CLIs - id: mount-mcp-clis - continue-on-error: true - env: - MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} - MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} - MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); - await main(); - - name: Clean credentials - continue-on-error: true - run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" - - name: Audit pre-agent workspace - id: pre_agent_audit - continue-on-error: true - run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 20 - run: | - set -o pipefail - printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - trap 'rm -f "$HOME/.copilot/settings.json"' EXIT - mkdir -p "$HOME/.copilot" - printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" - export XDG_CONFIG_HOME="$HOME" - export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" - touch /tmp/gh-aw/agent-step-summary.md - GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) - export GH_AW_NODE_BIN - export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" - (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.docker.com\",\"*.docker.io\",\"*.githubusercontent.com\",\"*.vsblob.vsassets.io\",\"aka.ms\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.npms.io\",\"api.nuget.org\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"aspire.dev\",\"auth.docker.io\",\"azure.archive.ubuntu.com\",\"azuresearch-usnc.nuget.org\",\"azuresearch-ussc.nuget.org\",\"builds.dotnet.microsoft.com\",\"bun.sh\",\"cdn.jsdelivr.net\",\"ci.dot.net\",\"codeload.github.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"dc.services.visualstudio.com\",\"deb.nodesource.com\",\"deno.land\",\"dist.nuget.org\",\"dl.k8s.io\",\"docs.github.com\",\"dot.net\",\"dotnet.microsoft.com\",\"dotnetcli.blob.core.windows.net\",\"esm.sh\",\"gcr.io\",\"get.pnpm.io\",\"ghcr.io\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"googleapis.deno.dev\",\"googlechromelabs.github.io\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"jsr.io\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"mcr.microsoft.com\",\"nodejs.org\",\"npm.pkg.github.com\",\"npmjs.com\",\"npmjs.org\",\"nuget.org\",\"nuget.pkg.github.com\",\"nugetregistryv2prod.blob.core.windows.net\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"oneocsp.microsoft.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"pkgs.dev.azure.com\",\"pkgs.k8s.io\",\"ppa.launchpad.net\",\"production.cloudflare.docker.com\",\"quay.io\",\"raw.githubusercontent.com\",\"registry.bower.io\",\"registry.hub.docker.com\",\"registry.npmjs.com\",\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"repo.yarnpkg.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"skimdb.npmjs.com\",\"storage.googleapis.com\",\"telemetry.enterprise.githubcopilot.com\",\"telemetry.vercel.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\",\"www.microsoft.com\",\"www.npmjs.com\",\"www.npmjs.org\",\"yarnpkg.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" - cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json - export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" - GH_AW_DOCKER_HOST="" - if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then - GH_AW_DOCKER_HOST="${DOCKER_HOST}" - fi - GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" - if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then - GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" - GH_AW_CHROOT_BINARIES_SOURCE_PATH=/tmp/gh-aw GH_AW_CHROOT_IDENTITY_HOME=/tmp/gh-aw/home node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" - fi - GH_AW_TOOL_CACHE_MOUNT="" - GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" - if [ -d "$GH_AW_TOOL_CACHE" ]; then - if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then - GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" - fi - fi - # shellcheck disable=SC1003,SC2086 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log - env: - AWF_REFLECT_ENABLED: 1 - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode - COPILOT_GITHUB_TOKEN: ${{ github.token }} - COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} - GH_AW_LLM_PROVIDER: github - GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} - GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} - GH_AW_PHASE: agent - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_TIMEOUT_MINUTES: 20 - GH_AW_VERSION: v0.81.6 - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_AW: true - GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md - GITHUB_WORKSPACE: ${{ github.workspace }} - GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com - GIT_AUTHOR_NAME: github-actions[bot] - GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com - GIT_COMMITTER_NAME: github-actions[bot] - RUNNER_TEMP: ${{ runner.temp }} - S2STOKENS: true - TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} - - name: Detect agent errors - if: always() - id: detect-agent-errors - continue-on-error: true - run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs" - - name: Configure Git credentials - env: - GITHUB_REPOSITORY: ${{ github.repository }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_TOKEN: ${{ github.token }} - run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - - name: Copy Copilot session state files to logs - if: always() - continue-on-error: true - run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" - - name: Stop MCP Gateway - if: always() - continue-on-error: true - env: - MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} - MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} - GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} - run: | - bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" - - name: Redact secrets in logs - if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); - await main(); - env: - GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Append agent step summary - if: always() - run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" - - name: Copy Safe Outputs - if: always() - env: - GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - run: | - mkdir -p /tmp/gh-aw - cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true - - name: Ingest agent output - id: collect_output - if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.docker.com,*.docker.io,*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,aspire.dev,auth.docker.io,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,bun.sh,cdn.jsdelivr.net,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,deb.nodesource.com,deno.land,dist.nuget.org,dl.k8s.io,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,esm.sh,gcr.io,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkgs.dev.azure.com,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_API_URL: ${{ github.api_url }} - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); - await main(); - - name: Parse agent logs for step summary - if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); - await main(); - - name: Parse MCP Gateway logs for step summary - if: always() - id: parse-mcp-gateway - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); - await main(); - - name: Print firewall logs - if: always() - continue-on-error: true - env: - AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs - run: | - # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts - # AWF runs with sudo, creating files owned by root - sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true - # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) - if command -v awf &> /dev/null; then - awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" - else - echo 'AWF binary not installed, skipping firewall log summary' - fi - - name: Parse token usage for step summary - if: always() - continue-on-error: true - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); - await main(); - - name: Print AWF reflect summary - if: always() - continue-on-error: true - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs'); - await main(); - - name: Write agent output placeholder if missing - if: always() - run: | - if [ ! -f /tmp/gh-aw/agent_output.json ]; then - echo '{"items":[]}' > /tmp/gh-aw/agent_output.json - fi - - name: Upload agent artifacts - if: always() - continue-on-error: true - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: agent - path: | - /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - /tmp/gh-aw/mcp-logs/ - /tmp/gh-aw/agent_usage.json - /tmp/gh-aw/agent-stdio.log - /tmp/gh-aw/pre-agent-audit.txt - /tmp/gh-aw/agent/ - /tmp/gh-aw/github_rate_limits.jsonl - /tmp/gh-aw/safeoutputs.jsonl - /tmp/gh-aw/agent_output.json - /tmp/gh-aw/aw-*.patch - /tmp/gh-aw/aw-*.bundle - /tmp/gh-aw/awf-config.json - /tmp/gh-aw/sandbox/firewall/logs/ - /tmp/gh-aw/sandbox/firewall/audit/ - /tmp/gh-aw/sandbox/firewall/awf-reflect.json - if-no-files-found: ignore - - conclusion: - needs: - - activation - - agent - - detection - - safe_outputs - if: > - always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || - needs.activation.outputs.stale_lock_file_failed == 'true' || needs.activation.outputs.daily_ai_credits_exceeded == 'true') - runs-on: ubuntu-slim - permissions: - contents: write - pull-requests: write - concurrency: - group: "gh-aw-conclusion-update-integration-data" - cancel-in-progress: false - queue: max - env: - GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} - outputs: - incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} - noop_message: ${{ steps.noop.outputs.noop_message }} - tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} - total_count: ${{ steps.missing_tool.outputs.total_count }} - steps: - - name: Setup Scripts - id: setup - uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6 - with: - destination: ${{ runner.temp }}/gh-aw/actions - job-name: ${{ github.job }} - trace-id: ${{ needs.activation.outputs.setup-trace-id }} - parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} - env: - GH_AW_SETUP_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/update-integration-data.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" - - name: Generate GitHub App token - id: safe-outputs-app-token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - with: - client-id: ${{ secrets.ASPIRE_BOT_APP_ID }} - private-key: ${{ secrets.ASPIRE_BOT_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: ${{ github.event.repository.name }} - github-api-url: ${{ github.api_url }} - permission-contents: write - permission-pull-requests: write - - name: Download agent output artifact - id: download-agent-output - continue-on-error: true - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: agent - path: /tmp/gh-aw/ - - name: Setup agent output environment variable - id: setup-agent-output-env - if: steps.download-agent-output.outcome == 'success' - run: | - mkdir -p /tmp/gh-aw/ - find "/tmp/gh-aw/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" - - name: Collect usage artifact files - if: always() - continue-on-error: true - run: | - mkdir -p /tmp/gh-aw/usage/agent /tmp/gh-aw/usage/detection - echo "Usage artifact source file status:" - for file in /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/agent_usage.json /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl; do - [ -f "$file" ] && echo "FOUND: $file" || echo "MISSING: $file" - done - [ -f /tmp/gh-aw/aw_info.json ] && cp /tmp/gh-aw/aw_info.json /tmp/gh-aw/usage/aw_info.json || true - [ -f /tmp/gh-aw/aw-info.jsonl ] && cp /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/usage/aw-info.jsonl || true - [ -f /tmp/gh-aw/agent_usage.json ] && cp /tmp/gh-aw/agent_usage.json /tmp/gh-aw/usage/agent_usage.json || true - [ -f /tmp/gh-aw/agent_usage.jsonl ] && cp /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/usage/agent_usage.jsonl || true - [ -f /tmp/gh-aw/detection_usage.jsonl ] && cp /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/usage/detection_usage.jsonl || true - [ -f /tmp/gh-aw/github_rate_limits.jsonl ] && cp /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/usage/github_rate_limits.jsonl || true - [ -s /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true - [ -s /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true - [ -s /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true - [ -s /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true - [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true - [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true - [ -f /tmp/gh-aw/usage/agent/token_usage.jsonl ] || : > /tmp/gh-aw/usage/agent/token_usage.jsonl - [ -f /tmp/gh-aw/usage/detection/token_usage.jsonl ] || : > /tmp/gh-aw/usage/detection/token_usage.jsonl - mkdir -p /tmp/gh-aw/usage/activity - node ${{ runner.temp }}/gh-aw/actions/generate_usage_activity_summary.cjs - find /tmp/gh-aw/usage -type f -print | sort - - name: Upload usage artifact - if: always() - continue-on-error: true - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: usage - path: | - /tmp/gh-aw/usage/aw_info.json - /tmp/gh-aw/usage/aw-info.jsonl - /tmp/gh-aw/usage/agent_usage.json - /tmp/gh-aw/usage/agent_usage.jsonl - /tmp/gh-aw/usage/detection_usage.jsonl - /tmp/gh-aw/usage/github_rate_limits.jsonl - /tmp/gh-aw/usage/agent/token_usage.jsonl - /tmp/gh-aw/usage/detection/token_usage.jsonl - /tmp/gh-aw/usage/activity/summary.json - if-no-files-found: ignore - - name: Restore daily AIC usage cache - id: restore-daily-aic-cache-conclusion - if: always() - continue-on-error: true - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - key: agentic-workflow-usage-updateintegrationdata-${{ github.run_id }} - restore-keys: agentic-workflow-usage-updateintegrationdata- - path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl - - name: Write daily AIC usage cache entry - id: write-daily-aic-cache - if: always() - continue-on-error: true - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - github-token: ${{ github.token }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context); - const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs'); - await main(); - - name: Save daily AIC usage cache - id: save-daily-aic-cache - if: always() - continue-on-error: true - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - key: agentic-workflow-usage-updateintegrationdata-${{ github.run_id }} - path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl - - name: Upload daily AIC usage cache artifact - id: upload-daily-aic-cache - if: always() - continue-on-error: true - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: aic-usage-cache - path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl - if-no-files-found: ignore - retention-days: 7 - - name: Process no-op messages - id: noop - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/update-integration-data.md" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_NOOP_REPORT_AS_ISSUE: "true" - GH_AW_AIC: ${{ needs.agent.outputs.aic }} - GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} - GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} - GH_AW_WORKFLOW_ID: "update-integration-data" - with: - github-token: ${{ steps.safe-outputs-app-token.outputs.token }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); - await main(); - - name: Log detection run - id: detection_runs - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/update-integration-data.md" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} - GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} - with: - github-token: ${{ steps.safe-outputs-app-token.outputs.token }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs'); - await main(); - - name: Record missing tool - id: missing_tool - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" - GH_AW_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/update-integration-data.md" - with: - github-token: ${{ steps.safe-outputs-app-token.outputs.token }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); - await main(); - - name: Record incomplete - id: report_incomplete - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" - GH_AW_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/update-integration-data.md" - with: - github-token: ${{ steps.safe-outputs-app-token.outputs.token }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); - await main(); - - name: Handle agent failure - id: handle_agent_failure - if: always() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/update-integration-data.md" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_WORKFLOW_ID: "update-integration-data" - GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" - GH_AW_ENGINE_ID: "copilot" - GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} - GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} - GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }} - GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }} - GH_AW_AIC: ${{ needs.agent.outputs.aic }} - GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} - GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} - GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} - GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} - GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} - GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} - GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" - GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} - GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} - GH_AW_SAFE_OUTPUTS_APP_TOKEN_MINTING_FAILED: ${{ needs.safe_outputs.outputs.app_token_minting_failed }} - GH_AW_CONCLUSION_APP_TOKEN_MINTING_FAILED: ${{ steps.safe-outputs-app-token.outcome == 'failure' }} - GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} - GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} - GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} - GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }} - GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }} - GH_AW_GROUP_REPORTS: "false" - GH_AW_FAILURE_REPORT_AS_ISSUE: "true" - GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" - GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" - GH_AW_TIMEOUT_MINUTES: "20" - with: - github-token: ${{ steps.safe-outputs-app-token.outputs.token }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); - await main(); - - detection: - needs: - - activation - - agent - if: always() && needs.agent.result != 'skipped' - runs-on: ubuntu-latest - permissions: - contents: read - copilot-requests: write - env: - GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} - outputs: - aic: ${{ steps.parse_detection_token_usage.outputs.aic }} - detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} - detection_reason: ${{ steps.detection_conclusion.outputs.reason }} - detection_success: ${{ steps.detection_conclusion.outputs.success }} - steps: - - name: Setup Scripts - id: setup - uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6 - with: - destination: ${{ runner.temp }}/gh-aw/actions - job-name: ${{ github.job }} - trace-id: ${{ needs.activation.outputs.setup-trace-id }} - parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} - env: - GH_AW_SETUP_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/update-integration-data.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" - - name: Download agent output artifact - id: download-agent-output - continue-on-error: true - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: agent - path: /tmp/gh-aw/ - - name: Setup agent output environment variable - id: setup-agent-output-env - if: steps.download-agent-output.outcome == 'success' - run: | - mkdir -p /tmp/gh-aw/ - find "/tmp/gh-aw/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" - - name: Checkout repository for patch context - if: needs.agent.outputs.has_patch == 'true' - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - persist-credentials: false - # --- Threat Detection --- - - name: Clean stale firewall files from agent artifact - run: | - rm -rf /tmp/gh-aw/sandbox/firewall/logs - rm -rf /tmp/gh-aw/sandbox/firewall/audit - - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d - - name: Check if detection needed - id: detection_guard - if: always() - env: - OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} - HAS_PATCH: ${{ needs.agent.outputs.has_patch }} - run: | - if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then - echo "run_detection=true" >> "$GITHUB_OUTPUT" - echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" - else - echo "run_detection=false" >> "$GITHUB_OUTPUT" - echo "Detection skipped: no agent outputs or patches to analyze" - fi - - name: Clear MCP Config for detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" - rm -f "$HOME/.copilot/mcp-config.json" - rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" - - name: Prepare threat detection files - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection/aw-prompts - rm -f /tmp/gh-aw/agent_usage.json - cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true - if [ ! -s /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt ]; then - echo "::warning::ERR_VALIDATION: Missing or empty detection context prompt at /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt. Ensure the agent artifact includes /tmp/gh-aw/aw-prompts/prompt.txt. Detection will continue with fallback workflow context." - fi - cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true - for f in /tmp/gh-aw/aw-*.patch; do - [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true - done - for f in /tmp/gh-aw/aw-*.bundle; do - [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true - done - echo "Prepared threat detection files:" - ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true - - name: Setup threat detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - WORKFLOW_NAME: "Integration Data Updater" - WORKFLOW_DESCRIPTION: "Daily update of integration data, sample metadata, and GitHub statistics by running pnpm update:all and creating a PR with the changes. When integration package versions change, also regenerates the C# and TypeScript API reference JSON files (and the twoslash bundle) and includes them in the same PR." - HAS_PATCH: ${{ needs.agent.outputs.has_patch }} - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); - await main(); - - name: Ensure threat-detection directory and log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection - touch /tmp/gh-aw/threat-detection/detection.log - - name: Setup Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: '24' - package-manager-cache: false - - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 - env: - GH_HOST: github.com - - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 - - name: Execute GitHub Copilot CLI - if: always() && steps.detection_guard.outputs.run_detection == 'true' - continue-on-error: true - id: detection_agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 20 - run: | - set -o pipefail - printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - trap 'rm -f "$HOME/.copilot/settings.json"' EXIT - mkdir -p "$HOME/.copilot" - printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" - export XDG_CONFIG_HOME="$HOME" - touch /tmp/gh-aw/agent-step-summary.md - GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) - export GH_AW_NODE_BIN - export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" - (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) - GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" - cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json - export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" - GH_AW_DOCKER_HOST="" - if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then - GH_AW_DOCKER_HOST="${DOCKER_HOST}" - fi - GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" - if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then - GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" - _GH_AW_CHROOT_JSON=$(jq -c --arg src /tmp/gh-aw --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home /tmp/gh-aw/home '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; } - printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" - printf '%s\n' "$_GH_AW_CHROOT_JSON" > "/tmp/gh-aw/awf-config.json" - fi - GH_AW_TOOL_CACHE_MOUNT="" - GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" - if [ -d "$GH_AW_TOOL_CACHE" ]; then - if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then - GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" - fi - fi - # shellcheck disable=SC1003,SC2086 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log - env: - AWF_REFLECT_ENABLED: 1 - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode - COPILOT_GITHUB_TOKEN: ${{ github.token }} - COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} - GH_AW_LLM_PROVIDER: github - GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }} - GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} - GH_AW_PHASE: detection - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_TIMEOUT_MINUTES: 20 - GH_AW_VERSION: v0.81.6 - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_AW: true - GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md - GITHUB_WORKSPACE: ${{ github.workspace }} - GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com - GIT_AUTHOR_NAME: github-actions[bot] - GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com - GIT_COMMITTER_NAME: github-actions[bot] - RUNNER_TEMP: ${{ runner.temp }} - S2STOKENS: true - TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} - - name: Parse threat detection token usage for step summary - id: parse_detection_token_usage - if: always() - continue-on-error: true - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); - await main(); - - name: Upload threat detection log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: detection - path: /tmp/gh-aw/threat-detection/detection.log - if-no-files-found: ignore - - name: Parse and conclude threat detection - id: detection_conclusion - if: always() - continue-on-error: true - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} - DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} - GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" - with: - script: | - try { - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); - await main(); - } catch (loadErr) { - const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; - const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; - const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); - core.error(msg); - core.setOutput('reason', 'parse_error'); - if (continueOnError && !detectionExecutionFailed) { - core.warning('\u26A0\uFE0F ' + msg); - core.setOutput('conclusion', 'warning'); - core.setOutput('success', 'false'); - } else { - core.setOutput('conclusion', 'failure'); - core.setOutput('success', 'false'); - core.setFailed(msg); - } - } - - safe_outputs: - needs: - - activation - - agent - - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' - runs-on: ubuntu-slim - permissions: - contents: write - pull-requests: write - timeout-minutes: 45 - env: - GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }} - GH_AW_AIC: ${{ needs.agent.outputs.aic }} - GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} - GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/update-integration-data" - GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} - GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} - GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} - GH_AW_ENGINE_ID: "copilot" - GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} - GH_AW_ENGINE_VERSION: "1.0.65" - GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} - GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} - GH_AW_WORKFLOW_ID: "update-integration-data" - GH_AW_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/update-integration-data.md" - outputs: - app_token_minting_failed: ${{ steps.safe-outputs-app-token.outcome == 'failure' }} - code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} - code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} - create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} - create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} - created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }} - created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }} - process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} - process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} - steps: - - name: Setup Scripts - id: setup - uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6 - with: - destination: ${{ runner.temp }}/gh-aw/actions - job-name: ${{ github.job }} - trace-id: ${{ needs.activation.outputs.setup-trace-id }} - parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} - env: - GH_AW_SETUP_WORKFLOW_NAME: "Integration Data Updater" - GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/update-integration-data.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" - - name: Download agent output artifact - id: download-agent-output - continue-on-error: true - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: agent - path: /tmp/gh-aw/ - - name: Setup agent output environment variable - id: setup-agent-output-env - if: steps.download-agent-output.outcome == 'success' - run: | - mkdir -p /tmp/gh-aw/ - find "/tmp/gh-aw/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" - - name: Download patch artifact - continue-on-error: true - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: agent - path: /tmp/gh-aw/ - - name: Generate GitHub App token - id: safe-outputs-app-token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - with: - client-id: ${{ secrets.ASPIRE_BOT_APP_ID }} - private-key: ${{ secrets.ASPIRE_BOT_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: ${{ github.event.repository.name }} - github-api-url: ${{ github.api_url }} - permission-contents: write - permission-pull-requests: write - - name: Checkout repository - if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: true - token: ${{ steps.safe-outputs-app-token.outputs.token }} - - name: Configure Git credentials - if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') - env: - GITHUB_REPOSITORY: ${{ github.repository }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GIT_TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }} - run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - - name: Configure GH_HOST for enterprise compatibility - id: ghes-host-config - shell: bash - run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. - # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct - # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. - GH_HOST="${GITHUB_SERVER_URL#https://}" - GH_HOST="${GH_HOST#http://}" - echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" - - name: Process Safe Outputs - id: process_safe_outputs - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} - GH_AW_ALLOWED_DOMAINS: "*.docker.com,*.docker.io,*.githubusercontent.com,*.vsblob.vsassets.io,aka.ms,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,aspire.dev,auth.docker.io,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,bun.sh,cdn.jsdelivr.net,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,deb.nodesource.com,deno.land,dist.nuget.org,dl.k8s.io,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,esm.sh,gcr.io,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkgs.dev.azure.com,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"close_pull_request\":{\"max\":5,\"required_labels\":[\":octocat: auto-merge\"],\"required_title_prefix\":\"chore: Update integration data\",\"target\":\"*\"},\"create_pull_request\":{\"allowed_files\":[\"src/frontend/src/data/aspire-integrations.json\",\"src/frontend/src/data/github-stats.json\",\"src/frontend/src/data/samples.json\",\"src/frontend/src/assets/samples/**\",\"src/frontend/src/data/pkgs/**\",\"src/frontend/src/data/ts-modules/**\",\"src/frontend/src/data/twoslash/aspire.d.ts\"],\"draft\":false,\"fallback_as_issue\":false,\"labels\":[\":octocat: auto-merge\"],\"max\":1,\"max_patch_files\":500,\"max_patch_size\":10240,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"chore: \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" - GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} - GITHUB_TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }} - with: - github-token: ${{ steps.safe-outputs-app-token.outputs.token }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); - await main(); - - name: Upload Safe Outputs Items - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: safe-outputs-items - path: | - /tmp/gh-aw/safe-output-items.jsonl - /tmp/gh-aw/temporary-id-map.json - if-no-files-found: ignore - diff --git a/.github/workflows/update-integration-data.md b/.github/workflows/update-integration-data.md deleted file mode 100644 index ae5145978..000000000 --- a/.github/workflows/update-integration-data.md +++ /dev/null @@ -1,258 +0,0 @@ ---- -description: Daily update of integration data, sample metadata, and GitHub statistics by running pnpm update:all and creating a PR with the changes. When integration package versions change, also regenerates the C# and TypeScript API reference JSON files (and the twoslash bundle) and includes them in the same PR. -on: - schedule: daily on weekdays -permissions: - contents: read - actions: read - copilot-requests: write -runtimes: - node: - version: "24" -steps: - - name: Setup .NET SDK - uses: actions/setup-dotnet@v5 - with: - global-json-file: global.json - - name: Install Aspire CLI - run: | - set -euo pipefail - aspire_bin="${RUNNER_TEMP}/gh-aw/aspire/bin" - curl -sSL https://aspire.dev/install.sh | bash -s -- --install-path "$aspire_bin" --skip-path - echo "ASPIRE_CLI_PATH=$aspire_bin/aspire" >> "$GITHUB_ENV" - echo "$aspire_bin" >> "$GITHUB_PATH" - export PATH="$aspire_bin:$PATH" - "$aspire_bin/aspire" --version --banner - - name: Setup pnpm - run: corepack enable && corepack install - working-directory: src/frontend - - name: Install dependencies - run: pnpm install --frozen-lockfile - working-directory: src/frontend -network: - allowed: - - defaults - - containers - - node - - dotnet - - aka.ms - - aspire.dev - - github -safe-outputs: - # Raise the default 100-file patch cap: the API-reference regen branch can - # write hundreds of files under pkgs/ and ts-modules/ on a single run. - max-patch-files: 500 - # Raise the patch-size cap to the schema maximum (10,240 KB ≈ 10 MB). The - # regenerated C#/TS API JSON plus the twoslash bundle routinely produces a - # ~1,777 KB patch today (≈ 1.8 MB), and this gives plenty of headroom for - # the integration set to grow. - max-patch-size: 10240 - github-app: - client-id: ${{ secrets.ASPIRE_BOT_APP_ID }} - private-key: ${{ secrets.ASPIRE_BOT_PRIVATE_KEY }} - create-pull-request: - title-prefix: "chore: " - labels: [":octocat: auto-merge"] - draft: false - fallback-as-issue: false - allowed-files: - - "src/frontend/src/data/aspire-integrations.json" - - "src/frontend/src/data/github-stats.json" - - "src/frontend/src/data/samples.json" - - "src/frontend/src/assets/samples/**" - - "src/frontend/src/data/pkgs/**" - - "src/frontend/src/data/ts-modules/**" - - "src/frontend/src/data/twoslash/aspire.d.ts" - close-pull-request: - required-labels: [":octocat: auto-merge"] - required-title-prefix: "chore: Update integration data" - target: "*" - max: 5 - noop: ---- - -# Integration Data Updater - -You are an automation agent that updates integration data and GitHub statistics for the aspire.dev documentation site. - -## Your Task - -1. Run the update script from the repository root: `pnpm update:all` -2. Check if any files were modified using `git diff --stat` -3. Review the generated integration data for official Aspire package icon warnings -4. **Detect integration package version changes** in `src/frontend/src/data/aspire-integrations.json` (see "Version-change detection" below) -5. **If version changes were detected, run the API reference regeneration branch** (see "API reference regeneration" below) -6. Check for existing open PRs created by this workflow -7. If there are changes, create a single pull request with the updated data files (and, when triggered, the regenerated API reference files) -8. If there are no changes, call the `noop` safe output explaining that integration data is already up to date - -## Version-change detection - -After `pnpm update:all` completes, determine whether any package version field in `src/frontend/src/data/aspire-integrations.json` changed. The C# and TypeScript API reference data are keyed by package + version, so we only regenerate them when a version actually moves — metadata-only changes (icons, descriptions, download counts) must **not** trigger the regen branch. - -Run this from the repository root: - -```bash -git diff --unified=0 -- src/frontend/src/data/aspire-integrations.json \ - | grep -E '^[+-][[:space:]]*"version":' \ - | sort -u -``` - -Treat it as a version change when **any** matching `+/-` `"version":` line exists (a value moved up, was added for a new package, or was removed for a deprecated package). Capture the affected package titles for the PR body — read each diff hunk and extract the `"title"` field of the surrounding entry so the PR body can list the packages that triggered the regen. - -If `git diff` shows no `"version":` lines, skip the regeneration branch and record "no version changes — API regen skipped" in the PR body. - -## API reference regeneration - -This branch only runs when version-change detection found at least one changed `"version":` line. The three phases below **must run in order** — each phase depends on the output of the previous one. Phases 2 and 3 happen to be wired together inside the same `pnpm` script (`scripts/update-ts-api.ts` chains the twoslash generator after the TS API generator), but they are described separately so the dependency chain is explicit and so per-phase failures can be reported in the PR body. - -1. **Regenerate C# API reference JSON.** Produces the per-package JSON that the TS API phase depends on for package discovery. - - ```bash - pwsh src/tools/PackageJsonGenerator/generate-package-json.ps1 - ``` - - The script writes `src/frontend/src/data/pkgs/{Package}.{Version}.json` files for every package in `aspire-integrations.json`. Capture the script's success/failure summary for the PR body — successes, failures, and skipped packages (meta-packages without `lib/` content typically appear as skipped). - -2. **Regenerate TypeScript API reference JSON.** Depends on the C# package JSON from phase 1. - - Before running this phase, verify the Aspire CLI is available in the agent environment using the explicit path exported by the setup step: - - ```bash - aspire_cli="${ASPIRE_CLI_PATH:-${RUNNER_TEMP}/gh-aw/aspire/bin/aspire}" - "$aspire_cli" --version --banner - ASPIRE_CLI_PATH="$aspire_cli" pnpm --filter ./src/frontend run update:ts-api - ``` - - `scripts/update-ts-api.ts` reads the freshly written `pkgs/*.json`, selects the `Aspire.Hosting` and `Aspire.Hosting.*` packages, runs `aspire sdk dump` for each via `src/tools/AtsJsonGenerator/generate-ts-api-json.ps1`, and writes `src/frontend/src/data/ts-modules/{Package}.{Version}.json` files. The script honors `ASPIRE_CLI_PATH`, so do not probe for or invoke bare `aspire` in gh-aw runs; the AWF sandbox does not inherit the host runner `PATH`. Capture any per-package failures for the PR body. Once the TS API JSON is written, the same script automatically continues into phase 3 — do not invoke it a second time. - -3. **Regenerate the twoslash `.d.ts` bundle.** Depends on the TS API JSON from phase 2. This phase is **chained automatically** by `pnpm update:ts-api`; it runs `scripts/generate-twoslash-types.ts` against the freshly written `ts-modules/*.json` and rewrites `src/frontend/src/data/twoslash/aspire.d.ts`. If `pnpm update:ts-api` fails partway through the chain, distinguish in the PR body between a phase-2 failure (TS API generation) and a phase-3 failure (twoslash bundle) using the script's `❌ Generation failed:` vs `❌ Twoslash type generation failed:` log markers. - -4. **Verify the regen diff is well-scoped.** After all three phases complete, run `git status` and confirm changes are limited to: - - - `src/frontend/src/data/pkgs/**` - - `src/frontend/src/data/ts-modules/**` - - `src/frontend/src/data/twoslash/aspire.d.ts` - - plus the metadata files already produced by `pnpm update:all`. If the diff includes anything outside these paths, stop and report a `missing_tool` or `missing_data` safe output explaining the unexpected file. - -5. **File-count summary for the PR body.** Use `git diff --name-status` to count added (`A`), modified (`M`), and removed (`D`) files under `src/frontend/src/data/pkgs/`, `src/frontend/src/data/ts-modules/`, and whether `src/frontend/src/data/twoslash/aspire.d.ts` changed. - -If version changes triggered this branch, all three regeneration phases are required. Do **not** create a partial PR that includes only C# `pkgs/` updates. If the Aspire CLI check fails, `pnpm update:ts-api` fails, or the twoslash bundle is not regenerated, call the `noop` safe output with the failure details instead of `create-pull-request` so the workflow does not open a CI-failing data PR. - -## Existing PR Handling - -Before creating a PR, check if there are already open PRs created by this workflow using the workflow marker, not just the PR title: - -```bash -gh pr list \ - --state open \ - --limit 20 \ - --search "\"gh-aw-workflow-id: update-integration-data\"" \ - --json number,title,headRefName,author,url,body,labels -``` - -Treat a PR as workflow-created only when its body contains ``. Prefer PRs authored by `app/aspire-repo-bot` or `github-actions[bot]` and with a title that starts with `chore: Update integration data`. - -Do not push directly to an existing PR branch from this workflow. The agent job runs with a read-only GitHub token; PR writes must go through safe outputs. If existing workflow-created PRs are open and this run has new changes: - -1. Create a fresh replacement PR using `create-pull-request`. -2. Call `close-pull-request` for each superseded workflow-created PR number, with a short body explaining that it was superseded by the latest Integration Data Updater run. -3. Mention the superseded PR numbers in the new PR body. - -If no files were modified but an existing workflow-created PR is open, call `noop` and include the existing PR URL so reviewers know there is already a pending update. - -## Icon Integrity Rules - -Official Aspire packages (`title` starts with `Aspire.`) must not be changed from a package-specific NuGet icon URL to the default NuGet icon. For packages with a version, the expected icon shape is: - -```text -https://api.nuget.org/v3-flatcontainer///icon -``` - -Treat changes like this as a high-signal warning: - -```diff -- "icon": "https://api.nuget.org/v3-flatcontainer/aspire.azure.ai.openai/13.1.3-preview.1.26166.8/icon" -+ "icon": "https://www.nuget.org/Content/gallery/img/default-package-icon.svg" -``` - -The update script should resolve official Aspire package icons to package-specific NuGet URLs whenever package version data is available. If the script warns that an official Aspire package still resolved to the default NuGet icon, do not edit the JSON by hand and do not fail the workflow for that warning alone. Continue creating the data PR when the update command succeeds, and add an **Icon resolution warnings** note to the PR body with the affected package list so reviewers can investigate the script or upstream NuGet data. - -## Guidelines - -- The `pnpm update:all` command delegates to `src/frontend` to update integration data from the NuGet API and GitHub repository statistics -- The primary files that change are under `src/frontend/src/data/`: - - `aspire-integrations.json` — latest NuGet package information - - `github-stats.json` — repository star counts, descriptions, and license info - - `samples.json` — Aspire sample metadata -- `pnpm update:all` can also refresh sample thumbnail assets under `src/frontend/src/assets/samples/` -- When the API reference regeneration branch runs, additional files change under: - - `src/frontend/src/data/pkgs/` — per-package C# API JSON - - `src/frontend/src/data/ts-modules/` — per-package TypeScript API JSON - - `src/frontend/src/data/twoslash/aspire.d.ts` — consolidated twoslash type bundle -- Use today's date in the PR title formatted as `M/D/YY` (e.g., `2/24/26`) -- When creating a PR, first create a local branch, add only the allowed generated files, and commit the changes locally. Then call `create-pull-request` with `branch` set to the exact output of `git branch --show-current`. -- Never include workflow files, package manifests, lockfiles, source files, or documentation edits in the generated data PR. - -## PR Details - -**Title**: `Update integration data and GitHub stats (DATE)` - -**Body** (use this template; include the **API reference regeneration** section only when that branch actually ran): - -```markdown -## Automated Integration Data Update - DATE - -This PR contains automated updates to: -- **Integration data** from NuGet API -- **GitHub repository statistics** -- **Aspire sample metadata** - -### What's Updated -- `src/frontend/src/data/aspire-integrations.json` — Latest package information -- `src/frontend/src/data/github-stats.json` — Repository statistics -- `src/frontend/src/data/samples.json` — Sample metadata, when changed -- `src/frontend/src/assets/samples/` — Sample thumbnails, when changed - -### API reference regeneration - - - - - -Versions changed for the following packages, so the C# and TypeScript API reference data and the twoslash bundle were regenerated: - -- `` `` → `` -- `` `` → `` -- _(list truncated to first 10; full list visible in the diff)_ - -| Area | Added | Modified | Removed | -|---|---|---|---| -| `src/frontend/src/data/pkgs/**` | _N_ | _N_ | _N_ | -| `src/frontend/src/data/ts-modules/**` | _N_ | _N_ | _N_ | -| `src/frontend/src/data/twoslash/aspire.d.ts` | — | _yes/no_ | — | - -Generator summary: - -- C# API JSON (`generate-package-json.ps1` → `pkgs/`): _S_ succeeded, _F_ failed, _K_ skipped -- TS API JSON (`update-ts-api.ts` → `ts-modules/`): _S_ succeeded, _F_ failed -- Twoslash bundle (`generate-twoslash-types.ts` → `twoslash/aspire.d.ts`): _succeeded / failed_ -- Failures (if any): `` — _reason_ - -### Review Checklist - -- [ ] Verify integration data looks reasonable -- [ ] Check for any suspicious changes or anomalies -- [ ] Ensure package counts and versions are updated appropriately -- [ ] Confirm official Aspire package icons still use package-specific NuGet icon URLs -- [ ] (If regen ran) The set of new/removed `pkgs/` and `ts-modules/` files matches the version changes — no orphaned files for packages that didn't change -- [ ] (If regen ran) `src/frontend/src/data/twoslash/aspire.d.ts` was committed (the diff must include this file or hover tooltips fall back to `any`) -- [ ] (If regen ran) Generator failure list is empty or limited to known meta-packages -``` - -## Safe Outputs - -- If files were modified and no existing PR: use `create-pull-request` with the title and body above. The PR body must include the **API reference regeneration** section, populated when the regen branch ran or marked as skipped otherwise. -- If files were modified and existing workflow-created PRs were found: use `create-pull-request` for the replacement and `close-pull-request` for the superseded PRs -- If no files were modified: use `noop` with a clear explanation diff --git a/.github/workflows/update-integration-data.yml b/.github/workflows/update-integration-data.yml new file mode 100644 index 000000000..81a412e15 --- /dev/null +++ b/.github/workflows/update-integration-data.yml @@ -0,0 +1,177 @@ +name: Integration Data Updater + +# Deterministic replacement for the former `gh aw` agentic workflow. Runs the +# update scripts directly (no LLM inference, no agent timeout, no PAT auth) and +# opens a PR through the Aspire bot GitHub App when the generated data changes. +# +# All logic lives in src/frontend/scripts/update-integration-data.ps1 so the +# exact same flow can be reproduced locally: +# pwsh src/frontend/scripts/update-integration-data.ps1 + +on: + schedule: + # Daily on weekdays (mirrors the previous cadence). + - cron: "10 21 * * 1-5" + workflow_dispatch: + +permissions: {} + +concurrency: + group: update-integration-data + cancel-in-progress: false + +jobs: + update: + name: Update integration data + runs-on: ubuntu-latest + timeout-minutes: 45 + permissions: + contents: read + steps: + - name: Create GitHub App token + id: app-token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + with: + app-id: ${{ secrets.ASPIRE_BOT_APP_ID }} + private-key: ${{ secrets.ASPIRE_BOT_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + github-api-url: ${{ github.api_url }} + permission-contents: write + permission-pull-requests: write + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # Full history is required so the orchestrator can diff the committed + # aspire-integrations.json (HEAD) against the freshly generated one. + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Setup .NET SDK + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 + with: + global-json-file: global.json + + - name: Install Aspire CLI + shell: bash + run: | + set -euo pipefail + aspire_bin="${RUNNER_TEMP}/aspire/bin" + curl -sSL https://aspire.dev/install.sh | bash -s -- --install-path "$aspire_bin" --skip-path + echo "ASPIRE_CLI_PATH=$aspire_bin/aspire" >> "$GITHUB_ENV" + echo "$aspire_bin" >> "$GITHUB_PATH" + "$aspire_bin/aspire" --version --banner + + - name: Setup pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + + - name: Setup Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: "24.x" + cache: pnpm + cache-dependency-path: src/frontend/pnpm-lock.yaml + + - name: Install dependencies + working-directory: src/frontend + run: pnpm install --frozen-lockfile --prefer-offline + + - name: Update integration data + id: update + shell: pwsh + run: ./src/frontend/scripts/update-integration-data.ps1 + + - name: Configure git identity + if: ${{ steps.update.outputs.changed == 'true' }} + shell: bash + run: | + git config user.name "aspire-repo-bot[bot]" + git config user.email "aspire-repo-bot[bot]@users.noreply.github.com" + + - name: Commit and push branch + if: ${{ steps.update.outputs.changed == 'true' }} + id: push + env: + PR_TITLE: ${{ steps.update.outputs.pr_title }} + shell: bash + run: | + set -euo pipefail + branch="chore/update-integration-data-$(date -u +%Y%m%d-%H%M%S)" + + # Stage only the allowed generated data files — never workflow files, + # source, manifests, or docs. Directories with no changes are no-ops. + git add -- \ + src/frontend/src/data/aspire-integrations.json \ + src/frontend/src/data/github-stats.json \ + src/frontend/src/data/samples.json \ + src/frontend/src/assets/samples \ + src/frontend/src/data/pkgs \ + src/frontend/src/data/ts-modules \ + src/frontend/src/data/twoslash/aspire.d.ts + + if git diff --cached --quiet; then + echo "No staged changes after filtering to allowed paths; skipping PR." + echo "pushed=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + git checkout -b "$branch" + git commit \ + -m "$PR_TITLE" \ + -m "Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>" + git push origin "HEAD:refs/heads/$branch" + echo "branch=$branch" >> "$GITHUB_OUTPUT" + echo "pushed=true" >> "$GITHUB_OUTPUT" + + - name: Create pull request + if: ${{ steps.push.outputs.pushed == 'true' }} + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + GH_REPO: ${{ github.repository }} + BRANCH: ${{ steps.push.outputs.branch }} + PR_TITLE: ${{ steps.update.outputs.pr_title }} + PR_BODY_FILE: ${{ steps.update.outputs.pr_body_file }} + shell: bash + run: | + set -euo pipefail + + new_pr=$(gh pr create \ + --base main \ + --head "$BRANCH" \ + --title "$PR_TITLE" \ + --body-file "$PR_BODY_FILE" \ + --label ":octocat: auto-merge") + echo "Created $new_pr" + + # Supersede any older open integration-data PRs from this automation. + old_prs=$(gh pr list \ + --state open \ + --limit 30 \ + --json number,headRefName \ + --jq '.[] | select(.headRefName | startswith("chore/update-integration-data")) | select(.headRefName != env.BRANCH) | .number') + + for pr in $old_prs; do + echo "Superseding older integration-data PR #$pr" + gh pr close "$pr" \ + --delete-branch \ + --comment "Superseded by the latest Integration Data Updater run ($new_pr)." + done + + - name: Summary + if: ${{ always() }} + shell: bash + run: | + { + echo "## Integration Data Updater" + echo "" + echo "- Changed: \`${{ steps.update.outputs.changed || 'unknown' }}\`" + echo "- Versions changed: \`${{ steps.update.outputs.versions_changed || 'n/a' }}\`" + echo "- API regen ran: \`${{ steps.update.outputs.regen_ran || 'n/a' }}\`" + if [ -n "${{ steps.update.outputs.icon_warnings }}" ]; then + echo "- Icon warnings: \`${{ steps.update.outputs.icon_warnings }}\`" + fi + if [ "${{ steps.push.outputs.pushed }}" = "true" ]; then + echo "- Branch: \`${{ steps.push.outputs.branch }}\`" + fi + } >> "$GITHUB_STEP_SUMMARY" diff --git a/src/frontend/scripts/update-integration-data.ps1 b/src/frontend/scripts/update-integration-data.ps1 new file mode 100644 index 000000000..ec9c338de --- /dev/null +++ b/src/frontend/scripts/update-integration-data.ps1 @@ -0,0 +1,391 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Deterministic orchestrator for the Integration Data Updater. + +.DESCRIPTION + Refreshes the aspire.dev integration data and, when integration package + versions change, regenerates the C#/TypeScript API reference JSON and the + twoslash bundle. Replaces the former `gh aw` agentic workflow with plain, + reliable scripting that runs identically in CI and locally. + + Phases: + 1. `pnpm update:all` — integration metadata, GitHub stats, sample metadata. + 2. Version-change detection — compares the committed + aspire-integrations.json against the freshly written one by package + title -> version. Metadata-only changes (icons, descriptions, download + counts) do NOT trigger regeneration. + 3. Conditional API-reference regeneration (only when a version changed): + a. generate-package-json.ps1 -> src/data/pkgs/*.json (C# API) + b. pnpm update:ts-api -> src/data/ts-modules/*.json (TS API) + + chains twoslash aspire.d.ts bundle + 4. Scope check — the working tree must only contain allowed data files. + 5. Emits a summary + PR title/body. In CI, writes step outputs to + $GITHUB_OUTPUT and the PR body to a file; locally prints a summary. + + Exit codes: + 0 success (whether or not there were changes) + 1 a required phase failed (update:all, TS API regen, out-of-scope diff). + The caller must NOT open a PR on a non-zero exit. + + Per-package failures inside generate-package-json.ps1 are tolerated (they + are common for meta-packages without a public API surface); their counts are + reported in the PR body but do not fail the run. + +.PARAMETER SkipRegen + Skip the API-reference regeneration phase even when versions changed. Useful + for fast, data-only local runs. + +.PARAMETER Framework + Target framework passed through to generate-package-json.ps1. Defaults to + the script's own default (net10.0). + +.EXAMPLE + pwsh src/frontend/scripts/update-integration-data.ps1 + pwsh src/frontend/scripts/update-integration-data.ps1 -SkipRegen +#> +[CmdletBinding()] +param( + [switch]$SkipRegen, + [string]$Framework +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +# PowerShell 7.4+ defaults this to $true, which turns any native command's +# non-zero exit into a terminating error under ErrorActionPreference='Stop'. +# This script deliberately inspects $LASTEXITCODE itself (e.g. the tolerated +# `git show HEAD:` fallback and the ts-api phase-2/3 distinction), so disable it. +$PSNativeCommandUseErrorActionPreference = $false + +# ── Paths ─────────────────────────────────────────────────────────────────── +$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..' '..' '..')).Path +$FrontendDir = Join-Path $RepoRoot 'src' 'frontend' +$DataDir = Join-Path $FrontendDir 'src' 'data' +$IntegrationsJson = Join-Path $DataDir 'aspire-integrations.json' +$PkgGenScript = Join-Path $RepoRoot 'src' 'tools' 'PackageJsonGenerator' 'generate-package-json.ps1' + +# Working-tree paths that this automation is allowed to change. Anything outside +# this set appearing in `git status` is treated as a scope violation. +$AllowedPaths = @( + 'src/frontend/src/data/aspire-integrations.json', + 'src/frontend/src/data/github-stats.json', + 'src/frontend/src/data/samples.json', + 'src/frontend/src/assets/samples/', + 'src/frontend/src/data/pkgs/', + 'src/frontend/src/data/ts-modules/', + 'src/frontend/src/data/twoslash/aspire.d.ts' +) + +$IsCI = [bool]$env:GITHUB_ACTIONS + +function Write-Section([string]$Text) { + Write-Host "" + Write-Host "══ $Text ══" -ForegroundColor Cyan +} + +function Invoke-Git { + param([Parameter(Mandatory)][string[]]$Arguments) + $out = & git -C $RepoRoot @Arguments 2>&1 + return , @($out) +} + +function Set-Output { + param([Parameter(Mandatory)][string]$Name, [string]$Value) + if ($env:GITHUB_OUTPUT) { + # Multi-line safe via heredoc delimiter. + $delim = "ghadelim_$([Guid]::NewGuid().ToString('N'))" + Add-Content -Path $env:GITHUB_OUTPUT -Value "$Name<<$delim`n$Value`n$delim" + } +} + +function Test-PathAllowed { + param([Parameter(Mandatory)][string]$RelPath) + $normalized = $RelPath -replace '\\', '/' + foreach ($allowed in $AllowedPaths) { + if ($allowed.EndsWith('/')) { + if ($normalized.StartsWith($allowed)) { return $true } + } + elseif ($normalized -eq $allowed) { return $true } + } + return $false +} + +# ── Phase 1: data update ──────────────────────────────────────────────────── +Write-Section 'Phase 1 — pnpm update:all' +Push-Location $FrontendDir +try { + # Capture combined output so icon-integrity warnings can be surfaced in the + # PR body without re-running anything. Tee keeps the live log intact. + $updateLog = & pnpm run update:all 2>&1 | Tee-Object -Variable teed | Out-String + if ($LASTEXITCODE -ne 0) { + Write-Error "pnpm update:all failed (exit $LASTEXITCODE). Aborting; no PR will be opened." + exit 1 + } +} +finally { + Pop-Location +} + +# Icon-integrity warning (script emits this line when an official Aspire package +# still resolved to the default NuGet icon). Not fatal — surfaced for reviewers. +$iconWarnings = '' +$iconLine = $updateLog -split "`n" | + Where-Object { $_ -match 'Official Aspire packages resolved to the default NuGet icon:' } | + Select-Object -First 1 +if ($iconLine) { + $iconWarnings = ($iconLine -replace '.*default NuGet icon:\s*', '').Trim() + Write-Warning "Icon resolution warning captured for PR body: $iconWarnings" +} + +# ── Detect any changes to allowed data files ──────────────────────────────── +$statusLines = (Invoke-Git @('status', '--porcelain', '--', 'src/frontend/src/data', 'src/frontend/src/assets/samples')) | + Where-Object { $_ -and $_.Trim().Length -gt 0 } +$anyChanges = [bool]$statusLines + +if (-not $anyChanges) { + Write-Section 'Result — no changes' + Write-Host "Integration data is already up to date." -ForegroundColor Green + Set-Output 'changed' 'false' + Set-Output 'versions_changed' 'false' + Set-Output 'regen_ran' 'false' + exit 0 +} + +# ── Phase 2: version-change detection ─────────────────────────────────────── +Write-Section 'Phase 2 — version-change detection' + +function Get-VersionMap { + param([Parameter(Mandatory)][string]$JsonText) + $map = @{} + if ([string]::IsNullOrWhiteSpace($JsonText)) { return $map } + $entries = $JsonText | ConvertFrom-Json + foreach ($entry in @($entries)) { + if ($entry.PSObject.Properties.Name -contains 'title' -and + $entry.PSObject.Properties.Name -contains 'version') { + $map[[string]$entry.title] = [string]$entry.version + } + } + return $map +} + +# Old = committed HEAD copy; New = freshly written working-tree copy. +$oldJson = (Invoke-Git @('show', 'HEAD:src/frontend/src/data/aspire-integrations.json')) -join "`n" +if ($LASTEXITCODE -ne 0) { $oldJson = '' } +$newJson = Get-Content -Path $IntegrationsJson -Raw + +$oldVersions = Get-VersionMap -JsonText $oldJson +$newVersions = Get-VersionMap -JsonText $newJson + +$versionChanges = [System.Collections.Generic.List[string]]::new() +foreach ($title in ($newVersions.Keys | Sort-Object)) { + $newVer = $newVersions[$title] + if ($oldVersions.ContainsKey($title)) { + $oldVer = $oldVersions[$title] + if ($oldVer -ne $newVer) { + $versionChanges.Add("``$title`` ``$oldVer`` → ``$newVer``") + } + } + else { + $versionChanges.Add("``$title`` _(new)_ → ``$newVer``") + } +} +foreach ($title in ($oldVersions.Keys | Sort-Object)) { + if (-not $newVersions.ContainsKey($title)) { + $versionChanges.Add("``$title`` ``$($oldVersions[$title])`` → _(removed)_") + } +} + +$versionsChanged = $versionChanges.Count -gt 0 +if ($versionsChanged) { + Write-Host "Detected $($versionChanges.Count) package version change(s)." -ForegroundColor Yellow + $versionChanges | ForEach-Object { Write-Host " - $_" } +} +else { + Write-Host "No package version changes — API reference regeneration will be skipped." -ForegroundColor Green +} + +# ── Phase 3: conditional API-reference regeneration ───────────────────────── +$regenRan = $false +$pkgSummary = '' +$tsApiSummary = '' +$twoslashSummary = '' + +if ($versionsChanged -and -not $SkipRegen) { + Write-Section 'Phase 3 — API reference regeneration' + + # 3a. C# API JSON. Per-package failures are tolerated (meta-packages). + Write-Host "→ generate-package-json.ps1 (C# API → pkgs/)" -ForegroundColor Cyan + $pkgArgs = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $PkgGenScript) + if ($Framework) { $pkgArgs += @('-Framework', $Framework) } + $pkgLog = & pwsh @pkgArgs 2>&1 | Tee-Object -Variable pkgTeed | Out-String + if ($LASTEXITCODE -ne 0) { + Write-Error "generate-package-json.ps1 failed hard (exit $LASTEXITCODE). Aborting; no PR will be opened." + exit 1 + } + $pkgDone = ($pkgLog -split "`n" | Where-Object { $_ -match 'Done!\s+Success:' } | Select-Object -First 1) + $pkgSummary = if ($pkgDone) { ($pkgDone -replace '.*Done!\s+', '').Trim() } else { 'summary unavailable' } + Write-Host " C# API: $pkgSummary" + + # 3b. TS API JSON (+ chained twoslash bundle). Requires the Aspire CLI; the + # script honours ASPIRE_CLI_PATH. A non-zero exit here IS fatal — we must not + # ship a PR with C#-only pkgs updates. + Write-Host "→ pnpm update:ts-api (TS API → ts-modules/ + twoslash aspire.d.ts)" -ForegroundColor Cyan + Push-Location $FrontendDir + try { + $tsLog = & pnpm run update:ts-api 2>&1 | Tee-Object -Variable tsTeed | Out-String + $tsExit = $LASTEXITCODE + } + finally { + Pop-Location + } + if ($tsExit -ne 0) { + # Distinguish phase-2 vs phase-3 failure using the script's log markers. + if ($tsLog -match 'Twoslash type generation failed') { + Write-Error "Twoslash bundle generation failed. Aborting; no PR will be opened." + } + else { + Write-Error "TypeScript API generation failed. Aborting; no PR will be opened." + } + exit 1 + } + $tsApiSummary = 'succeeded' + $twoslashSummary = 'succeeded' + $regenRan = $true +} +elseif ($versionsChanged -and $SkipRegen) { + Write-Warning "Versions changed but -SkipRegen was set; regeneration skipped (local/dev use only)." +} + +# ── Phase 4: scope check ──────────────────────────────────────────────────── +Write-Section 'Phase 4 — scope check' +$allStatus = (Invoke-Git @('status', '--porcelain')) | Where-Object { $_ -and $_.Trim().Length -gt 0 } +$outOfScope = [System.Collections.Generic.List[string]]::new() +foreach ($line in $allStatus) { + # Porcelain format: "XY " (path starts at column 4). Handle renames "old -> new". + $path = ($line.Substring(3)).Trim() + if ($path -match ' -> ') { $path = ($path -split ' -> ')[-1].Trim() } + $path = $path.Trim('"') + if (-not (Test-PathAllowed -RelPath $path)) { + $outOfScope.Add($path) + } +} +if ($outOfScope.Count -gt 0) { + Write-Error ("Working tree contains changes outside the allowed data paths:`n " + + ($outOfScope -join "`n ") + + "`nAborting; no PR will be opened.") + exit 1 +} +Write-Host "All changes are within the allowed data paths." -ForegroundColor Green + +# ── File-count summary (for the PR body) ──────────────────────────────────── +function Get-AreaCounts { + param([Parameter(Mandatory)][string]$Prefix) + $names = (Invoke-Git @('diff', '--name-status', 'HEAD', '--', $Prefix)) | + Where-Object { $_ -and $_.Trim().Length -gt 0 } + $added = 0; $modified = 0; $removed = 0 + foreach ($n in $names) { + $code = ($n.Trim())[0] + switch ($code) { + 'A' { $added++ } + 'M' { $modified++ } + 'D' { $removed++ } + 'R' { $modified++ } + default { } + } + } + return [PSCustomObject]@{ Added = $added; Modified = $modified; Removed = $removed } +} + +$pkgsCounts = Get-AreaCounts -Prefix 'src/frontend/src/data/pkgs/' +$tsModulesCounts = Get-AreaCounts -Prefix 'src/frontend/src/data/ts-modules/' +$twoslashChanged = [bool]((Invoke-Git @('diff', '--name-only', 'HEAD', '--', 'src/frontend/src/data/twoslash/aspire.d.ts')) | + Where-Object { $_ -and $_.Trim().Length -gt 0 }) + +# ── PR title + body ───────────────────────────────────────────────────────── +$dateShort = (Get-Date).ToString('M/d/yy') +$prTitle = "chore: Update integration data and GitHub stats ($dateShort)" + +$sb = [System.Text.StringBuilder]::new() +[void]$sb.AppendLine("## Automated integration data update — $dateShort") +[void]$sb.AppendLine("") +[void]$sb.AppendLine("Generated deterministically by ``src/frontend/scripts/update-integration-data.ps1`` (no agent inference).") +[void]$sb.AppendLine("") +[void]$sb.AppendLine("### What's updated") +[void]$sb.AppendLine("- ``src/frontend/src/data/aspire-integrations.json`` — latest package information") +[void]$sb.AppendLine("- ``src/frontend/src/data/github-stats.json`` — repository statistics") +[void]$sb.AppendLine("- ``src/frontend/src/data/samples.json`` — sample metadata, when changed") +[void]$sb.AppendLine("- ``src/frontend/src/assets/samples/`` — sample thumbnails, when changed") +[void]$sb.AppendLine("") +[void]$sb.AppendLine("### API reference regeneration") +[void]$sb.AppendLine("") +if ($regenRan) { + [void]$sb.AppendLine("Versions changed for the following packages, so the C# and TypeScript API reference data and the twoslash bundle were regenerated:") + [void]$sb.AppendLine("") + $shown = 0 + foreach ($change in $versionChanges) { + if ($shown -ge 10) { break } + [void]$sb.AppendLine("- $change") + $shown++ + } + if ($versionChanges.Count -gt 10) { + [void]$sb.AppendLine("- _(list truncated to first 10; full list visible in the diff)_") + } + [void]$sb.AppendLine("") + [void]$sb.AppendLine("| Area | Added | Modified | Removed |") + [void]$sb.AppendLine("|---|---|---|---|") + [void]$sb.AppendLine("| ``src/frontend/src/data/pkgs/**`` | $($pkgsCounts.Added) | $($pkgsCounts.Modified) | $($pkgsCounts.Removed) |") + [void]$sb.AppendLine("| ``src/frontend/src/data/ts-modules/**`` | $($tsModulesCounts.Added) | $($tsModulesCounts.Modified) | $($tsModulesCounts.Removed) |") + [void]$sb.AppendLine("| ``src/frontend/src/data/twoslash/aspire.d.ts`` | — | $(if ($twoslashChanged) { 'yes' } else { 'no' }) | — |") + [void]$sb.AppendLine("") + [void]$sb.AppendLine("Generator summary:") + [void]$sb.AppendLine("") + [void]$sb.AppendLine("- C# API JSON (``generate-package-json.ps1`` → ``pkgs/``): $pkgSummary") + [void]$sb.AppendLine("- TS API JSON (``update:ts-api`` → ``ts-modules/``): $tsApiSummary") + [void]$sb.AppendLine("- Twoslash bundle (``twoslash/aspire.d.ts``): $twoslashSummary") +} +else { + [void]$sb.AppendLine("_No integration package versions changed in this run — API reference regeneration was skipped._") +} +[void]$sb.AppendLine("") +if ($iconWarnings) { + [void]$sb.AppendLine("### Icon resolution warnings") + [void]$sb.AppendLine("") + [void]$sb.AppendLine("The following official Aspire packages resolved to the default NuGet icon (investigate the update script or upstream NuGet data — not a blocker):") + [void]$sb.AppendLine("") + [void]$sb.AppendLine("``$iconWarnings``") + [void]$sb.AppendLine("") +} +[void]$sb.AppendLine("### Review checklist") +[void]$sb.AppendLine("") +[void]$sb.AppendLine("- [ ] Integration data looks reasonable (no suspicious anomalies)") +[void]$sb.AppendLine("- [ ] Package counts and versions updated appropriately") +[void]$sb.AppendLine("- [ ] Official Aspire package icons still use package-specific NuGet icon URLs") +if ($regenRan) { + [void]$sb.AppendLine("- [ ] New/removed ``pkgs/`` and ``ts-modules/`` files match the version changes") + [void]$sb.AppendLine("- [ ] ``src/frontend/src/data/twoslash/aspire.d.ts`` is included in the diff") +} +$prBody = $sb.ToString() + +# ── Emit outputs ──────────────────────────────────────────────────────────── +Write-Section 'Result — changes detected' +$prBodyFile = if ($env:RUNNER_TEMP) { Join-Path $env:RUNNER_TEMP 'integration-pr-body.md' } + else { Join-Path ([System.IO.Path]::GetTempPath()) 'integration-pr-body.md' } +Set-Content -Path $prBodyFile -Value $prBody -Encoding utf8 + +Set-Output 'changed' 'true' +Set-Output 'versions_changed' ($versionsChanged.ToString().ToLowerInvariant()) +Set-Output 'regen_ran' ($regenRan.ToString().ToLowerInvariant()) +Set-Output 'pr_title' $prTitle +Set-Output 'pr_body_file' $prBodyFile +Set-Output 'icon_warnings' $iconWarnings + +Write-Host "PR title : $prTitle" +Write-Host "PR body : $prBodyFile" +if (-not $IsCI) { + Write-Host "" + Write-Host "----- PR body preview -----" -ForegroundColor DarkGray + Write-Host $prBody +} +exit 0 From 9cd7790e2fc92f35f03e737a3ceb11d5ab427466 Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Fri, 10 Jul 2026 09:39:35 -0500 Subject: [PATCH 2/4] Address PR review feedback on update-integration-data.ps1 - Invoke-Git: emit git output as flat strings to the pipeline and normalize stderr ErrorRecords to their message text, instead of eturn , @() which wrapped output in a confusing nested array. - Change gate: check the FULL working tree (git status --porcelain) so an out-of-scope-only change still reaches the Phase 4 scope check and fails loudly instead of silently exiting 0. - PR date: use UTC + InvariantCulture ([DateTime]::UtcNow) so the PR title matches the UTC branch name and UTC schedules regardless of runner locale. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../scripts/update-integration-data.ps1 | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/frontend/scripts/update-integration-data.ps1 b/src/frontend/scripts/update-integration-data.ps1 index ec9c338de..1fa0872b6 100644 --- a/src/frontend/scripts/update-integration-data.ps1 +++ b/src/frontend/scripts/update-integration-data.ps1 @@ -87,8 +87,20 @@ function Write-Section([string]$Text) { function Invoke-Git { param([Parameter(Mandatory)][string[]]$Arguments) - $out = & git -C $RepoRoot @Arguments 2>&1 - return , @($out) + # Emit git output as plain strings, one per line, straight to the pipeline. + # `2>&1` folds stderr in as ErrorRecord objects on failure; normalize those + # to their message text so callers always receive strings — never + # ErrorRecords, and never a single nested array that would break `-join` or + # Where-Object on the receiving side. $LASTEXITCODE remains the git exit code. + $raw = & git -C $RepoRoot @Arguments 2>&1 + foreach ($item in $raw) { + if ($item -is [System.Management.Automation.ErrorRecord]) { + $item.Exception.Message + } + else { + [string]$item + } + } } function Set-Output { @@ -139,10 +151,14 @@ if ($iconLine) { Write-Warning "Icon resolution warning captured for PR body: $iconWarnings" } -# ── Detect any changes to allowed data files ──────────────────────────────── -$statusLines = (Invoke-Git @('status', '--porcelain', '--', 'src/frontend/src/data', 'src/frontend/src/assets/samples')) | - Where-Object { $_ -and $_.Trim().Length -gt 0 } -$anyChanges = [bool]$statusLines +# ── Detect any working-tree changes ───────────────────────────────────────── +# Gate on the FULL working tree, not just the in-scope data paths. If +# `pnpm run update:all` touched anything at all we must fall through to the +# Phase 4 scope check, so that an out-of-scope-only change fails loudly there +# instead of silently exiting 0 here. +$statusLines = @(Invoke-Git @('status', '--porcelain') | + Where-Object { $_ -and $_.Trim().Length -gt 0 }) +$anyChanges = $statusLines.Count -gt 0 if (-not $anyChanges) { Write-Section 'Result — no changes' @@ -260,7 +276,7 @@ elseif ($versionsChanged -and $SkipRegen) { # ── Phase 4: scope check ──────────────────────────────────────────────────── Write-Section 'Phase 4 — scope check' -$allStatus = (Invoke-Git @('status', '--porcelain')) | Where-Object { $_ -and $_.Trim().Length -gt 0 } +$allStatus = @(Invoke-Git @('status', '--porcelain') | Where-Object { $_ -and $_.Trim().Length -gt 0 }) $outOfScope = [System.Collections.Generic.List[string]]::new() foreach ($line in $allStatus) { # Porcelain format: "XY " (path starts at column 4). Handle renames "old -> new". @@ -282,8 +298,8 @@ Write-Host "All changes are within the allowed data paths." -ForegroundColor Gre # ── File-count summary (for the PR body) ──────────────────────────────────── function Get-AreaCounts { param([Parameter(Mandatory)][string]$Prefix) - $names = (Invoke-Git @('diff', '--name-status', 'HEAD', '--', $Prefix)) | - Where-Object { $_ -and $_.Trim().Length -gt 0 } + $names = @(Invoke-Git @('diff', '--name-status', 'HEAD', '--', $Prefix) | + Where-Object { $_ -and $_.Trim().Length -gt 0 }) $added = 0; $modified = 0; $removed = 0 foreach ($n in $names) { $code = ($n.Trim())[0] @@ -300,11 +316,11 @@ function Get-AreaCounts { $pkgsCounts = Get-AreaCounts -Prefix 'src/frontend/src/data/pkgs/' $tsModulesCounts = Get-AreaCounts -Prefix 'src/frontend/src/data/ts-modules/' -$twoslashChanged = [bool]((Invoke-Git @('diff', '--name-only', 'HEAD', '--', 'src/frontend/src/data/twoslash/aspire.d.ts')) | - Where-Object { $_ -and $_.Trim().Length -gt 0 }) +$twoslashChanged = @(Invoke-Git @('diff', '--name-only', 'HEAD', '--', 'src/frontend/src/data/twoslash/aspire.d.ts') | + Where-Object { $_ -and $_.Trim().Length -gt 0 }).Count -gt 0 # ── PR title + body ───────────────────────────────────────────────────────── -$dateShort = (Get-Date).ToString('M/d/yy') +$dateShort = [DateTime]::UtcNow.ToString('M/d/yy', [System.Globalization.CultureInfo]::InvariantCulture) $prTitle = "chore: Update integration data and GitHub stats ($dateShort)" $sb = [System.Text.StringBuilder]::new() From 271f2fae80d3bb4c65017527cac3a9d2fae655b8 Mon Sep 17 00:00:00 2001 From: David Pine <7679720+IEvangelist@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:22:15 -0500 Subject: [PATCH 3/4] Use a rolling branch/PR for the integration data updater Instead of creating a new timestamped branch and a brand-new PR on every run (and closing the previous day's PR), reuse a single long-lived branch chore/update-integration-data: - Force-push one fresh commit with the latest data on top of the newest main each run, so an unreviewed PR is refreshed in place rather than piling up a new PR per day. The single rebased snapshot also keeps the PR conflict-free. - Reuse the existing open PR when present (gh pr edit) so its review threads and history carry over; only open a new PR when none exists. - Still close any stray legacy timestamped automation PRs as a safety net. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/update-integration-data.yml | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/.github/workflows/update-integration-data.yml b/.github/workflows/update-integration-data.yml index 81a412e15..c4af805bd 100644 --- a/.github/workflows/update-integration-data.yml +++ b/.github/workflows/update-integration-data.yml @@ -89,7 +89,7 @@ jobs: git config user.name "aspire-repo-bot[bot]" git config user.email "aspire-repo-bot[bot]@users.noreply.github.com" - - name: Commit and push branch + - name: Commit and refresh automation branch if: ${{ steps.update.outputs.changed == 'true' }} id: push env: @@ -97,7 +97,13 @@ jobs: shell: bash run: | set -euo pipefail - branch="chore/update-integration-data-$(date -u +%Y%m%d-%H%M%S)" + + # Single long-lived automation branch. Each run force-pushes one commit + # with the latest generated data on top of the freshest main, so an + # unreviewed PR is refreshed in place instead of piling up a new PR (and + # a new branch) every day. The rolling commit is always a single snapshot + # rebased onto current main, which also keeps the PR conflict-free. + branch="chore/update-integration-data" # Stage only the allowed generated data files — never workflow files, # source, manifests, or docs. Directories with no changes are no-ops. @@ -116,15 +122,17 @@ jobs: exit 0 fi - git checkout -b "$branch" + # Reset the branch to a single fresh commit on top of the current main + # and force-push, so the open PR (if any) updates in place. + git checkout -B "$branch" git commit \ -m "$PR_TITLE" \ -m "Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>" - git push origin "HEAD:refs/heads/$branch" + git push --force origin "HEAD:refs/heads/$branch" echo "branch=$branch" >> "$GITHUB_OUTPUT" echo "pushed=true" >> "$GITHUB_OUTPUT" - - name: Create pull request + - name: Create or update pull request if: ${{ steps.push.outputs.pushed == 'true' }} env: GH_TOKEN: ${{ steps.app-token.outputs.token }} @@ -136,26 +144,42 @@ jobs: run: | set -euo pipefail - new_pr=$(gh pr create \ - --base main \ + # Reuse the existing open PR for the rolling branch if there is one, so + # its review threads and history carry over; otherwise open a fresh PR. + existing=$(gh pr list \ + --state open \ --head "$BRANCH" \ - --title "$PR_TITLE" \ - --body-file "$PR_BODY_FILE" \ - --label ":octocat: auto-merge") - echo "Created $new_pr" + --json number \ + --jq '.[0].number // empty') + + if [ -n "$existing" ]; then + echo "Refreshing existing PR #$existing in place." + gh pr edit "$existing" \ + --title "$PR_TITLE" \ + --body-file "$PR_BODY_FILE" + else + new_pr=$(gh pr create \ + --base main \ + --head "$BRANCH" \ + --title "$PR_TITLE" \ + --body-file "$PR_BODY_FILE" \ + --label ":octocat: auto-merge") + echo "Created $new_pr" + fi - # Supersede any older open integration-data PRs from this automation. - old_prs=$(gh pr list \ + # Close any stray older automation PRs (e.g. legacy timestamped branches + # from before the rolling-branch model) so only the single PR remains. + strays=$(gh pr list \ --state open \ --limit 30 \ --json number,headRefName \ - --jq '.[] | select(.headRefName | startswith("chore/update-integration-data")) | select(.headRefName != env.BRANCH) | .number') + --jq '.[] | select(.headRefName | startswith("chore/update-integration-data-")) | select(.headRefName != env.BRANCH) | .number') - for pr in $old_prs; do - echo "Superseding older integration-data PR #$pr" + for pr in $strays; do + echo "Closing stray automation PR #$pr" gh pr close "$pr" \ --delete-branch \ - --comment "Superseded by the latest Integration Data Updater run ($new_pr)." + --comment "Superseded by the rolling Integration Data Updater PR." done - name: Summary From c246c0febd5a37bb546ed5c58d0f7b0868a2f15c Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 17 Jul 2026 07:33:02 -0500 Subject: [PATCH 4/4] Address integration updater review feedback Fail on partial TypeScript API generation, reject duplicate integration titles, and protect maintainer commits on the rolling automation branch. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/update-integration-data.yml | 21 +++++++++++++++++- .../scripts/update-integration-data.ps1 | 22 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-integration-data.yml b/.github/workflows/update-integration-data.yml index c4af805bd..127d1513b 100644 --- a/.github/workflows/update-integration-data.yml +++ b/.github/workflows/update-integration-data.yml @@ -104,6 +104,21 @@ jobs: # a new branch) every day. The rolling commit is always a single snapshot # rebased onto current main, which also keeps the PR conflict-free. branch="chore/update-integration-data" + bot_email="aspire-repo-bot[bot]@users.noreply.github.com" + + # Refuse to replace a rolling branch that contains maintainer changes. + # The exact lease below also protects commits pushed after this check. + remote_tip=$(git ls-remote --heads origin "refs/heads/$branch" | awk '{print $1}') + if [ -n "$remote_tip" ]; then + git fetch --no-tags origin "refs/heads/$branch:refs/remotes/origin/$branch" + non_bot_commits=$(git log --format='%H %ae %ce' "origin/main..origin/$branch" | + awk -v bot="$bot_email" '$2 != bot || $3 != bot') + if [ -n "$non_bot_commits" ]; then + echo "::error::Refusing to replace $branch because it contains non-bot commits:" + echo "$non_bot_commits" + exit 1 + fi + fi # Stage only the allowed generated data files — never workflow files, # source, manifests, or docs. Directories with no changes are no-ops. @@ -128,7 +143,11 @@ jobs: git commit \ -m "$PR_TITLE" \ -m "Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>" - git push --force origin "HEAD:refs/heads/$branch" + if [ -n "$remote_tip" ]; then + git push --force-with-lease="refs/heads/$branch:$remote_tip" origin "HEAD:refs/heads/$branch" + else + git push origin "HEAD:refs/heads/$branch" + fi echo "branch=$branch" >> "$GITHUB_OUTPUT" echo "pushed=true" >> "$GITHUB_OUTPUT" diff --git a/src/frontend/scripts/update-integration-data.ps1 b/src/frontend/scripts/update-integration-data.ps1 index 1fa0872b6..15445f7aa 100644 --- a/src/frontend/scripts/update-integration-data.ps1 +++ b/src/frontend/scripts/update-integration-data.ps1 @@ -180,7 +180,11 @@ function Get-VersionMap { foreach ($entry in @($entries)) { if ($entry.PSObject.Properties.Name -contains 'title' -and $entry.PSObject.Properties.Name -contains 'version') { - $map[[string]$entry.title] = [string]$entry.version + $title = [string]$entry.title + if ($map.ContainsKey($title)) { + throw "Duplicate integration title '$title' prevents reliable version-change detection." + } + $map[$title] = [string]$entry.version } } return $map @@ -266,7 +270,21 @@ if ($versionsChanged -and -not $SkipRegen) { } exit 1 } - $tsApiSummary = 'succeeded' + + $tsApiDone = ($tsLog -split "`n" | + Where-Object { $_ -match 'Complete:\s+\d+\s+succeeded,\s+\d+\s+failed,\s+\d+\s+skipped' } | + Select-Object -First 1) + if (-not $tsApiDone -or + $tsApiDone -notmatch 'Complete:\s+(?\d+)\s+succeeded,\s+(?\d+)\s+failed,\s+(?\d+)\s+skipped') { + Write-Error "TypeScript API generation did not emit a valid completion summary. Aborting; no PR will be opened." + exit 1 + } + $tsApiSummary = "$($Matches.Succeeded) succeeded, $($Matches.Failed) failed, $($Matches.Skipped) skipped" + if ([int]$Matches.Failed -gt 0) { + Write-Error "TypeScript API generation reported $($Matches.Failed) package failure(s). Aborting; no PR will be opened." + exit 1 + } + $twoslashSummary = 'succeeded' $regenRan = $true }