diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 9ddba1ee2b..7963a068af 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -350,15 +350,7 @@ jobs: DIFC_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.2.19' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_difc_proxy.sh" - - env: - GH_HOST: localhost:18443 - GH_REPO: ${{ github.repository }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_API_URL: https://localhost:18443/api/v3 - GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Fetch and filter PRs + - name: Fetch and filter PRs run: |- # Fetch open PRs from the target repository opened in the last 24 hours SINCE=$(date -d '24 hours ago' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \ @@ -407,6 +399,14 @@ jobs: echo "# No CONTRIBUTING.md found" > "$GITHUB_WORKSPACE/contributing-guidelines.md" echo "ℹ No CONTRIBUTING.md found in $TARGET_REPOSITORY (checked root, .github/, docs/)" fi + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index 70bc4301ce..0da4862d2a 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"2b1837fd30fd6b8bd53bb35dc16aa6fb9f2ae1e8b255a37e984419ca8200a91b","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","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":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-node","sha":"53b83947a5a98c8d113130e565377fae1a50d02f","version":"v6.3.0"},{"repo":"actions/setup-python","sha":"a309ff8b426b58ec0e2a45f0f869d46889d02405","version":"v6.2.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20","digest":"sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20","digest":"sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20","digest":"sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19","digest":"sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0","digest":"sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28","pinned_image":"ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","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":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-node","sha":"53b83947a5a98c8d113130e565377fae1a50d02f","version":"v6.3.0"},{"repo":"actions/setup-python","sha":"a309ff8b426b58ec0e2a45f0f869d46889d02405","version":"v6.2.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20","digest":"sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20","digest":"sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20","digest":"sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19","digest":"sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0","digest":"sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28","pinned_image":"ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -52,7 +52,7 @@ # - actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 # - actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 # - actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 -# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 # # Container images used: @@ -401,13 +401,7 @@ jobs: DIFC_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.2.19' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_difc_proxy.sh" - - env: - GH_HOST: localhost:18443 - GH_REPO: ${{ github.repository }} - GITHUB_API_URL: https://localhost:18443/api/v3 - GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Setup jq utilities directory + - name: Setup jq utilities directory run: |- mkdir -p /tmp/gh-aw cat > /tmp/gh-aw/jqschema.sh << 'EOF' @@ -427,24 +421,22 @@ jobs: ' EOF chmod +x /tmp/gh-aw/jqschema.sh - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Install gh CLI + - name: Install gh CLI run: | bash "${RUNNER_TEMP}/gh-aw/actions/install_gh_cli.sh" - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Fetch issues + - name: Fetch issues run: |- # Create output directories mkdir -p /tmp/gh-aw/issues-data @@ -496,13 +488,15 @@ jobs: # Always ensure data is available at expected locations for backward compatibility echo "Issues data available at: /tmp/gh-aw/issues-data/issues.json" echo "Schema available at: /tmp/gh-aw/issues-data/issues-schema.json" - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Setup Python environment + - name: Setup Python environment run: | # Create working directory for Python scripts mkdir -p /tmp/gh-aw/python @@ -515,13 +509,13 @@ jobs: echo "Data directory: /tmp/gh-aw/python/data" echo "Charts directory: /tmp/gh-aw/python/charts" echo "Artifacts directory: /tmp/gh-aw/python/artifacts" - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Install Python scientific libraries + - name: Install Python scientific libraries run: | # Create a virtual environment for proper package isolation (avoids --break-system-packages) if [ ! -d /tmp/gh-aw/venv ]; then @@ -538,15 +532,21 @@ jobs: /tmp/gh-aw/venv/bin/python3 -c "import scipy; print(f'SciPy {scipy.__version__} installed')" echo "All scientific libraries installed successfully" - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt + - name: Upload source files and data if: always() - name: Upload source files and data - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt with: if-no-files-found: warn name: python-source-and-data @@ -554,13 +554,7 @@ jobs: /tmp/gh-aw/python/*.py /tmp/gh-aw/python/data/* retention-days: 30 - - env: - GH_HOST: localhost:18443 - GH_REPO: ${{ github.repository }} - GITHUB_API_URL: https://localhost:18443/api/v3 - GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Setup Python NLP environment + - name: Setup Python NLP environment run: |- mkdir -p /tmp/gh-aw/python/{data,charts,artifacts} # Create a virtual environment for proper package isolation (avoids --break-system-packages) @@ -579,6 +573,12 @@ jobs: " /tmp/gh-aw/venv/bin/python3 -c "import sklearn; print(f'scikit-learn {sklearn.__version__}')" + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt # Cache memory file share configuration from frontmatter processed below - name: Create cache-memory directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh" diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index 7893fe9518..5467498eb5 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -347,13 +347,7 @@ jobs: DIFC_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.2.19' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_difc_proxy.sh" - - env: - GH_HOST: localhost:18443 - GH_REPO: ${{ github.repository }} - GITHUB_API_URL: https://localhost:18443/api/v3 - GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Setup jq utilities directory + - name: Setup jq utilities directory run: |- mkdir -p /tmp/gh-aw cat > /tmp/gh-aw/jqschema.sh << 'EOF' @@ -373,15 +367,13 @@ jobs: ' EOF chmod +x /tmp/gh-aw/jqschema.sh - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Fetch issues + - name: Fetch issues run: | # Create output directory mkdir -p /tmp/gh-aw/issues-data @@ -406,6 +398,14 @@ jobs: echo "" echo "Schema of the issues data:" cat /tmp/gh-aw/issues-data/issues-schema.json | jq . + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index d6c1e7bdc8..fc05047206 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"5e48f7d5dba2241f2c017d613dda4c6455a91439c75a526d4350c90d299f6b54","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_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":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-python","sha":"a309ff8b426b58ec0e2a45f0f869d46889d02405","version":"v6.2.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"},{"repo":"github/stale-repos","sha":"5f2e18fc5432823f96c1feb69327f665c2acab59","version":"5f2e18fc5432823f96c1feb69327f665c2acab59"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20","digest":"sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20","digest":"sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20","digest":"sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19","digest":"sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0","digest":"sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28","pinned_image":"ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_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":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-python","sha":"a309ff8b426b58ec0e2a45f0f869d46889d02405","version":"v6.2.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7"},{"repo":"github/stale-repos","sha":"5f2e18fc5432823f96c1feb69327f665c2acab59","version":"v9.0.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20","digest":"sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20","digest":"sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20","digest":"sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19","digest":"sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0","digest":"sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28","pinned_image":"ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -48,9 +48,9 @@ # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 # - actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 -# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/stale-repos@5f2e18fc5432823f96c1feb69327f665c2acab59 +# - github/stale-repos@5f2e18fc5432823f96c1feb69327f665c2acab59 # v9.0.8 # # Container images used: # - ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682 @@ -386,13 +386,7 @@ jobs: DIFC_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.2.19' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_difc_proxy.sh" - - env: - GH_HOST: localhost:18443 - GH_REPO: ${{ github.repository }} - GITHUB_API_URL: https://localhost:18443/api/v3 - GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Setup Python environment + - name: Setup Python environment run: | # Create working directory for Python scripts mkdir -p /tmp/gh-aw/python @@ -405,13 +399,13 @@ jobs: echo "Data directory: /tmp/gh-aw/python/data" echo "Charts directory: /tmp/gh-aw/python/charts" echo "Artifacts directory: /tmp/gh-aw/python/artifacts" - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Install Python scientific libraries + - name: Install Python scientific libraries run: | # Create a virtual environment for proper package isolation (avoids --break-system-packages) if [ ! -d /tmp/gh-aw/venv ]; then @@ -428,15 +422,21 @@ jobs: /tmp/gh-aw/venv/bin/python3 -c "import scipy; print(f'SciPy {scipy.__version__} installed')" echo "All scientific libraries installed successfully" - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt + - name: Upload source files and data if: always() - name: Upload source files and data - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt with: if-no-files-found: warn name: python-source-and-data @@ -444,13 +444,7 @@ jobs: /tmp/gh-aw/python/*.py /tmp/gh-aw/python/data/* retention-days: 30 - - env: - GH_HOST: localhost:18443 - GH_REPO: ${{ github.repository }} - GITHUB_API_URL: https://localhost:18443/api/v3 - GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql - NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Setup jq utilities directory + - name: Setup jq utilities directory run: |- mkdir -p /tmp/gh-aw cat > /tmp/gh-aw/jqschema.sh << 'EOF' @@ -470,13 +464,13 @@ jobs: ' EOF chmod +x /tmp/gh-aw/jqschema.sh - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Setup Python environment + - name: Setup Python environment run: | mkdir -p /tmp/gh-aw/python/{data,charts,artifacts} # Create a virtual environment for proper package isolation (avoids --break-system-packages) @@ -485,15 +479,21 @@ jobs: fi echo "/tmp/gh-aw/venv/bin" >> "$GITHUB_PATH" /tmp/gh-aw/venv/bin/pip install --quiet numpy pandas matplotlib seaborn scipy - - env: + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt + - name: Upload source files and data if: always() - name: Upload source files and data - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt with: if-no-files-found: warn name: trending-source-and-data @@ -501,7 +501,10 @@ jobs: /tmp/gh-aw/python/*.py /tmp/gh-aw/python/data/* retention-days: 30 - - env: + - name: Run stale-repos tool + id: stale-repos + uses: github/stale-repos@5f2e18fc5432823f96c1feb69327f665c2acab59 # v9.0.8 + env: ADDITIONAL_METRICS: release,pr EXEMPT_TOPICS: keep,template GH_HOST: localhost:18443 @@ -512,22 +515,19 @@ jobs: INACTIVE_DAYS: "365" NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt ORGANIZATION: ${{ env.ORGANIZATION }} - id: stale-repos - name: Run stale-repos tool - uses: github/stale-repos@5f2e18fc5432823f96c1feb69327f665c2acab59 - - env: + - name: Save stale repos output + run: |- + mkdir -p /tmp/stale-repos-data + echo "$INACTIVE_REPOS" > /tmp/stale-repos-data/inactive-repos.json + echo "Stale repositories data saved" + echo "Total stale repositories: $(jq 'length' /tmp/stale-repos-data/inactive-repos.json)" + env: GH_HOST: localhost:18443 GH_REPO: ${{ github.repository }} GITHUB_API_URL: https://localhost:18443/api/v3 GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql INACTIVE_REPOS: ${{ steps.stale-repos.outputs.inactiveRepos }} NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Save stale repos output - run: |- - mkdir -p /tmp/stale-repos-data - echo "$INACTIVE_REPOS" > /tmp/stale-repos-data/inactive-repos.json - echo "Stale repositories data saved" - echo "Total stale repositories: $(jq 'length' /tmp/stale-repos-data/inactive-repos.json)" # Cache memory file share configuration from frontmatter processed below - name: Create cache-memory directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh" diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000000..01021df64d --- /dev/null +++ b/.mcp.json @@ -0,0 +1,8 @@ +{ + "servers": { + "github-agentic-workflows": { + "command": "gh", + "args": ["aw", "mcp-server"] + } + } +} diff --git a/docs/adr/26322-step-scoped-env-injection-for-difc-proxy.md b/docs/adr/26322-step-scoped-env-injection-for-difc-proxy.md index c4504e136b..532932114d 100644 --- a/docs/adr/26322-step-scoped-env-injection-for-difc-proxy.md +++ b/docs/adr/26322-step-scoped-env-injection-for-difc-proxy.md @@ -1,7 +1,7 @@ # ADR-26322: Step-Scoped Env Injection for DIFC Proxy Routing **Date**: 2026-04-15 -**Status**: Draft +**Status**: Accepted **Deciders**: lpcox, Copilot --- @@ -57,7 +57,7 @@ Wrap each custom step's `run:` block in a shell preamble that sets and unsets th 1. When the DIFC proxy is active for a main-job workflow, implementations **MUST** inject proxy routing env vars (`GH_HOST`, `GH_REPO`, `GITHUB_API_URL`, `GITHUB_GRAPHQL_URL`, `NODE_EXTRA_CA_CERTS`) as step-level `env:` blocks on each custom step. 2. Implementations **MUST NOT** write proxy routing env vars to `$GITHUB_ENV` (the runner's persistent env file) for the purpose of routing custom steps through the proxy. -3. Implementations **MUST** preserve existing step-level env vars (e.g. `GH_TOKEN`) when injecting proxy vars; proxy vars **MUST NOT** overwrite user-defined env vars that share the same key. +3. Implementations **MUST** preserve existing step-level env vars that are not proxy routing vars (e.g. `GH_TOKEN`) when injecting proxy vars. Proxy routing vars (`GH_HOST`, `GH_REPO`, `GITHUB_API_URL`, `GITHUB_GRAPHQL_URL`, `NODE_EXTRA_CA_CERTS`) **MUST** take precedence over any user-defined values for those same keys; user-provided values for these keys are intentionally overwritten to guarantee correct proxy routing. 4. If YAML parsing of the custom steps string fails, implementations **SHOULD** log the error and return the original custom steps string unchanged rather than aborting compilation. ### Shell Script Responsibilities @@ -69,8 +69,6 @@ Wrap each custom step's `run:` block in a shell preamble that sets and unsets th ### Conformance -An implementation is considered conformant with this ADR if it satisfies all **MUST** and **MUST NOT** requirements above. Specifically: proxy routing vars appear only as step-level `env:` blocks on custom steps (never in `$GITHUB_ENV` writes within the proxy scripts), and existing step env vars are preserved when proxy vars are merged. Failure to meet any **MUST** or **MUST NOT** requirement constitutes non-conformance. +An implementation is considered conformant with this ADR if it satisfies all **MUST** and **MUST NOT** requirements above. Specifically: proxy routing vars appear only as step-level `env:` blocks on custom steps (never in `$GITHUB_ENV` writes within the proxy scripts); proxy routing vars (`GH_HOST`, `GH_REPO`, `GITHUB_API_URL`, `GITHUB_GRAPHQL_URL`, `NODE_EXTRA_CA_CERTS`) take precedence and overwrite any user-defined values for those keys; and all other step env vars are preserved. Failure to meet any **MUST** or **MUST NOT** requirement constitutes non-conformance. --- - -*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/24431428234) workflow. The PR author must review, complete, and finalize this document before the PR can merge.* diff --git a/docs/adr/26357-yaml-round-trip-fidelity-for-proxy-env-injection.md b/docs/adr/26357-yaml-round-trip-fidelity-for-proxy-env-injection.md new file mode 100644 index 0000000000..60fefcd592 --- /dev/null +++ b/docs/adr/26357-yaml-round-trip-fidelity-for-proxy-env-injection.md @@ -0,0 +1,79 @@ +# ADR-26357: YAML Round-Trip Fidelity for Proxy Env Injection Steps + +**Date**: 2026-04-15 +**Status**: Draft +**Deciders**: lpcox, Copilot + +--- + +## Part 1 — Narrative (Human-Friendly) + +### Context + +`injectProxyEnvIntoCustomSteps` (introduced in ADR-26322 / PR #26322) injects DIFC proxy routing env vars into each custom step by parsing the step YAML with `goccy/go-yaml`, merging env vars, and re-serializing. The initial implementation used `yaml.MarshalWithOptions(map[string]any{...})` which has two correctness problems: (1) YAML deserialization strips inline comments (e.g., `uses: actions/upload-artifact@sha # v7` loses `# v7`), causing `gh-aw-manifest` to record bare SHAs instead of human-readable version tags; (2) serializing through an unordered `map[string]any` produces alphabetical field ordering, placing `env:` before `name:` and `uses:` in the output and generating noisy lock-file diffs. The codebase already solves both problems in `DeduplicateRuntimeSetupStepsFromCustomSteps` and `compiler_yaml_step_conversion.go` via established utilities (`OrderMapFields`, `unquoteUsesWithComments`), which `injectProxyEnvIntoCustomSteps` was not using. + +### Decision + +We will fix `injectProxyEnvIntoCustomSteps` to achieve YAML round-trip fidelity by applying three techniques already proven elsewhere in the compiler. First, version comments (e.g., `# v7`) are extracted from `uses:` lines *before* `yaml.Unmarshal` strips them and re-appended to the `uses` value string after processing (same pre-extraction pattern used in `DeduplicateRuntimeSetupStepsFromCustomSteps`). Second, each step is converted to an ordered `yaml.MapSlice` via `OrderMapFields(constants.PriorityStepFields)` before marshaling, keeping `name`/`uses` ahead of `env:`. Third, `unquoteUsesWithComments` is called on the serialized output to remove the quotes that `goccy/go-yaml` adds around strings containing `#`. Reusing existing utilities ensures consistent behavior across all compiler code paths that round-trip step YAML. + +### Alternatives Considered + +#### Alternative 1: Comment-Aware YAML Library + +Use a YAML library that natively preserves comments during round-trip serialization (e.g., `gopkg.in/yaml.v3` with node-based API, or a dedicated comment-preserving fork). This would avoid the pre-extraction workaround. It was not chosen because no comment-preserving YAML library is currently used in the codebase, adding one introduces a new dependency and a different API surface, and the pre-extraction pattern is already established and tested in `DeduplicateRuntimeSetupStepsFromCustomSteps`. + +#### Alternative 2: Raw String Manipulation Instead of Parse-and-Serialize + +Inject proxy env vars using regex or line-based string processing directly on the YAML string, bypassing `yaml.Unmarshal`/`yaml.Marshal` entirely. This avoids the comment-stripping and ordering problems at their source. It was not chosen because raw string manipulation of YAML is fragile (indentation-sensitive, breaks on multi-line scalars, hard to reason about correctness), and the parse-and-serialize approach is the established pattern for step mutation in this compiler. + +#### Alternative 3: Struct-Based Serialization with Tagged Fields + +Define a Go struct for the step schema (with `yaml` struct tags in the desired field order) and marshal through that struct instead of `map[string]any`. This would give deterministic field ordering without `OrderMapFields`. It was not chosen because the step schema is open-ended (custom steps can have any fields), making a fully general struct impractical. `OrderMapFields` with a priority list handles both the known priority fields and unknown fields without requiring schema completeness. + +### Consequences + +#### Positive +- `gh-aw-manifest` records human-readable version tags (e.g., `"version":"v7"`) instead of bare SHAs when compiled lock files contain annotated `uses:` references. +- Lock-file diffs are stable and readable: `name:` and `uses:` fields appear before `env:` in every compiled step. +- No new dependencies or utility functions: the fix reuses `OrderMapFields`, `unquoteUsesWithComments`, and the version-comment pre-extraction pattern already present in the codebase. + +#### Negative +- The pre-extraction pattern is an extra pass over the input string (one `strings.SplitSeq` scan) before parsing. For typical workflow sizes this is negligible, but it adds a code path that must be maintained if the `uses:` comment convention changes. +- Embedding the comment inside the `uses` string value (e.g., `"actions/upload-artifact@sha # v7"`) before re-serialization is a semantic hack: the string briefly holds a value that is not a valid action reference. The `unquoteUsesWithComments` post-processing step is required to make the output valid again, creating a subtle ordering dependency between these two steps. + +#### Neutral +- Four workflow lock files (`contribution-check`, `daily-issues-report`, `issue-arborist`, `stale-repo-identifier`) were recompiled; `uses:` lines regain their `# vX` annotations and field ordering normalizes to `name`/`uses` before `env:`. +- The fix is consistent with how `DeduplicateRuntimeSetupStepsFromCustomSteps` handles the same round-trip fidelity problem, so the two functions now share the same approach. + +--- + +## Part 2 — Normative Specification (RFC 2119) + +> The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this section are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). + +### Version Comment Preservation + +1. Implementations of `injectProxyEnvIntoCustomSteps` **MUST** extract inline version comments (of the form `# vX` or `# vX.Y.Z`) from `uses:` lines in the input string *before* calling `yaml.Unmarshal`, storing them in a map keyed by the action reference (e.g., `actions/upload-artifact@sha`). +2. Implementations **MUST** re-append the extracted comment to the `uses` field value in the parsed step map before re-serialization, so that the serialized output contains the comment. +3. Implementations **MUST** call `unquoteUsesWithComments` on the serialized output to remove YAML quotes added around strings containing `#`. +4. Implementations **MUST NOT** rely on `goccy/go-yaml` (or any other YAML library used in this codebase) to preserve comments during an `Unmarshal`/`Marshal` round-trip. + +### Field Ordering + +1. Implementations of `injectProxyEnvIntoCustomSteps` **MUST** convert each step map to an ordered `yaml.MapSlice` using `OrderMapFields(constants.PriorityStepFields)` before marshaling. +2. Implementations **MUST NOT** marshal step maps directly as `map[string]any`, as this produces non-deterministic (alphabetical) field ordering. +3. When `constants.PriorityStepFields` is updated, implementations **SHOULD** verify that `injectProxyEnvIntoCustomSteps` output ordering remains consistent with other step-serializing code paths (e.g., `compiler_yaml_step_conversion.go`). + +### Error Handling + +1. If `yaml.Unmarshal` fails or the parsed result contains zero steps, implementations **MUST** log the error and return the original `customSteps` string unchanged. +2. If `yaml.MarshalWithOptions` fails, implementations **MUST** log the error and return the original `customSteps` string unchanged. +3. Implementations **MUST NOT** return an empty string or a partial result when either parsing or serialization fails. + +### Conformance + +An implementation is considered conformant with this ADR if it satisfies all **MUST** and **MUST NOT** requirements above. Specifically: version comments present in the input **MUST** appear in the output (unquoted), step field ordering in the output **MUST** place priority fields (`name`, `uses`) before non-priority fields (e.g., `env`), and any parse or serialization failure **MUST** result in the original input being returned unchanged. Failure to meet any **MUST** or **MUST NOT** requirement constitutes non-conformance. + +--- + +*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/24435732366) workflow. The PR author must review, complete, and finalize this document before the PR can merge.* diff --git a/pkg/workflow/compiler_difc_proxy.go b/pkg/workflow/compiler_difc_proxy.go index 80de1e55dd..5e5113260b 100644 --- a/pkg/workflow/compiler_difc_proxy.go +++ b/pkg/workflow/compiler_difc_proxy.go @@ -308,11 +308,32 @@ func proxyEnvVars() map[string]string { // If a step already has an env: block, the proxy vars are merged into it (existing // vars like GH_TOKEN are preserved). If parsing or serialization fails, the original // customSteps string is returned unchanged. +// +// Version comments on uses lines (e.g. "uses: actions/foo@sha # v4") are preserved +// and re-applied after re-serialization. Step fields are ordered using +// constants.PriorityStepFields so name/uses stay ahead of env for stable diffs. func injectProxyEnvIntoCustomSteps(customSteps string) string { if customSteps == "" { return customSteps } + // Extract version comments from uses lines before unmarshaling. + // YAML treats "# comment" as a comment and strips it during Unmarshal, so we + // must capture them here and re-apply after processing to preserve annotations + // like "uses: actions/upload-artifact@sha # v7" in the compiled lock file. + // Without this, gh-aw-manifest falls back to recording the SHA as the version. + versionComments := make(map[string]string) // key: action@sha, value: " # vX" + for line := range strings.SplitSeq(customSteps, "\n") { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "uses:") && strings.Contains(trimmed, " # ") { + parts := strings.SplitN(trimmed, " # ", 2) + if len(parts) == 2 { + usesValue := strings.TrimSpace(strings.TrimPrefix(parts[0], "uses:")) + versionComments[usesValue] = " # " + parts[1] + } + } + } + var parsed struct { Steps []map[string]any `yaml:"steps"` } @@ -322,6 +343,10 @@ func injectProxyEnvIntoCustomSteps(customSteps string) string { } proxyEnv := proxyEnvVars() + + // Convert each step to an ordered MapSlice with priority fields first so that + // name/uses stay ahead of env for stable diffs, then merge proxy env vars. + orderedSteps := make([]yaml.MapSlice, len(parsed.Steps)) for i, step := range parsed.Steps { envMap, ok := step["env"].(map[string]any) if !ok { @@ -331,11 +356,21 @@ func injectProxyEnvIntoCustomSteps(customSteps string) string { envMap[k] = v } step["env"] = envMap - parsed.Steps[i] = step + + // Re-apply version comment to uses value so the comment survives re-serialization. + if usesVal, hasUses := step["uses"]; hasUses { + if usesStr, ok := usesVal.(string); ok { + if comment, hasComment := versionComments[usesStr]; hasComment { + step["uses"] = usesStr + comment + } + } + } + + orderedSteps[i] = OrderMapFields(step, constants.PriorityStepFields) } resultBytes, err := yaml.MarshalWithOptions( - map[string]any{"steps": parsed.Steps}, + map[string]any{"steps": orderedSteps}, yaml.Indent(2), yaml.UseLiteralStyleIfMultiline(true), ) @@ -344,7 +379,9 @@ func injectProxyEnvIntoCustomSteps(customSteps string) string { return customSteps } - return strings.TrimRight(string(resultBytes), "\n") + // The YAML marshaller quotes strings containing "#" (version comments), but + // GitHub Actions expects unquoted uses values. + return unquoteUsesWithComments(strings.TrimRight(string(resultBytes), "\n")) } // generateStopDIFCProxyStep generates a step that stops the DIFC proxy container diff --git a/pkg/workflow/compiler_difc_proxy_test.go b/pkg/workflow/compiler_difc_proxy_test.go index 4e7e9a9630..4757c6e35f 100644 --- a/pkg/workflow/compiler_difc_proxy_test.go +++ b/pkg/workflow/compiler_difc_proxy_test.go @@ -732,6 +732,72 @@ func TestInjectProxyEnvIntoCustomSteps(t *testing.T) { }, desc: "multiline run content should be preserved after injection", }, + { + // Pinned actions carry an inline version comment ("# v4") that is stripped + // by YAML unmarshaling. injectProxyEnvIntoCustomSteps must extract these + // comments before parsing and re-apply them after so that the compiled lock + // file retains "uses: actions/checkout@sha # v4" and gh-aw-manifest can + // record "version":"v4" instead of falling back to the bare SHA. + // The uses value must also remain unquoted (the YAML marshaller quotes + // strings that contain "#", but GitHub Actions requires bare values). + name: "uses version comments are preserved and unquoted", + customSteps: "steps:\n" + + "- name: Upload artifacts\n" + + " uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7\n" + + " with:\n" + + " name: output\n" + + " path: /tmp/output\n", + expectedContains: []string{ + "uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7", + "GH_HOST: localhost:18443", + "GH_REPO: ${{ github.repository }}", + }, + expectedAbsent: []string{ + // Must not be quoted: 'uses: "actions/upload-artifact@sha # v7"' + `uses: "actions/upload-artifact@`, + }, + desc: "uses version comment must survive YAML round-trip and remain unquoted", + }, + { + // Step fields should follow constants.PriorityStepFields ordering + // (name/uses before env) so that lock-file diffs are stable and + // reviewers see the step identity before the injected env block. + name: "step fields are ordered with name before env", + customSteps: "steps:\n" + + "- name: Run script\n" + + " run: echo hello\n", + expectedContains: []string{ + "name: Run script", + "GH_HOST: localhost:18443", + }, + desc: "name field should appear before env in the output", + }, + { + // Proxy routing vars must take precedence over user-defined values for the + // same keys so that traffic is always routed through the proxy. Non-routing + // vars (e.g. GH_TOKEN) are preserved. This behavior is normative in ADR-26322. + name: "conflicting proxy routing env vars are overwritten by proxy values", + customSteps: "steps:\n" + + "- name: Step with conflicting proxy env\n" + + " env:\n" + + " GH_TOKEN: ${{ github.token }}\n" + + " GH_HOST: example.com\n" + + " GITHUB_API_URL: https://example.com/api/v3\n" + + " run: gh issue list\n", + expectedContains: []string{ + "GH_TOKEN: ${{ github.token }}", + "GH_HOST: localhost:18443", + "GITHUB_API_URL: https://localhost:18443/api/v3", + "GH_REPO: ${{ github.repository }}", + "GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql", + "NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt", + }, + expectedAbsent: []string{ + "GH_HOST: example.com", + "GITHUB_API_URL: https://example.com/api/v3", + }, + desc: "proxy routing env vars should take precedence over conflicting custom values", + }, } for _, tt := range tests { @@ -754,6 +820,15 @@ func TestInjectProxyEnvIntoCustomSteps(t *testing.T) { // Result should still start with "steps:" so addCustomStepsAsIs can process it assert.True(t, strings.HasPrefix(result, "steps:"), "result should start with 'steps:': %s", tt.desc) + + // For the ordering test: verify name appears before env in the output. + if tt.name == "step fields are ordered with name before env" { + nameIdx := strings.Index(result, "name:") + envIdx := strings.Index(result, "env:") + if nameIdx != -1 && envIdx != -1 { + assert.Less(t, nameIdx, envIdx, "name field should appear before env field: %s", tt.desc) + } + } }) } }