fix(ci): rewrite ci_trace_report.py in English to fix encoding issues
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m5s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m23s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m5s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m23s
This commit is contained in:
@@ -1,24 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CI Trace 涓婃姤鑴氭湰 - 鐢ㄤ簬 Gitea Actions workflow 涓笂鎶?Trace 鏁版嵁鍒?AgentLoop
|
||||
CI Trace Report Script - Reports CI Trace data to AgentLoop from Gitea Actions workflows.
|
||||
|
||||
鍦?CI workflow 鐨勬瘡涓?job 涓皟鐢細
|
||||
- 寮€濮嬫椂锛歱ython3 .gitea/scripts/ci_trace_report.py --status running
|
||||
- 缁撴潫鏃讹細python3 .gitea/scripts/ci_trace_report.py --status ok --start-time $CI_TRACE_START_TIME
|
||||
Usage in CI workflow jobs:
|
||||
- At start: python3 scripts/ci/ci_trace_report.py --status running
|
||||
- At end: python3 scripts/ci/ci_trace_report.py --status ok --start-time $CI_TRACE_START_TIME
|
||||
|
||||
鐜鍙橀噺锛圙itea Actions 鍐呯疆锛夛細
|
||||
GITEA_REPOSITORY / GITHUB_REPOSITORY - 浠撳簱鍚?(owner/repo)
|
||||
GITEA_WORKFLOW / GITHUB_WORKFLOW - workflow 鍚嶇О
|
||||
Environment variables (built-in Gitea Actions):
|
||||
GITEA_REPOSITORY / GITHUB_REPOSITORY - repository (owner/repo)
|
||||
GITEA_WORKFLOW / GITHUB_WORKFLOW - workflow name
|
||||
GITEA_JOB / GITHUB_JOB - job ID
|
||||
GITEA_SHA / GITHUB_SHA - commit SHA
|
||||
GITEA_REF_NAME / GITHUB_REF_NAME - 鍒嗘敮鍚? GITEA_RUN_ID / GITHUB_RUN_ID - run ID
|
||||
GITEA_ACTOR / GITHUB_ACTOR - 瑙﹀彂鑰? GITEA_EVENT_NAME / GITHUB_EVENT_NAME - 浜嬩欢绫诲瀷
|
||||
PR_NUMBER / GITEA_PR_NUMBER - PR 鍙凤紙濡傛灉鏄?PR 瑙﹀彂锛?
|
||||
AgentLoop 閰嶇疆锛堥€氳繃 Secrets 娉ㄥ叆锛夛細
|
||||
AGENTLOOP_LICENSE_KEY - LicenseKey锛堝繀濉級
|
||||
AGENTLOOP_ENDPOINT - Trace 涓婃姤鍦板潃锛堝彲閫夛紝鏈夐粯璁ゅ€硷級
|
||||
AGENTLOOP_PROJECT - SLS Project 鍚嶏紙鍙€夛級
|
||||
AGENTLOOP_WORKSPACE - CMS Workspace 鍚嶏紙鍙€夛級
|
||||
GITEA_REF_NAME / GITHUB_REF_NAME - branch name
|
||||
GITEA_RUN_ID / GITHUB_RUN_ID - run ID
|
||||
GITEA_ACTOR / GITHUB_ACTOR - trigger actor
|
||||
GITEA_EVENT_NAME / GITHUB_EVENT_NAME - event type
|
||||
PR_NUMBER / GITEA_PR_NUMBER - PR number (if PR triggered)
|
||||
|
||||
AgentLoop configuration (injected via Secrets):
|
||||
AGENTLOOP_LICENSE_KEY - LicenseKey (required)
|
||||
AGENTLOOP_ENDPOINT - Trace endpoint (optional, has default)
|
||||
AGENTLOOP_PROJECT - SLS Project name (optional)
|
||||
AGENTLOOP_WORKSPACE - CMS Workspace name (optional)
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -31,13 +34,13 @@ import urllib.request
|
||||
import urllib.error
|
||||
|
||||
|
||||
# ========== 榛樿閰嶇疆 ==========
|
||||
# ========== Default Configuration ==========
|
||||
DEFAULT_ENDPOINT = "https://proj-xtrace-495e81719a1fd9a2c5fd671eefafbe-cn-hangzhou.cn-hangzhou.log.aliyuncs.com/apm/trace/opentelemetry/v1/traces"
|
||||
DEFAULT_PROJECT = "proj-xtrace-495e81719a1fd9a2c5fd671eefafbe-cn-hangzhou"
|
||||
DEFAULT_WORKSPACE = "agentloop-13b8d6efb7fde6e9b193eb982ade68e2"
|
||||
|
||||
|
||||
# ========== OTLP Protobuf 鎵嬪姩缂栫爜 ==========
|
||||
# ========== OTLP Protobuf Manual Encoding ==========
|
||||
|
||||
def _encode_varint(value):
|
||||
result = bytearray()
|
||||
@@ -47,32 +50,40 @@ def _encode_varint(value):
|
||||
result.append(value & 0x7F)
|
||||
return bytes(result)
|
||||
|
||||
|
||||
def _encode_tag(field_number, wire_type):
|
||||
return _encode_varint((field_number << 3) | wire_type)
|
||||
|
||||
|
||||
def _encode_string_field(field_number, value):
|
||||
value_bytes = value.encode('utf-8')
|
||||
value_bytes = value.encode("utf-8")
|
||||
return _encode_tag(field_number, 2) + _encode_varint(len(value_bytes)) + value_bytes
|
||||
|
||||
|
||||
def _encode_bytes_field(field_number, value_bytes):
|
||||
return _encode_tag(field_number, 2) + _encode_varint(len(value_bytes)) + value_bytes
|
||||
|
||||
|
||||
def _encode_int_field(field_number, value):
|
||||
return _encode_tag(field_number, 0) + _encode_varint(value & 0xFFFFFFFFFFFFFFFF)
|
||||
|
||||
|
||||
def _encode_message_field(field_number, message_bytes):
|
||||
return _encode_tag(field_number, 2) + _encode_varint(len(message_bytes)) + message_bytes
|
||||
|
||||
|
||||
def _encode_key_value(key, value_str):
|
||||
any_value = _encode_string_field(1, value_str)
|
||||
return _encode_string_field(1, key) + _encode_message_field(2, any_value)
|
||||
|
||||
|
||||
def _encode_status(status_code, status_msg=""):
|
||||
data = _encode_int_field(1, status_code)
|
||||
if status_msg:
|
||||
data += _encode_string_field(2, status_msg)
|
||||
return data
|
||||
|
||||
|
||||
def _encode_span(trace_id_bytes, span_id_bytes, parent_span_id_bytes,
|
||||
name, start_time_unix_nano, end_time_unix_nano,
|
||||
span_kind, attributes, status_code, status_msg=""):
|
||||
@@ -92,6 +103,7 @@ def _encode_span(trace_id_bytes, span_id_bytes, parent_span_id_bytes,
|
||||
data += _encode_message_field(12, status)
|
||||
return data
|
||||
|
||||
|
||||
def _encode_resource_spans(service_name, scope_spans_bytes):
|
||||
svc_kv = _encode_key_value("service.name", service_name)
|
||||
resource = _encode_message_field(1, svc_kv)
|
||||
@@ -99,6 +111,7 @@ def _encode_resource_spans(service_name, scope_spans_bytes):
|
||||
data += _encode_message_field(2, scope_spans_bytes)
|
||||
return data
|
||||
|
||||
|
||||
def _encode_scope_spans(scope_name, spans_bytes_list):
|
||||
scope = _encode_string_field(1, scope_name)
|
||||
data = _encode_message_field(1, scope)
|
||||
@@ -106,6 +119,7 @@ def _encode_scope_spans(scope_name, spans_bytes_list):
|
||||
data += _encode_message_field(2, span_bytes)
|
||||
return data
|
||||
|
||||
|
||||
def _encode_traces_data(resource_spans_bytes_list):
|
||||
data = b""
|
||||
for rs_bytes in resource_spans_bytes_list:
|
||||
@@ -113,20 +127,21 @@ def _encode_traces_data(resource_spans_bytes_list):
|
||||
return data
|
||||
|
||||
|
||||
# ========== 杈呭姪鍑芥暟 ==========
|
||||
# ========== Helper Functions ==========
|
||||
|
||||
def _gen_trace_id():
|
||||
return uuid.uuid4().bytes
|
||||
|
||||
|
||||
def _gen_span_id():
|
||||
return uuid.uuid4().bytes[:8]
|
||||
|
||||
|
||||
def _env(name, default=""):
|
||||
"""鑾峰彇鐜鍙橀噺锛屾敮鎸?GITEA_ 鍜?GITHUB_ 鍓嶇紑"""
|
||||
"""Get env var with GITEA_/GITHUB_ prefix fallback."""
|
||||
val = os.getenv(name, "")
|
||||
if val:
|
||||
return val
|
||||
# 灏濊瘯鍙︿竴绉嶅墠缂€
|
||||
if name.startswith("GITEA_"):
|
||||
alt = "GITHUB_" + name[6:]
|
||||
return os.getenv(alt, default)
|
||||
@@ -137,12 +152,11 @@ def _env(name, default=""):
|
||||
|
||||
|
||||
def _get_pr_number():
|
||||
"""浠庣幆澧冨彉閲忔垨浜嬩欢鏂囦欢涓幏鍙?PR 鍙?""
|
||||
# 鐩存帴浠庣幆澧冨彉閲? pr = os.getenv("PR_NUMBER", "") or os.getenv("GITEA_PR_NUMBER", "")
|
||||
"""Get PR number from environment or event file."""
|
||||
pr = os.getenv("PR_NUMBER", "") or os.getenv("GITEA_PR_NUMBER", "")
|
||||
if pr:
|
||||
return pr
|
||||
|
||||
# 浠?GITHUB_EVENT_PATH 璇诲彇
|
||||
event_path = os.getenv("GITHUB_EVENT_PATH", "") or os.getenv("GITEA_EVENT_PATH", "")
|
||||
if event_path and os.path.isfile(event_path):
|
||||
try:
|
||||
@@ -157,7 +171,7 @@ def _get_pr_number():
|
||||
|
||||
|
||||
def _get_ci_attributes():
|
||||
"""浠?CI 鐜鍙橀噺涓敹闆嗗睘鎬?""
|
||||
"""Collect attributes from CI environment variables."""
|
||||
attrs = {
|
||||
"ci.repo": _env("GITEA_REPOSITORY") or _env("GITHUB_REPOSITORY") or "unknown",
|
||||
"ci.workflow": _env("GITEA_WORKFLOW") or _env("GITHUB_WORKFLOW") or "unknown",
|
||||
@@ -174,10 +188,10 @@ def _get_ci_attributes():
|
||||
return attrs
|
||||
|
||||
|
||||
# ========== 鏍稿績涓婃姤閫昏緫 ==========
|
||||
# ========== Trace Building & Reporting ==========
|
||||
|
||||
def build_trace(service_name, trace_name, status, duration_ms, attributes=None):
|
||||
"""鏋勫缓涓€鏉″畬鏁寸殑 Trace 鏁版嵁锛岃繑鍥?protobuf bytes銆?""
|
||||
"""Build an OTLP trace payload (protobuf bytes). No external dependencies."""
|
||||
trace_id = _gen_trace_id()
|
||||
end_time = int(time.time() * 1e9)
|
||||
start_time = end_time - int(duration_ms * 1e6)
|
||||
@@ -214,8 +228,9 @@ def report_ci_trace(service_name, trace_name, status="ok", duration_ms=1000,
|
||||
endpoint=None, license_key=None, project=None, workspace=None,
|
||||
extra_attributes=None):
|
||||
"""
|
||||
涓婃姤 CI Trace 鏁版嵁銆傝繑鍥?(success: bool, message: str)
|
||||
姘歌繙涓嶄細鎶涘嚭寮傚父锛屽け璐ヤ篃杩斿洖 False銆? """
|
||||
Report CI Trace data. Returns (success: bool, message: str).
|
||||
Never raises exceptions; returns False on failure.
|
||||
"""
|
||||
try:
|
||||
endpoint = endpoint or os.getenv("AGENTLOOP_ENDPOINT", DEFAULT_ENDPOINT)
|
||||
license_key = license_key or os.getenv("AGENTLOOP_LICENSE_KEY", "")
|
||||
@@ -223,9 +238,9 @@ def report_ci_trace(service_name, trace_name, status="ok", duration_ms=1000,
|
||||
workspace = workspace or os.getenv("AGENTLOOP_WORKSPACE", DEFAULT_WORKSPACE)
|
||||
|
||||
if not license_key:
|
||||
return False, "[Trace] 璺宠繃涓婃姤锛氭湭閰嶇疆 AGENTLOOP_LICENSE_KEY"
|
||||
return False, "[Trace] skipped: AGENTLOOP_LICENSE_KEY not configured"
|
||||
|
||||
# 鏀堕泦 CI 灞炴€? attrs = _get_ci_attributes()
|
||||
attrs = _get_ci_attributes()
|
||||
if extra_attributes:
|
||||
attrs.update(extra_attributes)
|
||||
|
||||
@@ -254,39 +269,43 @@ def report_ci_trace(service_name, trace_name, status="ok", duration_ms=1000,
|
||||
resp_body = e.read().decode("utf-8", errors="replace")
|
||||
|
||||
if status_code in (200, 202):
|
||||
return True, f"[Trace] 鉁?涓婃姤鎴愬姛: {service_name} / {trace_name} ({status}, {duration_ms}ms)"
|
||||
return True, (
|
||||
f"[Trace] success: {service_name} / {trace_name} "
|
||||
f"({status}, {duration_ms}ms)"
|
||||
)
|
||||
else:
|
||||
return False, f"[Trace] 鈿?涓婃姤澶辫触: HTTP {status_code} - {resp_body[:200]}"
|
||||
return False, (
|
||||
f"[Trace] failed: HTTP {status_code} - {resp_body[:200]}"
|
||||
)
|
||||
except Exception as e:
|
||||
return False, f"[Trace] 鈿?涓婃姤寮傚父: {type(e).__name__}: {str(e)}"
|
||||
return False, f"[Trace] error: {type(e).__name__}: {str(e)}"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="CI AgentLoop Trace 涓婃姤")
|
||||
parser = argparse.ArgumentParser(description="CI AgentLoop Trace Reporter")
|
||||
parser.add_argument("--service", dest="service_name",
|
||||
default=os.getenv("TRACE_SERVICE", ""),
|
||||
help="鏈嶅姟鍚嶇О锛堜篃鍙€氳繃 TRACE_SERVICE 鐜鍙橀噺璁剧疆锛?)
|
||||
help="Service name (also via TRACE_SERVICE env)")
|
||||
parser.add_argument("--name", dest="trace_name",
|
||||
default=os.getenv("TRACE_NAME", ""),
|
||||
help="Trace 鍚嶇О锛堜篃鍙€氳繃 TRACE_NAME 鐜鍙橀噺璁剧疆锛?)
|
||||
help="Trace name (also via TRACE_NAME env)")
|
||||
parser.add_argument("--status", default=os.getenv("TRACE_STATUS", "ok"),
|
||||
choices=["ok", "error", "running"],
|
||||
help="鐘舵€? ok / error / running锛堥粯璁?ok锛?)
|
||||
help="Status: ok / error / running (default ok)")
|
||||
parser.add_argument("--start-time", dest="start_time",
|
||||
default=os.getenv("TRACE_START_TIME", ""),
|
||||
help="寮€濮嬫椂闂存埑锛堢锛夛紝鐢ㄤ簬璁$畻 duration锛涗笉濉垯鐢ㄩ粯璁?1s")
|
||||
help="Start timestamp (seconds) for duration calculation")
|
||||
parser.add_argument("--duration-ms", dest="duration_ms", type=int, default=0,
|
||||
help="鐩存帴鎸囧畾鑰楁椂锛堟绉掞級锛屼紭鍏堢骇楂樹簬 --start-time")
|
||||
help="Direct duration in ms; takes precedence over --start-time")
|
||||
parser.add_argument("--attrs", default="",
|
||||
help="闄勫姞灞炴€э紙JSON 瀛楃涓诧級")
|
||||
help="Extra attributes (JSON string)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 蹇呴』鍙傛暟妫€鏌? if not args.service_name:
|
||||
print("[Trace] 鈿?璺宠繃涓婃姤锛氭湭鎸囧畾 service锛?-service 鎴?TRACE_SERVICE锛?)
|
||||
if not args.service_name:
|
||||
print("[Trace] skipped: no service specified (--service or TRACE_SERVICE)")
|
||||
sys.exit(0)
|
||||
|
||||
# 璁$畻鑰楁椂
|
||||
duration_ms = args.duration_ms
|
||||
if duration_ms <= 0 and args.start_time:
|
||||
try:
|
||||
@@ -295,15 +314,15 @@ def main():
|
||||
except (ValueError, TypeError):
|
||||
duration_ms = 1000
|
||||
if duration_ms <= 0:
|
||||
duration_ms = 1000 # 榛樿 1 绉?
|
||||
# 瑙f瀽闄勫姞灞炴€? extra_attrs = {}
|
||||
duration_ms = 1000
|
||||
|
||||
extra_attrs = {}
|
||||
if args.attrs:
|
||||
try:
|
||||
extra_attrs = json.loads(args.attrs)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 鐢熸垚 trace_name锛氬鏋滄病鎸囧畾锛岀敤 workflow+job
|
||||
trace_name = args.trace_name
|
||||
if not trace_name:
|
||||
wf = _env("GITEA_WORKFLOW") or _env("GITHUB_WORKFLOW") or "CI"
|
||||
@@ -319,7 +338,6 @@ def main():
|
||||
)
|
||||
|
||||
print(msg)
|
||||
# 姘歌繙 exit 0锛屼笉褰卞搷 CI
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user