diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..261ec5d6 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +custom: # paypal and afdian ['https://paypal.me/libxengine', 'https://www.afdian.com/a/xengine'] diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 00000000..48d5f81f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/workflows/auto-code.lock.yml b/.github/workflows/auto-code.lock.yml new file mode 100644 index 00000000..919efe73 --- /dev/null +++ b/.github/workflows/auto-code.lock.yml @@ -0,0 +1,1179 @@ +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"158e10df0b3421742ed672f7a5667899e0977be63eed6a6fa996ccb9de250211","compiler_version":"v0.71.5","agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"b8068426813005612b960b5ab0b8bd2c27142323","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.71.5). DO NOT EDIT. +# +# 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/ +# +# +# Secrets used: +# - COPILOT_GITHUB_TOKEN +# - GH_AW_CI_TRIGGER_TOKEN +# - GH_AW_GITHUB_MCP_SERVER_TOKEN +# - GH_AW_GITHUB_TOKEN +# - GITHUB_TOKEN +# - OPENAI_API_KEY +# +# Custom actions used: +# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 +# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 +# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 +# +# Container images used: +# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 +# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + +name: "自动处理 Issue" +"on": + issues: + types: + - labeled + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}" + +run-name: "自动处理 Issue" + +jobs: + activation: + needs: pre_activation + if: needs.pre_activation.outputs.activated == 'true' + runs-on: ubuntu-slim + permissions: + actions: read + contents: read + outputs: + body: ${{ steps.sanitized.outputs.body }} + comment_id: "" + comment_repo: "" + 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-trace-id: ${{ steps.setup.outputs.trace-id }} + stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} + text: ${{ steps.sanitized.outputs.text }} + title: ${{ steps.sanitized.outputs.title }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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 || 'claude-sonnet-4.6' }} + GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_AGENT_VERSION: "1.0.40" + GH_AW_INFO_CLI_VERSION: "v0.71.5" + GH_AW_INFO_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","ark.cn-beijing.volces.com"]' + GH_AW_INFO_FIREWALL_ENABLED: "false" + GH_AW_INFO_AWF_VERSION: "" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "" + GH_AW_COMPILED_STRICT: "false" + 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: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + .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 .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.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: "auto-code.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.71.5" + 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: Compute current body text + id: sanitized + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ark.cn-beijing.volces.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + 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/compute_text.cjs'); + await main(); + - 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_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + 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_de410bcdbc8f0b37_EOF' + + GH_AW_PROMPT_de410bcdbc8f0b37_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_de410bcdbc8f0b37_EOF' + + Tools: add_comment, create_pull_request, missing_tool, missing_data, noop + GH_AW_PROMPT_de410bcdbc8f0b37_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" + cat << 'GH_AW_PROMPT_de410bcdbc8f0b37_EOF' + + GH_AW_PROMPT_de410bcdbc8f0b37_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" + cat << 'GH_AW_PROMPT_de410bcdbc8f0b37_EOF' + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_de410bcdbc8f0b37_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + cat << 'GH_AW_PROMPT_de410bcdbc8f0b37_EOF' + + {{#runtime-import .github/workflows/auto-code.md}} + GH_AW_PROMPT_de410bcdbc8f0b37_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" + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + 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_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + 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' + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + 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_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + 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, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED + } + }); + - 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/aw-prompts/prompt.txt + /tmp/gh-aw/github_rate_limits.jsonl + /tmp/gh-aw/base + if-no-files-found: ignore + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + issues: read + pull-requests: read + 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_WORKFLOW_ID_SANITIZED: autocode + outputs: + agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} + 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-copilot-errors.outputs.inference_access_error || 'false' }} + mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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: 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: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + github.event.pull_request || github.event.issue.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.40 + env: + GH_HOST: github.com + - name: Parse integrity filter lists + id: parse-guard-vars + env: + GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} + GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} + GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} + run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" + - 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 .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + - 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_982fd4401303461a_EOF' + {"add_comment":{"max":1},"create_pull_request":{"base_branch":"develop","max":1,"max_patch_files":100,"max_patch_size":1024,"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"]},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_982fd4401303461a_EOF + - name: Generate Safe Outputs Tools + env: + GH_AW_TOOLS_META_JSON: | + { + "description_suffixes": { + "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Supports reply_to_id for discussion threading.", + "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created." + }, + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_VALIDATION_JSON: | + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "reply_to_id": { + "type": "string", + "maxLength": 256 + }, + "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: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh" + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_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="localhost" + 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') + DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/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 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_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 GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -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 ghcr.io/github/gh-aw-mcpg:v0.3.6' + + mkdir -p /home/runner/.copilot + GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) + cat << GH_AW_MCP_CONFIG_01a7963a8b9bf043_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.0.3", + "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": { + "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, + "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, + "min-integrity": "none", + "repos": "all", + "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }} + } + } + }, + "safeoutputs": { + "type": "http", + "url": "http://localhost:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "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_01a7963a8b9bf043_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): + # --allow-tool github + # --allow-tool safeoutputs + # --allow-tool shell(cat) + # --allow-tool shell(date) + # --allow-tool shell(echo) + # --allow-tool shell(git add:*) + # --allow-tool shell(git branch:*) + # --allow-tool shell(git checkout:*) + # --allow-tool shell(git commit:*) + # --allow-tool shell(git merge:*) + # --allow-tool shell(git rm:*) + # --allow-tool shell(git status) + # --allow-tool shell(git switch:*) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(ls) + # --allow-tool shell(pwd) + # --allow-tool shell(safeoutputs:*) + # --allow-tool shell(sort) + # --allow-tool shell(tail) + # --allow-tool shell(uniq) + # --allow-tool shell(wc) + # --allow-tool shell(yq) + # --allow-tool write + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + (umask 177 && touch /tmp/gh-aw/agent-stdio.log) + mkdir -p /tmp/ + mkdir -p /tmp/gh-aw/ + mkdir -p /tmp/gh-aw/agent/ + mkdir -p /tmp/gh-aw/sandbox/agent/logs/ + 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 || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-tool github --allow-tool safeoutputs --allow-tool 'shell(cat)' --allow-tool 'shell(date)' --allow-tool 'shell(echo)' --allow-tool 'shell(git add:*)' --allow-tool 'shell(git branch:*)' --allow-tool 'shell(git checkout:*)' --allow-tool 'shell(git commit:*)' --allow-tool 'shell(git merge:*)' --allow-tool 'shell(git rm:*)' --allow-tool 'shell(git status)' --allow-tool 'shell(git switch:*)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(ls)' --allow-tool 'shell(pwd)' --allow-tool 'shell(safeoutputs:*)' --allow-tool 'shell(sort)' --allow-tool 'shell(tail)' --allow-tool 'shell(uniq)' --allow-tool 'shell(wc)' --allow-tool 'shell(yq)' --allow-tool write --allow-all-paths --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt 2>&1 | tee /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: doubao-seed-2-0-code-preview-260215 + COPILOT_PROVIDER_API_KEY: ${{ secrets.OPENAI_API_KEY }} + COPILOT_PROVIDER_BASE_URL: https://ark.cn-beijing.volces.com/api/v3 + COPILOT_PROVIDER_TYPE: openai + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + 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_VERSION: v0.71.5 + 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 }} + XDG_CONFIG_HOME: /home/runner + - name: Detect Copilot errors + id: detect-copilot-errors + if: always() + continue-on-error: true + run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs" + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - 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: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_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 }} + SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + - 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: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ark.cn-beijing.volces.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.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: 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/proxy-logs/ + !/tmp/gh-aw/proxy-logs/proxy-tls/ + /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 + if-no-files-found: ignore + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: > + always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || + needs.activation.outputs.stale_lock_file_failed == 'true') + runs-on: ubuntu-slim + permissions: + contents: write + discussions: write + issues: write + pull-requests: write + concurrency: + group: "gh-aw-conclusion-auto-code" + cancel-in-progress: false + 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@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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: 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: "自动处理 Issue" + 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" + with: + github-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/handle_noop_message.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: "自动处理 Issue" + with: + github-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/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: "自动处理 Issue" + with: + github-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/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: "自动处理 Issue" + 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: "auto-code" + 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_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_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} + GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} + 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: ${{ 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/handle_agent_failure.cjs'); + await main(); + + pre_activation: + runs-on: ubuntu-slim + outputs: + activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + matched_command: '' + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_REQUIRED_ROLES: "admin,maintainer,write" + 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_membership.cjs'); + await main(); + + safe_outputs: + needs: + - activation + - agent + if: (!cancelled()) && needs.agent.result != 'skipped' + runs-on: ubuntu-slim + permissions: + contents: write + discussions: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/auto-code" + 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.40" + GH_AW_WORKFLOW_ID: "auto-code" + GH_AW_WORKFLOW_NAME: "自动处理 Issue" + outputs: + 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 }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} + 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@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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: 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: + ref: develop + token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + persist-credentials: false + fetch-depth: 1 + - name: Configure Git credentials + if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Configure GH_HOST for enterprise compatibility + id: ghes-host-config + shell: bash + run: | + # 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_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ark.cn-beijing.volces.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"develop\",\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"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\"]},\"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 }} + with: + github-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/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/auto-code.md b/.github/workflows/auto-code.md new file mode 100644 index 00000000..f73bb475 --- /dev/null +++ b/.github/workflows/auto-code.md @@ -0,0 +1,123 @@ +--- +on: + issues: + types: [labeled] + +engine: + id: copilot + env: + COPILOT_PROVIDER_BASE_URL: "https://ark.cn-beijing.volces.com/api/v3" + COPILOT_PROVIDER_BEARER_TOKEN: ${{ secrets.OPENAI_API_KEY }} + COPILOT_MODEL: doubao-seed-evolving + COPILOT_PROVIDER_TYPE: openai + COPILOT_PROVIDER_WIRE_API: responses + +features: + dangerously-disable-sandbox-agent: "controlled environment for issue triage automation" +sandbox: + agent: false +strict: false + +network: + allowed: + - defaults + - ark.cn-beijing.volces.com + +tools: + github: + min-integrity: none + +permissions: + contents: read + issues: read + pull-requests: read + copilot-requests: write + +safe-outputs: + threat-detection: false + create-pull-request: + base-branch: develop + protected-files: allowed + add-comment: + max: 1 +--- + +# 自动处理 Issue + +当 Issue 被打上 `bug` 或 `enhancement` 或 `feature` 标签时触发。其他标签直接退出,不做任何操作。 + +## 判断任务类型 + +读取 Issue #${{ github.event.issue.number }} 当前的标签: +- 如果包含 `bug` 标签 → 执行【Bug 修复流程】 +- 如果包含 `feature` 标签 → 执行【新功能开发流程】 +- 如果包含 `enhancement` 标签 → 执行【功能改进开发流程】 +- 其他情况 → 直接退出 + +## 任务执行限制 +- 根据需求查找相对应的可能关联的代码文件 +- 尽量只读取相关代码文件和文档,不去操作无关代码和文件 +- 尽量减少操作时间和步骤,减少TOKEN和时间消耗 + +--- + + +## Bug 修复流程 + +1. 阅读 Issue 的完整标题和正文,理解问题现象 +2. 浏览仓库相关代码和文档,定位问题所在的文件和函数 +3. 分析根本原因 +4. 实现修复方案,注意: + - 保持与现有代码风格一致 + - 只修改必要的部分,不做无关改动 +5. 创建 Pull Request,标题格式:`fix: `,描述中说明: + - 问题根因 + - 修复方式 + - 如何验证 +6. 在原 Issue 下使用用户提问的语言进行回复,说明已提交 PR 及修复思路 + +如果问题过于复杂或信息不足,在 Issue 下用中文说明原因,不创建 PR。 + +--- + +## 新功能开发流程 + +1. 阅读 Issue 的完整标题和正文,理解需求目标 +2. 浏览仓库相关代码结构和文档,找到最相关的模块和文件 +3. 制定实现方案: + - 需要新增哪些文件或函数 + - 需要修改哪些现有文件 +4. 按方案实现代码,注意: + - 保持与现有代码风格一致 + - 新增函数/类需要添加注释 + - 如果涉及接口变更,同步更新相关调用方 +5. 创建 Pull Request,标题格式:`feat: `,描述中说明: + - 实现了哪些功能 + - 涉及哪些文件改动 + - 如何验证/测试 +6. 在原 Issue 下使用用户提问的语言进行回复,说明已提交 PR、实现思路和测试建议 + +如果需求描述不清晰或实现风险过大,在 Issue 下用中文说明原因,不创建 PR。 + +--- + +## 功能改进开发流程 + +1. 阅读 Issue 的完整标题和正文,明确功能改进与优化的具体目标(如性能提升、代码结构重构、用户体验优化等)。 +2. 浏览仓库相关代码,评估受影响的范围,定位需要进行优化或重构的核心文件及函数。 +3. 制定改进方案,需特别注意: + - **向下兼容性**:确保本次改进不会破坏现有的公开接口(API)和已有功能。 + - **防御性编程**:优化逻辑的同时,不能降低代码的健壮性。 +4. 按照方案实施代码改动,注意: + - 保持与现有代码风格高度一致。 + - 仅针对性能、可读性或结构进行局部优化,严禁引入未经需求的无关大范围改动。 +5. 创建 Pull Request,根据改进的核心侧重点选择标题格式: + - 侧重于代码重构/可读性优化:`refactor: ` + - 侧重于运行运行效率/性能优化:`perf: ` + 在 PR 描述中说明: + - 优化的动机与改进点 + - 改进前后的对比或预期收益 + - 验证优化是否生效的测试方法 +6. 在原 Issue 下使用用户提问的语言进行回复,说明已提交 PR、优化思路及预期的提升效果。 + +如果功能改进可能带来重大的破坏性变更(Breaking Changes)或者现有代码结构不支持盲目优化,在 Issue 下用中文说明原因,保持维持原状,不创建 PR。 \ No newline at end of file diff --git a/.github/workflows/codeql-to-commit.yml b/.github/workflows/codeql-to-commit.yml new file mode 100644 index 00000000..98ab97f9 --- /dev/null +++ b/.github/workflows/codeql-to-commit.yml @@ -0,0 +1,194 @@ +name: Auto Copilot Autofix (High & Medium Only) + +on: + workflow_dispatch: + workflow_run: + workflows: ["CodeQL Advanced"] + types: [completed] + +jobs: + auto-fix: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + permissions: + security-events: read + contents: write + pull-requests: write + + steps: + - name: Trigger Autofix for High & Medium alerts + env: + GH_TOKEN: ${{ secrets.AUTOFIX_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + run: | + set +e + DEFAULT_BRANCH=$(gh api /repos/$OWNER/$REPO --jq '.default_branch') + echo "Default branch: $DEFAULT_BRANCH" + + ALERTS=$(gh api "/repos/$OWNER/$REPO/code-scanning/alerts?state=open&per_page=100" \ + --jq '[.[] | select(.rule.security_severity_level == "critical" or .rule.security_severity_level == "high" or .rule.security_severity_level == "medium" or .rule.severity == "warning") | {number: .number, level: (.rule.security_severity_level // .rule.severity)}]') + + COUNT=$(echo $ALERTS | jq 'length') + echo "Found $COUNT alerts with high / medium / warning" + echo "$ALERTS" | jq -r '.[] | " Alert #\(.number) [\(.level)]"' + + if [ "$COUNT" -eq 0 ]; then + echo "No alerts to process, exiting." + exit 0 + fi + + for ROW in $(echo $ALERTS | jq -r '.[] | @base64'); do + _jq() { echo "$ROW" | base64 -d | jq -r "$1"; } + + NUMBER=$(_jq '.number') + SEC_LEVEL=$(_jq '.level') + BRANCH="autofix/${SEC_LEVEL}/alert-${NUMBER}" + + echo "--- Alert #$NUMBER [$SEC_LEVEL] ---" + + # 检查是否已有 autofix + EXISTING=$(gh api \ + /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix \ + --jq '.status' 2>/dev/null || echo "none") + + if [ "$EXISTING" = "success" ]; then + echo "✅ Fix already exists" + else + echo "⏳ Generating fix..." + gh api -X POST \ + /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix || { + echo "⚠️ Failed to trigger autofix for #$NUMBER, skipping" + continue + } + + for i in 1 2 3; do + sleep 30 + EXISTING=$(gh api \ + /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix \ + --jq '.status' 2>/dev/null || echo "none") + echo " Attempt $i: status = $EXISTING" + [ "$EXISTING" = "success" ] && break + done + fi + + if [ "$EXISTING" != "success" ]; then + echo "⚠️ Autofix not available for alert #$NUMBER (status: $EXISTING), skipping" + continue + fi + + # 检查分支是否已存在 + BRANCH_STATUS=$(gh api \ + /repos/$OWNER/$REPO/git/refs/heads/$BRANCH \ + --silent 2>/dev/null && echo "exists" || echo "not_found") + echo "DEBUG branch status: $BRANCH_STATUS" + + if [ "$BRANCH_STATUS" = "not_found" ]; then + # 创建分支 + SHA=$(gh api /repos/$OWNER/$REPO/git/refs/heads/$DEFAULT_BRANCH \ + --jq '.object.sha') + + gh api -X POST /repos/$OWNER/$REPO/git/refs \ + -f ref="refs/heads/$BRANCH" \ + -f sha="$SHA" 2>/dev/null || true + echo "🌿 Created branch: $BRANCH" + + # 提交 fix + COMMIT_RESULT=$(gh api -X POST \ + /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix/commits \ + -f target_ref="$BRANCH" 2>&1) + echo "DEBUG commit result: $COMMIT_RESULT" + + if echo "$COMMIT_RESULT" | grep -q "target_ref"; then + echo "✅ Committed fix to branch: $BRANCH" + else + echo "⚠️ No code changes generated, deleting branch and skipping" + gh api -X DELETE \ + /repos/$OWNER/$REPO/git/refs/heads/$BRANCH 2>/dev/null || true + continue + fi + else + # 分支已存在,检查是否已有 open PR + EXISTING_PR=$(gh pr list \ + --repo "$OWNER/$REPO" \ + --head "$BRANCH" \ + --state open \ + --json number \ + --jq '.[0].number // empty') + + if [ -n "$EXISTING_PR" ]; then + echo "⏭️ PR #$EXISTING_PR already exists, skipping" + continue + fi + + echo "🌿 Branch exists, creating PR with existing branch" + fi + + # 获取 alert 详情 + ALERT_INFO=$(gh api \ + /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER) + + ALERT_TITLE=$(echo $ALERT_INFO | jq -r '.rule.description') + ALERT_HELP=$(echo $ALERT_INFO | jq -r '.rule.help // "暂无详细说明"' | head -c 800) + ALERT_TAGS=$(echo $ALERT_INFO | jq -r '.rule.tags // [] | join(", ")') + ALERT_FILE=$(echo $ALERT_INFO | jq -r '.most_recent_instance.location.path // "未知文件"') + ALERT_LINE=$(echo $ALERT_INFO | jq -r '.most_recent_instance.location.start_line // "未知行"') + ALERT_URL=$(echo $ALERT_INFO | jq -r '.html_url') + CWE_TAGS=$(echo $ALERT_INFO | jq -r '[.rule.tags[] | select(startswith("external/cwe/"))] | join(", ")') + + AUTOFIX_DESC=$(gh api \ + /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix \ + --jq '.description // "暂无 AI 修复说明"') + + # 创建 Draft PR + gh pr create \ + --repo "$OWNER/$REPO" \ + --base "$DEFAULT_BRANCH" \ + --head "$BRANCH" \ + --draft \ + --title "[Autofix][$SEC_LEVEL] Alert #$NUMBER: $ALERT_TITLE" \ + --body "## 🤖 Copilot Autofix 自动修复报告 + + --- + + ### 📋 基本信息 + + | 字段 | 内容 | + |------|------| + | **Alert ID** | [#$NUMBER]($ALERT_URL) | + | **安全级别** | $SEC_LEVEL | + | **规则名称** | $ALERT_TITLE | + | **问题文件** | \`$ALERT_FILE\` 第 $ALERT_LINE 行 | + | **CWE 分类** | $CWE_TAGS | + | **规则标签** | $ALERT_TAGS | + + --- + + ### 🔍 问题说明 + + $ALERT_HELP + + --- + + ### 🤖 AI 修复思路 + + $AUTOFIX_DESC + + --- + + ### ✅ Review 检查清单 + + - [ ] 理解了漏洞的成因和影响范围 + - [ ] 确认 AI 修复逻辑正确,没有遗漏边界情况 + - [ ] 确认修复没有改变原有业务逻辑 + - [ ] 确认没有引入新的安全问题 + - [ ] CI / 单元测试全部通过 + - [ ] 如有必要,已补充对应的测试用例 + + --- + + > 此 PR 由 GitHub Copilot Autofix 自动生成,请仔细审核后再 merge。" && \ + echo "🎉 PR created for alert #$NUMBER" || \ + echo "❌ Failed to create PR for alert #$NUMBER" + + done diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6e5e7d9a..c5417f85 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,6 +23,8 @@ jobs: matrix: include: - language: c-cpp + build-mode: manual + steps: - name: Checkout repository uses: actions/checkout@v6 @@ -49,17 +51,24 @@ jobs: chmod +x ./XEngine_LINEnv.sh sudo ./XEngine_LINEnv.sh -i 3 + - name: make pre + run: | + cd XEngine_Source + make BUILDTYPE=1 + - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} + build-mode: manual + queries: security-and-quality - - name: make + - name: make check run: | cd XEngine_Source - make + make BUILDTYPE=2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml deleted file mode 100644 index c6884d54..00000000 --- a/.github/workflows/cppcheck.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: cpp check workflows - -on: - push: - branches: - - 'develop' - paths: - - 'XEngine_Source/**' - - 'XEngine_Release/**' - - '.github/**' - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout main repository code - uses: actions/checkout@v6 - with: - ref: 'develop' - - - name: Create static_analysis directory - run: mkdir -p static_analysis - - - name: Run Cppcheck - run: | - sudo apt-get install -y cppcheck - cppcheck --enable=all --language=c++ --std=c++20 ./XEngine_Source/ --output-file=static_analysis/log.xml --xml - continue-on-error: true - - - name: Upload Cppcheck Results - uses: actions/upload-artifact@v7 - with: - name: cppcheck_results - path: static_analysis/log.xml \ No newline at end of file diff --git a/.github/workflows/issue-triage.lock.yml b/.github/workflows/issue-triage.lock.yml new file mode 100644 index 00000000..4dd05499 --- /dev/null +++ b/.github/workflows/issue-triage.lock.yml @@ -0,0 +1,1097 @@ +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"49f1529d95cbf3c8e3d0da1df4b6cee935245532511b98ca9ecd7a44f1d0bb68","compiler_version":"v0.71.5","agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"b8068426813005612b960b5ab0b8bd2c27142323","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.71.5). DO NOT EDIT. +# +# 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/ +# +# +# Secrets used: +# - COPILOT_GITHUB_TOKEN +# - GH_AW_GITHUB_MCP_SERVER_TOKEN +# - GH_AW_GITHUB_TOKEN +# - GITHUB_TOKEN +# - OPENAI_API_KEY +# +# Custom actions used: +# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 +# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 +# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 +# +# Container images used: +# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 +# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + +name: "Issue 自动分类与回复" +"on": + issues: + types: + - opened + - reopened + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}" + +run-name: "Issue 自动分类与回复" + +jobs: + activation: + needs: pre_activation + if: needs.pre_activation.outputs.activated == 'true' + runs-on: ubuntu-slim + permissions: + actions: read + contents: read + outputs: + body: ${{ steps.sanitized.outputs.body }} + comment_id: "" + comment_repo: "" + 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-trace-id: ${{ steps.setup.outputs.trace-id }} + stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} + text: ${{ steps.sanitized.outputs.text }} + title: ${{ steps.sanitized.outputs.title }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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 || 'claude-sonnet-4.6' }} + GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_AGENT_VERSION: "1.0.40" + GH_AW_INFO_CLI_VERSION: "v0.71.5" + GH_AW_INFO_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","ark.cn-beijing.volces.com"]' + GH_AW_INFO_FIREWALL_ENABLED: "false" + GH_AW_INFO_AWF_VERSION: "" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "" + GH_AW_COMPILED_STRICT: "false" + 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: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + .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 .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.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: "issue-triage.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.71.5" + 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: Compute current body text + id: sanitized + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ark.cn-beijing.volces.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + 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/compute_text.cjs'); + await main(); + - 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_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + 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_1857816c8098cba9_EOF' + + GH_AW_PROMPT_1857816c8098cba9_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_1857816c8098cba9_EOF' + + Tools: add_comment, add_labels, missing_tool, missing_data, noop + + GH_AW_PROMPT_1857816c8098cba9_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" + cat << 'GH_AW_PROMPT_1857816c8098cba9_EOF' + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_1857816c8098cba9_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + cat << 'GH_AW_PROMPT_1857816c8098cba9_EOF' + + {{#runtime-import .github/workflows/issue-triage.md}} + GH_AW_PROMPT_1857816c8098cba9_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" + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + 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_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + 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' + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + 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_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + 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, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED + } + }); + - 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/aw-prompts/prompt.txt + /tmp/gh-aw/github_rate_limits.jsonl + /tmp/gh-aw/base + if-no-files-found: ignore + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + issues: read + pull-requests: read + 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_WORKFLOW_ID_SANITIZED: issuetriage + outputs: + agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} + 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-copilot-errors.outputs.inference_access_error || 'false' }} + mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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: 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: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + github.event.pull_request || github.event.issue.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.40 + env: + GH_HOST: github.com + - name: Parse integrity filter lists + id: parse-guard-vars + env: + GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} + GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} + GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} + run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" + - 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 .claude .codex .crush .gemini .github .opencode .pi" + GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f + - 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_18efef56cd5f6986_EOF' + {"add_comment":{"max":1},"add_labels":{"allowed":["bug","feature","enhancement","documentation","question","duplicate","invalid"]},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_18efef56cd5f6986_EOF + - name: Generate Safe Outputs Tools + env: + GH_AW_TOOLS_META_JSON: | + { + "description_suffixes": { + "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Supports reply_to_id for discussion threading.", + "add_labels": " CONSTRAINTS: Only these labels are allowed: [\"bug\" \"feature\" \"enhancement\" \"documentation\" \"question\" \"duplicate\" \"invalid\"]." + }, + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_VALIDATION_JSON: | + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "reply_to_id": { + "type": "string", + "maxLength": 256 + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "add_labels": { + "defaultMax": 5, + "fields": { + "item_number": { + "issueNumberOrTemporaryId": true + }, + "labels": { + "required": true, + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "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: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh" + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_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="localhost" + 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') + DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/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 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_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 GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -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 ghcr.io/github/gh-aw-mcpg:v0.3.6' + + mkdir -p /home/runner/.copilot + GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) + cat << GH_AW_MCP_CONFIG_0138aecd21cfde50_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.0.3", + "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": { + "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, + "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, + "min-integrity": "none", + "repos": "all", + "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }} + } + } + }, + "safeoutputs": { + "type": "http", + "url": "http://localhost:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "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_0138aecd21cfde50_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): + # --allow-tool github + # --allow-tool safeoutputs + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + (umask 177 && touch /tmp/gh-aw/agent-stdio.log) + mkdir -p /tmp/ + mkdir -p /tmp/gh-aw/ + mkdir -p /tmp/gh-aw/agent/ + mkdir -p /tmp/gh-aw/sandbox/agent/logs/ + 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 || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-tool github --allow-tool safeoutputs --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt 2>&1 | tee /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_API_KEY: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: doubao-seed-2-0-code-preview-260215 + COPILOT_PROVIDER_API_KEY: ${{ secrets.OPENAI_API_KEY }} + COPILOT_PROVIDER_BASE_URL: https://ark.cn-beijing.volces.com/api/v3 + COPILOT_PROVIDER_TYPE: openai + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + 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_VERSION: v0.71.5 + 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 }} + XDG_CONFIG_HOME: /home/runner + - name: Detect Copilot errors + id: detect-copilot-errors + if: always() + continue-on-error: true + run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs" + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - 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: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_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 }} + SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + - 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: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ark.cn-beijing.volces.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.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: 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/proxy-logs/ + !/tmp/gh-aw/proxy-logs/proxy-tls/ + /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 + if-no-files-found: ignore + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: > + always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || + needs.activation.outputs.stale_lock_file_failed == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + concurrency: + group: "gh-aw-conclusion-issue-triage" + cancel-in-progress: false + 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@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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: 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: "Issue 自动分类与回复" + 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" + with: + github-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/handle_noop_message.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: "Issue 自动分类与回复" + with: + github-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/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: "Issue 自动分类与回复" + with: + github-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/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: "Issue 自动分类与回复" + 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: "issue-triage" + 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_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_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} + GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} + 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: ${{ 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/handle_agent_failure.cjs'); + await main(); + + pre_activation: + runs-on: ubuntu-slim + outputs: + activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + matched_command: '' + setup-trace-id: ${{ steps.setup.outputs.trace-id }} + steps: + - name: Setup Scripts + id: setup + uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_REQUIRED_ROLES: "admin,maintainer,write" + 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_membership.cjs'); + await main(); + + safe_outputs: + needs: + - activation + - agent + if: (!cancelled()) && needs.agent.result != 'skipped' + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/issue-triage" + 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.40" + GH_AW_WORKFLOW_ID: "issue-triage" + GH_AW_WORKFLOW_NAME: "Issue 自动分类与回复" + outputs: + 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 }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + 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@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.40" + - 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: Configure GH_HOST for enterprise compatibility + id: ghes-host-config + shell: bash + run: | + # 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_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ark.cn-beijing.volces.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\",\"question\",\"duplicate\",\"invalid\"]},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" + with: + github-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/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/issue-triage.md b/.github/workflows/issue-triage.md new file mode 100644 index 00000000..ae117408 --- /dev/null +++ b/.github/workflows/issue-triage.md @@ -0,0 +1,72 @@ +--- +on: + issues: + types: [opened, reopened] + +engine: + id: copilot + env: + COPILOT_PROVIDER_BASE_URL: "https://ark.cn-beijing.volces.com/api/v3" + COPILOT_PROVIDER_BEARER_TOKEN: ${{ secrets.OPENAI_API_KEY }} + COPILOT_MODEL: doubao-seed-evolving + COPILOT_PROVIDER_TYPE: openai + COPILOT_PROVIDER_WIRE_API: responses + +features: + dangerously-disable-sandbox-agent: "controlled environment for issue triage automation" +sandbox: + agent: false +strict: false + +network: + allowed: + - defaults + - ark.cn-beijing.volces.com + +tools: + github: + min-integrity: none + +permissions: + issues: read + contents: read + pull-requests: read + copilot-requests: write + +safe-outputs: + threat-detection: false + add-comment: + max: 1 + add-labels: + allowed: + - bug + - feature + - enhancement + - documentation + - question + - duplicate + - invalid +--- + +# Issue 自动分类与回复 + +分析仓库 ${{ github.repository }} 中新开的 Issue。 + +## 任务 + +1. 阅读 Issue 的标题和正文 +2. 判断类型,添加合适的标签: + - `bug`:程序出错、功能异常 + - `feature`:新功能请求 + - `enhancement`:改进现有功能 + - `documentation`:文档问题 + - `question`:使用疑问 + - `duplicate`:与已有 Issue 重复 + - `invalid`:信息不完整或无效 + +3. 使用用户提问的语言回复一条友好的评论,包含: + - 感谢用户提交 Issue + - 说明你打的标签和原因 + - 如果是 bug,请用户补充:复现步骤、环境信息、错误日志 + - 如果是 question,尝试根据仓库代码和文档给出初步解答 + - 预计处理时间(普通问题 3 个工作日内) \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index ae6e359c..54a2ae3d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,23 @@ +XEngine_Storage V3.23.0.1001 + +ci:删除了多余的动作 +ci:增加新的AI代理问题处理工作流 +更新:vs的版本到2026 +更新:依赖库 +修改:ql扫描出的问题 +修改:匹配了vs的crt 运行时库解决某些问题 +修改:速度限制处理方法 +修正:没有参数检测的问题 + +ci:delete cppcheck.yml and codeql.yml +ci:add agentic workflows for issue triage and auto fix +update:vs to 2026 version +update:depend library +modify:scan code ql problem +modify:match runtime crt for every compiler choice +modify:speed limit handle away +fixed:lost paramter check +====================================================================================== XEngine_Storage V3.22.0.1001 增加:当启动的时候打印占用硬盘空间信息 diff --git a/XEngine_APPClient/APPClient_BTorrent/APPClient_BTorrent.vcxproj b/XEngine_APPClient/APPClient_BTorrent/APPClient_BTorrent.vcxproj index 63dd7e08..e67849b9 100644 --- a/XEngine_APPClient/APPClient_BTorrent/APPClient_BTorrent.vcxproj +++ b/XEngine_APPClient/APPClient_BTorrent/APPClient_BTorrent.vcxproj @@ -29,26 +29,26 @@ Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/XEngine_APPClient/APPClient_Download/APPClient_Download.vcxproj b/XEngine_APPClient/APPClient_Download/APPClient_Download.vcxproj index af9566ea..9badac1d 100644 --- a/XEngine_APPClient/APPClient_Download/APPClient_Download.vcxproj +++ b/XEngine_APPClient/APPClient_Download/APPClient_Download.vcxproj @@ -29,26 +29,26 @@ Application true - v143 + v145 MultiByte Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/XEngine_APPClient/APPClient_RestApi/APPClient_RestApi.vcxproj b/XEngine_APPClient/APPClient_RestApi/APPClient_RestApi.vcxproj index f44b5105..568c41b7 100644 --- a/XEngine_APPClient/APPClient_RestApi/APPClient_RestApi.vcxproj +++ b/XEngine_APPClient/APPClient_RestApi/APPClient_RestApi.vcxproj @@ -29,26 +29,26 @@ Application true - v143 + v145 MultiByte Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/XEngine_APPClient/APPClient_UPDate/APPClient_UPFile/APPClient_UPFile.vcxproj b/XEngine_APPClient/APPClient_UPDate/APPClient_UPFile/APPClient_UPFile.vcxproj index 4f49951e..6c0cd8e2 100644 --- a/XEngine_APPClient/APPClient_UPDate/APPClient_UPFile/APPClient_UPFile.vcxproj +++ b/XEngine_APPClient/APPClient_UPDate/APPClient_UPFile/APPClient_UPFile.vcxproj @@ -29,26 +29,26 @@ Application true - v143 + v145 MultiByte Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/XEngine_APPClient/APPClient_UPDate/APPClient_UPMaker/APPClient_UPMaker.vcxproj b/XEngine_APPClient/APPClient_UPDate/APPClient_UPMaker/APPClient_UPMaker.vcxproj index 8d867ef3..6bd108e0 100644 --- a/XEngine_APPClient/APPClient_UPDate/APPClient_UPMaker/APPClient_UPMaker.vcxproj +++ b/XEngine_APPClient/APPClient_UPDate/APPClient_UPMaker/APPClient_UPMaker.vcxproj @@ -29,26 +29,26 @@ Application true - v143 + v145 MultiByte Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/XEngine_APPClient/APPClient_UPDownload/APPClient_UPDownload.vcxproj b/XEngine_APPClient/APPClient_UPDownload/APPClient_UPDownload.vcxproj index 48174e24..7df92cae 100644 --- a/XEngine_APPClient/APPClient_UPDownload/APPClient_UPDownload.vcxproj +++ b/XEngine_APPClient/APPClient_UPDownload/APPClient_UPDownload.vcxproj @@ -29,26 +29,26 @@ Application true - v143 + v145 MultiByte Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/XEngine_APPClient/VSClean.bat b/XEngine_APPClient/VSClean.bat new file mode 100644 index 00000000..1ca651dd --- /dev/null +++ b/XEngine_APPClient/VSClean.bat @@ -0,0 +1,11 @@ +@echo Off +del /s /a *.ncb *.so *.o *.db +FOR /R . %%d IN (.) DO rd /s /q "%%d\Debug" 2>nul +FOR /R . %%d IN (.) DO rd /s /q "%%d\Release" 2>nul +FOR /R . %%d IN (.) DO rd /s /q "%%d\x64" 2>nul +FOR /R . %%d IN (.) DO rd /s /q "%%d\Debug" 2>nul +FOR /R . %%d IN (.) DO rd /s /q "%%d\ipch" 2>nul +FOR /R . %%d IN (.) DO rd /s /q "%%d\ARM64" 2>nul + +rem If the Properties directory is empty, remove it +FOR /R . %%d in (.) do rd /q "%%d\Properties" 2> nul diff --git a/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json b/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json index 85c43b21..fa912628 100644 --- a/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json +++ b/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json @@ -1,5 +1,6 @@ { "XVer":[ + "3.23.0.1001 Build20260807", "3.22.0.1001 Build20260427", "3.21.0.1001 Build20250923", "3.20.0.1001 Build20250620", diff --git a/XEngine_Source/Makefile b/XEngine_Source/Makefile index 42c27110..13330016 100644 --- a/XEngine_Source/Makefile +++ b/XEngine_Source/Makefile @@ -1,6 +1,7 @@ RELEASE = 0 UNICODE = 0 PLATFORM=linux +BUILDTYPE=0 FLAGS= THIRD_JSONCPP_PATH = ./XEngine_DependLibrary/XEngine_Module/jsoncpp @@ -23,9 +24,22 @@ else ifeq ($(PLATFORM),mac) FILEEXT = dylib endif -XENGINE_MODULES = libjsoncpp.so libtinyxml2.so libXEngine_InfoReport.so libXEngine_Verification.so \ - libStorageModule_Database.so libStorageModule_Config.so libStorageModule_Session.so libStorageModule_APIHelp.so libStorageModule_Protocol.so libStorageModule_BTorrent.so \ - XEngine_StorageApp.exe +# 第三方库模块 +MODULES_THIRD = libjsoncpp.so libtinyxml2.so libXEngine_InfoReport.so libXEngine_Verification.so + +# 业务模块 + 应用 +MODULES_STORAGE = libStorageModule_Database.so libStorageModule_Config.so libStorageModule_Session.so \ + libStorageModule_APIHelp.so libStorageModule_Protocol.so libStorageModule_BTorrent.so \ + XEngine_StorageApp.exe + +# 根据 BUILDTYPE 决定编译目标 +ifeq ($(BUILDTYPE),0) + XENGINE_MODULES = $(MODULES_THIRD) $(MODULES_STORAGE) +else ifeq ($(BUILDTYPE),1) + XENGINE_MODULES = $(MODULES_THIRD) +else ifeq ($(BUILDTYPE),2) + XENGINE_MODULES = $(MODULES_STORAGE) +endif .PHONY:MakeAll MakeAll:$(XENGINE_MODULES) @@ -69,4 +83,4 @@ libStorageModule_BTorrent.so: make -C $(BASE_BTORRENT_PATH) PLATFORM=$(PLATFORM) UNICODE=$(UNICODE) RELEASE=$(RELEASE) $(FLAGS) XEngine_StorageApp.exe: - make -C $(APPSERVICE_STORAGE_PATH) PLATFORM=$(PLATFORM) UNICODE=$(UNICODE) RELEASE=$(RELEASE) $(FLAGS) + make -C $(APPSERVICE_STORAGE_PATH) PLATFORM=$(PLATFORM) UNICODE=$(UNICODE) RELEASE=$(RELEASE) $(FLAGS) \ No newline at end of file diff --git a/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj b/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj index 3df46e65..d6a482e8 100644 --- a/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj +++ b/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj @@ -37,39 +37,39 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -154,7 +154,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) @@ -209,7 +209,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) diff --git a/XEngine_Source/StorageModule_BTorrent/BTorrent_Creator/BTorrent_Creator.cpp b/XEngine_Source/StorageModule_BTorrent/BTorrent_Creator/BTorrent_Creator.cpp index 30c1fa36..c52ed7dd 100644 --- a/XEngine_Source/StorageModule_BTorrent/BTorrent_Creator/BTorrent_Creator.cpp +++ b/XEngine_Source/StorageModule_BTorrent/BTorrent_Creator/BTorrent_Creator.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "BTorrent_Creator.h" +#include /******************************************************************** // Created: 2023/02/02 16:26:25 // File Name: D:\XEngine\XEngine_SourceCode\XEngine_NetHelp\NetHelp_BTorrent\BTorrent_Creator\BTorrent_Creator.cpp @@ -49,7 +50,7 @@ XHANDLE CBTorrent_Creator::BTorrent_Creator_Init(LPCXSTR lpszBTPath, int nPieceS return NULL; } //申请空间 - BTORRENT_CREATORINFO* pSt_BTCreator = new BTORRENT_CREATORINFO; + BTORRENT_CREATORINFO* pSt_BTCreator = new(std::nothrow) BTORRENT_CREATORINFO; if (NULL == pSt_BTCreator) { BTDload_IsErrorOccur = true; @@ -58,11 +59,15 @@ XHANDLE CBTorrent_Creator::BTorrent_Creator_Init(LPCXSTR lpszBTPath, int nPieceS } *pSt_BTCreator = {}; //申请内存 - pSt_BTCreator->pStl_ListNode = new list; - pSt_BTCreator->pStl_ListSeeds = new list; - pSt_BTCreator->pStl_ListTracker = new list; + pSt_BTCreator->pStl_ListNode = new(std::nothrow) list; + pSt_BTCreator->pStl_ListSeeds = new(std::nothrow) list; + pSt_BTCreator->pStl_ListTracker = new(std::nothrow) list; if ((NULL == pSt_BTCreator->pStl_ListNode) || (NULL == pSt_BTCreator->pStl_ListSeeds) || (NULL == pSt_BTCreator->pStl_ListTracker)) { + delete pSt_BTCreator->pStl_ListNode; + delete pSt_BTCreator->pStl_ListSeeds; + delete pSt_BTCreator->pStl_ListTracker; + delete pSt_BTCreator; BTDload_IsErrorOccur = true; BTDload_dwErrorCode = ERROR_STORAGE_MODULE_BTORRENT_CREATOR_INIT_MALLOC; return NULL; diff --git a/XEngine_Source/StorageModule_BTorrent/BTorrent_DLoader/BTorrent_DLoader.cpp b/XEngine_Source/StorageModule_BTorrent/BTorrent_DLoader/BTorrent_DLoader.cpp index c74a6803..eba98313 100644 --- a/XEngine_Source/StorageModule_BTorrent/BTorrent_DLoader/BTorrent_DLoader.cpp +++ b/XEngine_Source/StorageModule_BTorrent/BTorrent_DLoader/BTorrent_DLoader.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "BTorrent_DLoader.h" +#include /******************************************************************** // Created: 2023/02/02 16:26:33 // File Name: D:\XEngine\XEngine_SourceCode\XEngine_NetHelp\NetHelp_BTorrent\BTorrent_DLoader\BTorrent_DLoader.cpp @@ -54,7 +55,7 @@ XHANDLE CBTorrent_DLoader::BTorrent_DLoader_Create(LPCXSTR lpszAddr, LPCXSTR lps return NULL; } //申请空间 - BTORRENT_DLOADINFO* pSt_BTDLoader = new BTORRENT_DLOADINFO; + BTORRENT_DLOADINFO* pSt_BTDLoader = new (std::nothrow) BTORRENT_DLOADINFO; if (NULL == pSt_BTDLoader) { BTDload_IsErrorOccur = true; @@ -171,7 +172,6 @@ bool CBTorrent_DLoader::BTorrent_DLoader_Query(XHANDLE xhToken, ENUM_BTORRENT_EV //是否接受到错误的信号 if (lt::alert_cast(stl_ListElement)) { - //std::cout << stl_ListElement->message() << std::endl; *((*pppenEventList)[i]) = ENUM_BTORRENT_EVENT_TYPE_ERROR; } //接受到临时文件保存请求,需要保存他 @@ -191,7 +191,6 @@ bool CBTorrent_DLoader::BTorrent_DLoader_Query(XHANDLE xhToken, ENUM_BTORRENT_EV //UPNP信息 if (lt::alert_cast(stl_ListElement)) { - //stl_ListElement->message().c_str(); *((*pppenEventList)[i]) = ENUM_BTORRENT_EVENT_TYPE_UPNPERROR; } else if (lt::alert_cast(stl_ListElement)) diff --git a/XEngine_Source/StorageModule_BTorrent/BTorrent_Parse/BTorrent_Parse.cpp b/XEngine_Source/StorageModule_BTorrent/BTorrent_Parse/BTorrent_Parse.cpp index 694f242f..a31d4d58 100644 --- a/XEngine_Source/StorageModule_BTorrent/BTorrent_Parse/BTorrent_Parse.cpp +++ b/XEngine_Source/StorageModule_BTorrent/BTorrent_Parse/BTorrent_Parse.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "BTorrent_Parse.h" +#include /******************************************************************** // Created: 2023/02/02 16:26:41 // File Name: D:\XEngine\XEngine_SourceCode\XEngine_NetHelp\NetHelp_BTorrent\BTorrent_Parse\BTorrent_Parse.cpp @@ -44,7 +45,7 @@ XHANDLE CBTorrent_Parse::BTorrent_Parse_Init(LPCXSTR lpszAddr) return NULL; } //申请空间 - BTORRENT_PARSEINFO* pSt_BTParse = new BTORRENT_PARSEINFO; + BTORRENT_PARSEINFO* pSt_BTParse = new (std::nothrow) BTORRENT_PARSEINFO; if (NULL == pSt_BTParse) { BTDload_IsErrorOccur = true; diff --git a/XEngine_Source/StorageModule_BTorrent/StorageModule_BTorrent.vcxproj b/XEngine_Source/StorageModule_BTorrent/StorageModule_BTorrent.vcxproj index 1f689876..82ec1f75 100644 --- a/XEngine_Source/StorageModule_BTorrent/StorageModule_BTorrent.vcxproj +++ b/XEngine_Source/StorageModule_BTorrent/StorageModule_BTorrent.vcxproj @@ -37,39 +37,39 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -148,6 +148,7 @@ Use pch.h /utf-8 %(AdditionalOptions) + MultiThreadedDLL Windows @@ -201,6 +202,7 @@ Use pch.h /utf-8 %(AdditionalOptions) + MultiThreadedDLL Windows diff --git a/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp b/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp index 784013bf..2f3ec423 100644 --- a/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp +++ b/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp @@ -42,18 +42,23 @@ CConfig_Json::~CConfig_Json() *********************************************************************/ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG* pSt_ServerConfig) { + // 初始化错误状态;函数开始默认“无错误”,出现任何失败分支时再设置错误码并返回。 Config_IsErrorOccur = false; + // 参数有效性检查:配置文件路径与输出结构体指针都不允许为空。 if ((NULL == lpszConfigFile) || (NULL == pSt_ServerConfig)) { Config_IsErrorOccur = true; Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_PARAMENT; return false; } + + // JSON 解析相关对象:错误信息、根节点、Reader 构造器。 JSONCPP_STRING st_JsonError; Json::Value st_JsonRoot; Json::CharReaderBuilder st_JsonBuilder; + // 以二进制只读方式打开配置文件;打开失败视为参数/路径错误。 FILE* pSt_File = _xtfopen(lpszConfigFile, _X("rb")); if (NULL == pSt_File) { @@ -61,10 +66,13 @@ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_PARAMENT; return false; } + + // 读取文件内容到固定缓冲区,并在读取后立即关闭文件句柄。 XCHAR tszMsgBuffer[8192] = {}; size_t nRet = fread(tszMsgBuffer, 1, sizeof(tszMsgBuffer), pSt_File); fclose(pSt_File); + // 执行 JSON 反序列化;解析失败时设置解析错误码并返回。 std::unique_ptr const pSt_JsonReader(st_JsonBuilder.newCharReader()); if (!pSt_JsonReader->parse(tszMsgBuffer, tszMsgBuffer + nRet, &st_JsonRoot, &st_JsonError)) { @@ -72,6 +80,8 @@ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_PARSE; return false; } + + // 将 JSON 节点字段映射到服务配置结构体。 _tcsxcpy(pSt_ServerConfig->tszIPAddr, st_JsonRoot["tszIPAddr"].asCString()); pSt_ServerConfig->bDeamon = st_JsonRoot["bDeamon"].asInt(); pSt_ServerConfig->nCenterPort = st_JsonRoot["nCenterPort"].asInt(); diff --git a/XEngine_Source/StorageModule_Config/StorageModule_Config.vcxproj b/XEngine_Source/StorageModule_Config/StorageModule_Config.vcxproj index 6d143e70..8a137bd5 100644 --- a/XEngine_Source/StorageModule_Config/StorageModule_Config.vcxproj +++ b/XEngine_Source/StorageModule_Config/StorageModule_Config.vcxproj @@ -37,39 +37,39 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -149,7 +149,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) @@ -204,7 +204,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL /utf-8 %(AdditionalOptions) diff --git a/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj b/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj index 55bc9246..989dd4be 100644 --- a/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj +++ b/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj @@ -37,39 +37,39 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -154,6 +154,7 @@ Use pch.h /utf-8 %(AdditionalOptions) + MultiThreadedDLL Windows @@ -207,6 +208,7 @@ Use pch.h /utf-8 %(AdditionalOptions) + MultiThreadedDLL Windows diff --git a/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj b/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj index 45bd840d..6490e353 100644 --- a/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj +++ b/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj @@ -37,39 +37,39 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -155,7 +155,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) @@ -210,7 +210,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL /utf-8 %(AdditionalOptions) diff --git a/XEngine_Source/StorageModule_Session/Session_Stroage/Session_DLStroage.cpp b/XEngine_Source/StorageModule_Session/Session_Stroage/Session_DLStroage.cpp index 957c9c52..349635ac 100644 --- a/XEngine_Source/StorageModule_Session/Session_Stroage/Session_DLStroage.cpp +++ b/XEngine_Source/StorageModule_Session/Session_Stroage/Session_DLStroage.cpp @@ -138,13 +138,26 @@ bool CSession_DLStroage::Session_DLStroage_Insert(LPCXSTR lpszClientAddr, LPCXST } st_Locker.unlock_shared(); - SESSION_STORAGEINFO st_Client; + SESSION_STORAGEINFO st_Client = {}; + //先打开文件,再基于已打开句柄获取文件属性,避免TOCTOU + st_Client.pSt_File = _xtfopen(lpszFileDir, _X("rb")); + if (NULL == st_Client.pSt_File) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_OPENFILE; + return false; + } +#ifdef _MSC_BUILD struct _xtstat st_FStat; - - memset(&st_Client, '\0', sizeof(SESSION_STORAGEINFO)); - int nRet = _xtstat(lpszFileDir, &st_FStat); + int nRet = _fstat(_fileno(st_Client.pSt_File), &st_FStat); +#else + struct stat st_FStat; + int nRet = fstat(fileno(st_Client.pSt_File), &st_FStat); +#endif if (-1 == nRet) { + fclose(st_Client.pSt_File); + st_Client.pSt_File = NULL; Session_IsErrorOccur = true; Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_OPENFILE; return false; @@ -166,13 +179,6 @@ bool CSession_DLStroage::Session_DLStroage_Insert(LPCXSTR lpszClientAddr, LPCXST st_Client.xhToken = xhToken; } //填充下载信息 - st_Client.pSt_File = _xtfopen(lpszFileDir, _X("rb")); - if (NULL == st_Client.pSt_File) - { - Session_IsErrorOccur = true; - Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_OPENFILE; - return false; - } //是否有范围 if ((nPosStart > 0) || (nPostEnd > 0)) { diff --git a/XEngine_Source/StorageModule_Session/Session_Stroage/Session_UPStroage.cpp b/XEngine_Source/StorageModule_Session/Session_Stroage/Session_UPStroage.cpp index 595601a5..56a47279 100644 --- a/XEngine_Source/StorageModule_Session/Session_Stroage/Session_UPStroage.cpp +++ b/XEngine_Source/StorageModule_Session/Session_Stroage/Session_UPStroage.cpp @@ -151,13 +151,9 @@ bool CSession_UPStroage::Session_UPStroage_Insert(LPCXSTR lpszClientAddr, LPCXST _tcsxcpy(st_Client.st_StorageInfo.tszFileDir, lpszFileDir); _tcsxcpy(st_Client.st_StorageInfo.tszClientAddr, lpszClientAddr); //文件是否存在 - if ((m_bResume) && ((0 != nPosStart) || (0 != nPostEnd)) && (0 == _xtaccess(lpszFileDir, 0))) + if ((m_bResume) && ((0 != nPosStart) || (0 != nPostEnd))) { - struct _xtstat st_FStat; - memset(&st_FStat, '\0', sizeof(struct _xtstat)); - _xtstat(st_Client.st_StorageInfo.tszFileDir, &st_FStat); - st_Client.st_StorageInfo.ullRWLen = st_FStat.st_size; - //追加打开 + //直接打开,避免先检查再使用导致TOCTOU st_Client.st_StorageInfo.pSt_File = _xtfopen(lpszFileDir, _X("rb+")); if (NULL == st_Client.st_StorageInfo.pSt_File) { @@ -165,6 +161,11 @@ bool CSession_UPStroage::Session_UPStroage_Insert(LPCXSTR lpszClientAddr, LPCXST Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_OPENFILE; return false; } + + //基于已打开的文件句柄获取文件长度 + fseek(st_Client.st_StorageInfo.pSt_File, 0, SEEK_END); + st_Client.st_StorageInfo.ullRWLen = ftell(st_Client.st_StorageInfo.pSt_File); + //是不是覆写? if (st_Client.st_StorageInfo.ullRWLen > nPosStart) { @@ -190,9 +191,25 @@ bool CSession_UPStroage::Session_UPStroage_Insert(LPCXSTR lpszClientAddr, LPCXST return false; } - st_Client.st_StorageInfo.pSt_File = _xtfopen(lpszFileDir, _X("wb")); +#ifdef _MSC_BUILD + int nFileHandle = _xtopen(lpszFileDir, _O_WRONLY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE); +#else + int nFileHandle = _xtopen(lpszFileDir, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); +#endif + if (nFileHandle < 0) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_OPENFILE; + return false; + } +#ifdef _MSC_BUILD + st_Client.st_StorageInfo.pSt_File = _fdopen(nFileHandle, "wb"); +#else + st_Client.st_StorageInfo.pSt_File = fdopen(nFileHandle, "wb"); +#endif if (NULL == st_Client.st_StorageInfo.pSt_File) { + _close(nFileHandle); Session_IsErrorOccur = true; Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_OPENFILE; return false; @@ -508,11 +525,7 @@ bool CSession_UPStroage::Session_UPStroage_MaxConnect(LPCXSTR lpszClientAddr) int nExistNumber = 0; st_Locker.lock_shared(); - unordered_map::iterator stl_MapIterator = stl_MapStroage.find(lpszClientAddr); - if (stl_MapIterator == stl_MapStroage.end()) - { - - } + unordered_map::iterator stl_MapIterator = stl_MapStroage.begin(); for (; stl_MapIterator != stl_MapStroage.end(); stl_MapIterator++) { XCHAR tszIPSource[128]; diff --git a/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj b/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj index 5303126b..80b07130 100644 --- a/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj +++ b/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj @@ -37,39 +37,39 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -154,7 +154,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) @@ -209,7 +209,7 @@ true Use pch.h - MultiThreaded + MultiThreadedDLL /utf-8 %(AdditionalOptions) diff --git a/XEngine_Source/StorageModule_Session/pch.h b/XEngine_Source/StorageModule_Session/pch.h index 4f672e1f..799b6f9c 100644 --- a/XEngine_Source/StorageModule_Session/pch.h +++ b/XEngine_Source/StorageModule_Session/pch.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/XEngine_Source/XEngine_DependLibrary b/XEngine_Source/XEngine_DependLibrary index fa19ea88..dc73149a 160000 --- a/XEngine_Source/XEngine_DependLibrary +++ b/XEngine_Source/XEngine_DependLibrary @@ -1 +1 @@ -Subproject commit fa19ea8880dfa312221b5d17ece40c3bbc0f00cf +Subproject commit dc73149af8aef839085e1f7ad254aea41f0a2f86 diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_Config.cpp b/XEngine_Source/XEngine_StorageApp/StorageApp_Config.cpp index 2ace27dc..46d16532 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_Config.cpp +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_Config.cpp @@ -2,10 +2,14 @@ bool StorageApp_Config_Parament(int argc,char **argv) { + // 默认配置文件路径:基础服务配置、版本配置、负载均衡配置。 + // 当启动参数未指定替代路径时,按以下默认文件进行加载。 LPCXSTR lpszBaseCfg = _X("./XEngine_Config/XEngine_Config.json"); LPCXSTR lpszVerCfg = _X("./XEngine_Config/XEngine_VersionConfig.json"); LPCXSTR lpszLoadCfg = _X("./XEngine_Config/XEngine_LBConfig.json"); + // 重新加载配置前先释放历史配置中可能已分配的动态容器, + // 避免重复加载时发生内存泄漏或悬挂旧数据。 if (NULL != st_ServiceCfg.st_XVer.pStl_ListStorage) { delete st_ServiceCfg.st_XVer.pStl_ListStorage; @@ -27,9 +31,11 @@ bool StorageApp_Config_Parament(int argc,char **argv) st_LoadbalanceCfg.st_LoadBalance.pStl_ListUPLoader = NULL; } + // 清空全局配置结构体,确保后续解析在干净状态下执行。 memset(&st_ServiceCfg, '\0', sizeof(XENGINE_SERVERCONFIG)); memset(&st_LoadbalanceCfg, '\0', sizeof(XENGINE_LBCONFIG)); + // 依次解析三类配置文件;任一步失败都立即返回并输出错误码。 if (!Config_Json_File(lpszBaseCfg, &st_ServiceCfg)) { printf("解析配置文件失败,Config_Json_File:%lX\n",Config_GetLastError()); @@ -53,7 +59,7 @@ bool StorageApp_Config_Parament(int argc,char **argv) StorageApp_Config_ParamentHelp(); return false; } - if (0 == _tcsxicmp("-v", argv[i])) + else if (0 == _tcsxicmp("-v", argv[i])) { string m_StrVersion = st_ServiceCfg.st_XVer.pStl_ListStorage->front(); printf("Version:%s\n", m_StrVersion.c_str()); @@ -61,6 +67,11 @@ bool StorageApp_Config_Parament(int argc,char **argv) } else if (0 == _tcsxicmp("-d", argv[i])) { + if (i + 1 >= argc) + { + printf("Parameter error: lost value\n"); + return false; + } st_ServiceCfg.bDeamon = _ttxoi(argv[++i]); } else if (0 == _tcsxicmp("-r", argv[i])) @@ -77,10 +88,20 @@ bool StorageApp_Config_Parament(int argc,char **argv) } else if (0 == _tcsxicmp("-lt", argv[i])) { + if (i + 1 >= argc) + { + printf("Parameter error: lost value\n"); + return false; + } st_ServiceCfg.st_XLog.nLogType = _ttxoi(argv[++i]); } else if (0 == _tcsxicmp("-l", argv[i])) { + if (i + 1 >= argc) + { + printf("Parameter error: lost value\n"); + return false; + } LPCXSTR lpszLogLevel = argv[++i]; if (0 == _tcsxicmp("debug", lpszLogLevel)) { diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_Download.cpp b/XEngine_Source/XEngine_StorageApp/StorageApp_Download.cpp index 1d49f9f7..d5b8a6b6 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_Download.cpp +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_Download.cpp @@ -88,7 +88,6 @@ void XCALLBACK XEngine_Download_CBSend(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nPLen = XPATH_MAX; int nHttpCode = 0; XCHAR tszProxyStr[XPATH_MAX]; - SESSION_STORAGEINFO st_StorageInfo; memset(tszProxyStr, '\0', XPATH_MAX); memset(&st_StorageInfo, '\0', sizeof(SESSION_STORAGEINFO)); @@ -138,13 +137,11 @@ void XCALLBACK XEngine_Download_CBSend(LPCXSTR lpszClientAddr, XSOCKET hSocket, bool XEngine_Task_HttpDownload(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, int nMsgLen, RFCCOMPONENTS_HTTP_REQPARAM* pSt_HTTPParam, XCHAR** pptszListHdr, int nHdrCount, int nNetType) { int nSDLen = 2048; - int nRVLen = 2048; int nLimit = 0; XHANDLE xhLimit = NULL; __int64x ullCount = 0; //总大小 __int64x ullSize = 0; //需要下载大小 XCHAR tszSDBuffer[2048] = {}; - XCHAR tszRVBuffer[2048] = {}; XCHAR tszFileDir[1024]; RFCCOMPONENTS_HTTP_HDRPARAM st_HDRParam; @@ -263,6 +260,7 @@ bool XEngine_Task_HttpDownload(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, in Cryption_Api_Digest(tszFileDir, tszHashKey, &nHashLen, true, st_ServiceCfg.st_XStorage.nHashMode); BaseLib_String_StrToHex((char*)tszHashKey, nHashLen, tszHashStr); BaseLib_String_GetFileAndPath(tszFileDir, NULL, NULL, NULL, st_HDRParam.tszMimeType); + nLimit = st_ServiceCfg.st_XLimit.bLimitMode ? st_ServiceCfg.st_XLimit.nMaxDNLoader : 0; if (nLimit > 0) { xhLimit = Algorithm_Calculation_Create(); diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h b/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h index 25c2eb0a..bcfe1b24 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h @@ -16,8 +16,10 @@ #include #include #include +#include #include #else +#include #include #include #endif diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp b/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp index fbbd38e4..78206984 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp @@ -182,30 +182,49 @@ void XCALLBACK XEngine_Callback_HBWebdav(LPCXSTR lpszClientAddr, XSOCKET hSocket XEngine_Net_CloseClient(lpszClientAddr, STORAGE_LEAVETYPE_HEARTBEAT, STORAGE_NETTYPE_HTTPWEBDAV); } ////////////////////////////////////////////////////////////////////////// +/* + * 统一处理客户端离线后的资源清理逻辑。 + * 参数说明: + * - lpszClientAddr: 客户端地址(作为会话、心跳、连接资源的主键) + * - nLeaveType: 离线原因(心跳超时/被动断开/主动关闭) + * - nClientType: 客户端类型(上传/下载/中心/WebDAV) + * + * 处理内容: + * 1) 根据离线原因选择是否主动关闭底层连接; + * 2) 清理心跳跟踪; + * 3) 统计并回收会话信息; + * 4) 关闭对应 HTTP 与 SSL 资源; + * 5) 输出统一离线日志(在函数后续分支中完成)。 + */ bool XEngine_Net_CloseClient(LPCXSTR lpszClientAddr, int nLeaveType, int nClientType) { string m_StrLeaveMsg; string m_StrClient; + // 上传客户端离线处理分支 if (STORAGE_NETTYPE_HTTPUPLOADER == nClientType) { m_StrClient = _X("上传客户端"); + // 心跳超时:由服务端主动关闭连接 if (STORAGE_LEAVETYPE_HEARTBEAT == nLeaveType) { m_StrLeaveMsg = _X("心跳超时"); NetCore_TCPXCore_CloseForClientEx(xhNetUPLoader, lpszClientAddr); } + // 被动断开:连接已断开,仅清理心跳记录 else if (STORAGE_LEAVETYPE_BYSELF == nLeaveType) { m_StrLeaveMsg = _X("被动断开"); SocketOpt_HeartBeat_DeleteAddrEx(xhHBUPLoader, lpszClientAddr); } + // 主动关闭:关闭连接并清理心跳记录 else { m_StrLeaveMsg = _X("主动关闭"); NetCore_TCPXCore_CloseForClientEx(xhNetUPLoader, lpszClientAddr); SocketOpt_HeartBeat_DeleteAddrEx(xhHBUPLoader, lpszClientAddr); } + // 关闭上传会话并更新统计信息 Algorithm_Calculation_PassiveClose(Session_UPStroage_GetSpeed(lpszClientAddr), true); Algorithm_Calculation_Close(Session_UPStroage_GetSpeed(lpszClientAddr)); Session_UPStroage_Delete(lpszClientAddr); diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_UPLoader.cpp b/XEngine_Source/XEngine_StorageApp/StorageApp_UPLoader.cpp index 22b4c016..6a269ae4 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_UPLoader.cpp +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_UPLoader.cpp @@ -1,5 +1,6 @@ #include "StorageApp_Hdr.h" + XHTHREAD XCALLBACK XEngine_UPLoader_HTTPThread(XPVOID lParam) { int nThreadPos = *(int*)lParam; @@ -56,10 +57,9 @@ void XCALLBACK XEngine_UPLoader_UPFlow(XHANDLE xhToken, bool bSDFlow, bool bRVFl bool XEngine_Task_HttpUPLoader(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, int nMsgLen, RFCCOMPONENTS_HTTP_REQPARAM* pSt_HTTPParam, XCHAR** pptszListHdr, int nHdrCount, int nNetType) { int nSDLen = 2048; - int nRVLen = 2048; int nLimit = 0; + XHANDLE xhLimit = NULL; XCHAR tszSDBuffer[2048] = {}; - XCHAR tszRVBuffer[2048] = {}; XCHAR tszFileDir[1024] = {}; RFCCOMPONENTS_HTTP_HDRPARAM st_HDRParam = {}; @@ -192,9 +192,28 @@ bool XEngine_Task_HttpUPLoader(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, in return true; } //文件是否可写 - FILE* pSt_File = _xtfopen(tszFileDir, _X("wb")); +#ifdef _MSC_BUILD + int nFileFD = _xtopen(tszFileDir, _O_WRONLY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE); +#else + int nFileFD = _xtopen(tszFileDir, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); +#endif + if (nFileFD < 0) + { + st_HDRParam.bIsClose = true; + st_HDRParam.nHttpCode = 403; + HttpProtocol_Server_SendMsgEx(xhUPHttp, tszSDBuffer, &nSDLen, &st_HDRParam); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("上传客户端:%s,准备上传文件:%s 失败,创建文件失败"), lpszClientAddr, tszFileDir); + return true; + } +#ifdef _MSC_BUILD + FILE* pSt_File = _fdopen(nFileFD, "wb"); +#else + FILE* pSt_File = fdopen(nFileFD, "wb"); +#endif if (NULL == pSt_File) { + _close(nFileFD); st_HDRParam.bIsClose = true; st_HDRParam.nHttpCode = 403; HttpProtocol_Server_SendMsgEx(xhUPHttp, tszSDBuffer, &nSDLen, &st_HDRParam); @@ -272,8 +291,8 @@ bool XEngine_Task_HttpUPLoader(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, in } SystemApi_File_CreateMutilFolder(tszTmpPath); } - XHANDLE xhUPSpeed = NULL; - if (nLimit > 0 || (st_ServiceCfg.st_XLimit.bLimitMode && st_ServiceCfg.st_XLimit.nMaxUPLoader > 0)) + nLimit = st_ServiceCfg.st_XLimit.bLimitMode ? st_ServiceCfg.st_XLimit.nMaxUPLoader : 0; + if (nLimit > 0) { //处理限速情况 XCHAR* ptszIPClient = (XCHAR*)malloc(XPATH_MAX); @@ -290,13 +309,12 @@ bool XEngine_Task_HttpUPLoader(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, in memset(ptszIPClient, '\0', XPATH_MAX); _tcsxcpy(ptszIPClient, lpszClientAddr); - nLimit = nLimit == 0 ? (int)st_ServiceCfg.st_XLimit.nMaxUPLoader : nLimit; - xhUPSpeed = Algorithm_Calculation_Create(); - Algorithm_Calculation_PassiveOPen(xhUPSpeed, XEngine_UPLoader_UPFlow, nLimit, 0, 0, false, ptszIPClient); + xhLimit = Algorithm_Calculation_Create(); + Algorithm_Calculation_PassiveOPen(xhLimit, XEngine_UPLoader_UPFlow, nLimit, 0, 0, false, ptszIPClient); NetCore_TCPXCore_PasueRecvEx(xhNetUPLoader, lpszClientAddr, false); XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("上传客户端:%s,上传限速被启用,文件:%s,限速:%d"), lpszClientAddr, tszFileDir, nLimit); } - if (!Session_UPStroage_Insert(lpszClientAddr, st_StorageBucket.tszBuckKey, tszFileDir, xhUPSpeed, nPosCount, st_StorageBucket.st_PermissionFlags.bRewrite, nLimit, nPosStart, nPosEnd)) + if (!Session_UPStroage_Insert(lpszClientAddr, st_StorageBucket.tszBuckKey, tszFileDir, xhLimit, nPosCount, st_StorageBucket.st_PermissionFlags.bRewrite, nLimit, nPosStart, nPosEnd)) { st_HDRParam.bIsClose = true; st_HDRParam.nHttpCode = 500; diff --git a/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskAction.cpp b/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskAction.cpp index 711e0e02..a3a546ea 100644 --- a/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskAction.cpp +++ b/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskAction.cpp @@ -69,8 +69,13 @@ XHTHREAD Session_Action_Thread() } return 0; } +// Handle action requests from business clients: +// 1) validate service switch and parse request payload, +// 2) resolve bucket routing and local file path, +// 3) execute upload/download action and return unified HTTP result. bool Storage_TaskAction(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, int nMsgLen, RFCCOMPONENTS_HTTP_REQPARAM* pSt_HTTPParam) { + // Fixed-size send/receive buffers used to build HTTP response packets. int nSDLen = 10240; int nRVLen = 10240; XCHAR tszSDBuffer[10240] = {}; @@ -80,9 +85,11 @@ bool Storage_TaskAction(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lps XENGINE_ACTIONINFO st_ActionInfo = {}; RFCCOMPONENTS_HTTP_HDRPARAM st_HDRParam = {}; + // Action API always returns an HTTP body; connection is closed after reply. st_HDRParam.bIsClose = true; st_HDRParam.nHttpCode = 200; + // Global service switch guard: reject all action requests when disabled. if (!st_ServiceCfg.st_XAction.bEnable) { Protocol_StoragePacket_HTTPPacket(tszRVBuffer, &nRVLen, ERROR_STORAGE_PROTOCOL_HTTP_MANAGE_DISABLE, "function is disable"); @@ -91,6 +98,7 @@ bool Storage_TaskAction(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lps XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("业务客户端:%s,处理用户转录动作失败,功能被禁用"), lpszClientAddr); return false; } + // Parse incoming JSON/action payload into normalized action structure. if (!Protocol_StorageParse_Action(lpszMsgBuffer, nMsgLen, &st_ActionInfo)) { Protocol_StoragePacket_HTTPPacket(tszRVBuffer, &nRVLen, ERROR_STORAGE_PROTOCOL_HTTP_MANAGE_PARSE, "parse json is incorrect"); @@ -101,6 +109,7 @@ bool Storage_TaskAction(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lps } XCHAR tszFileName[1024] = {}; XENGINE_STORAGEBUCKET st_StorageBucket = {}; + // Resolve bucket to actual storage node/path from load-balance configuration. if (!APIHelp_Distributed_CTStorage(st_ActionInfo.tszBucketStr, st_LoadbalanceCfg.st_LoadBalance.pStl_ListBucket, &st_StorageBucket)) { Protocol_StoragePacket_HTTPPacket(tszRVBuffer, &nRVLen, ERROR_STORAGE_PROTOCOL_HTTP_MANAGE_NOTFOUND, "bucket name is incorrect"); @@ -109,6 +118,7 @@ bool Storage_TaskAction(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lps XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("业务客户端:%s,处理用户转录动作失败,存储Key解析失败,URL:%s,路径:%s,Bucket:%s"), lpszClientAddr, st_ActionInfo.tszFileUrl, st_ActionInfo.tszFileName, st_ActionInfo.tszBucketStr); return false; } + // Build absolute/local target filename used by subsequent action handlers. _xstprintf(tszFileName, _X("%s/%s"), st_StorageBucket.tszFilePath, st_ActionInfo.tszFileName); if (0 == st_ActionInfo.byType || 1 == st_ActionInfo.byType) diff --git a/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskManage.cpp b/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskManage.cpp index ca5f03c2..ea3d2285 100644 --- a/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskManage.cpp +++ b/XEngine_Source/XEngine_StorageApp/Storage_APPTask/Storage_TaskManage.cpp @@ -30,13 +30,11 @@ bool XEngine_Task_Manage(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lp //查询文件列表 int nMode = 0; int nListCount = 0; - int nMsgLen = 10240; XCHAR tszFileName[XPATH_MAX]; XCHAR tszFileHash[XPATH_MAX]; XCHAR tszBucketKey[128]; XCHAR tszTimeStart[128]; XCHAR tszTimeEnd[128]; - XCHAR tszMsgBuffer[10240]; XSTORAGECORE_DBFILE** ppSt_ListFile; memset(tszFileName, '\0', XPATH_MAX); @@ -44,7 +42,6 @@ bool XEngine_Task_Manage(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lp memset(tszBucketKey, '\0', sizeof(tszBucketKey)); memset(tszTimeStart, '\0', sizeof(tszTimeStart)); memset(tszTimeEnd, '\0', sizeof(tszTimeEnd)); - memset(tszMsgBuffer, '\0', sizeof(tszMsgBuffer)); Protocol_StorageParse_QueryFile(lpszMsgBuffer, tszTimeStart, tszTimeEnd, tszBucketKey, tszFileName, tszFileHash, &nMode); //根据使用模式来操作 @@ -68,8 +65,8 @@ bool XEngine_Task_Manage(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lp XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("业务客户端:%s,请求查询文件失败,没有找到文件,查找文件名:%s,文件HASH:%s!"), lpszClientAddr, tszFileName, tszFileHash); return false; } - Protocol_StoragePacket_QueryFile(tszMsgBuffer, &nMsgLen, &ppSt_ListFile, nListCount, tszTimeStart, tszTimeEnd); - HttpProtocol_Server_SendMsgEx(xhCenterHttp, tszSDBuffer, &nSDLen, &st_HDRParam, tszMsgBuffer, nMsgLen); + Protocol_StoragePacket_QueryFile(tszRVBuffer, &nRVLen, &ppSt_ListFile, nListCount, tszTimeStart, tszTimeEnd); + HttpProtocol_Server_SendMsgEx(xhCenterHttp, tszSDBuffer, &nSDLen, &st_HDRParam, tszRVBuffer, nRVLen); XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, STORAGE_NETTYPE_HTTPCENTER); BaseLib_Memory_Free((XPPPMEM)&ppSt_ListFile, nListCount); XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("业务客户端:%s,请求查询文件列表成功,列表个数:%d"), lpszClientAddr, nListCount); @@ -148,7 +145,7 @@ bool XEngine_Task_Manage(LPCXSTR lpszAPIName, LPCXSTR lpszClientAddr, LPCXSTR lp } else { - int nListCount = 0; + nListCount = 0; XSTORAGECORE_DBFILE** ppSt_ListPacket; APIHelp_Distributed_FileList(&stl_ListFile, &ppSt_ListPacket, &nListCount); Protocol_StoragePacket_QueryFile(tszRVBuffer, &nRVLen, &ppSt_ListPacket, nListCount); diff --git a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp index 64bf695b..28417a8e 100644 --- a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp +++ b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp @@ -145,14 +145,24 @@ LONG WINAPI Coredump_ExceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) return EXCEPTION_EXECUTE_HANDLER; } #endif +// Application entry point. +// Responsibilities in this function include: +// 1) Platform-specific runtime initialization. +// 2) Service/component startup orchestration. +// 3) Main running/dispatch loop and process lifetime control. +// 4) Ordered shutdown and resource cleanup. +// NOTE: Keep behavior changes out of this function unless lifecycle impact is reviewed. int main(int argc, char** argv) { #ifdef _MSC_BUILD + // Windows-specific network stack initialization (required before socket usage). WSADATA st_WSAData; WSAStartup(MAKEWORD(2, 2), &st_WSAData); + // Install unhandled exception filter to persist crash dumps for diagnostics. SetUnhandledExceptionFilter(Coredump_ExceptionFilter); #ifndef _DEBUG + // In release mode, force UTF-8 locale to keep log/output encoding consistent. if (setlocale(LC_ALL, ".UTF8") == NULL) { fprintf(stderr, "Error setting locale.\n"); diff --git a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj index 173d0d39..53fb21cc 100644 --- a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj +++ b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj @@ -37,39 +37,39 @@ Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode Application false - v143 + v145 true Unicode @@ -148,7 +148,7 @@ false WIN32;NDEBUG;_CONSOLE;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true - MultiThreaded + MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) @@ -191,7 +191,7 @@ false NDEBUG;_CONSOLE;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true - MultiThreaded + MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions)