diff --git a/.github/workflows/Alma_build.yml b/.github/workflows/Alma_build.yml index d8f12d2a..66603b6d 100644 --- a/.github/workflows/Alma_build.yml +++ b/.github/workflows/Alma_build.yml @@ -44,16 +44,16 @@ jobs: steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/libxengine path: libxengine - name: sub module checkout (opensource) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/XEngine_OPenSource path: XEngine_Source/XEngine_DependLibrary diff --git a/.github/workflows/Centos_build.yml b/.github/workflows/Centos_build.yml index de3c462a..2308490a 100644 --- a/.github/workflows/Centos_build.yml +++ b/.github/workflows/Centos_build.yml @@ -48,16 +48,16 @@ jobs: steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/libxengine path: libxengine - name: sub module checkout (opensource) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/XEngine_OPenSource path: XEngine_Source/XEngine_DependLibrary diff --git a/.github/workflows/Rocky_build.yml b/.github/workflows/Rocky_build.yml index 1d9b0008..5727b28a 100644 --- a/.github/workflows/Rocky_build.yml +++ b/.github/workflows/Rocky_build.yml @@ -44,18 +44,18 @@ jobs: steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: 'develop' - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/libxengine path: libxengine - name: sub module checkout (opensource) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/XEngine_OPenSource path: XEngine_Source/XEngine_DependLibrary diff --git a/.github/workflows/auto-code.lock.yml b/.github/workflows/auto-code.lock.yml index 919efe73..2069653a 100644 --- a/.github/workflows/auto-code.lock.yml +++ b/.github/workflows/auto-code.lock.yml @@ -1,20 +1,21 @@ -# 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"}]} -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"23b28a35eed881f99b0993f93aa191082f29f11a3e44fdecfd3f57aeba289a57","body_hash":"79b2f3136ee21d4020cafaeb6080eb7afd5497cedeb721755b22c21725ae7a10","compiler_version":"v0.88.7","engine_base_url_customized":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.80"}} +# gh-aw-manifest: {"version":1,"secrets":["GH_AW_CI_TRIGGER_TOKEN","GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"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":"5e508589e03a7757a7e05b26e834292f5445bfb6","version":"v0.88.7"}],"containers":[{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23"},{"image":"ghcr.io/github/github-mcp-server:v1.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"}],"mcp_servers":[{"name":"github","tools":["get_commit","get_file_contents","get_latest_release","get_me","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"safeoutputs","tools":["add_comment","create_pull_request","missing_data","missing_tool","noop"]}]} +# This file was automatically generated by gh-aw (v0.88.7). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ # | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ +# | | | | (_| | __/ | | | |_| | (__ # \_| |_/\__, |\___|_| |_|\__|_|\___| # __/ | -# _ _ |___/ +# _ _ |___/ # | | | | / _| | # | | | | ___ _ __ _ __| |_| | _____ ____ # | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| # \ /\ / (_) | | | | ( | | | | (_) \ 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 @@ -24,37 +25,49 @@ # # # Secrets used: -# - COPILOT_GITHUB_TOKEN # - GH_AW_CI_TRIGGER_TOKEN +# - GH_AW_DEFAULT_OTLP_HEADERS # - 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/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 +# - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 +# - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 +# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 +# - github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 # # Container images used: -# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 -# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f +# - ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 +# - ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699 name: "自动处理 Issue" -"on": +on: issues: types: - - labeled + - labeled permissions: {} concurrency: group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}" + queue: max run-name: "自动处理 Issue" +env: + OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }} + OTEL_SERVICE_NAME: gh-aw.auto-code + OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=%E8%87%AA%E5%8A%A8%E5%A4%84%E7%90%86%20Issue,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=copilot' + OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }} + GH_AW_OTLP_ENDPOINTS: '[{"url":"${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }}","headers":"${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }}"}]' + GH_AW_OTLP_IF_MISSING: ignore + jobs: activation: needs: pre_activation @@ -63,13 +76,23 @@ jobs: permissions: actions: read contents: read + env: + GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: "" comment_repo: "" + daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }} + daily_ai_credits_guardrail_status: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_status || '' }} + daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} + daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} model: ${{ steps.generate_aw_info.outputs.model }} + oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} text: ${{ steps.sanitized.outputs.text }} @@ -77,24 +100,29 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }} + safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} 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" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Mask OTLP telemetry headers + run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - 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_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }} + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_AGENT_VERSION: "1.0.80" + GH_AW_INFO_CLI_VERSION: "v0.88.7" GH_AW_INFO_WORKFLOW_NAME: "自动处理 Issue" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -104,16 +132,79 @@ jobs: GH_AW_INFO_AWF_VERSION: "" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "" + GH_AW_INFO_AGENT_RUNTIME: "" GH_AW_COMPILED_STRICT: "false" + GH_AW_INFO_FEATURES: '{"dangerously-disable-sandbox-agent":true}' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); + const { main } = require(path.join(actionsDir, 'generate_aw_info.cjs')); await main(core, context); + - name: Enforce strict mode policy + if: ${{ vars.GH_AW_POLICY_STRICT == 'true' }} + run: | + echo "::error::GH_AW_POLICY_STRICT=true but this workflow was not compiled in strict mode. Recompile with --strict or strict: true." + exit 1 + - name: Restore daily AIC usage cache + id: restore-daily-aic-cache + if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} + continue-on-error: true + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: agentic-workflow-usage-autocode-${{ github.run_id }} + restore-keys: agentic-workflow-usage-autocode- + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + - name: Restore daily AIC usage cache (artifact fallback) + id: restore-daily-aic-cache-fallback + if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }} + GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'restore_aic_usage_cache_fallback.cjs')); + await main(); + - name: Check daily workflow token guardrail + id: daily-effective-workflow-guardrail + if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_WORKFLOW_ID: "auto-code" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }} + GH_AW_HAS_SLASH_COMMAND: "false" + GH_AW_HAS_LABEL_COMMAND: "false" + GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'check_daily_aic_workflow_guardrail.cjs')); + await main(); + - name: Check for OAuth tokens + id: check-oauth-tokens + run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh" + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - name: Checkout .github and .agents folders - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false sparse-checkout: | @@ -121,18 +212,16 @@ jobs: .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" + GH_AW_AGENT_FOLDERS: ".agents .github" + GH_AW_AGENT_FILES: "AGENTS.md" + 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 @@ -141,141 +230,116 @@ jobs: GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); + const { main } = require(path.join(actionsDir, '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" + GH_AW_COMPILED_VERSION: "v0.88.7" with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); + const { main } = require(path.join(actionsDir, '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" + GH_AW_ALLOWED_DOMAINS: "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,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,s.symcb.com,s.symcd.com,security.ubuntu.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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/compute_text.cjs'); + const { main } = require(path.join(actionsDir, 'compute_text.cjs')); await main(); + - name: Log runtime features + if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} + run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh" - name: Create prompt with built-in context + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_ACTIONS_DIR: ${{ runner.temp }}/gh-aw/actions + GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"file\":\"safe_outputs_create_pull_request.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"github_mcp_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"}]}" + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_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" + GH_AW_PROMPT_CONTENT_0000: "\n" + GH_AW_PROMPT_CONTENT_0001: "\nTools: add_comment, create_pull_request, missing_tool, missing_data, noop\n" + GH_AW_PROMPT_CONTENT_0002: "\n" + GH_AW_PROMPT_CONTENT_0003: "\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_802A9F6A__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_1A3A194A__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_463A214A__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_FF1D34CE__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n\n\n" + GH_AW_PROMPT_CONTENT_0004: "\n" + GH_AW_PROMPT_CONTENT_0005: "{{#runtime-import .github/workflows/auto-code.md}}\n" + with: + script: | + const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(process.env.GH_AW_ACTIONS_DIR + '/create_prompt.cjs'); + await main(core); - 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_PROMPT: ${{ runner.temp }}/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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); + const { main } = require(path.join(actionsDir, '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_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_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_MCP_CLI_SERVERS_LIST: "- `github` — run `github --help` to see available tools\n- `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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - - const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); - + + const substitutePlaceholders = require(path.join(actionsDir, 'substitute_placeholders.cjs')); + // Call the substitution function return await substitutePlaceholders({ file: process.env.GH_AW_PROMPT, substitutions: { + GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, + GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, + GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_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, @@ -285,76 +349,118 @@ jobs: }); - 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" + GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt + 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" + GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" + - name: Stage prompt files for artifact upload + run: | + mkdir -p /tmp/gh-aw/aw-prompts + cp -a "${RUNNER_TEMP}/gh-aw/aw-prompts/." /tmp/gh-aw/aw-prompts/ - name: Upload activation artifact - if: success() + if: success() || failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: activation include-hidden-files: true path: | /tmp/gh-aw/aw_info.json + /tmp/gh-aw/models.json /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw-prompts/prompt-template.txt + /tmp/gh-aw/aw-prompts/prompt-import-tree.json /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/base + /tmp/gh-aw/.github/agents + /tmp/gh-aw/.github/skills if-no-files-found: ignore retention-days: 1 agent: needs: activation + if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' runs-on: ubuntu-latest permissions: contents: read + copilot-requests: write issues: read pull-requests: read + timeout-minutes: 60 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_PR_HEAD_BASE_BRANCH: "" + GH_AW_PR_HEAD_BASE_PR_NUMBER: "" + GH_AW_PR_HEAD_BASE_REF: "" + GH_AW_PR_HEAD_BASE_REPO: "" + GH_AW_PR_HEAD_BASE_SHA: "" + GH_AW_PR_HEAD_REPO: "" + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: autocode outputs: - agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} + agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} + ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} + aic: ${{ steps.parse-mcp-gateway.outputs.aic }} + ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }} checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} has_patch: ${{ steps.collect_output.outputs.has_patch }} - inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }} - mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} + http_400_response_error: ${{ steps.detect-agent-errors.outputs.http_400_response_error || 'false' }} + inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} + invocation_cap_exceeded: ${{ steps.detect-agent-errors.outputs.invocation_cap_exceeded || 'false' }} + max_cache_misses_exceeded: ${{ steps.detect-agent-errors.outputs.max_cache_misses_exceeded || 'false' }} + mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} + missing_model_pricing_error: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_error || 'false' }} + missing_model_pricing_model_name: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_model_name || '' }} model: ${{ needs.activation.outputs.model }} - model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} + model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} + shell_expansion_guard_rejected: ${{ steps.detect-agent-errors.outputs.shell_expansion_guard_rejected || 'false' }} + unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths + env: + GH_AW_RUNNER_TOOL_CACHE: ${{ runner.tool_cache }} run: | + if [ -z "${RUNNER_TOOL_CACHE:-}" ]; then + echo "RUNNER_TOOL_CACHE=${GH_AW_RUNNER_TOOL_CACHE}" >> "$GITHUB_ENV" + fi { 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: Mask OTLP telemetry headers + run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" + - name: Check OTLP telemetry configuration + run: bash "${RUNNER_TEMP}/gh-aw/actions/check_otlp_default_credentials.sh" - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Create gh-aw temp directory @@ -363,37 +469,51 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" env: GH_TOKEN: ${{ github.token }} + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: activation + path: /tmp/gh-aw - name: Configure Git credentials env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_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" + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - name: Checkout PR branch id: checkout-pr if: | - github.event.pull_request || github.event.issue.pull_request + github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); + const { main } = require(path.join(actionsDir, '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 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" env: GH_HOST: github.com + GH_AW_COMPILED_VERSION: v0.88.7 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + GH_AW_GITHUB_MIN_INTEGRITY: 'approved' + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const determineAutomaticLockdown = require(path.join(actionsDir, 'determine_automatic_lockdown.cjs')); + await determineAutomaticLockdown(github, context, core); - name: Parse integrity filter lists id: parse-guard-vars env: @@ -401,27 +521,42 @@ jobs: 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" + GH_AW_AGENT_FOLDERS: ".agents .github" + GH_AW_AGENT_FILES: "AGENTS.md" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" + - name: Restore inline sub-agents from activation artifact + env: + GH_AW_SUB_AGENT_DIR: ".github/agents" + GH_AW_SUB_AGENT_EXT: ".agent.md" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" + - name: Restore inline skills from activation artifact + env: + GH_AW_SKILL_DIR: ".github/skills" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f - - name: Generate Safe Outputs Config + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699 + - name: Prepare Safe Outputs Directories 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 Config + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw" + GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}" + GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"develop\",\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"allowed\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'create_files.cjs')); + await main(); - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -444,9 +579,18 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "comment_id": { + "optionalPositiveInteger": true + }, "item_number": { "issueOrPRNumber": true }, + "pr": { + "issueOrPRNumber": true + }, + "pr_number": { + "issueOrPRNumber": true + }, "reply_to_id": { "type": "string", "maxLength": 256 @@ -454,6 +598,16 @@ jobs: "repo": { "type": "string", "maxLength": 256 + }, + "target": { + "type": "string", + "enum": [ + "status" + ] + }, + "temporary_id": { + "type": "string", + "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$" } } }, @@ -477,6 +631,12 @@ jobs: "sanitize": true, "maxLength": 256 }, + "dependencies": { + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 256 + }, "draft": { "type": "boolean" }, @@ -490,6 +650,18 @@ jobs: "type": "string", "maxLength": 256 }, + "stack_position": { + "optionalPositiveInteger": true + }, + "stack_root": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "temporary_id": { + "type": "string", + "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$" + }, "title": { "required": true, "type": "string", @@ -575,90 +747,68 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); + const { main } = require(path.join(actionsDir, '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_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} + GH_AW_SINK_VISIBILITY: ${{ steps.determine-automatic-lockdown.outputs.visibility }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -eo pipefail mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" - + if [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -r "${GITHUB_EVENT_PATH}" ]; then + GH_AW_SAFEOUTPUTS_EVENT_PATH="${RUNNER_TEMP}/gh-aw/safeoutputs/github_event.json" + cp "${GITHUB_EVENT_PATH}" "${GH_AW_SAFEOUTPUTS_EVENT_PATH}" + export GITHUB_EVENT_PATH="${GH_AW_SAFEOUTPUTS_EVENT_PATH}" + fi + # 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 + MCP_GATEWAY_AGENT_ID=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_AGENT_ID}" + export MCP_GATEWAY_AGENT_ID export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:ro,${RUNNER_TEMP}/gh-aw/safeoutputs:rw,/opt:ro,/tmp:rw,/usr/bin/gh:ro" + export GH_AW_PR_HEAD_BASE_BRANCH="${GH_AW_PR_HEAD_BASE_BRANCH:-}" + export GH_AW_PR_HEAD_BASE_SHA="${GH_AW_PR_HEAD_BASE_SHA:-}" + export GH_AW_PR_HEAD_BASE_REPO="${GH_AW_PR_HEAD_BASE_REPO:-}" + export GH_AW_PR_HEAD_BASE_PR_NUMBER="${GH_AW_PR_HEAD_BASE_PR_NUMBER:-}" + export GH_AW_PR_HEAD_BASE_REF="${GH_AW_PR_HEAD_BASE_REF:-}" + export GH_AW_PR_HEAD_REPO="${GH_AW_PR_HEAD_REPO:-}" 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 + source "${RUNNER_TEMP}/gh-aw/actions/resolve_docker_socket_gid.sh" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_AGENT_ID -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_PR_HEAD_BASE_BRANCH -e GH_AW_PR_HEAD_BASE_SHA -e GH_AW_PR_HEAD_BASE_REPO -e GH_AW_PR_HEAD_BASE_PR_NUMBER -e GH_AW_PR_HEAD_BASE_REF -e GH_AW_PR_HEAD_REPO -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GH_AW_SINK_VISIBILITY -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e RUNNER_TOOL_CACHE -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.4.18' + + mkdir -p "$HOME/.copilot" GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_01a7963a8b9bf043_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_c70074f9d4c4762c_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", + "container": "ghcr.io/github/github-mcp-server:v1.11.0", "env": { - "GITHUB_HOST": "\${GITHUB_SERVER_URL}", - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_FEATURES": "fields_param", + "GITHUB_HOST": "${GITHUB_SERVER_URL}", + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" }, @@ -666,23 +816,50 @@ jobs: "allow-only": { "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, - "min-integrity": "none", + "min-integrity": "approved", "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}" + "type": "stdio", + "container": "ghcr.io/github/gh-aw-node", + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["-w", "\${GITHUB_WORKSPACE}"], + "entrypoint": "sh", + "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"], + "env": { + "DEBUG": "*", + "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", + "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", + "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", + "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", + "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", + "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", + "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", + "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", + "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", + "GH_AW_PR_HEAD_BASE_BRANCH": "\${GH_AW_PR_HEAD_BASE_BRANCH}", + "GH_AW_PR_HEAD_BASE_SHA": "\${GH_AW_PR_HEAD_BASE_SHA}", + "GH_AW_PR_HEAD_BASE_REPO": "\${GH_AW_PR_HEAD_BASE_REPO}", + "GH_AW_PR_HEAD_BASE_PR_NUMBER": "\${GH_AW_PR_HEAD_BASE_PR_NUMBER}", + "GH_AW_PR_HEAD_BASE_REF": "\${GH_AW_PR_HEAD_BASE_REF}", + "GH_AW_PR_HEAD_REPO": "\${GH_AW_PR_HEAD_REPO}", + "GITHUB_EVENT_NAME": "\${GITHUB_EVENT_NAME}", + "GITHUB_EVENT_PATH": "\${GITHUB_EVENT_PATH}", + "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", + "GITHUB_SHA": "\${GITHUB_SHA}", + "GITHUB_TOKEN": "\${GITHUB_TOKEN}", + "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", + "RUNNER_TEMP": "\${RUNNER_TEMP}" }, "guard-policies": { "write-sink": { "accept": [ "*" - ] + ], + "sink-visibility": "${GH_AW_SINK_VISIBILITY}" } } } @@ -690,24 +867,32 @@ jobs: "gateway": { "port": $MCP_GATEWAY_PORT, "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + "agentId": "${MCP_GATEWAY_AGENT_ID}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}", + "startupTimeout": 120, + "opentelemetry": { + "endpoint": "${OTEL_EXPORTER_OTLP_ENDPOINT}", + "traceId": "${GITHUB_AW_OTEL_TRACE_ID}", + "spanId": "${GITHUB_AW_OTEL_PARENT_SPAN_ID}" + } } } - GH_AW_MCP_CONFIG_01a7963a8b9bf043_EOF + GH_AW_MCP_CONFIG_c70074f9d4c4762c_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_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} 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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io); - const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); + const { main } = require(path.join(actionsDir, 'mount_mcp_as_cli.cjs')); await main(); - name: Clean credentials continue-on-error: true @@ -732,9 +917,11 @@ jobs: # --allow-tool shell(git rm:*) # --allow-tool shell(git status) # --allow-tool shell(git switch:*) + # --allow-tool shell(github:*) # --allow-tool shell(grep) # --allow-tool shell(head) # --allow-tool shell(ls) + # --allow-tool shell(printf) # --allow-tool shell(pwd) # --allow-tool shell(safeoutputs:*) # --allow-tool shell(sort) @@ -743,29 +930,37 @@ jobs: # --allow-tool shell(wc) # --allow-tool shell(yq) # --allow-tool write - timeout-minutes: 20 + timeout-minutes: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md (umask 177 && touch /tmp/gh-aw/agent-stdio.log) + trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; rm -f "$HOME/.copilot/settings.json"; if [ "$gh_aw_exit_code" -ne 0 ]; then echo "::error::Agent execution exited with code $gh_aw_exit_code"; fi' EXIT + mkdir -p "$HOME/.copilot" + printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" + export XDG_CONFIG_HOME="$HOME" + export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" 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 + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs" 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(github:*)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(ls)' --allow-tool 'shell(printf)' --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 + COPILOT_MODEL: ${{ vars.MODEL_AI_AGENT_NAME }} + COPILOT_PROVIDER_BASE_URL: ${{ vars.MODEL_AI_AGENT_ADDR }} + COPILOT_PROVIDER_BEARER_TOKEN: ${{ secrets.OPENAI_API_KEY }} + COPILOT_PROVIDER_TYPE: ${{ vars.MODEL_AI_AGENT_TYPE }} + COPILOT_PROVIDER_WIRE_API: ${{ vars.MODEL_AI_AGENT_API }} + GH_AW_LLM_PROVIDER: github + GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} + GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.71.5 + GH_AW_TIMEOUT_MINUTES: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} + GH_AW_VERSION: v0.88.7 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -775,25 +970,31 @@ jobs: 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 + RUNNER_TEMP: ${{ runner.temp }} + S2STOKENS: true + TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} + - name: Detect agent errors if: always() + id: detect-agent-errors continue-on-error: true - run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs" + env: + GH_AW_AGENTIC_EXECUTION_OUTCOME: ${{ steps.agentic_execution.outcome }} + GH_AW_ENGINE_STEP_TIMEOUT_MINUTES: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'detect_agent_errors.cjs')); + await main(); - name: Configure Git credentials env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_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" + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - name: Copy Copilot session state files to logs if: always() continue-on-error: true @@ -803,7 +1004,7 @@ jobs: 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 }} + MCP_GATEWAY_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} run: | bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" @@ -812,13 +1013,14 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); + const { main } = require(path.join(actionsDir, '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 }} + GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' 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 }} @@ -839,25 +1041,30 @@ jobs: 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" + GH_AW_ALLOWED_DOMAINS: "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,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,s.symcb.com,s.symcd.com,security.ubuntu.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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); + const { main } = require(path.join(actionsDir, '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/ + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); + const { main } = require(path.join(actionsDir, 'parse_copilot_log.cjs')); await main(); - name: Parse MCP Gateway logs for step summary if: always() @@ -865,16 +1072,41 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); + const { main } = require(path.join(actionsDir, 'parse_mcp_gateway_log.cjs')); await main(); + - name: Generate observability summary + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'generate_observability_summary.cjs')); + await main(core); - 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 + # Small dedicated copy of the agent output so safe-output processing + # survives a failed or timed-out upload of the larger agent artifact + - name: Upload agent output fallback artifact + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: agent-output-fallback + path: | + /tmp/gh-aw/agent_output.json + /tmp/gh-aw/safeoutputs.jsonl + if-no-files-found: ignore - name: Upload agent artifacts if: always() continue-on-error: true @@ -890,8 +1122,9 @@ jobs: !/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/otel.jsonl + /tmp/gh-aw/otlp-export-errors.jsonl /tmp/gh-aw/safeoutputs.jsonl /tmp/gh-aw/agent_output.json /tmp/gh-aw/aw-*.patch @@ -905,16 +1138,20 @@ jobs: - safe_outputs if: > always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || - needs.activation.outputs.stale_lock_file_failed == 'true') + needs.activation.outputs.oauth_token_check_failed == 'true' || needs.activation.outputs.stale_lock_file_failed == 'true' || + needs.activation.outputs.daily_ai_credits_exceeded == 'true') runs-on: ubuntu-slim permissions: + actions: read contents: write - discussions: write issues: write pull-requests: write concurrency: group: "gh-aw-conclusion-auto-code" cancel-in-progress: false + queue: max + env: + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} outputs: incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} noop_message: ${{ steps.noop.outputs.noop_message }} @@ -923,21 +1160,24 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent + pattern: "{agent,agent-output-fallback}" + merge-multiple: true path: /tmp/gh-aw/ - name: Setup agent output environment variable id: setup-agent-output-env @@ -945,7 +1185,83 @@ jobs: 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" + if [ -f "/tmp/gh-aw/agent_output.json" ]; then + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + fi + - name: Download Safe Outputs Items Manifest + id: download-safe-outputs-manifest + if: always() + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: safe-outputs-items + merge-multiple: true + path: /tmp/gh-aw/ + - name: Collect usage artifact files + if: always() + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/collect_usage_artifact_files.sh" + - name: Upload usage artifact + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: usage + path: | + /tmp/gh-aw/usage/aw_info.json + /tmp/gh-aw/usage/aw-info.jsonl + /tmp/gh-aw/usage/agent_usage.json + /tmp/gh-aw/usage/agent_usage.jsonl + /tmp/gh-aw/usage/detection_usage.jsonl + /tmp/gh-aw/usage/evals.jsonl + /tmp/gh-aw/usage/graders/grader_manifest.json + /tmp/gh-aw/usage/graders/grader_results.json + /tmp/gh-aw/usage/github_rate_limits.jsonl + /tmp/gh-aw/usage/agent/token_usage.jsonl + /tmp/gh-aw/usage/detection/token_usage.jsonl + /tmp/gh-aw/usage/activity/summary.json + if-no-files-found: ignore + - name: Restore daily AIC usage cache + id: restore-daily-aic-cache-conclusion + if: always() + continue-on-error: true + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: agentic-workflow-usage-autocode-${{ github.run_id }} + restore-keys: agentic-workflow-usage-autocode- + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + - name: Write daily AIC usage cache entry + id: write-daily-aic-cache + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{ github.token }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context); + const { main } = require(path.join(actionsDir, 'write_daily_aic_usage_cache.cjs')); + await main(); + - name: Save daily AIC usage cache + id: save-daily-aic-cache + if: always() + continue-on-error: true + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: agentic-workflow-usage-autocode-${{ github.run_id }} + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + - name: Upload daily AIC usage cache artifact + id: upload-daily-aic-cache + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: aic-usage-cache + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + if-no-files-found: ignore + retention-days: 7 - name: Process no-op messages id: noop uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -953,15 +1269,21 @@ jobs: 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_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/auto-code.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_NOOP_REPORT_AS_ISSUE: "true" + GH_AW_NOOP_REPORT_AS_ISSUE: "false" + GH_AW_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} + GH_AW_WORKFLOW_ID: "auto-code" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); + const { main } = require(path.join(actionsDir, 'handle_noop_message.cjs')); await main(); - name: Record missing tool id: missing_tool @@ -970,12 +1292,15 @@ jobs: 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" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/auto-code.md" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); + const { main } = require(path.join(actionsDir, 'missing_tool.cjs')); await main(); - name: Record incomplete id: report_incomplete @@ -984,12 +1309,15 @@ jobs: 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" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/auto-code.md" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); + const { main } = require(path.join(actionsDir, 'report_incomplete_handler.cjs')); await main(); - name: Handle agent failure id: handle_agent_failure @@ -998,51 +1326,92 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/auto-code.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "auto-code" - GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" + GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "0" GH_AW_ENGINE_ID: "copilot" GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} + GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }} + GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }} + GH_AW_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} + GH_AW_HTTP_400_RESPONSE_ERROR: ${{ needs.agent.outputs.http_400_response_error }} + GH_AW_MAX_CACHE_MISSES_EXCEEDED: ${{ needs.agent.outputs.max_cache_misses_exceeded }} + GH_AW_MISSING_MODEL_PRICING_ERROR: ${{ needs.agent.outputs.missing_model_pricing_error }} + GH_AW_MISSING_MODEL_PRICING_MODEL_NAME: ${{ needs.agent.outputs.missing_model_pricing_model_name }} + GH_AW_SHELL_EXPANSION_GUARD_REJECTED: ${{ needs.agent.outputs.shell_expansion_guard_rejected }} 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_OAUTH_TOKEN_CHECK_FAILED: ${{ needs.activation.outputs.oauth_token_check_failed }} GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} + GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} + GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }} + GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }} GH_AW_GROUP_REPORTS: "false" GH_AW_FAILURE_REPORT_AS_ISSUE: "true" GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" - GH_AW_TIMEOUT_MINUTES: "20" + GH_AW_TIMEOUT_MINUTES: "${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'handle_agent_failure.cjs')); + await main(); + - name: Report failed jobs + id: report_failed_jobs + 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_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/auto-code.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_REPORT_FAILED_JOBS: "true" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); + const { main } = require(path.join(actionsDir, 'report_failed_jobs.cjs')); await main(); pre_activation: runs-on: ubuntu-slim + env: + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} matched_command: '' + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 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" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Check team membership for workflow id: check_membership uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1051,9 +1420,11 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); + const { main } = require(path.join(actionsDir, 'check_membership.cjs')); await main(); safe_outputs: @@ -1064,18 +1435,21 @@ jobs: runs-on: ubuntu-slim permissions: contents: write - discussions: write issues: write pull-requests: write - timeout-minutes: 15 + timeout-minutes: 45 env: + GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/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_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID: "auto-code" GH_AW_WORKFLOW_NAME: "自动处理 Issue" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/auto-code.md" 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 }} @@ -1085,26 +1459,39 @@ jobs: 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_items_applied: ${{ steps.process_safe_outputs.outputs.items_applied }} + process_safe_outputs_items_cancelled: ${{ steps.process_safe_outputs.outputs.items_cancelled }} + process_safe_outputs_items_deferred: ${{ steps.process_safe_outputs.outputs.items_deferred }} + process_safe_outputs_items_failed: ${{ steps.process_safe_outputs.outputs.items_failed }} + process_safe_outputs_items_skipped: ${{ steps.process_safe_outputs.outputs.items_skipped }} + process_safe_outputs_items_succeeded: ${{ steps.process_safe_outputs.outputs.items_succeeded }} + process_safe_outputs_items_warnings: ${{ steps.process_safe_outputs.outputs.items_warnings }} process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_status: ${{ steps.process_safe_outputs.outputs.status }} 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 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "自动处理 Issue" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-code.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Mask OTLP telemetry headers + run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - name: Download agent output artifact id: download-agent-output continue-on-error: true uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent + pattern: "{agent,agent-output-fallback}" + merge-multiple: true path: /tmp/gh-aw/ - name: Setup agent output environment variable id: setup-agent-output-env @@ -1112,7 +1499,9 @@ jobs: 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" + if [ -f "/tmp/gh-aw/agent_output.json" ]; then + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + fi - name: Download patch artifact continue-on-error: true uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -1121,30 +1510,21 @@ jobs: 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 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - ref: develop + persist-credentials: true 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 }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_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" + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - name: Configure GH_HOST for enterprise compatibility id: ghes-host-config shell: bash - run: | + run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. GH_HOST="${GITHUB_SERVER_URL#https://}" @@ -1155,17 +1535,20 @@ jobs: 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" + GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} + GH_AW_ALLOWED_DOMAINS: "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,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,s.symcb.com,s.symcd.com,security.ubuntu.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_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"develop\",\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"allowed\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); + const { main } = require(path.join(actionsDir, 'process_safe_outputs.cjs')); await main(); - name: Upload Safe Outputs Items if: always() @@ -1175,5 +1558,5 @@ jobs: path: | /tmp/gh-aw/safe-output-items.jsonl /tmp/gh-aw/temporary-id-map.json + /tmp/gh-aw/safe-output-errors.json if-no-files-found: ignore - diff --git a/.github/workflows/auto-code.md b/.github/workflows/auto-code.md index f73bb475..6f7ae4b4 100644 --- a/.github/workflows/auto-code.md +++ b/.github/workflows/auto-code.md @@ -6,16 +6,17 @@ on: engine: id: copilot env: - COPILOT_PROVIDER_BASE_URL: "https://ark.cn-beijing.volces.com/api/v3" + COPILOT_PROVIDER_BASE_URL: ${{ vars.MODEL_AI_AGENT_ADDR }} COPILOT_PROVIDER_BEARER_TOKEN: ${{ secrets.OPENAI_API_KEY }} - COPILOT_MODEL: doubao-seed-evolving - COPILOT_PROVIDER_TYPE: openai - COPILOT_PROVIDER_WIRE_API: responses + COPILOT_MODEL: ${{ vars.MODEL_AI_AGENT_NAME }} + COPILOT_PROVIDER_TYPE: ${{ vars.MODEL_AI_AGENT_TYPE }} + COPILOT_PROVIDER_WIRE_API: ${{ vars.MODEL_AI_AGENT_API }} features: - dangerously-disable-sandbox-agent: "controlled environment for issue triage automation" + dangerously-disable-sandbox-agent: true sandbox: agent: false + strict: false network: @@ -25,7 +26,7 @@ network: tools: github: - min-integrity: none + min-integrity: approved permissions: contents: read @@ -44,14 +45,14 @@ safe-outputs: # 自动处理 Issue -当 Issue 被打上 `bug` 或 `enhancement` 或 `feature` 标签时触发。其他标签直接退出,不做任何操作。 +当 Issue type 被打上 `bug` 或 `feature` 或 `task` 标签时触发。其他标签直接退出,不做任何操作。 ## 判断任务类型 读取 Issue #${{ github.event.issue.number }} 当前的标签: - 如果包含 `bug` 标签 → 执行【Bug 修复流程】 - 如果包含 `feature` 标签 → 执行【新功能开发流程】 -- 如果包含 `enhancement` 标签 → 执行【功能改进开发流程】 +- 如果包含 `task` 标签 → 执行【功能改进开发流程】 - 其他情况 → 直接退出 ## 任务执行限制 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c5417f85..efa3c5fe 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,15 +27,9 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: 'develop' - - - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 - with: - repository: libxengine/libxengine - path: libxengine - name: sub module checkout (opensource) run: | @@ -47,8 +41,12 @@ jobs: - name: Set up Dependency Environment run: | - cd libxengine - chmod +x ./XEngine_LINEnv.sh + latest_tag=$(curl -s https://api.github.com/repos/libxengine/libxengine/releases/latest | jq -r .tag_name) + wget https://github.com/libxengine/libxengine/releases/download/$latest_tag/XEngine_UBuntu_24.04_x86-64.zip + unzip ./XEngine_UBuntu_24.04_x86-64.zip -d ./XEngine_UBuntu_24.04_x86-64 + cd XEngine_UBuntu_24.04_x86-64 + + chmod 777 * sudo ./XEngine_LINEnv.sh -i 3 - name: make pre diff --git a/.github/workflows/debian_build.yml b/.github/workflows/debian_build.yml index dd26d828..bf1177dd 100644 --- a/.github/workflows/debian_build.yml +++ b/.github/workflows/debian_build.yml @@ -36,18 +36,18 @@ jobs: steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: 'develop' - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/libxengine path: libxengine - name: sub module checkout (opensource) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/XEngine_OPenSource path: XEngine_Source/XEngine_DependLibrary diff --git a/.github/workflows/fedora_build.yml b/.github/workflows/fedora_build.yml index 4e239df7..7d03d6ae 100644 --- a/.github/workflows/fedora_build.yml +++ b/.github/workflows/fedora_build.yml @@ -24,26 +24,46 @@ jobs: - arch: amd64 runner: ubuntu-24.04 platform: linux/amd64 - fedora-version: 42 + fedora-version: 44 artifact: x86-64 - arch: amd64 runner: ubuntu-24.04 platform: linux/amd64 - fedora-version: 41 + fedora-version: 43 artifact: x86-64 + - arch: amd64 + runner: ubuntu-24.04 + platform: linux/amd64 + fedora-version: 42 + artifact: x86-64 + - arch: arm64 + runner: ubuntu-24.04-arm + platform: linux/arm64 + fedora-version: 44 + artifact: arm64 + - arch: arm64 + runner: ubuntu-24.04-arm + platform: linux/arm64 + fedora-version: 43 + artifact: arm64 + - arch: arm64 + runner: ubuntu-24.04-arm + platform: linux/arm64 + fedora-version: 42 + artifact: arm64 steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/libxengine path: libxengine - name: sub module checkout (opensource) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: libxengine/XEngine_OPenSource path: XEngine_Source/XEngine_DependLibrary diff --git a/.github/workflows/issue-translator.yml b/.github/workflows/issue-translator.yml deleted file mode 100644 index d9fdac8f..00000000 --- a/.github/workflows/issue-translator.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Issue Translator -on: - issue_comment: - types: [created] - issues: - types: [opened] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: usthe/issues-translate-action@v2.7 - with: - IS_MODIFY_TITLE: false - CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. \ No newline at end of file diff --git a/.github/workflows/issue-triage.lock.yml b/.github/workflows/issue-triage.lock.yml index 4dd05499..a876db61 100644 --- a/.github/workflows/issue-triage.lock.yml +++ b/.github/workflows/issue-triage.lock.yml @@ -1,20 +1,21 @@ -# 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"}]} -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"af27d3b31edc3b5d16c78e82737fb3d4a89fbff38f4d8b6be28f0184b7d87597","body_hash":"b6960e4f39280aa221d03a942063c639cb664ff5d4c013a58a22fa74fdf17f2d","compiler_version":"v0.88.7","engine_base_url_customized":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.80"}} +# gh-aw-manifest: {"version":1,"secrets":["GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"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":"5e508589e03a7757a7e05b26e834292f5445bfb6","version":"v0.88.7"}],"containers":[{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23"},{"image":"ghcr.io/github/github-mcp-server:v1.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"}],"mcp_servers":[{"name":"github","tools":["get_commit","get_file_contents","get_latest_release","get_me","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"safeoutputs","tools":["add_comment","add_labels","missing_data","missing_tool","noop"]}]} +# This file was automatically generated by gh-aw (v0.88.7). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ # | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ +# | | | | (_| | __/ | | | |_| | (__ # \_| |_/\__, |\___|_| |_|\__|_|\___| # __/ | -# _ _ |___/ +# _ _ |___/ # | | | | / _| | # | | | | ___ _ __ _ __| |_| | _____ ____ # | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| # \ /\ / (_) | | | | ( | | | | (_) \ 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 @@ -24,37 +25,49 @@ # # # Secrets used: -# - COPILOT_GITHUB_TOKEN +# - GH_AW_DEFAULT_OTLP_HEADERS # - 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/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 +# - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 +# - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 +# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 +# - github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 # # Container images used: -# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 -# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f +# - ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 +# - ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699 name: "Issue 自动分类与回复" -"on": +on: issues: types: - - opened - - reopened + - opened + - reopened permissions: {} concurrency: group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}" + queue: max run-name: "Issue 自动分类与回复" +env: + OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }} + OTEL_SERVICE_NAME: gh-aw.issue-triage + OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Issue%20%E8%87%AA%E5%8A%A8%E5%88%86%E7%B1%BB%E4%B8%8E%E5%9B%9E%E5%A4%8D,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=copilot' + OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }} + GH_AW_OTLP_ENDPOINTS: '[{"url":"${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }}","headers":"${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }}"}]' + GH_AW_OTLP_IF_MISSING: ignore + jobs: activation: needs: pre_activation @@ -63,13 +76,23 @@ jobs: permissions: actions: read contents: read + env: + GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: "" comment_repo: "" + daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }} + daily_ai_credits_guardrail_status: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_status || '' }} + daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} + daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} model: ${{ steps.generate_aw_info.outputs.model }} + oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} text: ${{ steps.sanitized.outputs.text }} @@ -77,24 +100,29 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }} + safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} 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" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Mask OTLP telemetry headers + run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - 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_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }} + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_AGENT_VERSION: "1.0.80" + GH_AW_INFO_CLI_VERSION: "v0.88.7" GH_AW_INFO_WORKFLOW_NAME: "Issue 自动分类与回复" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -104,16 +132,79 @@ jobs: GH_AW_INFO_AWF_VERSION: "" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "" + GH_AW_INFO_AGENT_RUNTIME: "" GH_AW_COMPILED_STRICT: "false" + GH_AW_INFO_FEATURES: '{"dangerously-disable-sandbox-agent":true}' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); + const { main } = require(path.join(actionsDir, 'generate_aw_info.cjs')); await main(core, context); + - name: Enforce strict mode policy + if: ${{ vars.GH_AW_POLICY_STRICT == 'true' }} + run: | + echo "::error::GH_AW_POLICY_STRICT=true but this workflow was not compiled in strict mode. Recompile with --strict or strict: true." + exit 1 + - name: Restore daily AIC usage cache + id: restore-daily-aic-cache + if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} + continue-on-error: true + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: agentic-workflow-usage-issuetriage-${{ github.run_id }} + restore-keys: agentic-workflow-usage-issuetriage- + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + - name: Restore daily AIC usage cache (artifact fallback) + id: restore-daily-aic-cache-fallback + if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }} + GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'restore_aic_usage_cache_fallback.cjs')); + await main(); + - name: Check daily workflow token guardrail + id: daily-effective-workflow-guardrail + if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_WORKFLOW_ID: "issue-triage" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }} + GH_AW_HAS_SLASH_COMMAND: "false" + GH_AW_HAS_LABEL_COMMAND: "false" + GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'check_daily_aic_workflow_guardrail.cjs')); + await main(); + - name: Check for OAuth tokens + id: check-oauth-tokens + run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh" + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - name: Checkout .github and .agents folders - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false sparse-checkout: | @@ -121,18 +212,16 @@ jobs: .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" + GH_AW_AGENT_FOLDERS: ".agents .github" + GH_AW_AGENT_FILES: "AGENTS.md" + 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 @@ -141,138 +230,113 @@ jobs: GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); + const { main } = require(path.join(actionsDir, '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" + GH_AW_COMPILED_VERSION: "v0.88.7" with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); + const { main } = require(path.join(actionsDir, '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" + GH_AW_ALLOWED_DOMAINS: "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,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,s.symcb.com,s.symcd.com,security.ubuntu.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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/compute_text.cjs'); + const { main } = require(path.join(actionsDir, 'compute_text.cjs')); await main(); + - name: Log runtime features + if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} + run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh" - name: Create prompt with built-in context + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_ACTIONS_DIR: ${{ runner.temp }}/gh-aw/actions + GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"github_mcp_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"}]}" + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_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" + GH_AW_PROMPT_CONTENT_0000: "\n" + GH_AW_PROMPT_CONTENT_0001: "\nTools: add_comment, add_labels, missing_tool, missing_data, noop\n" + GH_AW_PROMPT_CONTENT_0002: "\n" + GH_AW_PROMPT_CONTENT_0003: "\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_802A9F6A__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_1A3A194A__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_463A214A__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_FF1D34CE__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n\n\n" + GH_AW_PROMPT_CONTENT_0004: "\n" + GH_AW_PROMPT_CONTENT_0005: "{{#runtime-import .github/workflows/issue-triage.md}}\n" + with: + script: | + const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(process.env.GH_AW_ACTIONS_DIR + '/create_prompt.cjs'); + await main(core); - 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_PROMPT: ${{ runner.temp }}/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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); + const { main } = require(path.join(actionsDir, '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_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_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_MCP_CLI_SERVERS_LIST: "- `github` — run `github --help` to see available tools\n- `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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - - const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); - + + const substitutePlaceholders = require(path.join(actionsDir, 'substitute_placeholders.cjs')); + // Call the substitution function return await substitutePlaceholders({ file: process.env.GH_AW_PROMPT, substitutions: { + GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, + GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, + GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_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, @@ -282,76 +346,118 @@ jobs: }); - 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" + GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt + 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" + GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" + - name: Stage prompt files for artifact upload + run: | + mkdir -p /tmp/gh-aw/aw-prompts + cp -a "${RUNNER_TEMP}/gh-aw/aw-prompts/." /tmp/gh-aw/aw-prompts/ - name: Upload activation artifact - if: success() + if: success() || failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: activation include-hidden-files: true path: | /tmp/gh-aw/aw_info.json + /tmp/gh-aw/models.json /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw-prompts/prompt-template.txt + /tmp/gh-aw/aw-prompts/prompt-import-tree.json /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/base + /tmp/gh-aw/.github/agents + /tmp/gh-aw/.github/skills if-no-files-found: ignore retention-days: 1 agent: needs: activation + if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' runs-on: ubuntu-latest permissions: contents: read + copilot-requests: write issues: read pull-requests: read + timeout-minutes: 60 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_PR_HEAD_BASE_BRANCH: "" + GH_AW_PR_HEAD_BASE_PR_NUMBER: "" + GH_AW_PR_HEAD_BASE_REF: "" + GH_AW_PR_HEAD_BASE_REPO: "" + GH_AW_PR_HEAD_BASE_SHA: "" + GH_AW_PR_HEAD_REPO: "" + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID_SANITIZED: issuetriage outputs: - agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }} + agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} + ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} + aic: ${{ steps.parse-mcp-gateway.outputs.aic }} + ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }} checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} has_patch: ${{ steps.collect_output.outputs.has_patch }} - inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }} - mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }} + http_400_response_error: ${{ steps.detect-agent-errors.outputs.http_400_response_error || 'false' }} + inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} + invocation_cap_exceeded: ${{ steps.detect-agent-errors.outputs.invocation_cap_exceeded || 'false' }} + max_cache_misses_exceeded: ${{ steps.detect-agent-errors.outputs.max_cache_misses_exceeded || 'false' }} + mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} + missing_model_pricing_error: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_error || 'false' }} + missing_model_pricing_model_name: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_model_name || '' }} model: ${{ needs.activation.outputs.model }} - model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }} + model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} + shell_expansion_guard_rejected: ${{ steps.detect-agent-errors.outputs.shell_expansion_guard_rejected || 'false' }} + unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths + env: + GH_AW_RUNNER_TOOL_CACHE: ${{ runner.tool_cache }} run: | + if [ -z "${RUNNER_TOOL_CACHE:-}" ]; then + echo "RUNNER_TOOL_CACHE=${GH_AW_RUNNER_TOOL_CACHE}" >> "$GITHUB_ENV" + fi { 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: Mask OTLP telemetry headers + run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" + - name: Check OTLP telemetry configuration + run: bash "${RUNNER_TEMP}/gh-aw/actions/check_otlp_default_credentials.sh" - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Create gh-aw temp directory @@ -360,37 +466,51 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" env: GH_TOKEN: ${{ github.token }} + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: activation + path: /tmp/gh-aw - name: Configure Git credentials env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_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" + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - name: Checkout PR branch id: checkout-pr if: | - github.event.pull_request || github.event.issue.pull_request + github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); + const { main } = require(path.join(actionsDir, '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 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" env: GH_HOST: github.com + GH_AW_COMPILED_VERSION: v0.88.7 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + GH_AW_GITHUB_MIN_INTEGRITY: 'none' + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const determineAutomaticLockdown = require(path.join(actionsDir, 'determine_automatic_lockdown.cjs')); + await determineAutomaticLockdown(github, context, core); - name: Parse integrity filter lists id: parse-guard-vars env: @@ -398,27 +518,42 @@ jobs: 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" + GH_AW_AGENT_FOLDERS: ".agents .github" + GH_AW_AGENT_FILES: "AGENTS.md" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" + - name: Restore inline sub-agents from activation artifact + env: + GH_AW_SUB_AGENT_DIR: ".github/agents" + GH_AW_SUB_AGENT_EXT: ".agent.md" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" + - name: Restore inline skills from activation artifact + env: + GH_AW_SKILL_DIR: ".github/skills" + run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f - - name: Generate Safe Outputs Config + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699 + - name: Prepare Safe Outputs Directories 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 Config + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw" + GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}" + GH_AW_SAFE_OUTPUTS_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\":\"false\"},\"report_incomplete\":{}}" + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'create_files.cjs')); + await main(); - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -441,9 +576,18 @@ jobs: "sanitize": true, "maxLength": 65000 }, + "comment_id": { + "optionalPositiveInteger": true + }, "item_number": { "issueOrPRNumber": true }, + "pr": { + "issueOrPRNumber": true + }, + "pr_number": { + "issueOrPRNumber": true + }, "reply_to_id": { "type": "string", "maxLength": 256 @@ -451,6 +595,16 @@ jobs: "repo": { "type": "string", "maxLength": 256 + }, + "target": { + "type": "string", + "enum": [ + "status" + ] + }, + "temporary_id": { + "type": "string", + "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$" } } }, @@ -462,10 +616,7 @@ jobs: }, "labels": { "required": true, - "type": "array", - "itemType": "string", - "itemSanitize": true, - "itemMaxLength": 128 + "type": "array" }, "repo": { "type": "string", @@ -550,90 +701,68 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); + const { main } = require(path.join(actionsDir, '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_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} + GH_AW_SINK_VISIBILITY: ${{ steps.determine-automatic-lockdown.outputs.visibility }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -eo pipefail mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" - + if [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -r "${GITHUB_EVENT_PATH}" ]; then + GH_AW_SAFEOUTPUTS_EVENT_PATH="${RUNNER_TEMP}/gh-aw/safeoutputs/github_event.json" + cp "${GITHUB_EVENT_PATH}" "${GH_AW_SAFEOUTPUTS_EVENT_PATH}" + export GITHUB_EVENT_PATH="${GH_AW_SAFEOUTPUTS_EVENT_PATH}" + fi + # 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 + MCP_GATEWAY_AGENT_ID=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_AGENT_ID}" + export MCP_GATEWAY_AGENT_ID export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:ro,${RUNNER_TEMP}/gh-aw/safeoutputs:rw,/opt:ro,/tmp:rw,/usr/bin/gh:ro" + export GH_AW_PR_HEAD_BASE_BRANCH="${GH_AW_PR_HEAD_BASE_BRANCH:-}" + export GH_AW_PR_HEAD_BASE_SHA="${GH_AW_PR_HEAD_BASE_SHA:-}" + export GH_AW_PR_HEAD_BASE_REPO="${GH_AW_PR_HEAD_BASE_REPO:-}" + export GH_AW_PR_HEAD_BASE_PR_NUMBER="${GH_AW_PR_HEAD_BASE_PR_NUMBER:-}" + export GH_AW_PR_HEAD_BASE_REF="${GH_AW_PR_HEAD_BASE_REF:-}" + export GH_AW_PR_HEAD_REPO="${GH_AW_PR_HEAD_REPO:-}" 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 + source "${RUNNER_TEMP}/gh-aw/actions/resolve_docker_socket_gid.sh" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_AGENT_ID -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_PR_HEAD_BASE_BRANCH -e GH_AW_PR_HEAD_BASE_SHA -e GH_AW_PR_HEAD_BASE_REPO -e GH_AW_PR_HEAD_BASE_PR_NUMBER -e GH_AW_PR_HEAD_BASE_REF -e GH_AW_PR_HEAD_REPO -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GH_AW_SINK_VISIBILITY -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e RUNNER_TOOL_CACHE -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.4.18' + + mkdir -p "$HOME/.copilot" GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_0138aecd21cfde50_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_137b94c134aafdc9_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", + "container": "ghcr.io/github/github-mcp-server:v1.11.0", "env": { - "GITHUB_HOST": "\${GITHUB_SERVER_URL}", - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_FEATURES": "fields_param", + "GITHUB_HOST": "${GITHUB_SERVER_URL}", + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" }, @@ -648,16 +777,43 @@ jobs: } }, "safeoutputs": { - "type": "http", - "url": "http://localhost:$GH_AW_SAFE_OUTPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + "type": "stdio", + "container": "ghcr.io/github/gh-aw-node", + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["-w", "\${GITHUB_WORKSPACE}"], + "entrypoint": "sh", + "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"], + "env": { + "DEBUG": "*", + "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", + "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", + "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", + "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", + "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", + "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", + "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", + "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", + "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", + "GH_AW_PR_HEAD_BASE_BRANCH": "\${GH_AW_PR_HEAD_BASE_BRANCH}", + "GH_AW_PR_HEAD_BASE_SHA": "\${GH_AW_PR_HEAD_BASE_SHA}", + "GH_AW_PR_HEAD_BASE_REPO": "\${GH_AW_PR_HEAD_BASE_REPO}", + "GH_AW_PR_HEAD_BASE_PR_NUMBER": "\${GH_AW_PR_HEAD_BASE_PR_NUMBER}", + "GH_AW_PR_HEAD_BASE_REF": "\${GH_AW_PR_HEAD_BASE_REF}", + "GH_AW_PR_HEAD_REPO": "\${GH_AW_PR_HEAD_REPO}", + "GITHUB_EVENT_NAME": "\${GITHUB_EVENT_NAME}", + "GITHUB_EVENT_PATH": "\${GITHUB_EVENT_PATH}", + "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", + "GITHUB_SHA": "\${GITHUB_SHA}", + "GITHUB_TOKEN": "\${GITHUB_TOKEN}", + "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", + "RUNNER_TEMP": "\${RUNNER_TEMP}" }, "guard-policies": { "write-sink": { "accept": [ "*" - ] + ], + "sink-visibility": "${GH_AW_SINK_VISIBILITY}" } } } @@ -665,24 +821,32 @@ jobs: "gateway": { "port": $MCP_GATEWAY_PORT, "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + "agentId": "${MCP_GATEWAY_AGENT_ID}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}", + "startupTimeout": 120, + "opentelemetry": { + "endpoint": "${OTEL_EXPORTER_OTLP_ENDPOINT}", + "traceId": "${GITHUB_AW_OTEL_TRACE_ID}", + "spanId": "${GITHUB_AW_OTEL_PARENT_SPAN_ID}" + } } } - GH_AW_MCP_CONFIG_0138aecd21cfde50_EOF + GH_AW_MCP_CONFIG_137b94c134aafdc9_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_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} 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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io); - const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); + const { main } = require(path.join(actionsDir, 'mount_mcp_as_cli.cjs')); await main(); - name: Clean credentials continue-on-error: true @@ -696,29 +860,52 @@ jobs: # Copilot CLI tool arguments (sorted): # --allow-tool github # --allow-tool safeoutputs - timeout-minutes: 20 + # --allow-tool shell(cat) + # --allow-tool shell(date) + # --allow-tool shell(echo) + # --allow-tool shell(github:*) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(ls) + # --allow-tool shell(printf) + # --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) + timeout-minutes: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} run: | set -o pipefail + printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt touch /tmp/gh-aw/agent-step-summary.md (umask 177 && touch /tmp/gh-aw/agent-stdio.log) + trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; rm -f "$HOME/.copilot/settings.json"; if [ "$gh_aw_exit_code" -ne 0 ]; then echo "::error::Agent execution exited with code $gh_aw_exit_code"; fi' EXIT + mkdir -p "$HOME/.copilot" + printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" + export XDG_CONFIG_HOME="$HOME" + export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" 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 + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs" 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(github:*)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(ls)' --allow-tool 'shell(printf)' --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)' --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 + COPILOT_MODEL: ${{ vars.MODEL_AI_AGENT_NAME }} + COPILOT_PROVIDER_BASE_URL: ${{ vars.MODEL_AI_AGENT_ADDR }} + COPILOT_PROVIDER_BEARER_TOKEN: ${{ secrets.OPENAI_API_KEY }} + COPILOT_PROVIDER_TYPE: ${{ vars.MODEL_AI_AGENT_TYPE }} + COPILOT_PROVIDER_WIRE_API: ${{ vars.MODEL_AI_AGENT_API }} + GH_AW_LLM_PROVIDER: github + GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} + GH_AW_MAX_TURNS: 5 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 + GH_AW_TIMEOUT_MINUTES: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} + GH_AW_VERSION: v0.88.7 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -728,25 +915,31 @@ jobs: 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 + RUNNER_TEMP: ${{ runner.temp }} + S2STOKENS: true + TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} + - name: Detect agent errors if: always() + id: detect-agent-errors continue-on-error: true - run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs" + env: + GH_AW_AGENTIC_EXECUTION_OUTCOME: ${{ steps.agentic_execution.outcome }} + GH_AW_ENGINE_STEP_TIMEOUT_MINUTES: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'detect_agent_errors.cjs')); + await main(); - name: Configure Git credentials env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_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" + run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - name: Copy Copilot session state files to logs if: always() continue-on-error: true @@ -756,7 +949,7 @@ jobs: 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 }} + MCP_GATEWAY_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} run: | bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" @@ -765,13 +958,14 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); + const { main } = require(path.join(actionsDir, '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 }} + GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' 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 }} @@ -792,25 +986,30 @@ jobs: 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" + GH_AW_ALLOWED_DOMAINS: "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,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,s.symcb.com,s.symcd.com,security.ubuntu.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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); + const { main } = require(path.join(actionsDir, '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/ + GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); + const { main } = require(path.join(actionsDir, 'parse_copilot_log.cjs')); await main(); - name: Parse MCP Gateway logs for step summary if: always() @@ -818,16 +1017,41 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); + const { main } = require(path.join(actionsDir, 'parse_mcp_gateway_log.cjs')); await main(); + - name: Generate observability summary + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'generate_observability_summary.cjs')); + await main(core); - 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 + # Small dedicated copy of the agent output so safe-output processing + # survives a failed or timed-out upload of the larger agent artifact + - name: Upload agent output fallback artifact + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: agent-output-fallback + path: | + /tmp/gh-aw/agent_output.json + /tmp/gh-aw/safeoutputs.jsonl + if-no-files-found: ignore - name: Upload agent artifacts if: always() continue-on-error: true @@ -843,8 +1067,9 @@ jobs: !/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/otel.jsonl + /tmp/gh-aw/otlp-export-errors.jsonl /tmp/gh-aw/safeoutputs.jsonl /tmp/gh-aw/agent_output.json if-no-files-found: ignore @@ -856,16 +1081,19 @@ jobs: - safe_outputs if: > always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || - needs.activation.outputs.stale_lock_file_failed == 'true') + needs.activation.outputs.oauth_token_check_failed == 'true' || needs.activation.outputs.stale_lock_file_failed == 'true' || + needs.activation.outputs.daily_ai_credits_exceeded == 'true') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + actions: read issues: write pull-requests: write concurrency: group: "gh-aw-conclusion-issue-triage" cancel-in-progress: false + queue: max + env: + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} outputs: incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} noop_message: ${{ steps.noop.outputs.noop_message }} @@ -874,21 +1102,24 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent + pattern: "{agent,agent-output-fallback}" + merge-multiple: true path: /tmp/gh-aw/ - name: Setup agent output environment variable id: setup-agent-output-env @@ -896,7 +1127,83 @@ jobs: 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" + if [ -f "/tmp/gh-aw/agent_output.json" ]; then + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + fi + - name: Download Safe Outputs Items Manifest + id: download-safe-outputs-manifest + if: always() + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: safe-outputs-items + merge-multiple: true + path: /tmp/gh-aw/ + - name: Collect usage artifact files + if: always() + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/collect_usage_artifact_files.sh" + - name: Upload usage artifact + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: usage + path: | + /tmp/gh-aw/usage/aw_info.json + /tmp/gh-aw/usage/aw-info.jsonl + /tmp/gh-aw/usage/agent_usage.json + /tmp/gh-aw/usage/agent_usage.jsonl + /tmp/gh-aw/usage/detection_usage.jsonl + /tmp/gh-aw/usage/evals.jsonl + /tmp/gh-aw/usage/graders/grader_manifest.json + /tmp/gh-aw/usage/graders/grader_results.json + /tmp/gh-aw/usage/github_rate_limits.jsonl + /tmp/gh-aw/usage/agent/token_usage.jsonl + /tmp/gh-aw/usage/detection/token_usage.jsonl + /tmp/gh-aw/usage/activity/summary.json + if-no-files-found: ignore + - name: Restore daily AIC usage cache + id: restore-daily-aic-cache-conclusion + if: always() + continue-on-error: true + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: agentic-workflow-usage-issuetriage-${{ github.run_id }} + restore-keys: agentic-workflow-usage-issuetriage- + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + - name: Write daily AIC usage cache entry + id: write-daily-aic-cache + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{ github.token }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context); + const { main } = require(path.join(actionsDir, 'write_daily_aic_usage_cache.cjs')); + await main(); + - name: Save daily AIC usage cache + id: save-daily-aic-cache + if: always() + continue-on-error: true + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: agentic-workflow-usage-issuetriage-${{ github.run_id }} + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + - name: Upload daily AIC usage cache artifact + id: upload-daily-aic-cache + if: always() + continue-on-error: true + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: aic-usage-cache + path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl + if-no-files-found: ignore + retention-days: 7 - name: Process no-op messages id: noop uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -904,15 +1211,21 @@ jobs: 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_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_NOOP_REPORT_AS_ISSUE: "true" + GH_AW_NOOP_REPORT_AS_ISSUE: "false" + GH_AW_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} + GH_AW_WORKFLOW_ID: "issue-triage" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); + const { main } = require(path.join(actionsDir, 'handle_noop_message.cjs')); await main(); - name: Record missing tool id: missing_tool @@ -921,12 +1234,15 @@ jobs: 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 自动分类与回复" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); + const { main } = require(path.join(actionsDir, 'missing_tool.cjs')); await main(); - name: Record incomplete id: report_incomplete @@ -935,12 +1251,15 @@ jobs: 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 自动分类与回复" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); + const { main } = require(path.join(actionsDir, 'report_incomplete_handler.cjs')); await main(); - name: Handle agent failure id: handle_agent_failure @@ -949,49 +1268,90 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "issue-triage" - GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" + GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "0" GH_AW_ENGINE_ID: "copilot" GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} + GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }} + GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }} + GH_AW_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} + GH_AW_HTTP_400_RESPONSE_ERROR: ${{ needs.agent.outputs.http_400_response_error }} + GH_AW_MAX_CACHE_MISSES_EXCEEDED: ${{ needs.agent.outputs.max_cache_misses_exceeded }} + GH_AW_MISSING_MODEL_PRICING_ERROR: ${{ needs.agent.outputs.missing_model_pricing_error }} + GH_AW_MISSING_MODEL_PRICING_MODEL_NAME: ${{ needs.agent.outputs.missing_model_pricing_model_name }} + GH_AW_SHELL_EXPANSION_GUARD_REJECTED: ${{ needs.agent.outputs.shell_expansion_guard_rejected }} 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_OAUTH_TOKEN_CHECK_FAILED: ${{ needs.activation.outputs.oauth_token_check_failed }} GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} + GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} + GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }} + GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }} GH_AW_GROUP_REPORTS: "false" GH_AW_FAILURE_REPORT_AS_ISSUE: "true" GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" - GH_AW_TIMEOUT_MINUTES: "20" + GH_AW_TIMEOUT_MINUTES: "${{ fromJSON(vars.GH_AW_DEFAULT_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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); + const { main } = require(path.join(actionsDir, 'handle_agent_failure.cjs')); + await main(); + - name: Report failed jobs + id: report_failed_jobs + 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_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_REPORT_FAILED_JOBS: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'report_failed_jobs.cjs')); await main(); pre_activation: runs-on: ubuntu-slim + env: + GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} matched_command: '' + setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} + setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 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" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Check team membership for workflow id: check_membership uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1000,9 +1360,11 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); + const { main } = require(path.join(actionsDir, 'check_membership.cjs')); await main(); safe_outputs: @@ -1012,19 +1374,21 @@ jobs: if: (!cancelled()) && needs.agent.result != 'skipped' runs-on: ubuntu-slim permissions: - contents: read - discussions: write issues: write pull-requests: write - timeout-minutes: 15 + timeout-minutes: 45 env: + GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_AIC: ${{ needs.agent.outputs.aic }} + GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/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_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_WORKFLOW_ID: "issue-triage" GH_AW_WORKFLOW_NAME: "Issue 自动分类与回复" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" 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 }} @@ -1032,26 +1396,39 @@ jobs: 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_items_applied: ${{ steps.process_safe_outputs.outputs.items_applied }} + process_safe_outputs_items_cancelled: ${{ steps.process_safe_outputs.outputs.items_cancelled }} + process_safe_outputs_items_deferred: ${{ steps.process_safe_outputs.outputs.items_deferred }} + process_safe_outputs_items_failed: ${{ steps.process_safe_outputs.outputs.items_failed }} + process_safe_outputs_items_skipped: ${{ steps.process_safe_outputs.outputs.items_skipped }} + process_safe_outputs_items_succeeded: ${{ steps.process_safe_outputs.outputs.items_succeeded }} + process_safe_outputs_items_warnings: ${{ steps.process_safe_outputs.outputs.items_warnings }} process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_status: ${{ steps.process_safe_outputs.outputs.status }} 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 + uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} env: GH_AW_SETUP_WORKFLOW_NAME: "Issue 自动分类与回复" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.40" + GH_AW_INFO_VERSION: "1.0.80" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Mask OTLP telemetry headers + run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - name: Download agent output artifact id: download-agent-output continue-on-error: true uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent + pattern: "{agent,agent-output-fallback}" + merge-multiple: true path: /tmp/gh-aw/ - name: Setup agent output environment variable id: setup-agent-output-env @@ -1059,11 +1436,13 @@ jobs: 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" + if [ -f "/tmp/gh-aw/agent_output.json" ]; then + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + fi - name: Configure GH_HOST for enterprise compatibility id: ghes-host-config shell: bash - run: | + run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. GH_HOST="${GITHUB_SERVER_URL#https://}" @@ -1074,16 +1453,19 @@ jobs: 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" + GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} + GH_AW_ALLOWED_DOMAINS: "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,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,s.symcb.com,s.symcd.com,security.ubuntu.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\":{}}" + 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\":\"false\"},\"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'); + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); + const { main } = require(path.join(actionsDir, 'process_safe_outputs.cjs')); await main(); - name: Upload Safe Outputs Items if: always() @@ -1093,5 +1475,5 @@ jobs: path: | /tmp/gh-aw/safe-output-items.jsonl /tmp/gh-aw/temporary-id-map.json + /tmp/gh-aw/safe-output-errors.json if-no-files-found: ignore - diff --git a/.github/workflows/issue-triage.md b/.github/workflows/issue-triage.md index ae117408..2c229957 100644 --- a/.github/workflows/issue-triage.md +++ b/.github/workflows/issue-triage.md @@ -5,17 +5,19 @@ on: engine: id: copilot + max-turns: 5 env: - COPILOT_PROVIDER_BASE_URL: "https://ark.cn-beijing.volces.com/api/v3" + COPILOT_PROVIDER_BASE_URL: ${{ vars.MODEL_AI_AGENT_ADDR }} COPILOT_PROVIDER_BEARER_TOKEN: ${{ secrets.OPENAI_API_KEY }} - COPILOT_MODEL: doubao-seed-evolving - COPILOT_PROVIDER_TYPE: openai - COPILOT_PROVIDER_WIRE_API: responses + COPILOT_MODEL: ${{ vars.MODEL_AI_AGENT_NAME }} + COPILOT_PROVIDER_TYPE: ${{ vars.MODEL_AI_AGENT_TYPE }} + COPILOT_PROVIDER_WIRE_API: ${{ vars.MODEL_AI_AGENT_API }} features: - dangerously-disable-sandbox-agent: "controlled environment for issue triage automation" + dangerously-disable-sandbox-agent: true sandbox: agent: false + strict: false network: @@ -26,6 +28,7 @@ network: tools: github: min-integrity: none + bash: ["grep", "cat", "head"] permissions: issues: read @@ -52,6 +55,11 @@ safe-outputs: 分析仓库 ${{ github.repository }} 中新开的 Issue。 +## 任务执行限制 +- 根据需求查找相对应的可能关联的代码文件 +- 尽量只读取相关代码文件和文档,不去操作无关代码和文件 +- 尽量减少操作时间和步骤,减少TOKEN和时间消耗 + ## 任务 1. 阅读 Issue 的标题和正文 diff --git a/.github/workflows/macbuild.yml b/.github/workflows/macbuild.yml index d44eba69..180a823e 100644 --- a/.github/workflows/macbuild.yml +++ b/.github/workflows/macbuild.yml @@ -17,44 +17,36 @@ jobs: strategy: matrix: include: - - os: macos-15 - - os: macos-15-intel - os: macos-26 - os: macos-26-intel runs-on: ${{ matrix.os }} steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: 'develop' - - # 检出依赖的xengine仓库到指定的xengine目录 - - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 - with: - repository: libxengine/libxengine - path: libxengine - name: sub module checkout (opensource) run: | git submodule init git submodule update - - name: brew install - run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - - name: Set TERM variable run: echo "TERM=xterm" >> $GITHUB_ENV - name: Set up Dependency x86_64 Environment - if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26-intel' + if: matrix.os == 'macos-26-intel' run: | - cd libxengine + latest_tag=$(git ls-remote --tags --sort="v:refname" https://github.com/libxengine/libxengine.git | awk '{print $2}' | sed 's/refs\/tags\///' | tail -n 1) + wget https://github.com/libxengine/libxengine/releases/download/$latest_tag/XEngine_Mac_x86-64.zip + unzip ./XEngine_Mac_x86-64.zip -d ./XEngine_Mac_x86-64 + cd XEngine_Mac_x86-64 + chmod 777 * ./XEngine_LINEnv.sh -i 3 - name: Set up Dependency Arm64 Environment - if: matrix.os == 'macos-15' || matrix.os == 'macos-26' + if: matrix.os == 'macos-26' run: | latest_tag=$(git ls-remote --tags --sort="v:refname" https://github.com/libxengine/libxengine.git | awk '{print $2}' | sed 's/refs\/tags\///' | tail -n 1) wget https://github.com/libxengine/libxengine/releases/download/$latest_tag/XEngine_Mac_Arm64.zip @@ -82,14 +74,14 @@ jobs: ./XEngine_XStorageApp -t - name: Upload folder as artifact with mac x64 - if: matrix.os == 'macos-15-intel' + if: matrix.os == 'macos-26-intel' uses: actions/upload-artifact@v7 with: name: XEngine_XStorageApp-Mac_x86_64 path: XEngine_Release/ retention-days: 1 - name: Upload folder as artifact with mac arm - if: matrix.os == 'macos-15' + if: matrix.os == 'macos-26' uses: actions/upload-artifact@v7 with: name: XEngine_XStorageApp-Mac_Arm64 diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index 0437b4e6..c5bc5bde 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -24,16 +24,10 @@ jobs: steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: 'develop' - - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 - with: - repository: libxengine/libxengine - path: libxengine - - name: sub module checkout (opensource) run: | git submodule init @@ -43,8 +37,16 @@ jobs: - name: Set up Dependency x86_64 Environment if: matrix.platform == 'x64' run: | - echo "XENGINE_INCLUDE=${{ github.workspace }}/libxengine" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "XENGINE_LIB64=${{ github.workspace }}/libxengine/XEngine_Windows" | Out-File -FilePath $env:GITHUB_ENV -Append + $response = Invoke-RestMethod -Uri "https://api.github.com/repos/libxengine/libxengine/releases/latest" -Headers @{"Accept"="application/vnd.github.v3+json"} + $latest_tag = $response.tag_name + Write-Host "Latest Tag: $latest_tag" + + $url = "https://github.com/libxengine/libxengine/releases/download/$latest_tag/XEngine_Windows_x86-64.zip" + Invoke-WebRequest -Uri $url -OutFile "XEngine_Windows_x86-64.zip" + Expand-Archive -Path ./XEngine_Windows_x86-64.zip -DestinationPath ./XEngine_Windows -Force + + echo "XENGINE_INCLUDE=${{ github.workspace }}/XEngine_Windows" | Out-File -FilePath $env:GITHUB_ENV -Append + echo "XENGINE_LIB64=${{ github.workspace }}/XEngine_Windows/XEngine_Windows" | Out-File -FilePath $env:GITHUB_ENV -Append shell: pwsh - name: Set up Dependency x86_32 Environment if: matrix.platform == 'x86' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f48abc10..0f6559ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,12 @@ jobs: steps: # 检出代码并包含标签 - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 - name: Download ubuntu build - uses: dawidd6/action-download-artifact@v20 + uses: dawidd6/action-download-artifact@v24 with: workflow: ubuntu_build.yml workflow_conclusion: success @@ -26,7 +26,7 @@ jobs: if_no_artifact_found: fail path: ./XRelease/ - name: Download debian build - uses: dawidd6/action-download-artifact@v20 + uses: dawidd6/action-download-artifact@v24 with: workflow: debian_build.yml workflow_conclusion: success @@ -35,7 +35,7 @@ jobs: if_no_artifact_found: fail path: ./XRelease/ - name: Download fedora build - uses: dawidd6/action-download-artifact@v20 + uses: dawidd6/action-download-artifact@v24 with: workflow: fedora_build.yml workflow_conclusion: success @@ -44,7 +44,7 @@ jobs: if_no_artifact_found: fail path: ./XRelease/ - name: Download Rocky build - uses: dawidd6/action-download-artifact@v20 + uses: dawidd6/action-download-artifact@v24 with: workflow: Rocky_build.yml workflow_conclusion: success @@ -53,7 +53,7 @@ jobs: if_no_artifact_found: fail path: ./XRelease/ - name: Download Alma build - uses: dawidd6/action-download-artifact@v20 + uses: dawidd6/action-download-artifact@v24 with: workflow: Alma_build.yml workflow_conclusion: success @@ -61,8 +61,17 @@ jobs: skip_unpack: true if_no_artifact_found: fail path: ./XRelease/ + - name: Download opensuse build + uses: dawidd6/action-download-artifact@v24 + with: + workflow: suse_build.yml + workflow_conclusion: success + check_artifacts: false + skip_unpack: true + if_no_artifact_found: fail + path: ./XRelease/ - name: Download macbuild - uses: dawidd6/action-download-artifact@v20 + uses: dawidd6/action-download-artifact@v24 with: workflow: macbuild.yml workflow_conclusion: success @@ -71,7 +80,7 @@ jobs: if_no_artifact_found: fail path: ./XRelease/ - name: Download msbuild - uses: dawidd6/action-download-artifact@v20 + uses: dawidd6/action-download-artifact@v24 with: workflow: msbuild.yml workflow_conclusion: success @@ -113,7 +122,7 @@ jobs: echo "$release_notes" > release_notes.txt - name: Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/suse_build.yml b/.github/workflows/suse_build.yml new file mode 100644 index 00000000..1ec4129a --- /dev/null +++ b/.github/workflows/suse_build.yml @@ -0,0 +1,91 @@ +name: opensuse build workflows + +on: + push: + branches: + - 'develop' + paths: + - 'XEngine_Source/**' + - 'XEngine_Release/**' + - '.github/**' + +permissions: + contents: read + +jobs: + build: + runs-on: ${{ matrix.runner }} + container: + image: opensuse/leap:${{ matrix.version }} + options: --platform=${{ matrix.platform }} + strategy: + matrix: + include: + - arch: amd64 + runner: ubuntu-24.04 + platform: linux/amd64 + version: 16.0 + artifact: x86-64 + - arch: arm64 + runner: ubuntu-24.04-arm + platform: linux/arm64 + version: 16.0 + artifact: Arm64 + + steps: + - name: Checkout main repository code + uses: actions/checkout@v7 + + - name: Checkout dependency repository (xengine) + uses: actions/checkout@v7 + with: + repository: libxengine/libxengine + path: libxengine + + - name: sub module checkout (opensource) + uses: actions/checkout@v7 + with: + repository: libxengine/XEngine_OPenSource + path: XEngine_Source/XEngine_DependLibrary + + - name: install system package + run: | + zypper --non-interactive update -y + zypper install -y gcc gcc-c++ make git jq unzip wget + + - name: Set TERM variable + run: echo "TERM=xterm" >> $GITHUB_ENV + + - name: install xengine library + env: + GH_TOKEN: ${{ github.token }} + run: | + latest_tag=$(curl -s -H "Authorization: Bearer ${{ github.token }}" https://api.github.com/repos/libxengine/libxengine/releases/latest | jq -r .tag_name) + wget https://github.com/libxengine/libxengine/releases/download/$latest_tag/XEngine_OpenSuse_${{ matrix.version }}_${{ matrix.artifact }}.zip + unzip ./XEngine_OpenSuse_${{ matrix.version }}_${{ matrix.artifact }}.zip -d ./XEngine_OpenSuse_${{ matrix.version }}_${{ matrix.artifact }} + cd XEngine_OpenSuse_${{ matrix.version }}_${{ matrix.artifact }} + + chmod 777 * + ./XEngine_LINEnv.sh -i 3 + - name: make + run: | + cd XEngine_Source + make + make FLAGS=InstallAll + make FLAGS=CleanAll + + make RELEASE=1 + make FLAGS=InstallAll + make FLAGS=CleanAll + + - name: test + run: | + cd XEngine_Release + ./XEngine_XStorageApp -t + + - name: Upload folder as artifact with OPensuse + uses: actions/upload-artifact@v7 + with: + name: XEngine_XStorageApp-OPensuse_${{ matrix.version }}_${{ matrix.artifact }} + path: XEngine_Release/ + retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/ubuntu_build.yml b/.github/workflows/ubuntu_build.yml index aab5f1e7..8edd086c 100644 --- a/.github/workflows/ubuntu_build.yml +++ b/.github/workflows/ubuntu_build.yml @@ -27,16 +27,10 @@ jobs: steps: - name: Checkout main repository code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: 'develop' - - name: Checkout dependency repository (xengine) - uses: actions/checkout@v6 - with: - repository: libxengine/libxengine - path: libxengine - - name: sub module checkout (opensource) run: | git submodule init @@ -48,7 +42,11 @@ jobs: - name: Set up Dependency ubuntu24.04 x86-64 Environment if: matrix.os == 'ubuntu-24.04' run: | - cd libxengine + latest_tag=$(curl -s https://api.github.com/repos/libxengine/libxengine/releases/latest | jq -r .tag_name) + wget https://github.com/libxengine/libxengine/releases/download/$latest_tag/XEngine_UBuntu_24.04_x86-64.zip + unzip ./XEngine_UBuntu_24.04_x86-64.zip -d ./XEngine_UBuntu_24.04_x86-64 + cd XEngine_UBuntu_24.04_x86-64 + chmod 777 * sudo ./XEngine_LINEnv.sh -i 3 - name: Set up Dependency ubuntu22.04 x86-64 Environment diff --git a/CHANGELOG b/CHANGELOG index 54a2ae3d..3c46d819 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,15 @@ +XEngine_Storage V3.24.0.1001 + +ci:增加了opnesuse系统支持 +增加:ftp协议文件操作支持 +更新:依赖库 +修改:vs编译器使用了c++20 + +ci:add open suse support +added:ftp protocol support +update:depend library +modify:change c++ version to 20 for msvc +====================================================================================== XEngine_Storage V3.23.0.1001 ci:删除了多余的动作 diff --git a/README.en.md b/README.en.md index ccf3031b..9cef8d92 100644 --- a/README.en.md +++ b/README.en.md @@ -35,6 +35,7 @@ feature list: 18. support convert record(service upload and download) 19. webdav protocol download and upload file support 20. preview file create +21. FTP upload and download ## install diff --git a/README.md b/README.md index 609a7d8c..e67571be 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ c c++ file storage service 18. 支持转录(服务器上传下载) 19. 支持WEBDAV协议上传下载文件 20. 预创建文件 +21. FTP上传下载支持 ## 安装教程 diff --git a/XEngine_Docment/Docment_en.docx b/XEngine_Docment/Docment_en.docx index add09482..5c0d470d 100644 Binary files a/XEngine_Docment/Docment_en.docx and b/XEngine_Docment/Docment_en.docx differ diff --git a/XEngine_Docment/Docment_zh.docx b/XEngine_Docment/Docment_zh.docx index 00f5b805..d2427288 100644 Binary files a/XEngine_Docment/Docment_zh.docx and b/XEngine_Docment/Docment_zh.docx differ diff --git a/XEngine_Release/XEngine_Config/FTPCode.types b/XEngine_Release/XEngine_Config/FTPCode.types new file mode 100644 index 00000000..dc7e5042 --- /dev/null +++ b/XEngine_Release/XEngine_Config/FTPCode.types @@ -0,0 +1,31 @@ +//Response 1xx (Preliminary) +125:Data connection already open; transfer starting +150:File status okay; about to open data connection +//Response 2xx (Completion) +200:Command okay +202:Command not implemented, superflous at this site +211:System status, or system help reply +220:Service ready for new user +221:Service closing control connection +226:Closing data connection +227:Entering Passive Mode +230:User logged in, proceed +250:Requested file action okay, completed +257:PATHNAME created +//Response 3xx (Intermediate) +331:User name okay, need password +332:Need account for login +350:Requested file action pending further information +//Response 4xx (Transient Negative) +421:Service not available, closing control connection +425:Can't open data connection +426:Connection closed; transfer aborted +450:Requested file action not taken +451:Requested action aborted: local error in processing +//Response 5xx (Permanent Negative) +500:Syntax error, command unrecognized +501:Syntax error in parameters or arguments +502:Command not implemented +503:Bad sequence of commands +530:Not logged in +550:Requested action not taken \ No newline at end of file diff --git a/XEngine_Release/XEngine_Config/XEngine_Config.json b/XEngine_Release/XEngine_Config/XEngine_Config.json index e889a4e6..f80b7483 100644 --- a/XEngine_Release/XEngine_Config/XEngine_Config.json +++ b/XEngine_Release/XEngine_Config/XEngine_Config.json @@ -1,10 +1,11 @@ { - "tszIPAddr": "10.0.1.88", + "tszIPAddr": "127.0.0.1", "bDeamon": 0, "nCenterPort": 5100, "nStorageDLPort": 5101, "nStorageUPPort": 5102, "nWebdavPort": 5103, + "nFTPPort": 5104, "XMax": { "MaxClient": 10000, "MaxQueue": 10000, @@ -12,7 +13,8 @@ "CenterThread": 2, "nStorageUPThread": 2, "nStorageDLThread": 2, - "nWebdavThread": 2 + "nWebdavThread": 2, + "nFTPThread": 2 }, "XTime": { "bHBTime": 0, @@ -20,7 +22,8 @@ "nTimeCheck": 3, "nCenterTimeOut": 5, "nStorageTimeOut": 5, - "nWebdavTimeOut": 5 + "nWebdavTimeOut": 5, + "nFTPTimeOut": 5 }, "XLog": { "LogFile": "./XEngine_XLog/XEngine_StorageApp.log", @@ -84,5 +87,10 @@ "bEnable": true, "nSDMax": 0, "nRVMax": 0 + }, + "XFtp": { + "nFTPStart": 50051, + "nFTPEnd": 51051, + "tszFTPDir": "D:\\XEngine_Storage\\XEngine_Source\\Debug" } } \ No newline at end of file diff --git a/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json b/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json index fa912628..388407c4 100644 --- a/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json +++ b/XEngine_Release/XEngine_Config/XEngine_VersionConfig.json @@ -1,5 +1,6 @@ { "XVer":[ + "3.24.0.1001 Build20260927", "3.23.0.1001 Build20260807", "3.22.0.1001 Build20260427", "3.21.0.1001 Build20250923", diff --git a/XEngine_Source/StorageModule_APIHelp/APIHelp_Define.h b/XEngine_Source/StorageModule_APIHelp/APIHelp_Define.h index ea12c6c7..479d544a 100644 --- a/XEngine_Source/StorageModule_APIHelp/APIHelp_Define.h +++ b/XEngine_Source/StorageModule_APIHelp/APIHelp_Define.h @@ -441,4 +441,63 @@ extern "C" bool APIHelp_Api_WDToUrl(LPCXSTR lpszUrl, XCHAR* ptszUrl); 意思:是否成功 备注: *********************************************************************/ -extern "C" bool APIHelp_Api_BSizeToStr(__int64x nSize, XCHAR* ptszSizeStr, int nType); \ No newline at end of file +extern "C" bool APIHelp_Api_BSizeToStr(__int64x nSize, XCHAR* ptszSizeStr, int nType); +/************************************************************************/ +/* 端口函数 */ +/************************************************************************/ +/******************************************************************** +函数名称:APIHelp_Port_Init +函数功能:初始化端口列表 + 参数.一:nPortStart + In/Out:In + 类型:整数型 + 可空:Y + 意思:可用起始端口 + 参数.二:nPortEnd + In/Out:In + 类型:整数型 + 可空:Y + 意思:可用结束端口 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool APIHelp_Port_Init(int nPortStart = 54000, int nPortEnd = 55000); +/******************************************************************** +函数名称:APIHelp_Port_Destroy +函数功能:销毁端口 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool APIHelp_Port_Destroy(); +/******************************************************************** +函数名称:APIHelp_Port_Get +函数功能:获取一个没有使用的端口 + 参数.一:pInt_Port + In/Out:Out + 类型:整数型指针 + 可空:N + 意思:输出获取到的端口 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool APIHelp_Port_Get(int* pInt_Port); +/******************************************************************** +函数名称:APIHelp_Port_Free +函数功能:还原一个端口 + 参数.一:nPort + In/Out:In + 类型:整数型 + 可空:N + 意思:输入要还原的端口号 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool APIHelp_Port_Free(int nPort); \ No newline at end of file diff --git a/XEngine_Source/StorageModule_APIHelp/APIHelp_Port/APIHelp_Port.cpp b/XEngine_Source/StorageModule_APIHelp/APIHelp_Port/APIHelp_Port.cpp new file mode 100644 index 00000000..18045084 --- /dev/null +++ b/XEngine_Source/StorageModule_APIHelp/APIHelp_Port/APIHelp_Port.cpp @@ -0,0 +1,132 @@ +#include "pch.h" +#include "APIHelp_Port.h" +/******************************************************************** +// Created: 2026/09/11 14:46:00 +// File Name: D:\XEngine_Storage\XEngine_Source\StorageModule_APIHelp\APIHelp_Port\APIHelp_Port.cpp +// File Path: D:\XEngine_Storage\XEngine_Source\StorageModule_APIHelp\APIHelp_Port +// File Base: APIHelp_Port +// File Ext: cpp +// Project: XEngine_Storage +// Repository: D:\XEngine_Storage (branch: develop) +// Author: qyt +// Purpose: 端口处理类 +// History: +*********************************************************************/ +CAPIHelp_Port::CAPIHelp_Port() +{ +} +CAPIHelp_Port::~CAPIHelp_Port() +{ +} +////////////////////////////////////////////////////////////////////////// +// 公有函数 +////////////////////////////////////////////////////////////////////////// +/******************************************************************** +函数名称:APIHelp_Port_Init +函数功能:初始化端口列表 + 参数.一:nPortStart + In/Out:In + 类型:整数型 + 可空:Y + 意思:可用起始端口 + 参数.二:nPortEnd + In/Out:In + 类型:整数型 + 可空:Y + 意思:可用结束端口 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CAPIHelp_Port::APIHelp_Port_Init(int nPortStart /* = 54000 */, int nPortEnd /* = 55000 */) +{ + APIHelp_IsErrorOccur = false; + + for (int i = nPortStart; i < nPortEnd; i++) + { + MODULEHELP_PORTINFO st_PortInfo = {}; + st_PortInfo.nPort = i; + st_PortInfo.bUsed = false; + + stl_ListPort.push_back(st_PortInfo); + } + return true; +} +/******************************************************************** +函数名称:APIHelp_Port_Destroy +函数功能:销毁端口 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CAPIHelp_Port::APIHelp_Port_Destroy() +{ + APIHelp_IsErrorOccur = false; + + stl_ListPort.clear(); + return true; +} +/******************************************************************** +函数名称:APIHelp_Port_Get +函数功能:获取一个没有使用的端口 + 参数.一:pInt_Port + In/Out:Out + 类型:整数型指针 + 可空:N + 意思:输出获取到的端口 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CAPIHelp_Port::APIHelp_Port_Get(int* pInt_Port) +{ + APIHelp_IsErrorOccur = false; + + if (NULL == pInt_Port) + { + APIHelp_IsErrorOccur = true; + APIHelp_dwErrorCode = ERROR_STORAGE_MODULE_APIHELP_PARAMENT; + return false; + } + std::lock_guard st_Lock(st_Locker); + for (auto stl_ListIterator = stl_ListPort.begin(); stl_ListIterator != stl_ListPort.end(); stl_ListIterator++) + { + if (!stl_ListIterator->bUsed) + { + stl_ListIterator->bUsed = true; + *pInt_Port = stl_ListIterator->nPort; + break; + } + } + return true; +} +/******************************************************************** +函数名称:APIHelp_Port_Free +函数功能:还原一个端口 + 参数.一:nPort + In/Out:In + 类型:整数型 + 可空:N + 意思:输入要还原的端口号 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CAPIHelp_Port::APIHelp_Port_Free(int nPort) +{ + APIHelp_IsErrorOccur = false; + + std::lock_guard st_Lock(st_Locker); + for (auto stl_ListIterator = stl_ListPort.begin(); stl_ListIterator != stl_ListPort.end(); stl_ListIterator++) + { + if (nPort == stl_ListIterator->nPort) + { + stl_ListIterator->bUsed = false; + } + } + return true; +} \ No newline at end of file diff --git a/XEngine_Source/StorageModule_APIHelp/APIHelp_Port/APIHelp_Port.h b/XEngine_Source/StorageModule_APIHelp/APIHelp_Port/APIHelp_Port.h new file mode 100644 index 00000000..924e9d52 --- /dev/null +++ b/XEngine_Source/StorageModule_APIHelp/APIHelp_Port/APIHelp_Port.h @@ -0,0 +1,34 @@ +#pragma once +/******************************************************************** +// Created: 2026/09/11 14:46:00 +// File Name: D:\XEngine_Storage\XEngine_Source\StorageModule_APIHelp\APIHelp_Port\APIHelp_Port.h +// File Path: D:\XEngine_Storage\XEngine_Source\StorageModule_APIHelp\APIHelp_Port +// File Base: APIHelp_Port +// File Ext: h +// Project: XEngine_Storage +// Repository: D:\XEngine_Storage (branch: develop) +// Author: qyt +// Purpose: 端口处理类 +// History: +*********************************************************************/ +typedef struct +{ + int nPort; + bool bUsed; +}MODULEHELP_PORTINFO; + +class CAPIHelp_Port +{ +public: + CAPIHelp_Port(); + ~CAPIHelp_Port(); +public: + bool APIHelp_Port_Init(int nPortStart = 54000, int nPortEnd = 55000); + bool APIHelp_Port_Destroy(); + bool APIHelp_Port_Get(int* pInt_Port); + bool APIHelp_Port_Free(int nPort); +private: + std::mutex st_Locker; +private: + std::list stl_ListPort; +}; \ No newline at end of file diff --git a/XEngine_Source/StorageModule_APIHelp/Makefile b/XEngine_Source/StorageModule_APIHelp/Makefile index b878bcde..19ba2b1d 100644 --- a/XEngine_Source/StorageModule_APIHelp/Makefile +++ b/XEngine_Source/StorageModule_APIHelp/Makefile @@ -6,7 +6,7 @@ LOADHDR = -I ./ -I ../XEngine_DependLibrary/XEngine_Module/jsoncpp LOADSO = -L ../XEngine_DependLibrary/XEngine_Module/jsoncpp LIB = -lXEngine_BaseLib -lXEngine_Cryption -lRfcComponents_HttpProtocol -lXEngine_SystemApi -ljsoncpp LIBEX = -OBJECTS = APIHelp_Distributed.o APIHelp_Api.o pch.o +OBJECTS = APIHelp_Distributed.o APIHelp_Api.o APIHelp_Port.o pch.o ifeq ($(RELEASE),1) FLAGS = -c -D _RELEASE @@ -55,6 +55,8 @@ APIHelp_Api.o:./APIHelp_Api/APIHelp_Api.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./APIHelp_Api/APIHelp_Api.cpp APIHelp_Cryption.o:./APIHelp_Cryption/APIHelp_Cryption.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./APIHelp_Cryption/APIHelp_Cryption.cpp +APIHelp_Port.o:./APIHelp_Port/APIHelp_Port.cpp + $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./APIHelp_Port/APIHelp_Port.cpp pch.o:./pch.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./pch.cpp diff --git a/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.def b/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.def index 7d462662..ec8e2ec6 100644 --- a/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.def +++ b/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.def @@ -21,4 +21,9 @@ EXPORTS APIHelp_Api_UrlStr APIHelp_Api_GetLastName APIHelp_Api_WDToUrl - APIHelp_Api_BSizeToStr \ No newline at end of file + APIHelp_Api_BSizeToStr + + APIHelp_Port_Init + APIHelp_Port_Destroy + APIHelp_Port_Get + APIHelp_Port_Free \ 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 d6a482e8..1e0680ef 100644 --- a/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj +++ b/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj @@ -136,6 +136,7 @@ Use pch.h 4819 + stdcpp20 Windows @@ -157,6 +158,7 @@ MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -175,6 +177,7 @@ true Use pch.h + stdcpp20 Windows @@ -191,6 +194,7 @@ true Use pch.h + stdcpp20 Windows @@ -212,6 +216,7 @@ MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -235,6 +240,7 @@ MultiThreaded 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -250,12 +256,14 @@ + + Create diff --git a/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj.filters b/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj.filters index 14ad6f8d..29ecf974 100644 --- a/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj.filters +++ b/XEngine_Source/StorageModule_APIHelp/StorageModule_APIHelp.vcxproj.filters @@ -25,6 +25,12 @@ {70c91174-907f-422b-8db6-f8ee8298447c} + + {1331dd62-d795-488f-b5d4-f19a55b301b3} + + + {ec0c3ded-f887-448b-b9de-8ea580cefcbe} + @@ -45,6 +51,9 @@ 头文件\APIHelp_Api + + 头文件\APIHelp_Port + @@ -59,6 +68,9 @@ 源文件\APIHelp_Api + + 源文件\APIHelp_Port + diff --git a/XEngine_Source/StorageModule_APIHelp/pch.cpp b/XEngine_Source/StorageModule_APIHelp/pch.cpp index 3e3fc05c..f5fc023b 100644 --- a/XEngine_Source/StorageModule_APIHelp/pch.cpp +++ b/XEngine_Source/StorageModule_APIHelp/pch.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "APIHelp_Distributed/APIHelp_Distributed.h" #include "APIHelp_Api/APIHelp_Api.h" +#include "APIHelp_Port/APIHelp_Port.h" /******************************************************************** // Created: 2021/07/08 16:40:04 // File Name: D:\XEngine_Storage\XEngine_Source\StorageModule_APIHelp\pch.cpp @@ -17,6 +18,7 @@ XLONG APIHelp_dwErrorCode = 0; ////////////////////////////////////////////////////////////////////////// CAPIHelp_Distributed m_APIDistributed; CAPIHelp_Api m_APIHelp; +CAPIHelp_Port m_APIPort; ////////////////////////////////////////////////////////////////////////// // 导出函数 ////////////////////////////////////////////////////////////////////////// @@ -105,4 +107,23 @@ extern "C" bool APIHelp_Api_WDToUrl(LPCXSTR lpszUrl, XCHAR* ptszUrl) extern "C" bool APIHelp_Api_BSizeToStr(__int64x nSize, XCHAR* ptszSizeStr, int nType) { return m_APIHelp.APIHelp_Api_BSizeToStr(nSize, ptszSizeStr, nType); +} +/************************************************************************/ +/* 端口函数 */ +/************************************************************************/ +extern "C" bool APIHelp_Port_Init(int nPortStart, int nPortEnd) +{ + return m_APIPort.APIHelp_Port_Init(nPortStart, nPortEnd); +} +extern "C" bool APIHelp_Port_Destroy() +{ + return m_APIPort.APIHelp_Port_Destroy(); +} +extern "C" bool APIHelp_Port_Get(int* pInt_Port) +{ + return m_APIPort.APIHelp_Port_Get(pInt_Port); +} +extern "C" bool APIHelp_Port_Free(int nPort) +{ + return m_APIPort.APIHelp_Port_Free(nPort); } \ No newline at end of file diff --git a/XEngine_Source/StorageModule_APIHelp/pch.h b/XEngine_Source/StorageModule_APIHelp/pch.h index 212e0e7e..a47bed49 100644 --- a/XEngine_Source/StorageModule_APIHelp/pch.h +++ b/XEngine_Source/StorageModule_APIHelp/pch.h @@ -17,11 +17,11 @@ #include #include #endif -#endif //PCH_H #include #include #include #include +#include #include using namespace std; #include @@ -80,4 +80,5 @@ extern XLONG APIHelp_dwErrorCode; #pragma comment(lib,"../Release/jsoncpp.lib") #endif #endif -#endif \ No newline at end of file +#endif +#endif //PCH_H \ No newline at end of file diff --git a/XEngine_Source/StorageModule_BTorrent/pch.h b/XEngine_Source/StorageModule_BTorrent/pch.h index e45d76b1..7fdb0e4c 100644 --- a/XEngine_Source/StorageModule_BTorrent/pch.h +++ b/XEngine_Source/StorageModule_BTorrent/pch.h @@ -12,7 +12,6 @@ #include "framework.h" #include #endif -#endif //PCH_H #include #include #include @@ -58,4 +57,5 @@ extern XLONG BTDload_dwErrorCode; #ifdef _MSC_BUILD #pragma comment(lib,"XEngine_BaseLib/XEngine_BaseLib.lib") -#endif \ No newline at end of file +#endif +#endif //PCH_H \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Config/Config_Define.h b/XEngine_Source/StorageModule_Config/Config_Define.h index 1f74a8f6..8f81405d 100644 --- a/XEngine_Source/StorageModule_Config/Config_Define.h +++ b/XEngine_Source/StorageModule_Config/Config_Define.h @@ -20,6 +20,7 @@ typedef struct tag_XEngine_ServerConfig bool bReuseraddr; int nCenterPort; int nWebdavPort; + int nFTPPort; int nStorageDLPort; int nStorageUPPort; struct @@ -35,6 +36,7 @@ typedef struct tag_XEngine_ServerConfig int nWebdavThread; int nStorageDLThread; int nStorageUPThread; + int nFTPThread; }st_XMax; struct { @@ -44,6 +46,7 @@ typedef struct tag_XEngine_ServerConfig int nCenterTimeOut; int nStorageTimeOut; int nWebdavTimeOut; + int nFTPTimeOut; }st_XTime; struct { @@ -118,6 +121,12 @@ typedef struct tag_XEngine_ServerConfig int nRVMax; bool bEnable; }st_XAction; + struct + { + XCHAR tszFTPDir[XPATH_MID]; + int nFTPStart; + int nFTPEnd; + }st_XFtp; struct { list *pStl_ListStorage; diff --git a/XEngine_Source/StorageModule_Config/Config_Error.h b/XEngine_Source/StorageModule_Config/Config_Error.h index c7bdff06..63086716 100644 --- a/XEngine_Source/StorageModule_Config/Config_Error.h +++ b/XEngine_Source/StorageModule_Config/Config_Error.h @@ -24,7 +24,9 @@ #define ERROR_XENGINE_BLOGIC_CONFIG_JSON_XPROXY 0x0020009 #define ERROR_XENGINE_BLOGIC_CONFIG_JSON_P2XP 0x002000A #define ERROR_XENGINE_BLOGIC_CONFIG_JSON_CERT 0x002000B -#define ERROR_XENGINE_BLOGIC_CONFIG_JSON_CREPORT 0x002000D +#define ERROR_XENGINE_BLOGIC_CONFIG_JSON_REPORT 0x002000D +#define ERROR_XENGINE_BLOGIC_CONFIG_JSON_ACTION 0x002000E +#define ERROR_XENGINE_BLOGIC_CONFIG_JSON_FTP 0x002000F #define ERROR_XENGINE_BLOGIC_CONFIG_JSON_LBDISTRIBUTED 0x0020010 #define ERROR_XENGINE_BLOGIC_CONFIG_JSON_LBLOCATION 0x0020011 diff --git a/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp b/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp index 2f3ec423..87a27a5f 100644 --- a/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp +++ b/XEngine_Source/StorageModule_Config/Config_Json/Config_Json.cpp @@ -88,8 +88,9 @@ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG pSt_ServerConfig->nWebdavPort = st_JsonRoot["nWebdavPort"].asInt(); pSt_ServerConfig->nStorageDLPort = st_JsonRoot["nStorageDLPort"].asInt(); pSt_ServerConfig->nStorageUPPort = st_JsonRoot["nStorageUPPort"].asInt(); + pSt_ServerConfig->nFTPPort = st_JsonRoot["nFTPPort"].asInt(); - if (st_JsonRoot["XMax"].empty() || (7 != st_JsonRoot["XMax"].size())) + if (st_JsonRoot["XMax"].empty() || (8 != st_JsonRoot["XMax"].size())) { Config_IsErrorOccur = true; Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_XMAX; @@ -103,8 +104,9 @@ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG pSt_ServerConfig->st_XMax.nStorageUPThread = st_JsonXMax["nStorageUPThread"].asInt(); pSt_ServerConfig->st_XMax.nStorageDLThread = st_JsonXMax["nStorageDLThread"].asInt(); pSt_ServerConfig->st_XMax.nWebdavThread = st_JsonXMax["nWebdavThread"].asInt(); + pSt_ServerConfig->st_XMax.nFTPThread = st_JsonXMax["nFTPThread"].asInt(); - if (st_JsonRoot["XTime"].empty() || (6 != st_JsonRoot["XTime"].size())) + if (st_JsonRoot["XTime"].empty() || (7 != st_JsonRoot["XTime"].size())) { Config_IsErrorOccur = true; Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_XTIME; @@ -117,6 +119,7 @@ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG pSt_ServerConfig->st_XTime.nCenterTimeOut = st_JsonXTime["nCenterTimeOut"].asInt(); pSt_ServerConfig->st_XTime.nStorageTimeOut = st_JsonXTime["nStorageTimeOut"].asInt(); pSt_ServerConfig->st_XTime.nWebdavTimeOut = st_JsonXTime["nWebdavTimeOut"].asInt(); + pSt_ServerConfig->st_XTime.nFTPTimeOut = st_JsonXTime["nFTPTimeOut"].asInt(); if (st_JsonRoot["XLog"].empty() || (5 != st_JsonRoot["XLog"].size())) { @@ -228,7 +231,7 @@ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG if (st_JsonRoot["XReport"].empty() || (3 != st_JsonRoot["XReport"].size())) { Config_IsErrorOccur = true; - Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_CREPORT; + Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_REPORT; return false; } Json::Value st_JsonReport = st_JsonRoot["XReport"]; @@ -239,13 +242,24 @@ bool CConfig_Json::Config_Json_File(LPCXSTR lpszConfigFile, XENGINE_SERVERCONFIG if (st_JsonRoot["XAction"].empty() || (3 != st_JsonRoot["XAction"].size())) { Config_IsErrorOccur = true; - Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_CREPORT; + Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_ACTION; return false; } Json::Value st_JsonXAction = st_JsonRoot["XAction"]; pSt_ServerConfig->st_XAction.bEnable = st_JsonXAction["bEnable"].asBool(); pSt_ServerConfig->st_XAction.nSDMax = st_JsonXAction["nSDMax"].asInt(); pSt_ServerConfig->st_XAction.nRVMax = st_JsonXAction["nRVMax"].asInt(); + + if (st_JsonRoot["XFtp"].empty() || (3 != st_JsonRoot["XFtp"].size())) + { + Config_IsErrorOccur = true; + Config_dwErrorCode = ERROR_XENGINE_BLOGIC_CONFIG_JSON_FTP; + return false; + } + Json::Value st_JsonXFtp = st_JsonRoot["XFtp"]; + pSt_ServerConfig->st_XFtp.nFTPStart = st_JsonXFtp["nFTPStart"].asInt(); + pSt_ServerConfig->st_XFtp.nFTPEnd = st_JsonXFtp["nFTPEnd"].asInt(); + _tcsxcpy(pSt_ServerConfig->st_XFtp.tszFTPDir, st_JsonXFtp["tszFTPDir"].asCString()); return true; } /******************************************************************** diff --git a/XEngine_Source/StorageModule_Config/pch.h b/XEngine_Source/StorageModule_Config/pch.h index d506fe0c..ffedd7a9 100644 --- a/XEngine_Source/StorageModule_Config/pch.h +++ b/XEngine_Source/StorageModule_Config/pch.h @@ -12,7 +12,6 @@ #include "framework.h" #include #endif -#endif //PCH_H #include #include #include @@ -58,4 +57,5 @@ extern XLONG Config_dwErrorCode; #pragma comment(lib,"../Release/jsoncpp.lib") #endif #endif -#endif \ No newline at end of file +#endif +#endif //PCH_H \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj b/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj index 989dd4be..856ed213 100644 --- a/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj +++ b/XEngine_Source/StorageModule_Database/StorageModule_Database.vcxproj @@ -135,6 +135,7 @@ true Use pch.h + stdcpp20 Windows @@ -155,6 +156,7 @@ pch.h /utf-8 %(AdditionalOptions) MultiThreadedDLL + stdcpp20 Windows @@ -173,6 +175,7 @@ true Use pch.h + stdcpp20 Windows @@ -189,6 +192,7 @@ true Use pch.h + stdcpp20 Windows @@ -209,6 +213,7 @@ pch.h /utf-8 %(AdditionalOptions) MultiThreadedDLL + stdcpp20 Windows @@ -230,6 +235,7 @@ Use pch.h /utf-8 %(AdditionalOptions) + stdcpp20 Windows diff --git a/XEngine_Source/StorageModule_Database/pch.h b/XEngine_Source/StorageModule_Database/pch.h index 188a06c9..86611a97 100644 --- a/XEngine_Source/StorageModule_Database/pch.h +++ b/XEngine_Source/StorageModule_Database/pch.h @@ -21,7 +21,6 @@ #include #include #endif -#endif //PCH_H #include #include #include @@ -71,3 +70,4 @@ extern XLONG Database_dwErrorCode; #pragma comment(lib,"XEngine_HelpComponents/HelpComponents_DataBase.lib") #pragma comment(lib,"XEngine_SystemSdk/XEngine_SystemApi.lib") #endif +#endif //PCH_H \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj b/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj index 6490e353..1c0e7f80 100644 --- a/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj +++ b/XEngine_Source/StorageModule_Protocol/StorageModule_Protocol.vcxproj @@ -136,7 +136,7 @@ Use pch.h 4819 - stdcpp17 + stdcpp20 Windows @@ -158,6 +158,7 @@ MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -176,6 +177,7 @@ true Use pch.h + stdcpp20 Windows @@ -192,6 +194,7 @@ true Use pch.h + stdcpp20 Windows @@ -212,6 +215,7 @@ pch.h MultiThreadedDLL /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -234,6 +238,7 @@ pch.h MultiThreaded /utf-8 %(AdditionalOptions) + stdcpp20 Windows diff --git a/XEngine_Source/StorageModule_Protocol/pch.h b/XEngine_Source/StorageModule_Protocol/pch.h index b0b23c1d..2445a372 100644 --- a/XEngine_Source/StorageModule_Protocol/pch.h +++ b/XEngine_Source/StorageModule_Protocol/pch.h @@ -23,7 +23,6 @@ #include #include #endif -#endif //PCH_H #include #include #include @@ -102,4 +101,5 @@ extern XLONG Protocol_dwErrorCode; #pragma comment(lib,"../Release/StorageModule_APIHelp.lib") #endif #endif -#endif \ No newline at end of file +#endif +#endif //PCH_H \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Session/Makefile b/XEngine_Source/StorageModule_Session/Makefile index 1cb6ef75..95389461 100644 --- a/XEngine_Source/StorageModule_Session/Makefile +++ b/XEngine_Source/StorageModule_Session/Makefile @@ -6,7 +6,7 @@ LOADHDR = -I ./ LOADSO = LIB = -lXEngine_BaseLib -lNetHelp_APIAddr LIBEX = -OBJECTS = Session_DLStroage.o Session_UPStroage.o Session_Action.o Session_Webdav.o pch.o +OBJECTS = Session_DLStroage.o Session_UPStroage.o Session_Action.o Session_Webdav.o Session_FTPContral.o pch.o ifeq ($(RELEASE),1) FLAGS = -c -D _RELEASE @@ -58,6 +58,8 @@ Session_Action.o:./Session_Action/Session_Action.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./Session_Action/Session_Action.cpp Session_Webdav.o:./Session_Webdav/Session_Webdav.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./Session_Webdav/Session_Webdav.cpp +Session_FTPContral.o:./Session_FTPContral/Session_FTPContral.cpp + $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./Session_FTPContral/Session_FTPContral.cpp pch.o:./pch.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./pch.cpp diff --git a/XEngine_Source/StorageModule_Session/Session_Define.h b/XEngine_Source/StorageModule_Session/Session_Define.h index d653feac..4a88ca07 100644 --- a/XEngine_Source/StorageModule_Session/Session_Define.h +++ b/XEngine_Source/StorageModule_Session/Session_Define.h @@ -637,4 +637,179 @@ extern "C" bool Session_Webdav_Get(LPCXSTR lpszFileName, XENGINE_WEBDAVLOCK* pSt 意思:是否成功 备注: *********************************************************************/ -extern "C" bool Session_Webdav_Delete(LPCXSTR lpszFileName); \ No newline at end of file +extern "C" bool Session_Webdav_Delete(LPCXSTR lpszFileName); +/************************************************************************/ +/* FTP会话导出的函数 */ +/************************************************************************/ +/******************************************************************** +函数名称:Session_FTP_Insert +函数功能:插入一个会话 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入客户端地址 + 参数.二:bUPLoader + In/Out:In + 类型:逻辑型 + 可空:N + 意思:输入是否为上传会话 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool Session_FTP_Insert(LPCXSTR lpszClientAddr, bool bUPLoader); +/******************************************************************** +函数名称:Session_FTP_Set +函数功能:设置 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入客户端地址 + 参数.二:lpszFileName + In/Out:In + 类型:常量字符指针 + 可空:Y + 意思:输入要操作的文件 + 参数.三:lpszFilePath + In/Out:In + 类型:常量字符指针 + 可空:Y + 意思:输入要操作的文件路径 + 参数.四:lpszAlisName + In/Out:In + 类型:常量字符指针 + 可空:Y + 意思:输入路径别名 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool Session_FTP_Set(LPCXSTR lpszClientAddr, LPCXSTR lpszFileName = NULL, LPCXSTR lpszFilePath = NULL, LPCXSTR lpszAlisName = NULL); +/******************************************************************** +函数名称:Session_FTP_Get +函数功能:获取信息 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:ptszFileName + In/Out:Out + 类型:字符指针 + 可空:Y + 意思:输出操作的文件 + 参数.三:ptszFilePath + In/Out:Out + 类型:字符指针 + 可空:Y + 意思:输出操作的文件路径 + 参数.四:ptszAlisPath + In/Out:Out + 类型:字符指针 + 可空:Y + 意思:输出操作的路径别名 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool Session_FTP_Get(LPCXSTR lpszClientAddr, XCHAR* ptszFileName = NULL, XCHAR* ptszFilePath = NULL, XCHAR* ptszAlisPath = NULL); +/******************************************************************** +函数名称:Session_FTP_SetSocket +函数功能:设置网络句柄 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:nPort + In/Out:In + 类型:整数型 + 可空:N + 意思:输入操作的端口 + 参数.二:xhToken + In/Out:In + 类型:句柄 + 可空:N + 意思:输入操作的套接字句柄 +返回值 + 类型:逻辑型 + 意思:是否设置成功 +备注: +*********************************************************************/ +extern "C" bool Session_FTP_SetSocket(LPCXSTR lpszClientAddr, int nPort, XHANDLE xhToken); +/******************************************************************** +函数名称:Session_FTP_GetSocket +函数功能:获取网络句柄 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:pInt_Port + In/Out:Out + 类型:整数型指针 + 可空:Y + 意思:输出操作的端口 +返回值 + 类型:句柄 + 意思:返回获取到的网络句柄 +备注: +*********************************************************************/ +extern "C" XHANDLE Session_FTP_GetSocket(LPCXSTR lpszClientAddr, int* pInt_Port = NULL); +/******************************************************************** +函数名称:Session_FTP_SetRetr +函数功能:设置断点续传 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:nPos + In/Out:In + 类型:整数型 + 可空:N + 意思:输入断点续传位置 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool Session_FTP_SetRetr(LPCXSTR lpszClientAddr, __int64u nPos); +/******************************************************************** +函数名称:Session_FTP_GetRetr +函数功能:获取断点续传 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:pInt_Pos + In/Out:Out + 类型:整数型指针 + 可空:Y + 意思:输出断点续传位置 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool Session_FTP_GetRetr(LPCXSTR lpszClientAddr, __int64u* pInt_Pos); +/******************************************************************** +函数名称:Session_FTP_Delete +函数功能:删除一个会话 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +extern "C" bool Session_FTP_Delete(LPCXSTR lpszClientAddr); \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Session/Session_FTPContral/Session_FTPContral.cpp b/XEngine_Source/StorageModule_Session/Session_FTPContral/Session_FTPContral.cpp new file mode 100644 index 00000000..ea6606ce --- /dev/null +++ b/XEngine_Source/StorageModule_Session/Session_FTPContral/Session_FTPContral.cpp @@ -0,0 +1,335 @@ +#include "pch.h" +#include "Session_FTPContral.h" +/******************************************************************** +// Created: 2024/08/05 16:14:56 +// File Name: D:\XEngine_Storage\XEngine_Source\StorageModule_Session\Session_FTPContral\Session_FTPContral.cpp +// File Path: D:\XEngine_Storage\XEngine_Source\StorageModule_Session\Session_FTPContral +// File Base: Session_FTPContral +// File Ext: cpp +// Project: XEngine +// Author: qyt +// Purpose: FTP会话 +// History: +*********************************************************************/ +CSession_FTPContral::CSession_FTPContral() +{ +} +CSession_FTPContral::~CSession_FTPContral() +{ +} +////////////////////////////////////////////////////////////////////////// +// 公有函数 +////////////////////////////////////////////////////////////////////////// +/******************************************************************** +函数名称:Session_FTP_Insert +函数功能:插入一个会话 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入客户端地址 + 参数.二:bUPLoader + In/Out:In + 类型:逻辑型 + 可空:N + 意思:输入是否为上传会话 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CSession_FTPContral::Session_FTP_Insert(LPCXSTR lpszClientAddr, bool bUPLoader) +{ + Session_IsErrorOccur = false; + + SESSION_FTPCONTRAL st_FTPSession = {}; + + st_FTPSession.nTimeStart = time(NULL); + st_FTPSession.bUPLoader = bUPLoader; + + st_Locker.lock(); + stl_MapAction.insert(make_pair(lpszClientAddr, st_FTPSession)); + st_Locker.unlock(); + return true; +} +/******************************************************************** +函数名称:Session_FTP_Set +函数功能:设置 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入客户端地址 + 参数.二:lpszFileName + In/Out:In + 类型:常量字符指针 + 可空:Y + 意思:输入要操作的文件 + 参数.三:lpszFilePath + In/Out:In + 类型:常量字符指针 + 可空:Y + 意思:输入要操作的文件路径 + 参数.四:lpszAlisName + In/Out:In + 类型:常量字符指针 + 可空:Y + 意思:输入路径别名 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CSession_FTPContral::Session_FTP_Set(LPCXSTR lpszClientAddr, LPCXSTR lpszFileName, LPCXSTR lpszFilePath, LPCXSTR lpszAlisName) +{ + Session_IsErrorOccur = false; + + std::shared_lock st_AutoLock(st_Locker); + + unordered_map::iterator stl_MapIterator = stl_MapAction.find(lpszClientAddr); + if (stl_MapIterator == stl_MapAction.end()) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_NOTFOUND; + return NULL; + } + + if (NULL != lpszFileName) + { + memset(stl_MapIterator->second.tszFileName, '\0', sizeof(stl_MapIterator->second.tszFileName)); + _tcsxcpy(stl_MapIterator->second.tszFileName, lpszFileName); + } + if (NULL != lpszFilePath) + { + memset(stl_MapIterator->second.tszFilePath, '\0', sizeof(stl_MapIterator->second.tszFilePath)); + _tcsxcpy(stl_MapIterator->second.tszFilePath, lpszFilePath); + } + if (NULL != lpszAlisName) + { + memset(stl_MapIterator->second.tszAlisPath, '\0', sizeof(stl_MapIterator->second.tszAlisPath)); + _tcsxcpy(stl_MapIterator->second.tszAlisPath, lpszAlisName); + } + return true; +} +/******************************************************************** +函数名称:Session_FTP_Get +函数功能:获取信息 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:ptszFileName + In/Out:Out + 类型:字符指针 + 可空:Y + 意思:输出操作的文件 + 参数.三:ptszFilePath + In/Out:Out + 类型:字符指针 + 可空:Y + 意思:输出操作的文件路径 + 参数.四:ptszAlisPath + In/Out:Out + 类型:字符指针 + 可空:Y + 意思:输出操作的路径别名 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CSession_FTPContral::Session_FTP_Get(LPCXSTR lpszClientAddr, XCHAR* ptszFileName, XCHAR* ptszFilePath, XCHAR* ptszAlisPath) +{ + Session_IsErrorOccur = false; + + std::shared_lock st_AutoLock(st_Locker); + unordered_map::iterator stl_MapIterator = stl_MapAction.find(lpszClientAddr); + if (stl_MapIterator == stl_MapAction.end()) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_NOTFOUND; + return NULL; + } + + if (NULL != ptszFilePath) + { + _tcsxcpy(ptszFilePath, stl_MapIterator->second.tszFilePath); + } + if (NULL != ptszFileName) + { + _tcsxcpy(ptszFileName, stl_MapIterator->second.tszFileName); + } + if (NULL != ptszAlisPath) + { + _tcsxcpy(ptszAlisPath, stl_MapIterator->second.tszAlisPath); + } + return true; +} +/******************************************************************** +函数名称:Session_FTP_SetSocket +函数功能:设置网络句柄 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:nPort + In/Out:In + 类型:整数型 + 可空:N + 意思:输入操作的端口 + 参数.二:xhToken + In/Out:In + 类型:句柄 + 可空:N + 意思:输入操作的套接字句柄 +返回值 + 类型:逻辑型 + 意思:是否设置成功 +备注: +*********************************************************************/ +bool CSession_FTPContral::Session_FTP_SetSocket(LPCXSTR lpszClientAddr, int nPort, XHANDLE xhToken) +{ + Session_IsErrorOccur = false; + std::shared_lock st_AutoLock(st_Locker); + unordered_map::iterator stl_MapIterator = stl_MapAction.find(lpszClientAddr); + if (stl_MapIterator == stl_MapAction.end()) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_NOTFOUND; + return NULL; + } + stl_MapIterator->second.nPort = nPort; + stl_MapIterator->second.xhToken = xhToken; + return true; +} +/******************************************************************** +函数名称:Session_FTP_GetSocket +函数功能:获取网络句柄 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:pInt_Port + In/Out:Out + 类型:整数型指针 + 可空:Y + 意思:输出操作的端口 +返回值 + 类型:句柄 + 意思:返回获取到的网络句柄 +备注: +*********************************************************************/ +XHANDLE CSession_FTPContral::Session_FTP_GetSocket(LPCXSTR lpszClientAddr, int* pInt_Port) +{ + Session_IsErrorOccur = false; + + std::shared_lock st_AutoLock(st_Locker); + unordered_map::iterator stl_MapIterator = stl_MapAction.find(lpszClientAddr); + if (stl_MapIterator == stl_MapAction.end()) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_NOTFOUND; + return NULL; + } + + if (NULL != pInt_Port) + { + *pInt_Port = stl_MapIterator->second.nPort; + } + return stl_MapIterator->second.xhToken; +} +/******************************************************************** +函数名称:Session_FTP_SetRetr +函数功能:设置断点续传 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:nPos + In/Out:In + 类型:整数型 + 可空:N + 意思:输入断点续传位置 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CSession_FTPContral::Session_FTP_SetRetr(LPCXSTR lpszClientAddr, __int64u nPos) +{ + Session_IsErrorOccur = false; + + std::shared_lock st_AutoLock(st_Locker); + unordered_map::iterator stl_MapIterator = stl_MapAction.find(lpszClientAddr); + if (stl_MapIterator == stl_MapAction.end()) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_NOTFOUND; + return false; + } + stl_MapIterator->second.nPos = nPos; + return true; +} +/******************************************************************** +函数名称:Session_FTP_GetRetr +函数功能:获取断点续传 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 + 参数.二:pInt_Pos + In/Out:Out + 类型:整数型指针 + 可空:Y + 意思:输出断点续传位置 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CSession_FTPContral::Session_FTP_GetRetr(LPCXSTR lpszClientAddr, __int64u* pInt_Pos) +{ + Session_IsErrorOccur = false; + + std::shared_lock st_AutoLock(st_Locker); + unordered_map::iterator stl_MapIterator = stl_MapAction.find(lpszClientAddr); + if (stl_MapIterator == stl_MapAction.end()) + { + Session_IsErrorOccur = true; + Session_dwErrorCode = ERROR_STORAGE_MODULE_SESSION_NOTFOUND; + return false; + } + *pInt_Pos = stl_MapIterator->second.nPort; + return true; +} +/******************************************************************** +函数名称:Session_FTP_Delete +函数功能:删除一个会话 + 参数.一:lpszClientAddr + In/Out:In + 类型:常量字符指针 + 可空:N + 意思:输入要操作的客户端地址 +返回值 + 类型:逻辑型 + 意思:是否成功 +备注: +*********************************************************************/ +bool CSession_FTPContral::Session_FTP_Delete(LPCXSTR lpszClientAddr) +{ + Session_IsErrorOccur = false; + + std::unique_lock st_AutoLock(st_Locker); + + unordered_map::iterator stl_MapIterator = stl_MapAction.find(lpszClientAddr); + if (stl_MapIterator != stl_MapAction.end()) + { + stl_MapAction.erase(stl_MapIterator); + } + return true; +} \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Session/Session_FTPContral/Session_FTPContral.h b/XEngine_Source/StorageModule_Session/Session_FTPContral/Session_FTPContral.h new file mode 100644 index 00000000..f99c69b9 --- /dev/null +++ b/XEngine_Source/StorageModule_Session/Session_FTPContral/Session_FTPContral.h @@ -0,0 +1,43 @@ +#pragma once +/******************************************************************** +// Created: 2024/08/05 16:14:56 +// File Name: D:\XEngine_Storage\XEngine_Source\StorageModule_Session\Session_FTPContral\Session_FTPContral.h +// File Path: D:\XEngine_Storage\XEngine_Source\StorageModule_Session\Session_FTPContral +// File Base: Session_FTPContral +// File Ext: h +// Project: XEngine +// Author: qyt +// Purpose: FTP会话 +// History: +*********************************************************************/ +typedef struct +{ + XCHAR tszFileName[XPATH_MAX]; + XCHAR tszFilePath[XPATH_MAX]; + XCHAR tszAlisPath[XPATH_MAX]; + __int64u nPos; + time_t nTimeStart; + int nPort; + bool bUPLoader; + XHANDLE xhToken; +}SESSION_FTPCONTRAL; + +class CSession_FTPContral +{ +public: + CSession_FTPContral(); + ~CSession_FTPContral(); +public: + bool Session_FTP_Insert(LPCXSTR lpszClientAddr, bool bUPLoader); + bool Session_FTP_Set(LPCXSTR lpszClientAddr, LPCXSTR lpszFileName = NULL, LPCXSTR lpszFilePath = NULL, LPCXSTR lpszAlisName = NULL); + bool Session_FTP_Get(LPCXSTR lpszClientAddr, XCHAR* ptszFileName = NULL, XCHAR* ptszFilePath = NULL, XCHAR* ptszAlisPath = NULL); + bool Session_FTP_SetSocket(LPCXSTR lpszClientAddr, int nPort, XHANDLE xhToken); + XHANDLE Session_FTP_GetSocket(LPCXSTR lpszClientAddr, int* pInt_Port = NULL); + bool Session_FTP_SetRetr(LPCXSTR lpszClientAddr, __int64u nPos); + bool Session_FTP_GetRetr(LPCXSTR lpszClientAddr, __int64u* pInt_Pos); + bool Session_FTP_Delete(LPCXSTR lpszClientAddr); +private: + shared_mutex st_Locker; +private: + unordered_map stl_MapAction; +}; \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Session/StorageModule_Session.def b/XEngine_Source/StorageModule_Session/StorageModule_Session.def index 5d442f0d..65fdf40c 100644 --- a/XEngine_Source/StorageModule_Session/StorageModule_Session.def +++ b/XEngine_Source/StorageModule_Session/StorageModule_Session.def @@ -36,4 +36,13 @@ EXPORTS Session_Webdav_Insert Session_Webdav_Get - Session_Webdav_Delete \ No newline at end of file + Session_Webdav_Delete + + Session_FTP_Insert + Session_FTP_Set + Session_FTP_Get + Session_FTP_SetSocket + Session_FTP_GetSocket + Session_FTP_SetRetr + Session_FTP_GetRetr + Session_FTP_Delete \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj b/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj index 80b07130..ab895d8d 100644 --- a/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj +++ b/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj @@ -136,6 +136,7 @@ Use pch.h 4819 + stdcpp20 Windows @@ -157,6 +158,7 @@ MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -175,6 +177,7 @@ true Use pch.h + stdcpp20 Windows @@ -191,6 +194,7 @@ true Use pch.h + stdcpp20 Windows @@ -211,6 +215,7 @@ pch.h MultiThreadedDLL /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -233,6 +238,7 @@ pch.h MultiThreaded /utf-8 %(AdditionalOptions) + stdcpp20 Windows @@ -249,6 +255,7 @@ + @@ -264,6 +271,7 @@ Create + diff --git a/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj.filters b/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj.filters index b9bc978e..3f11654d 100644 --- a/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj.filters +++ b/XEngine_Source/StorageModule_Session/StorageModule_Session.vcxproj.filters @@ -31,6 +31,12 @@ {ada350db-0019-4645-b441-fe09e2d37c31} + + {525464d9-6fe5-4f5a-acf5-61f0d9219561} + + + {ae4fe5fb-dab3-41d8-a51d-106684a4587c} + @@ -57,6 +63,9 @@ 头文件\Session_Webdav + + 头文件\Session_FTPContral + @@ -77,6 +86,9 @@ 源文件\Session_Webdav + + 源文件\Session_FTPContral + diff --git a/XEngine_Source/StorageModule_Session/pch.cpp b/XEngine_Source/StorageModule_Session/pch.cpp index e9eb29c4..0f6e3e37 100644 --- a/XEngine_Source/StorageModule_Session/pch.cpp +++ b/XEngine_Source/StorageModule_Session/pch.cpp @@ -3,6 +3,7 @@ #include "Session_Stroage/Session_UPStroage.h" #include "Session_Action/Session_Action.h" #include "Session_Webdav/Session_Webdav.h" +#include "Session_FTPContral/Session_FTPContral.h" /******************************************************************** // Created: 2021/06/02 14:38:34 // File Name: D:\XEngine_Storage\StorageModule_Session\pch.cpp @@ -21,6 +22,7 @@ CSession_DLStroage m_DLStorage; CSession_UPStroage m_UPStorage; CSession_Action m_Action; CSession_Webdav m_Webdav; +CSession_FTPContral m_FTPContral; ////////////////////////////////////////////////////////////////////////// // 导出函数定义机器 ////////////////////////////////////////////////////////////////////////// @@ -164,4 +166,39 @@ extern "C" bool Session_Webdav_Get(LPCXSTR lpszFileName, XENGINE_WEBDAVLOCK* pSt extern "C" bool Session_Webdav_Delete(LPCXSTR lpszFileName) { return m_Webdav.Session_Webdav_Delete(lpszFileName); +} +/************************************************************************/ +/* FTP会话导出的函数 */ +/************************************************************************/ +extern "C" bool Session_FTP_Insert(LPCXSTR lpszClientAddr, bool bUPLoader) +{ + return m_FTPContral.Session_FTP_Insert(lpszClientAddr, bUPLoader); +} +extern "C" bool Session_FTP_Set(LPCXSTR lpszClientAddr, LPCXSTR lpszFileName, LPCXSTR lpszFilePath, LPCXSTR lpszAlisName) +{ + return m_FTPContral.Session_FTP_Set(lpszClientAddr, lpszFileName, lpszFilePath, lpszAlisName); +} +extern "C" bool Session_FTP_Get(LPCXSTR lpszClientAddr, XCHAR* ptszFileName, XCHAR* ptszFilePath, XCHAR* ptszAlisPath) +{ + return m_FTPContral.Session_FTP_Get(lpszClientAddr, ptszFileName, ptszFilePath, ptszAlisPath); +} +extern "C" bool Session_FTP_SetSocket(LPCXSTR lpszClientAddr, int nPort, XHANDLE xhToken) +{ + return m_FTPContral.Session_FTP_SetSocket(lpszClientAddr, nPort, xhToken); +} +extern "C" XHANDLE Session_FTP_GetSocket(LPCXSTR lpszClientAddr, int* pInt_Port) +{ + return m_FTPContral.Session_FTP_GetSocket(lpszClientAddr, pInt_Port); +} +extern "C" bool Session_FTP_SetRetr(LPCXSTR lpszClientAddr, __int64u nPos) +{ + return m_FTPContral.Session_FTP_SetRetr(lpszClientAddr, nPos); +} +extern "C" bool Session_FTP_GetRetr(LPCXSTR lpszClientAddr, __int64u* pInt_Pos) +{ + return m_FTPContral.Session_FTP_GetRetr(lpszClientAddr, pInt_Pos); +} +extern "C" bool Session_FTP_Delete(LPCXSTR lpszClientAddr) +{ + return m_FTPContral.Session_FTP_Delete(lpszClientAddr); } \ No newline at end of file diff --git a/XEngine_Source/StorageModule_Session/pch.h b/XEngine_Source/StorageModule_Session/pch.h index 799b6f9c..8c291a23 100644 --- a/XEngine_Source/StorageModule_Session/pch.h +++ b/XEngine_Source/StorageModule_Session/pch.h @@ -15,7 +15,6 @@ #else #include #endif -#endif //PCH_H #include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include #include using namespace std; @@ -57,4 +57,5 @@ extern XLONG Session_dwErrorCode; #ifdef _MSC_BUILD #pragma comment(lib,"XEngine_BaseLib/XEngine_BaseLib.lib") #pragma comment(lib,"XEngine_NetHelp/NetHelp_APIAddr.lib") -#endif \ No newline at end of file +#endif +#endif //PCH_H \ No newline at end of file diff --git a/XEngine_Source/VSCopy_Arm64.bat b/XEngine_Source/VSCopy_Arm64.bat index 450a7fb7..eec2b63d 100644 --- a/XEngine_Source/VSCopy_Arm64.bat +++ b/XEngine_Source/VSCopy_Arm64.bat @@ -16,4 +16,5 @@ copy /y "%XEngine_LibArm64%\XEngine_NetHelp\NetHelp_XSocket.dll" "./" copy /y "%XEngine_LibArm64%\XEngine_HelpComponents\HelpComponents_DataBase.dll" "./" copy /y "%XEngine_LibArm64%\XEngine_HelpComponents\HelpComponents_XLog.dll" "./" -copy /y "%XEngine_LibArm64%\XEngine_RfcComponents\RfcComponents_HttpProtocol.dll" "./" \ No newline at end of file +copy /y "%XEngine_LibArm64%\XEngine_RfcComponents\RfcComponents_HttpProtocol.dll" "./" +copy /y "%XEngine_LibArm64%\XEngine_RfcComponents\RfcComponents_FTPProtocol.dll" "./" \ No newline at end of file diff --git a/XEngine_Source/VSCopy_Debug.bat b/XEngine_Source/VSCopy_Debug.bat index 654185d3..c0dd8f28 100644 --- a/XEngine_Source/VSCopy_Debug.bat +++ b/XEngine_Source/VSCopy_Debug.bat @@ -16,4 +16,5 @@ copy /y "D:\XEngine\XEngine_SourceCode\Debug\NetHelp_XSocket.dll" "./" copy /y "D:\XEngine\XEngine_SourceCode\Debug\HelpComponents_DataBase.dll" "./" copy /y "D:\XEngine\XEngine_SourceCode\Debug\HelpComponents_XLog.dll" "./" -copy /y "D:\XEngine\XEngine_SourceCode\Debug\RfcComponents_HttpProtocol.dll" "./" \ No newline at end of file +copy /y "D:\XEngine\XEngine_SourceCode\Debug\RfcComponents_HttpProtocol.dll" "./" +copy /y "D:\XEngine\XEngine_SourceCode\Debug\RfcComponents_FTPProtocol.dll" "./" \ No newline at end of file diff --git a/XEngine_Source/VSCopy_x64.bat b/XEngine_Source/VSCopy_x64.bat index 008422e6..efaa2553 100644 --- a/XEngine_Source/VSCopy_x64.bat +++ b/XEngine_Source/VSCopy_x64.bat @@ -16,4 +16,5 @@ copy /y "%XEngine_Lib64%\XEngine_NetHelp\NetHelp_XSocket.dll" "./" copy /y "%XEngine_Lib64%\XEngine_HelpComponents\HelpComponents_DataBase.dll" "./" copy /y "%XEngine_Lib64%\XEngine_HelpComponents\HelpComponents_XLog.dll" "./" -copy /y "%XEngine_Lib64%\XEngine_RfcComponents\RfcComponents_HttpProtocol.dll" "./" \ No newline at end of file +copy /y "%XEngine_Lib64%\XEngine_RfcComponents\RfcComponents_HttpProtocol.dll" "./" +copy /y "%XEngine_Lib64%\XEngine_RfcComponents\RfcComponents_FTPProtocol.dll" "./" \ No newline at end of file diff --git a/XEngine_Source/VSCopy_x86.bat b/XEngine_Source/VSCopy_x86.bat index 933255c5..93ef41a0 100644 --- a/XEngine_Source/VSCopy_x86.bat +++ b/XEngine_Source/VSCopy_x86.bat @@ -16,4 +16,5 @@ copy /y "%XEngine_Lib32%\XEngine_NetHelp\NetHelp_XSocket.dll" "./" copy /y "%XEngine_Lib32%\XEngine_HelpComponents\HelpComponents_DataBase.dll" "./" copy /y "%XEngine_Lib32%\XEngine_HelpComponents\HelpComponents_XLog.dll" "./" -copy /y "%XEngine_Lib32%\XEngine_RfcComponents\RfcComponents_HttpProtocol.dll" "./" \ No newline at end of file +copy /y "%XEngine_Lib32%\XEngine_RfcComponents\RfcComponents_HttpProtocol.dll" "./" +copy /y "%XEngine_Lib32%\XEngine_RfcComponents\RfcComponents_FTPProtocol.dll" "./" \ No newline at end of file diff --git a/XEngine_Source/XEngine_DependLibrary b/XEngine_Source/XEngine_DependLibrary index dc73149a..d3965d2e 160000 --- a/XEngine_Source/XEngine_DependLibrary +++ b/XEngine_Source/XEngine_DependLibrary @@ -1 +1 @@ -Subproject commit dc73149af8aef839085e1f7ad254aea41f0a2f86 +Subproject commit d3965d2e9584a0cea2af95ca07a6dbfb3758bc95 diff --git a/XEngine_Source/XEngine_StorageApp/Makefile b/XEngine_Source/XEngine_StorageApp/Makefile index cef0ef70..118796fc 100644 --- a/XEngine_Source/XEngine_StorageApp/Makefile +++ b/XEngine_Source/XEngine_StorageApp/Makefile @@ -3,12 +3,12 @@ PLATVER = LOADHDR = -I ./ LOADSO = -L ../XEngine_DependLibrary/XEngine_Module/jsoncpp -L ../XEngine_DependLibrary/XEngine_Module/tinyxml2 -L ../XEngine_DependLibrary/XEngine_Module/XEngine_InfoReport -L ../XEngine_DependLibrary/XEngine_Module/XEngine_Verification \ -L ../StorageModule_Database -L ../StorageModule_Config -L ../StorageModule_Session -L ../StorageModule_APIHelp -L ../StorageModule_Protocol -L ../StorageModule_BTorrent -LIB = -lXEngine_BaseLib -lXEngine_Algorithm -lXEngine_Core -lXEngine_ManagePool -lXEngine_Cryption -lXClient_APIHelp -lXEngine_SystemApi -lHelpComponents_XLog -lHelpComponents_Packets -lRfcComponents_HttpProtocol \ +LIB = -lXEngine_BaseLib -lXEngine_BaseSafe -lXEngine_Algorithm -lXEngine_Core -lXEngine_ManagePool -lXEngine_Cryption -lXClient_APIHelp -lNetHelp_APIAddr -lXEngine_SystemApi -lHelpComponents_XLog -lHelpComponents_Packets -lRfcComponents_HttpProtocol -lRfcComponents_FTPProtocol \ -lStorageModule_Database -lStorageModule_Config -lStorageModule_Session -lStorageModule_APIHelp -lStorageModule_Protocol -lStorageModule_BTorrent \ -ljsoncpp -ltinyxml2 -lXEngine_InfoReport -lXEngine_Verification LIBEX = OBJECTS = Storage_TaskPass.o Storage_TaskManage.o Storage_TaskP2p.o Storage_TaskAction.o \ - StorageApp_Config.o StorageApp_HTTPHelp.o StorageApp_Download.o StorageApp_Network.o StorageApp_Center.o StorageApp_UPLoader.o StorageApp_Webdav.o XEngine_StorageApp.o + StorageApp_Config.o StorageApp_HTTPHelp.o StorageApp_Download.o StorageApp_Network.o StorageApp_Center.o StorageApp_UPLoader.o StorageApp_Webdav.o StorageApp_FTP.o XEngine_StorageApp.o ifeq ($(RELEASE),1) FLAGS = -c -D _RELEASE @@ -61,6 +61,8 @@ StorageApp_UPLoader.o:./StorageApp_UPLoader.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./StorageApp_UPLoader.cpp StorageApp_Webdav.o:./StorageApp_Webdav.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./StorageApp_Webdav.cpp +StorageApp_FTP.o:./StorageApp_FTP.cpp + $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./StorageApp_FTP.cpp Storage_TaskPass.o:./Storage_APPTask/Storage_TaskPass.cpp $(CC) $(DEBUG) $(FLAGS) $(UNICODE) $(PLATVER) $(LOADHDR) ./Storage_APPTask/Storage_TaskPass.cpp diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_FTP.cpp b/XEngine_Source/XEngine_StorageApp/StorageApp_FTP.cpp new file mode 100644 index 00000000..b530f16e --- /dev/null +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_FTP.cpp @@ -0,0 +1,646 @@ +#include "StorageApp_Hdr.h" + +XHTHREAD XCALLBACK XEngine_FTPContral_Thread(XPVOID lParam) +{ + int nThreadPos = *(int*)lParam; + nThreadPos++; + + while (bIsRun) + { + //等待指定线程事件触发 + if (FTPProtocol_Parse_EventWaitEx(xhFTPContral, nThreadPos)) + { + int nListCount = 0; + XENGINE_MANAGEPOOL_TASKEVENT** ppSt_PKTClient; + //获取当前队列池中所有触发上传客户端 + FTPProtocol_Parse_GetPoolEx(xhFTPContral, nThreadPos, &ppSt_PKTClient, &nListCount); + for (int i = 0; i < nListCount; i++) + { + for (int j = 0; j < ppSt_PKTClient[i]->nPktCount; j++) + { + XENGINE_KEYVALUE st_KeyValue = {}; + //获得指定上传客户端触发信息 + if (FTPProtocol_Parse_GetClientEx(xhFTPContral, ppSt_PKTClient[i]->tszClientAddr, &st_KeyValue)) + { + XEngine_Task_FTP(ppSt_PKTClient[i]->tszClientAddr, &st_KeyValue, STORAGE_NETTYPE_FTPCONTRAL); + } + } + } + BaseLib_Memory_Free((XPPPMEM)&ppSt_PKTClient, nListCount); + } + } + return 0; +} +static void XEngine_FTPDownload_Thread(xstring lpszClientAddr, xstring lpszFileName) +{ + int nPort = 0; + XHANDLE xhToken = Session_FTP_GetSocket(lpszClientAddr.c_str(), &nPort); + if (NULL == xhToken) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求下载文件失败,获取网络连接失败,错误码:%lX"), lpszClientAddr.c_str(), Session_GetLastError()); + return; + } + int nMSGLen = 0; + XLONG dwEvent = XENGINE_NETCORE_TCP_SELECT_EVENT_LOGIN; + XCHAR tszClientAddr[XPATH_MIN] = {}; + XCHAR tszMSGBuffer[XPATH_8MAX] = {}; + if (!NetCore_TCPSelect_ReadIOEventEx(xhToken, tszClientAddr, tszMSGBuffer, &nMSGLen, &dwEvent)) + { + //关闭连接 + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求下载文件失败,获取网络事件失败,错误码:%lX"), lpszClientAddr.c_str(), NetCore_GetLastError()); + return; + } + FILE* pSt_File = _xtfopen(lpszFileName.c_str(), _X("rb")); + if (NULL == pSt_File) + { + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求下载文件失败,打开文件:%s失败"), lpszClientAddr.c_str(), lpszFileName.c_str()); + return; + } + //设置断点续传 + __int64u nPos = 0; + Session_FTP_GetRetr(lpszClientAddr.c_str(), &nPos); + if (nPos > 0) + { + fseek(pSt_File, (long)nPos, SEEK_SET); + } + + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,准备数据文件:%s 完成,开始下载文件数据,下载位置:%llu"), lpszClientAddr.c_str(), lpszFileName.c_str(), nPos); + + while (true) + { + XCHAR tszMSGBuffer[XPATH_2MAX] = {}; + int nRet = fread(tszMSGBuffer, 1, sizeof(tszMSGBuffer), pSt_File); + if (nRet <= 0) + { + break; + } + while (true) + { + if (NetCore_TCPSelect_SendEx(xhToken, tszClientAddr, tszMSGBuffer, nRet)) + { + break; + } + else + { + if (ERROR_XENGINE_BASELIB_BASELIB_IO_CLOSE == NetCore_GetLastError()) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求下载文件失败,网络连接已断开"), lpszClientAddr.c_str()); + break; + } + else + { + fseek(pSt_File, -nRet, SEEK_CUR); + } + } + //等待10毫秒 + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + fclose(pSt_File); + //关闭连接 + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + //通知 + int nSDLen = 1024; + XCHAR tszSDBuffer[1024] = {}; + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_226, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr.c_str(), tszSDBuffer, nSDLen, STORAGE_NETTYPE_FTPCONTRAL); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,连接地址:%s 请求下载文件成功,网络连接已断开"), lpszClientAddr.c_str(), tszClientAddr); +} +static void XEngine_FTPUPload_Thread(xstring lpszClientAddr, xstring lpszFileName) +{ + int nPort = 0; + XHANDLE xhToken = Session_FTP_GetSocket(lpszClientAddr.c_str(), &nPort); + if (NULL == xhToken) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件失败,获取网络连接失败,错误码:%lX"), lpszClientAddr.c_str(), Session_GetLastError()); + return; + } + FILE* pSt_File = NULL; + //设置断点续传 + __int64u nPos = 0; + Session_FTP_GetRetr(lpszClientAddr.c_str(), &nPos); + if (nPos > 0) + { + pSt_File = _xfopenA(lpszFileName.c_str(), _X("rb+"), true); + if (NULL == pSt_File) + { + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件失败,续传文件不存在:%s"), lpszClientAddr.c_str(), lpszFileName.c_str()); + return; + } + fseek(pSt_File, (long)nPos, SEEK_SET); + } + else + { + pSt_File = _xfopenA(lpszFileName.c_str(), _X("wb")); + if (NULL == pSt_File) + { + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件失败,创建文件:%s失败"), lpszClientAddr.c_str(), lpszFileName.c_str()); + return; + } + } + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,创建文件:%s 成功,开始接受数据,位置:%llu"), lpszClientAddr.c_str(), lpszFileName.c_str(), nPos); + + while (true) + { + int nMSGLen = 0; + XLONG dwEvent = XENGINE_NETCORE_TCP_SELECT_EVENT_ALL; + XCHAR tszClientAddr[XPATH_MIN] = {}; + XCHAR tszMSGBuffer[XENGINE_SOCKET_SR_TCP_BUFFSIZE] = {}; + + if (!NetCore_TCPSelect_ReadIOEventEx(xhToken, tszClientAddr, tszMSGBuffer, &nMSGLen, &dwEvent)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + + if (XENGINE_NETCORE_TCP_SELECT_EVENT_LEAVE == dwEvent) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件失败,获取网络事件,用户:%s 离开"), lpszClientAddr.c_str(), tszClientAddr); + break; + } + else if (XENGINE_NETCORE_TCP_SELECT_EVENT_RECV == dwEvent) + { + fwrite(tszMSGBuffer, 1, nMSGLen, pSt_File); + } + } + fclose(pSt_File); + //关闭连接 + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + //通知 + int nSDLen = 1024; + XCHAR tszSDBuffer[1024] = {}; + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_226, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr.c_str(), tszSDBuffer, nSDLen, STORAGE_NETTYPE_FTPCONTRAL); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s 请求上传文件完毕,网络连接已断开"), lpszClientAddr.c_str()); +} +static void XEngine_FTPFile_Thread(xstring lpszClientAddr, xstring lpszFilePath, xstring lpszAlisPath) +{ + int nPort = 0; + XHANDLE xhToken = Session_FTP_GetSocket(lpszClientAddr.c_str(), &nPort); + if (NULL == xhToken) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,获取网络连接失败,错误码:%lX"), lpszClientAddr.c_str(), Session_GetLastError()); + return; + } + int nMSGLen = 0; + XLONG dwEvent = XENGINE_NETCORE_TCP_SELECT_EVENT_LOGIN; + XCHAR tszClientAddr[XPATH_MIN] = {}; + XCHAR tszMSGBuffer[XPATH_8MAX] = {}; + if (!NetCore_TCPSelect_ReadIOEventEx(xhToken, tszClientAddr, tszMSGBuffer, &nMSGLen, &dwEvent)) + { + //关闭连接 + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,获取网络事件失败,错误码:%lX"), lpszClientAddr.c_str(), NetCore_GetLastError()); + return; + } + XCHAR tszDIRBuffer[XPATH_MAX] = {}; + XCHAR tszCMDBuffer[XPATH_2MAX] = {}; + _xstprintf(tszDIRBuffer, _X("%s%s"), lpszFilePath.c_str(), lpszAlisPath.c_str()); + BaseLib_String_FixPath(tszDIRBuffer, 1); + + _xstprintf(tszCMDBuffer, _X("dir %s"), tszDIRBuffer); + SystemApi_Process_ReadCmdReturn(tszCMDBuffer, tszMSGBuffer, 0, 6, &nMSGLen, 3); + + nMSGLen += 2; + _tcsxcat(tszMSGBuffer, _X("\r\n")); + + if (!NetCore_TCPSelect_SendEx(xhToken, tszClientAddr, tszMSGBuffer, nMSGLen)) + { + //关闭连接 + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,连接地址:%s,发送数据大小:%d 失败,错误码:%lX"), lpszClientAddr.c_str(), tszClientAddr, nMSGLen, NetCore_GetLastError()); + } + //关闭连接 + APIHelp_Port_Free(nPort); + NetCore_TCPSelect_StopEx(xhToken); + //通知 + int nSDLen = 1024; + XCHAR tszSDBuffer[1024] = {}; + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_226, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr.c_str(), tszSDBuffer, nSDLen, STORAGE_NETTYPE_FTPCONTRAL); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,连接地址:%s,枚举目录:%s 发送数据完毕,网络连接已断开"), lpszClientAddr.c_str(), tszClientAddr, tszDIRBuffer); +} + +bool XEngine_Task_FTP(LPCXSTR lpszClientAddr, XENGINE_KEYVALUE *pSt_KeyValue, int nNetType) +{ + int nSDLen = 1024; + int nRVLen = 1024; + XCHAR tszSDBuffer[1024] = {}; + XCHAR tszRVBuffer[1024] = {}; + + if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_USER, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_USER))) + { + //用户请求 + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_331, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,请求用户名成功,请求信息:%s"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_PASS, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_PASS))) + { + //密码请求 + Session_FTP_Insert(lpszClientAddr, false); + Session_FTP_Set(lpszClientAddr, NULL, st_ServiceCfg.st_XFtp.tszFTPDir); + + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_230, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,请求密码成功,请求信息:%s"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_TYPE, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_TYPE))) + { + //类型请求 + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_200, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,请求类型成功,请求信息:%s"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_PASV, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_PASV))) + { + //被动模式请求 + int nPort = 0; + APIADDR_IPADDR st_IPAddr = {}; + + APIAddr_IPAddr_IsIPV4Addr(st_ServiceCfg.tszIPAddr, &st_IPAddr); + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_227, tszRVBuffer, &nRVLen, false); + if (!APIHelp_Port_Get(&nPort)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_425, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求被动模式失败,资源耗尽,请求信息:%s"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + + int p1 = nPort / 256; + int p2 = nPort % 256; + nSDLen = _xstprintf(tszSDBuffer, _X("%d %s (%d,%d,%d,%d,%d,%d)"), XENGINE_FTPROTOCOL_RESPONSE_227, tszRVBuffer, st_IPAddr.nIPAddr1, st_IPAddr.nIPAddr2, st_IPAddr.nIPAddr3, st_IPAddr.nIPAddr4, p1, p2); + + XHANDLE xhToken = NetCore_TCPSelect_StartEx(nPort, 100, true); + if (NULL == xhToken) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_425, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求被动模式失败,资源耗尽,请求信息:%s"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + Session_FTP_SetSocket(lpszClientAddr, nPort, xhToken); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,请求被动模式成功,请求地址:%s:%d"), lpszClientAddr, st_ServiceCfg.tszIPAddr, nPort); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_RETR, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_RETR))) + { + //下载 + XCHAR tszFilePath[XPATH_MAX] = {}; + XCHAR tszAlisPath[XPATH_MAX] = {}; + if (!Session_FTP_Get(lpszClientAddr, NULL, tszFilePath, tszAlisPath)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求下载文件:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + XCHAR tszFileName[XPATH_MAX] = {}; + _xstprintf(tszFileName, _X("%s%s"), tszFilePath, tszAlisPath); + BaseLib_String_FixPath(tszFileName, 1); + + if (tszFileName[_tcsxlen(tszFileName)] != '\\' && tszFileName[_tcsxlen(tszFileName)] != '/') + { + _tcsxcat(tszFileName, _X("\\")); + } + _tcsxcat(tszFileName, pSt_KeyValue->tszStrVlu); + + if (0 != _xtaccess(tszFileName, 0)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_550, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求文件:%s 失败,文件不存在"), lpszClientAddr, tszFileName); + return false; + } + if (!Session_FTP_Set(lpszClientAddr, pSt_KeyValue->tszStrVlu)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求文件:%s 失败,没有设置服务器端口信息"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_150, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + + std::thread m_ThreadFTPDatas(XEngine_FTPDownload_Thread, xstring(lpszClientAddr), xstring(tszFileName)); + m_ThreadFTPDatas.detach(); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,请求文件成功,请求信息:%s"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_STOR, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_STOR))) + { + //上传 + XCHAR tszFilePath[XPATH_MAX] = {}; + XCHAR tszAlisPath[XPATH_MAX] = {}; + if (!Session_FTP_Get(lpszClientAddr, NULL, tszFilePath, tszAlisPath)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + XCHAR tszFileName[XPATH_MAX] = {}; + _xstprintf(tszFileName, _X("%s%s"), tszFilePath, tszAlisPath); + BaseLib_String_FixPath(tszFileName, 1); + + if (tszFileName[_tcsxlen(tszFileName)] != '\\' && tszFileName[_tcsxlen(tszFileName)] != '/') + { + _tcsxcat(tszFileName, _X("\\")); + } + _tcsxcat(tszFileName, pSt_KeyValue->tszStrVlu); + + if (!Session_FTP_Set(lpszClientAddr, pSt_KeyValue->tszStrVlu)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件:%s 失败,没有设置服务器端口信息"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + std::thread m_ThreadFTPDatas(XEngine_FTPUPload_Thread, xstring(lpszClientAddr), xstring(tszFileName)); + m_ThreadFTPDatas.detach(); + + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_150, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,请求文件成功,请求信息:%s"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_RETR, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_RETR))) + { + //断点续传 + if (!Session_FTP_SetRetr(lpszClientAddr, _ttxoll(pSt_KeyValue->tszStrVlu))) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + nSDLen = _xstprintf(tszSDBuffer, _X("350 Restarting at %s. Send STORE or RETRIEVE to initiate transfer.\r\n"), pSt_KeyValue->tszStrVlu); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,请求断点续传位置:%s 成功"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_DELE, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_DELE))) + { + //删除文件 + XCHAR tszFilePath[XPATH_MAX] = {}; + XCHAR tszAlisPath[XPATH_MAX] = {}; + if (!Session_FTP_Get(lpszClientAddr, NULL, tszFilePath, tszAlisPath)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + XCHAR tszFileName[XPATH_MAX] = {}; + _xstprintf(tszFileName, _X("%s%s"), tszFilePath, tszAlisPath); + BaseLib_String_FixPath(tszFileName, 1); + + if (tszFileName[_tcsxlen(tszFileName)] != '\\' && tszFileName[_tcsxlen(tszFileName)] != '/') + { + _tcsxcat(tszFileName, _X("\\")); + } + _tcsxcat(tszFileName, pSt_KeyValue->tszStrVlu); + _xtremove(tszFileName); + + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_250, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,删除文件成功:%s"), lpszClientAddr, tszFileName); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_PWD, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_PWD))) + { + //列举目录 + XCHAR tszFTPDir[XPATH_MAX] = {}; + XCHAR tszAliDir[XPATH_MAX] = {}; + Session_FTP_Get(lpszClientAddr, NULL, tszFTPDir, tszAliDir); + + if (_tcsxlen(tszAliDir) > 0) + { + nSDLen = _xstprintf(tszSDBuffer, _X("257 \"%s\" is current directory.\r\n"), tszAliDir); + } + else + { + nSDLen = _xstprintf(tszSDBuffer, _X("257 \"/\" is current directory.\r\n")); + } + + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,查询当前目录:%s,别名:%s 成功"), lpszClientAddr, tszFTPDir, tszAliDir); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_CWD, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_CWD))) + { + //切换目录 + XCHAR tszAliDir[XPATH_MAX] = {}; + Session_FTP_Get(lpszClientAddr, NULL, NULL, tszAliDir); + + if (_tcsxlen(tszAliDir) > 0) + { + //如果是根目录,//并且切换目标不是根目录 + if (1 == _tcsxlen(tszAliDir) && 1 != _tcsxlen(pSt_KeyValue->tszStrVlu)) + { + _tcsxcat(tszAliDir, pSt_KeyValue->tszStrVlu); + } + else if (0 != _tcsxncmp(pSt_KeyValue->tszStrVlu, tszAliDir, _tcsxlen(tszAliDir))) + { + _tcsxcat(tszAliDir, _X("/")); + _tcsxcat(tszAliDir, pSt_KeyValue->tszStrVlu); + } + } + else + { + _tcsxcat(tszAliDir, pSt_KeyValue->tszStrVlu); + } + Session_FTP_Set(lpszClientAddr, NULL, NULL, tszAliDir); + + nSDLen = _xstprintf(tszSDBuffer, _X("250 Directory changed to %s\r\n"), tszAliDir); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,切换目录:%s 成功"), lpszClientAddr, tszAliDir); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_MKD, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_MKD))) + { + //创建目录 + XCHAR tszFilePath[XPATH_MAX] = {}; + XCHAR tszAlisPath[XPATH_MAX] = {}; + if (!Session_FTP_Get(lpszClientAddr, NULL, tszFilePath, tszAlisPath)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + XCHAR tszFileName[XPATH_MAX] = {}; + _xstprintf(tszFileName, _X("%s%s"), tszFilePath, tszAlisPath); + BaseLib_String_FixPath(tszFileName, 1); + if (tszFileName[_tcsxlen(tszFileName)] != '\\' && tszFileName[_tcsxlen(tszFileName)] != '/') + { + _tcsxcat(tszFileName, _X("\\")); + } + _tcsxcat(tszFileName, pSt_KeyValue->tszStrVlu); + SystemApi_File_CreateMutilFolderA(tszFileName); + + nSDLen = _xstprintf(tszSDBuffer, _X("257 \"%s\" directory created\r\n"), pSt_KeyValue->tszStrVlu); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,创建目录:%s 成功"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_RMD, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_RMD))) + { + //删除目录 + XCHAR tszFilePath[XPATH_MAX] = {}; + XCHAR tszAlisPath[XPATH_MAX] = {}; + if (!Session_FTP_Get(lpszClientAddr, NULL, tszFilePath, tszAlisPath)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求上传文件:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + XCHAR tszFileName[XPATH_MAX] = {}; + _xstprintf(tszFileName, _X("%s%s"), tszFilePath, tszAlisPath); + BaseLib_String_FixPath(tszFileName, 1); + if (tszFileName[_tcsxlen(tszFileName)] != '\\' && tszFileName[_tcsxlen(tszFileName)] != '/') + { + _tcsxcat(tszFileName, _X("\\")); + } + _tcsxcat(tszFileName, pSt_KeyValue->tszStrVlu); + SystemApi_File_DeleteMutilFolderA(tszFileName); + + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_250, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,删除目录:%s 成功"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_CDUP, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_CDUP))) + { + XCHAR tszPDir[XPATH_MAX] = {}; + XCHAR tszAlisDir[XPATH_MAX] = {}; + Session_FTP_Get(lpszClientAddr, NULL, NULL, tszAlisDir); + //是否设置过目录 + if (_tcsxlen(tszAlisDir) <= 1) + { + //不允许切换更上级目录 + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_550, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + return false; + } + else + { + //如果路径是父目录 + BaseLib_String_GetFileAndPathA(tszAlisDir, tszPDir); + Session_FTP_Set(lpszClientAddr, NULL, NULL, tszPDir); + } + + nSDLen = _xstprintf(tszSDBuffer, _X("200 Directory changed to %s\r\n"), tszPDir); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,切换上级目录:%s 成功"), lpszClientAddr, tszPDir); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_LIST, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_LIST))) + { + //列举目录请求 + XCHAR tszFilePath[XPATH_MAX] = {}; + XCHAR tszAlisPath[XPATH_MAX] = {}; + if (!Session_FTP_Get(lpszClientAddr, NULL, tszFilePath, tszAlisPath)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求列举目录:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + if (_tcsxlen(tszAlisPath) <= 0) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求列举目录:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_150, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + + std::thread m_ThreadFTPFiles(XEngine_FTPFile_Thread, xstring(lpszClientAddr), xstring(tszFilePath), xstring(tszAlisPath)); + m_ThreadFTPFiles.detach(); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,列举目录:%s 成功"), lpszClientAddr, tszFilePath); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_REFR, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_REFR))) + { + //准备重命名文件 + XCHAR tszFilePath[XPATH_MAX] = {}; + XCHAR tszAlisPath[XPATH_MAX] = {}; + XCHAR tszFileName[XPATH_MAX] = {}; + if (!Session_FTP_Get(lpszClientAddr, NULL, tszFilePath, tszAlisPath)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求修改文件名:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + _xstprintf(tszFileName, _X("%s%s"), tszFilePath, tszAlisPath); + BaseLib_String_FixPath(tszFileName, 1); + + if (tszFileName[_tcsxlen(tszFileName)] != '\\' && tszFileName[_tcsxlen(tszFileName)] != '/') + { + _tcsxcat(tszFileName, _X("\\")); + } + _tcsxcat(tszFileName, pSt_KeyValue->tszStrVlu); + //得到文件属性 + if (0 != _xtaccess(tszFileName, 0)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_550, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求修改文件名:%s 失败,文件不存在或者不可用"), lpszClientAddr, tszFileName); + return false; + } + if (!Session_FTP_Set(lpszClientAddr, tszFileName)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求修改文件名:%s 失败,执行顺序错误"), lpszClientAddr, tszFileName); + return false; + } + + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_350, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,准备重命名文件:%s"), lpszClientAddr, tszFileName); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_RETO, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_RETO))) + { + XCHAR tszFileOld[XPATH_MAX] = {}; + XCHAR tszFileNew[XPATH_MAX] = {}; + + if (!Session_FTP_Get(lpszClientAddr, tszFileOld)) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_503, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,请求修改文件名:%s 失败,执行顺序错误"), lpszClientAddr, pSt_KeyValue->tszStrVlu); + return false; + } + BaseLib_String_GetFileAndPath(tszFileOld, tszFileNew); + _tcsxcat(tszFileNew, _X("\\")); + _tcsxcat(tszFileNew, pSt_KeyValue->tszStrVlu); + + _xtrename(tszFileOld, tszFileNew); + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_250, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,重命名文件:%s -> %s 完成"), lpszClientAddr, tszFileOld, tszFileNew); + } + else if (0 == _tcsxnicmp(XENGINE_FTPROTOCOL_QUESTION_QUIT, pSt_KeyValue->tszStrKey, _tcsxlen(XENGINE_FTPROTOCOL_QUESTION_QUIT))) + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_221, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,退出成功"), lpszClientAddr); + } + else + { + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_502, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, nNetType); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,发送的方法 %s:%s 不支持"), lpszClientAddr, pSt_KeyValue->tszStrKey, pSt_KeyValue->tszStrVlu); + return false; + } + + return true; +} \ No newline at end of file diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_FTP.h b/XEngine_Source/XEngine_StorageApp/StorageApp_FTP.h new file mode 100644 index 00000000..5046372d --- /dev/null +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_FTP.h @@ -0,0 +1,14 @@ +#pragma once +/******************************************************************** +// Created: 2026/09/10 17:37:00 +// File Name: D:\XEngine_Storage\XEngine_Source\XEngine_StorageApp\StorageApp_FTP.h +// File Path: D:\XEngine_Storage\XEngine_Source\XEngine_StorageApp +// File Base: StorageApp_FTP +// File Ext: h +// Project: XEngine +// Author: qyt +// Purpose: FTP服务端 +// History: +*********************************************************************/ +XHTHREAD XCALLBACK XEngine_FTPContral_Thread(XPVOID lParam); +bool XEngine_Task_FTP(LPCXSTR lpszClientAddr, XENGINE_KEYVALUE* pSt_KeyValue, int nNetType); \ No newline at end of file diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h b/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h index bcfe1b24..5f7de5a6 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_Hdr.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #else @@ -29,8 +30,8 @@ using namespace std; #include #include #include -#include -#include +#include +#include #include #include #include @@ -41,6 +42,8 @@ using namespace std; #include #include #include +#include +#include #include #include #include @@ -49,6 +52,8 @@ using namespace std; #include #include #include +#include +#include #include #include #include "../XStorage_Protocol.h" @@ -77,6 +82,7 @@ using namespace std; #define STORAGE_NETTYPE_HTTPDOWNLOAD 2 #define STORAGE_NETTYPE_HTTPCENTER 3 #define STORAGE_NETTYPE_HTTPWEBDAV 4 +#define STORAGE_NETTYPE_FTPCONTRAL 5 extern bool bIsRun; extern bool bIsTest; @@ -86,16 +92,19 @@ extern XHANDLE xhHBDownload; extern XHANDLE xhHBUPLoader; extern XHANDLE xhHBCenter; extern XHANDLE xhHBWebdav; +extern XHANDLE xhHBFTPContral; extern XHANDLE xhNetDownload; extern XHANDLE xhNetUPLoader; extern XHANDLE xhNetCenter; extern XHANDLE xhNetWebdav; +extern XHANDLE xhNetFTPContral; extern XHANDLE xhUPPool; extern XHANDLE xhDLPool; extern XHANDLE xhCTPool; extern XHANDLE xhWDPool; +extern XHANDLE xhFTPPoolContral; extern XHANDLE xhDLSsl; extern XHANDLE xhUPSsl; @@ -107,6 +116,7 @@ extern XHANDLE xhUPHttp; extern XHANDLE xhDLHttp; extern XHANDLE xhCenterHttp; extern XHANDLE xhWebdavHttp; +extern XHANDLE xhFTPContral; extern XSOCKET hBroadSocket; extern shared_ptr pSTDThread; @@ -122,6 +132,7 @@ extern XENGINE_LBCONFIG st_LoadbalanceCfg; #include "StorageApp_UPLoader.h" #include "StorageApp_Center.h" #include "StorageApp_Webdav.h" +#include "StorageApp_FTP.h" #include "Storage_APPTask/Storage_TaskPass.h" #include "Storage_APPTask/Storage_TaskP2p.h" #include "Storage_APPTask/Storage_TaskManage.h" @@ -131,14 +142,17 @@ extern XENGINE_LBCONFIG st_LoadbalanceCfg; #pragma comment(lib,"Ws2_32.lib") #pragma comment(lib,"Dbghelp.lib") #pragma comment(lib,"XEngine_BaseLib/XEngine_BaseLib.lib") +#pragma comment(lib,"XEngine_BaseLib/XEngine_BaseSafe.lib") #pragma comment(lib,"XEngine_BaseLib/XEngine_Algorithm.lib") #pragma comment(lib,"XEngine_Core/XEngine_Core.lib") #pragma comment(lib,"XEngine_Core/XEngine_ManagePool.lib") #pragma comment(lib,"XEngine_Core/XEngine_Cryption.lib") #pragma comment(lib,"XEngine_Client/XClient_APIHelp.lib") +#pragma comment(lib,"XEngine_NetHelp/NetHelp_APIAddr.lib") #pragma comment(lib,"XEngine_HelpComponents/HelpComponents_XLog.lib") #pragma comment(lib,"XEngine_HelpComponents/HelpComponents_Packets.lib") #pragma comment(lib,"XEngine_RfcComponents/RfcComponents_HttpProtocol.lib") +#pragma comment(lib,"XEngine_RfcComponents/RfcComponents_FTPProtocol.lib") #pragma comment(lib,"XEngine_SystemSdk/XEngine_SystemApi.lib") #ifdef _DEBUG #ifdef _M_X64 diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp b/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp index 78206984..ef07fc65 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_Network.cpp @@ -18,7 +18,7 @@ void XCALLBACK XEngine_Callback_DownloadRecv(LPCXSTR lpszClientAddr, XSOCKET hSo int nSLen = 0; XCHAR* ptszMsgBuffer = NULL; Cryption_Server_RecvMemoryEx(xhDLSsl, lpszClientAddr, &ptszMsgBuffer, &nSLen, lpszRecvMsg, nMsgLen); - if (!HttpProtocol_Server_InserQueueEx(xhDLHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhDLHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("下载客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -27,7 +27,7 @@ void XCALLBACK XEngine_Callback_DownloadRecv(LPCXSTR lpszClientAddr, XSOCKET hSo } else { - if (!HttpProtocol_Server_InserQueueEx(xhDLHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhDLHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("下载客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -60,7 +60,7 @@ void XCALLBACK XEngine_Callback_UPLoaderRecv(LPCXSTR lpszClientAddr, XSOCKET hSo int nSLen = 0; XCHAR* ptszMsgBuffer = NULL; Cryption_Server_RecvMemoryEx(xhUPSsl, lpszClientAddr, &ptszMsgBuffer, &nSLen, lpszRecvMsg, nMsgLen); - if (!HttpProtocol_Server_InserQueueEx(xhUPHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhUPHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("上传客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -69,7 +69,7 @@ void XCALLBACK XEngine_Callback_UPLoaderRecv(LPCXSTR lpszClientAddr, XSOCKET hSo } else { - if (!HttpProtocol_Server_InserQueueEx(xhUPHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhUPHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("上传客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -101,7 +101,7 @@ void XCALLBACK XEngine_Callback_CenterRecv(LPCXSTR lpszClientAddr, XSOCKET hSock int nSLen = 0; XCHAR* ptszMsgBuffer = NULL; Cryption_Server_RecvMemoryEx(xhCHSsl, lpszClientAddr, &ptszMsgBuffer, &nSLen, lpszRecvMsg, nMsgLen); - if (!HttpProtocol_Server_InserQueueEx(xhCenterHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhCenterHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("业务客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -110,7 +110,7 @@ void XCALLBACK XEngine_Callback_CenterRecv(LPCXSTR lpszClientAddr, XSOCKET hSock } else { - if (!HttpProtocol_Server_InserQueueEx(xhCenterHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhCenterHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("业务客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -142,7 +142,7 @@ void XCALLBACK XEngine_Callback_WebdavRecv(LPCXSTR lpszClientAddr, XSOCKET hSock int nSLen = 0; XCHAR* ptszMsgBuffer = NULL; Cryption_Server_RecvMemoryEx(xhWDSsl, lpszClientAddr, &ptszMsgBuffer, &nSLen, lpszRecvMsg, nMsgLen); - if (!HttpProtocol_Server_InserQueueEx(xhWebdavHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhWebdavHttp, lpszClientAddr, ptszMsgBuffer, nSLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("WEBDAV客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -151,7 +151,7 @@ void XCALLBACK XEngine_Callback_WebdavRecv(LPCXSTR lpszClientAddr, XSOCKET hSock } else { - if (!HttpProtocol_Server_InserQueueEx(xhWebdavHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) + if (!HttpProtocol_Server_InsertQueueEx(xhWebdavHttp, lpszClientAddr, lpszRecvMsg, nMsgLen)) { XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("WEBDAV客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, HttpProtocol_GetLastError()); return; @@ -165,6 +165,34 @@ void XCALLBACK XEngine_Callback_WebdavLeave(LPCXSTR lpszClientAddr, XSOCKET hSoc XEngine_Net_CloseClient(lpszClientAddr, STORAGE_LEAVETYPE_BYSELF, STORAGE_NETTYPE_HTTPWEBDAV); } ////////////////////////////////////////////////////////////////////////// +bool XCALLBACK XEngine_Callback_FTPContralLogin(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam) +{ + FTPProtocol_Parse_CreateClientEx(xhFTPContral, lpszClientAddr, 0); + SocketOpt_HeartBeat_InsertAddrEx(xhHBFTPContral, lpszClientAddr); + + int nSDLen = 0; + XCHAR tszSDBuffer[XPATH_MIN] = {}; + + FTPProtocol_Parse_SendPacketEx(xhFTPContral, XENGINE_FTPROTOCOL_RESPONSE_220, tszSDBuffer, &nSDLen); + XEngine_Net_SendMsg(lpszClientAddr, tszSDBuffer, nSDLen, STORAGE_NETTYPE_FTPCONTRAL); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("FTP客户端:%s,进入了服务器"), lpszClientAddr); + return true; +} +void XCALLBACK XEngine_Callback_FTPContralRecv(LPCXSTR lpszClientAddr, XSOCKET hSocket, LPCXSTR lpszRecvMsg, int nMsgLen, XPVOID lParam) +{ + if (!FTPProtocol_Parse_InsertQueueEx(xhFTPContral, lpszClientAddr, lpszRecvMsg, nMsgLen)) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("FTP客户端:%s,投递数据失败,大小:%d,错误;%lX"), lpszClientAddr, nMsgLen, FTPProtocol_GetLastError()); + return; + } + SocketOpt_HeartBeat_ActiveAddrEx(xhHBFTPContral, lpszClientAddr); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_DEBUG, _X("FTP客户端:%s,投递包成功,大小:%d"), lpszClientAddr, nMsgLen); +} +void XCALLBACK XEngine_Callback_FTPContralLeave(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam) +{ + XEngine_Net_CloseClient(lpszClientAddr, STORAGE_LEAVETYPE_BYSELF, STORAGE_NETTYPE_FTPCONTRAL); +} +////////////////////////////////////////////////////////////////////////// void XCALLBACK XEngine_Callback_HBDownload(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nStatus, XPVOID lParam) { XEngine_Net_CloseClient(lpszClientAddr, STORAGE_LEAVETYPE_HEARTBEAT, STORAGE_NETTYPE_HTTPDOWNLOAD); @@ -181,6 +209,10 @@ void XCALLBACK XEngine_Callback_HBWebdav(LPCXSTR lpszClientAddr, XSOCKET hSocket { XEngine_Net_CloseClient(lpszClientAddr, STORAGE_LEAVETYPE_HEARTBEAT, STORAGE_NETTYPE_HTTPWEBDAV); } +void XCALLBACK XEngine_Callback_HBFTPContral(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nStatus, XPVOID lParam) +{ + XEngine_Net_CloseClient(lpszClientAddr, STORAGE_LEAVETYPE_HEARTBEAT, STORAGE_NETTYPE_FTPCONTRAL); +} ////////////////////////////////////////////////////////////////////////// /* * 统一处理客户端离线后的资源清理逻辑。 @@ -308,6 +340,34 @@ bool XEngine_Net_CloseClient(LPCXSTR lpszClientAddr, int nLeaveType, int nClient HttpProtocol_Server_CloseClinetEx(xhWebdavHttp, lpszClientAddr); Cryption_Server_CloseClientEx(xhWDSsl, lpszClientAddr); } + else if (STORAGE_NETTYPE_FTPCONTRAL == nClientType) + { + m_StrClient = _X("FTP客户端"); + if (STORAGE_LEAVETYPE_HEARTBEAT == nLeaveType) + { + m_StrLeaveMsg = _X("心跳超时"); + NetCore_TCPXCore_CloseForClientEx(xhNetFTPContral, lpszClientAddr); + } + else if (STORAGE_LEAVETYPE_BYSELF == nLeaveType) + { + m_StrLeaveMsg = _X("被动断开"); + SocketOpt_HeartBeat_DeleteAddrEx(xhHBFTPContral, lpszClientAddr); + } + else + { + m_StrLeaveMsg = _X("主动关闭"); + NetCore_TCPXCore_CloseForClientEx(xhNetFTPContral, lpszClientAddr); + SocketOpt_HeartBeat_DeleteAddrEx(xhHBFTPContral, lpszClientAddr); + } + FTPProtocol_Parse_DeleteClientEx(xhFTPContral, lpszClientAddr); + Session_FTP_Delete(lpszClientAddr); + } + else + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("未知客户端类型:%s,无法处理离线逻辑"), lpszClientAddr); + return false; + } + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("%s:%s,与服务器断开,原因:%s"), m_StrClient.c_str(), lpszClientAddr, m_StrLeaveMsg.c_str()); return true; } @@ -396,6 +456,14 @@ bool XEngine_Net_SendMsg(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, int nMsg SocketOpt_HeartBeat_ActiveAddrEx(xhHBWebdav, lpszClientAddr); } } + else if (STORAGE_NETTYPE_FTPCONTRAL == nType) + { + bRet = NetCore_TCPXCore_SendEx(xhNetFTPContral, lpszClientAddr, lpszMsgBuffer, nMsgLen); + if (bRet && st_ServiceCfg.st_XTime.bHBTime) + { + SocketOpt_HeartBeat_ActiveAddrEx(xhHBFTPContral, lpszClientAddr); + } + } if (!bRet) { XLONG dwRet = NetCore_GetLastError(); diff --git a/XEngine_Source/XEngine_StorageApp/StorageApp_Network.h b/XEngine_Source/XEngine_StorageApp/StorageApp_Network.h index 643625dd..2854e23a 100644 --- a/XEngine_Source/XEngine_StorageApp/StorageApp_Network.h +++ b/XEngine_Source/XEngine_StorageApp/StorageApp_Network.h @@ -3,20 +3,28 @@ bool XCALLBACK XEngine_Callback_DownloadLogin(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); void XCALLBACK XEngine_Callback_DownloadRecv(LPCXSTR lpszClientAddr, XSOCKET hSocket, LPCXSTR lpszRecvMsg, int nMsgLen, XPVOID lParam); void XCALLBACK XEngine_Callback_DownloadLeave(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); + bool XCALLBACK XEngine_Callback_UPLoaderLogin(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); void XCALLBACK XEngine_Callback_UPLoaderRecv(LPCXSTR lpszClientAddr, XSOCKET hSocket, LPCXSTR lpszRecvMsg, int nMsgLen, XPVOID lParam); void XCALLBACK XEngine_Callback_UPLoaderLeave(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); + bool XCALLBACK XEngine_Callback_CenterLogin(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); void XCALLBACK XEngine_Callback_CenterRecv(LPCXSTR lpszClientAddr, XSOCKET hSocket, LPCXSTR lpszRecvMsg, int nMsgLen, XPVOID lParam); void XCALLBACK XEngine_Callback_CenterLeave(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); + bool XCALLBACK XEngine_Callback_WebdavLogin(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); void XCALLBACK XEngine_Callback_WebdavRecv(LPCXSTR lpszClientAddr, XSOCKET hSocket, LPCXSTR lpszRecvMsg, int nMsgLen, XPVOID lParam); void XCALLBACK XEngine_Callback_WebdavLeave(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); +bool XCALLBACK XEngine_Callback_FTPContralLogin(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); +void XCALLBACK XEngine_Callback_FTPContralRecv(LPCXSTR lpszClientAddr, XSOCKET hSocket, LPCXSTR lpszRecvMsg, int nMsgLen, XPVOID lParam); +void XCALLBACK XEngine_Callback_FTPContralLeave(LPCXSTR lpszClientAddr, XSOCKET hSocket, XPVOID lParam); + void XCALLBACK XEngine_Callback_HBDownload(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nStatus, XPVOID lParam); void XCALLBACK XEngine_Callback_HBUPLoader(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nStatus, XPVOID lParam); void XCALLBACK XEngine_Callback_HBCenter(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nStatus, XPVOID lParam); void XCALLBACK XEngine_Callback_HBWebdav(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nStatus, XPVOID lParam); +void XCALLBACK XEngine_Callback_HBFTPContral(LPCXSTR lpszClientAddr, XSOCKET hSocket, int nStatus, XPVOID lParam); bool XEngine_Net_CloseClient(LPCXSTR lpszClientAddr, int nLeaveType, int nClientType); bool XEngine_Net_SendMsg(LPCXSTR lpszClientAddr, LPCXSTR lpszMsgBuffer, int nMsgLen, int nType); \ No newline at end of file diff --git a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp index 28417a8e..570f8d10 100644 --- a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp +++ b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.cpp @@ -8,16 +8,19 @@ XHANDLE xhHBDownload = NULL; XHANDLE xhHBUPLoader = NULL; XHANDLE xhHBCenter = NULL; XHANDLE xhHBWebdav = NULL; +XHANDLE xhHBFTPContral = NULL; XHANDLE xhNetDownload = NULL; XHANDLE xhNetUPLoader = NULL; XHANDLE xhNetCenter = NULL; XHANDLE xhNetWebdav = NULL; +XHANDLE xhNetFTPContral = NULL; XHANDLE xhUPPool = NULL; XHANDLE xhDLPool = NULL; XHANDLE xhCTPool = NULL; XHANDLE xhWDPool = NULL; +XHANDLE xhFTPPoolContral = NULL; XHANDLE xhDLSsl = NULL; XHANDLE xhUPSsl = NULL; @@ -29,6 +32,7 @@ XHANDLE xhUPHttp = NULL; XHANDLE xhDLHttp = NULL; XHANDLE xhCenterHttp = NULL; XHANDLE xhWebdavHttp = NULL; +XHANDLE xhFTPContral = NULL; XSOCKET hBroadSocket = 0; shared_ptr pSTDThread = NULL; @@ -48,6 +52,7 @@ void ServiceApp_Stop(int signo) HttpProtocol_Server_DestroyEx(xhDLHttp); HttpProtocol_Server_DestroyEx(xhCenterHttp); HttpProtocol_Server_DestroyEx(xhWebdavHttp); + FTPProtocol_Parse_DestroyEx(xhFTPContral); Cryption_Server_StopEx(xhDLSsl); Cryption_Server_StopEx(xhUPSsl); @@ -58,16 +63,19 @@ void ServiceApp_Stop(int signo) NetCore_TCPXCore_DestroyEx(xhNetUPLoader); NetCore_TCPXCore_DestroyEx(xhNetCenter); NetCore_TCPXCore_DestroyEx(xhNetWebdav); + NetCore_TCPXCore_DestroyEx(xhNetFTPContral); SocketOpt_HeartBeat_DestoryEx(xhHBDownload); SocketOpt_HeartBeat_DestoryEx(xhHBUPLoader); SocketOpt_HeartBeat_DestoryEx(xhHBCenter); SocketOpt_HeartBeat_DestoryEx(xhHBWebdav); + SocketOpt_HeartBeat_DestoryEx(xhHBFTPContral); ManagePool_Thread_NQDestroy(xhUPPool); ManagePool_Thread_NQDestroy(xhDLPool); ManagePool_Thread_NQDestroy(xhCTPool); ManagePool_Thread_NQDestroy(xhWDPool); + ManagePool_Thread_NQDestroy(xhFTPPoolContral); Algorithm_Calculation_Close(xhLimit); HelpComponents_XLog_Destroy(xhLog); @@ -76,6 +84,7 @@ void ServiceApp_Stop(int signo) Session_UPStroage_Destory(); Database_File_Destory(); Database_Memory_Destory(); + APIHelp_Port_Destroy(); if (NULL != pSTDThread) { @@ -174,11 +183,13 @@ int main(int argc, char** argv) int nRet = -1; LPCXSTR lpszHTTPMime = _X("./XEngine_Config/HttpMime.types"); LPCXSTR lpszHTTPCode = _X("./XEngine_Config/HttpCode.types"); + LPCXSTR lpszFTPCodes = _X("./XEngine_Config/FTPCode.types"); HELPCOMPONENTS_XLOG_CONFIGURE st_XLogConfig; THREADPOOL_PARAMENT** ppSt_ListUPThread; THREADPOOL_PARAMENT** ppSt_ListDLThread; THREADPOOL_PARAMENT** ppSt_ListCTThread; THREADPOOL_PARAMENT** ppSt_ListWDThread; + THREADPOOL_PARAMENT** ppSt_ListFTPCThread; memset(&st_XLogConfig, '\0', sizeof(HELPCOMPONENTS_XLOG_CONFIGURE)); memset(&st_ServiceCfg, '\0', sizeof(XENGINE_SERVERCONFIG)); @@ -539,6 +550,48 @@ int main(int argc, char** argv) } XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("启动服务中,启动WEBDAV任务处理线程池成功,线程池个数:%d"), st_ServiceCfg.st_XMax.nWebdavThread); } + //FTP + if (st_ServiceCfg.nFTPPort > 0) + { + //控制端口启用 + xhFTPContral = FTPProtocol_Parse_InitEx(lpszFTPCodes, st_ServiceCfg.st_XMax.nFTPThread); + if (NULL == xhFTPContral) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("启动服务中,初始化FTP服务失败,错误:%lX"), FTPProtocol_GetLastError()); + goto XENGINE_EXITAPP; + } + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("启动服务中,初始化FTP服务成功,IO线程个数:%d"), st_ServiceCfg.st_XMax.nFTPThread); + + xhNetFTPContral = NetCore_TCPXCore_StartEx(st_ServiceCfg.nFTPPort, st_ServiceCfg.st_XMax.nMaxClient, st_ServiceCfg.st_XMax.nIOThread, false, st_ServiceCfg.bReuseraddr); + if (NULL == xhNetFTPContral) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("启动服务中,启动FTP网络服务失败,端口:%d,错误:%lX"), st_ServiceCfg.nFTPPort, NetCore_GetLastError()); + goto XENGINE_EXITAPP; + } + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("启动服务中,启动FTP网络服务成功,端口:%d,IO线程个数:%d"), st_ServiceCfg.nFTPPort, st_ServiceCfg.st_XMax.nIOThread); + NetCore_TCPXCore_RegisterCallBackEx(xhNetFTPContral, XEngine_Callback_FTPContralLogin, XEngine_Callback_FTPContralRecv, XEngine_Callback_FTPContralLeave); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("启动服务中,注册FTP网络服务事件成功!")); + + BaseLib_Memory_Malloc((XPPPMEM)&ppSt_ListFTPCThread, st_ServiceCfg.st_XMax.nFTPThread, sizeof(THREADPOOL_PARAMENT)); + for (int i = 0; i < st_ServiceCfg.st_XMax.nFTPThread; i++) + { + int* pInt_Pos = new int; + *pInt_Pos = i; + + ppSt_ListFTPCThread[i]->lParam = pInt_Pos; + ppSt_ListFTPCThread[i]->fpCall_ThreadsTask = XEngine_FTPContral_Thread; + } + xhFTPPoolContral = ManagePool_Thread_NQCreate(&ppSt_ListFTPCThread, st_ServiceCfg.st_XMax.nFTPThread); + if (NULL == xhFTPPoolContral) + { + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_ERROR, _X("启动服务中,启动FTP处理线程池失败,错误:%d"), errno); + goto XENGINE_EXITAPP; + } + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("启动服务中,启动FTP任务处理线程池成功,线程池个数:%d"), st_ServiceCfg.st_XMax.nFTPThread); + + APIHelp_Port_Init(st_ServiceCfg.st_XFtp.nFTPStart, st_ServiceCfg.st_XFtp.nFTPEnd); + XLOG_PRINT(xhLog, XENGINE_HELPCOMPONENTS_XLOG_IN_LOGLEVEL_INFO, _X("启动服务中,初始化FTP数据端口成功,端口范围:%d-%d"), st_ServiceCfg.st_XFtp.nFTPStart, st_ServiceCfg.st_XFtp.nFTPEnd); + } //只有使用了数据库,才启用P2P if (st_ServiceCfg.st_P2xp.bEnable) { @@ -645,6 +698,7 @@ int main(int argc, char** argv) HttpProtocol_Server_DestroyEx(xhDLHttp); HttpProtocol_Server_DestroyEx(xhCenterHttp); HttpProtocol_Server_DestroyEx(xhWebdavHttp); + FTPProtocol_Parse_DestroyEx(xhFTPContral); Cryption_Server_StopEx(xhDLSsl); Cryption_Server_StopEx(xhUPSsl); @@ -655,16 +709,19 @@ int main(int argc, char** argv) NetCore_TCPXCore_DestroyEx(xhNetUPLoader); NetCore_TCPXCore_DestroyEx(xhNetCenter); NetCore_TCPXCore_DestroyEx(xhNetWebdav); + NetCore_TCPXCore_DestroyEx(xhNetFTPContral); SocketOpt_HeartBeat_DestoryEx(xhHBDownload); SocketOpt_HeartBeat_DestoryEx(xhHBUPLoader); SocketOpt_HeartBeat_DestoryEx(xhHBCenter); SocketOpt_HeartBeat_DestoryEx(xhHBWebdav); + SocketOpt_HeartBeat_DestoryEx(xhHBFTPContral); ManagePool_Thread_NQDestroy(xhUPPool); ManagePool_Thread_NQDestroy(xhDLPool); ManagePool_Thread_NQDestroy(xhCTPool); ManagePool_Thread_NQDestroy(xhWDPool); + ManagePool_Thread_NQDestroy(xhFTPPoolContral); Algorithm_Calculation_Close(xhLimit); HelpComponents_XLog_Destroy(xhLog); @@ -673,6 +730,7 @@ int main(int argc, char** argv) Session_UPStroage_Destory(); Database_File_Destory(); Database_Memory_Destory(); + APIHelp_Port_Destroy(); if (NULL != pSTDThread) { diff --git a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj index 53fb21cc..d6b6068a 100644 --- a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj +++ b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj @@ -134,6 +134,7 @@ WIN32;_DEBUG;_CONSOLE;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true 4819 + stdcpp20 Console @@ -151,6 +152,7 @@ MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Console @@ -165,6 +167,7 @@ false _DEBUG;_CONSOLE;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true + stdcpp20 Console @@ -177,6 +180,7 @@ false _DEBUG;_CONSOLE;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true + stdcpp20 Console @@ -194,6 +198,7 @@ MultiThreadedDLL 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Console @@ -213,6 +218,7 @@ MultiThreaded 4819 /utf-8 %(AdditionalOptions) + stdcpp20 Console @@ -225,6 +231,7 @@ + @@ -239,6 +246,7 @@ + diff --git a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj.filters b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj.filters index 050ad75e..81869890 100644 --- a/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj.filters +++ b/XEngine_Source/XEngine_StorageApp/XEngine_StorageApp.vcxproj.filters @@ -57,6 +57,9 @@ 源文件 + + 源文件 + @@ -95,5 +98,8 @@ 头文件 + + 头文件 + \ No newline at end of file