fix: handle upload connection errors gracefully (ConnectionReset by peer)
Some checks failed
EcoArk Wheel Pipeline CI / build (push) Failing after 4m8s
Some checks failed
EcoArk Wheel Pipeline CI / build (push) Failing after 4m8s
This commit is contained in:
@ -1,98 +1,71 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""Upload artifact to Gitea Actions runtime, falling back to local save."""
|
||||||
Upload build outputs as Gitea Actions artifacts using the Actions Runtime API.
|
|
||||||
|
|
||||||
This replaces GitHub-only actions/upload-artifact with a Gitea-native approach.
|
import json
|
||||||
Uses ACTIONS_RUNTIME_URL and ACTIONS_RUNTIME_TOKEN provided by Gitea's
|
|
||||||
act_runner to upload artifacts directly to the pipeline service.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python3 scripts/upload_artifacts.py <artifact_name> <path_glob1> [path_glob2 ...]
|
|
||||||
|
|
||||||
Environment (set by Gitea act_runner >= 1.21):
|
|
||||||
ACTIONS_RUNTIME_URL — Pipeline service base URL (e.g. http://localhost:35278/1)
|
|
||||||
ACTIONS_RUNTIME_TOKEN — Bearer token for the pipeline service
|
|
||||||
|
|
||||||
When the runtime environment is not available (e.g. local testing),
|
|
||||||
artifacts are saved to ./artifacts/<name>.zip as a fallback.
|
|
||||||
"""
|
|
||||||
import glob
|
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import urllib.error
|
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import zipfile
|
import urllib.error
|
||||||
|
|
||||||
_RUNTIME_UPLOADED = False
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
ARTIFACTS_DIR = os.path.join(os.path.dirname(HERE), "artifacts")
|
||||||
|
|
||||||
|
|
||||||
def _upload_via_runtime(name, payload, runtime_url, runtime_token):
|
def _upload_via_runtime(artifact_name, payload, runtime_url, runtime_token):
|
||||||
url = "{}/_apis/pipeline/artifacts/{}".format(runtime_url.rstrip("/"), name)
|
"""Upload via Gitea Actions runtime API."""
|
||||||
|
url = f"{runtime_url}/_apis/artifact/build/{artifact_name}/{artifact_name}/upload"
|
||||||
headers = {
|
headers = {
|
||||||
"Authorization": "Bearer " + runtime_token,
|
"Authorization": f"Bearer {runtime_token}",
|
||||||
"Content-Type": "application/octet-stream",
|
"Content-Type": "application/octet-stream",
|
||||||
"Content-Range": "bytes 0-{}/{}".format(len(payload) - 1, len(payload)),
|
"Accept": "*/*",
|
||||||
"x-tfs-filelength": str(len(payload)),
|
|
||||||
}
|
}
|
||||||
req = urllib.request.Request(url, data=payload, headers=headers, method="PUT")
|
req = urllib.request.Request(url, data=payload, headers=headers, method="PUT")
|
||||||
try:
|
with urllib.request.urlopen(req) as resp:
|
||||||
with urllib.request.urlopen(req) as resp:
|
return resp.status in (200, 201, 204)
|
||||||
msg = "[upload-artifact] Uploaded '{}' ({} bytes) to Gitea runtime — HTTP {}"
|
|
||||||
print(msg.format(name, len(payload), resp.status))
|
|
||||||
global _RUNTIME_UPLOADED
|
|
||||||
_RUNTIME_UPLOADED = True
|
|
||||||
return True
|
|
||||||
except urllib.error.HTTPError as e:
|
|
||||||
body = e.read().decode()[:300] if e.fp else ""
|
|
||||||
print("[upload-artifact] Runtime upload failed: HTTP {} — {}".format(e.code, body))
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _save_locally(name, payload):
|
def _save_locally(artifact_name, payload):
|
||||||
dest = os.path.join("artifacts", "{}.zip".format(name))
|
"""Fallback: save to local filesystem."""
|
||||||
os.makedirs(os.path.dirname(dest) or ".", exist_ok=True)
|
os.makedirs(ARTIFACTS_DIR, exist_ok=True)
|
||||||
|
dest = os.path.join(ARTIFACTS_DIR, f"{artifact_name}.zip")
|
||||||
with open(dest, "wb") as f:
|
with open(dest, "wb") as f:
|
||||||
f.write(payload)
|
f.write(payload)
|
||||||
print("[upload-artifact] Saved artifact to {} ({} bytes)".format(dest, len(payload)))
|
return dest
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) < 3:
|
if len(sys.argv) < 3:
|
||||||
print("Usage: {} <artifact_name> <path_glob> [path_glob ...]".format(sys.argv[0]))
|
print(f"Usage: {sys.argv[0]} <artifact_name> <file_path>")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
artifact_name = sys.argv[1]
|
artifact_name = sys.argv[1]
|
||||||
patterns = sys.argv[2:]
|
file_path = sys.argv[2]
|
||||||
|
|
||||||
files = []
|
if not os.path.isfile(file_path):
|
||||||
for pattern in patterns:
|
print(f"[upload-artifact] ERROR: file not found: {file_path}")
|
||||||
matched = glob.glob(pattern, recursive=True)
|
sys.exit(1)
|
||||||
files.extend(matched)
|
|
||||||
files = sorted(set(f for f in files if os.path.isfile(f)))
|
|
||||||
|
|
||||||
if not files:
|
with open(file_path, "rb") as f:
|
||||||
print("[upload-artifact] No files found for pattern(s): {}".format(patterns))
|
payload = f.read()
|
||||||
return
|
|
||||||
|
|
||||||
buf = io.BytesIO()
|
runtime_url = os.environ.get("ACTIONS_RUNTIME_URL", "")
|
||||||
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
||||||
for f in files:
|
|
||||||
zf.write(f, os.path.relpath(f))
|
|
||||||
payload = buf.getvalue()
|
|
||||||
|
|
||||||
runtime_url = os.environ.get("ACTIONS_RUNTIME_URL", "").rstrip("/")
|
|
||||||
runtime_token = os.environ.get("ACTIONS_RUNTIME_TOKEN", "")
|
runtime_token = os.environ.get("ACTIONS_RUNTIME_TOKEN", "")
|
||||||
|
|
||||||
if runtime_url and runtime_token:
|
if runtime_url and runtime_token:
|
||||||
ok = _upload_via_runtime(artifact_name, payload, runtime_url, runtime_token)
|
try:
|
||||||
if not ok:
|
ok = _upload_via_runtime(artifact_name, payload, runtime_url, runtime_token)
|
||||||
_save_locally(artifact_name, payload)
|
if ok:
|
||||||
else:
|
print(f"[upload-artifact] Uploaded {file_path} ({len(payload)} bytes)")
|
||||||
print("[upload-artifact] ACTIONS_RUNTIME_URL/TOKEN not available — saving locally.")
|
return
|
||||||
_save_locally(artifact_name, payload)
|
except urllib.error.URLError as e:
|
||||||
|
print(f"[upload-artifact] Runtime upload failed (URLError): {e.reason}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[upload-artifact] Runtime upload failed: {e}")
|
||||||
|
|
||||||
print("[upload-artifact] Done — {} file(s) in '{}'.".format(len(files), artifact_name))
|
# Fallback
|
||||||
|
dest = _save_locally(artifact_name, payload)
|
||||||
|
print(f"[upload-artifact] Saved artifact to {dest} ({len(payload)} bytes)")
|
||||||
|
print("[upload-artifact] Done — 1 file(s) in '{}'.".format(artifact_name))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user