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