cleaunp issues
This commit is contained in:
@@ -76,12 +76,12 @@ jobs:
|
||||
./target
|
||||
key: rust-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Tar mumble-web2-gui
|
||||
run: |
|
||||
tar -czf mumble-web2-gui-macos-arm64.tar.gz -C gui dist
|
||||
|
||||
- name: Upload mumble-web2-gui Artifact
|
||||
run: ./scripts/upload-artifact.sh --name mumble-web2-gui-macos-arm64 --path mumble-web2-gui-macos-arm64.tar.gz
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: mumble-web2-gui-macos-arm64
|
||||
path: gui/dist
|
||||
retention-days: 5
|
||||
|
||||
windows_build:
|
||||
runs-on: windows
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# upload-artifact.sh - Upload artifacts to Gitea Actions using the v4 artifact protocol.
|
||||
# Replaces gitea-upload-artifact@v4 action with a simple shell script.
|
||||
#
|
||||
# Usage:
|
||||
# ./upload-artifact.sh --name <artifact-name> --path <file-or-dir> [--retention-days <days>]
|
||||
#
|
||||
# Required environment variables (set automatically by Gitea Actions runner):
|
||||
# ACTIONS_RUNTIME_TOKEN - JWT bearer token
|
||||
# ACTIONS_RESULTS_URL - Artifact service base URL
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
CHUNK_SIZE=$((8 * 1024 * 1024)) # 8 MB
|
||||
|
||||
# ---------- argument parsing ----------
|
||||
ARTIFACT_NAME=""
|
||||
ARTIFACT_PATH=""
|
||||
RETENTION_DAYS=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--name) ARTIFACT_NAME="$2"; shift 2 ;;
|
||||
--path) ARTIFACT_PATH="$2"; shift 2 ;;
|
||||
--retention-days) RETENTION_DAYS="$2"; shift 2 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$ARTIFACT_NAME" || -z "$ARTIFACT_PATH" ]]; then
|
||||
echo "Usage: $0 --name <name> --path <path> [--retention-days <days>]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${ACTIONS_RUNTIME_TOKEN:-}" ]]; then
|
||||
echo "Error: ACTIONS_RUNTIME_TOKEN is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${ACTIONS_RESULTS_URL:-}" ]]; then
|
||||
echo "Error: ACTIONS_RESULTS_URL is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------- helpers ----------
|
||||
|
||||
# Decode base64url (JWT uses base64url without padding)
|
||||
base64url_decode() {
|
||||
local input="$1"
|
||||
# Replace URL-safe chars with standard base64 chars
|
||||
input="${input//-/+}"
|
||||
input="${input//_//}"
|
||||
# Add padding
|
||||
local pad=$(( 4 - ${#input} % 4 ))
|
||||
if [[ $pad -lt 4 ]]; then
|
||||
for ((i=0; i<pad; i++)); do input+="="; done
|
||||
fi
|
||||
echo "$input" | base64 -d 2>/dev/null || echo "$input" | base64 -D 2>/dev/null
|
||||
}
|
||||
|
||||
# Extract backend IDs from the JWT token's scp claim
|
||||
# Format: "Actions.Results:<runBackendId>:<jobBackendId>"
|
||||
extract_backend_ids() {
|
||||
local token="$ACTIONS_RUNTIME_TOKEN"
|
||||
# JWT has 3 parts separated by dots; payload is the second
|
||||
local payload
|
||||
payload=$(echo "$token" | cut -d'.' -f2)
|
||||
local decoded
|
||||
decoded=$(base64url_decode "$payload")
|
||||
|
||||
# Extract the scp claim - look for the Actions.Results entry
|
||||
local scp_value
|
||||
# Try jq first, fall back to manual parsing
|
||||
if command -v jq &>/dev/null; then
|
||||
scp_value=$(echo "$decoded" | jq -r '.scp // empty')
|
||||
else
|
||||
# Simple extraction: find "scp":"..." or "scp": "..."
|
||||
scp_value=$(echo "$decoded" | sed -n 's/.*"scp"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
||||
fi
|
||||
|
||||
if [[ -z "$scp_value" ]]; then
|
||||
echo "Error: Could not extract scp claim from token" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The scp may contain multiple space-separated scopes
|
||||
# Find the one starting with "Actions.Results:"
|
||||
local results_scope=""
|
||||
for scope in $scp_value; do
|
||||
if [[ "$scope" == Actions.Results:* ]]; then
|
||||
results_scope="$scope"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$results_scope" ]]; then
|
||||
echo "Error: No Actions.Results scope found in token" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORKFLOW_RUN_BACKEND_ID=$(echo "$results_scope" | cut -d':' -f2)
|
||||
WORKFLOW_JOB_RUN_BACKEND_ID=$(echo "$results_scope" | cut -d':' -f3)
|
||||
|
||||
if [[ -z "$WORKFLOW_RUN_BACKEND_ID" || -z "$WORKFLOW_JOB_RUN_BACKEND_ID" ]]; then
|
||||
echo "Error: Could not parse backend IDs from scope: $results_scope" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Strip trailing slash from URL
|
||||
normalize_url() {
|
||||
echo "${1%/}"
|
||||
}
|
||||
|
||||
# ---------- main flow ----------
|
||||
|
||||
echo "==> Uploading artifact: $ARTIFACT_NAME"
|
||||
echo " Path: $ARTIFACT_PATH"
|
||||
|
||||
# 1. Extract backend IDs from JWT
|
||||
extract_backend_ids
|
||||
echo " Run backend ID: $WORKFLOW_RUN_BACKEND_ID"
|
||||
echo " Job backend ID: $WORKFLOW_JOB_RUN_BACKEND_ID"
|
||||
|
||||
BASE_URL=$(normalize_url "$ACTIONS_RESULTS_URL")
|
||||
TWIRP_BASE="${BASE_URL}/twirp/github.actions.results.api.v1.ArtifactService"
|
||||
|
||||
# 2. Create zip of the artifact content
|
||||
TMPDIR_UPLOAD=$(mktemp -d)
|
||||
trap 'rm -rf "$TMPDIR_UPLOAD"' EXIT
|
||||
|
||||
ZIP_FILE="${TMPDIR_UPLOAD}/artifact.zip"
|
||||
|
||||
if [[ -f "$ARTIFACT_PATH" ]]; then
|
||||
# Single file - zip it at the top level
|
||||
PARENT_DIR=$(dirname "$ARTIFACT_PATH")
|
||||
FILE_NAME=$(basename "$ARTIFACT_PATH")
|
||||
(cd "$PARENT_DIR" && zip -q "$ZIP_FILE" "$FILE_NAME")
|
||||
elif [[ -d "$ARTIFACT_PATH" ]]; then
|
||||
# Directory - zip its contents
|
||||
(cd "$ARTIFACT_PATH" && zip -qr "$ZIP_FILE" .)
|
||||
else
|
||||
echo "Error: Path does not exist: $ARTIFACT_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ZIP_SIZE=$(wc -c < "$ZIP_FILE" | tr -d ' ')
|
||||
echo " Zip size: $ZIP_SIZE bytes"
|
||||
|
||||
# Compute SHA-256 hash of the zip
|
||||
if command -v sha256sum &>/dev/null; then
|
||||
ZIP_HASH=$(sha256sum "$ZIP_FILE" | cut -d' ' -f1)
|
||||
elif command -v shasum &>/dev/null; then
|
||||
ZIP_HASH=$(shasum -a 256 "$ZIP_FILE" | cut -d' ' -f1)
|
||||
else
|
||||
echo "Error: Neither sha256sum nor shasum found" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo " SHA-256: $ZIP_HASH"
|
||||
|
||||
# 3. CreateArtifact - get signed upload URL
|
||||
echo "==> Creating artifact..."
|
||||
|
||||
CREATE_BODY="{\"workflow_run_backend_id\":\"${WORKFLOW_RUN_BACKEND_ID}\",\"workflow_job_run_backend_id\":\"${WORKFLOW_JOB_RUN_BACKEND_ID}\",\"name\":\"${ARTIFACT_NAME}\",\"version\":4}"
|
||||
|
||||
CREATE_RESPONSE=$(curl -sS -X POST \
|
||||
"${TWIRP_BASE}/CreateArtifact" \
|
||||
-H "Authorization: Bearer ${ACTIONS_RUNTIME_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$CREATE_BODY")
|
||||
|
||||
echo " Create response: $CREATE_RESPONSE"
|
||||
|
||||
# Extract signed upload URL
|
||||
if command -v jq &>/dev/null; then
|
||||
SIGNED_URL=$(echo "$CREATE_RESPONSE" | jq -r '.signed_upload_url // .signedUploadUrl // empty')
|
||||
CREATE_OK=$(echo "$CREATE_RESPONSE" | jq -r '.ok // empty')
|
||||
else
|
||||
# Fallback: extract URL from JSON manually
|
||||
SIGNED_URL=$(echo "$CREATE_RESPONSE" | sed -n 's/.*"signed_upload_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
||||
if [[ -z "$SIGNED_URL" ]]; then
|
||||
SIGNED_URL=$(echo "$CREATE_RESPONSE" | sed -n 's/.*"signedUploadUrl"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
||||
fi
|
||||
CREATE_OK="true"
|
||||
fi
|
||||
|
||||
if [[ -z "$SIGNED_URL" ]]; then
|
||||
echo "Error: Failed to get signed upload URL" >&2
|
||||
echo "Response: $CREATE_RESPONSE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Got signed upload URL"
|
||||
|
||||
# 4. Upload zip in chunks using Azure Blob block protocol
|
||||
NUM_CHUNKS=$(( (ZIP_SIZE + CHUNK_SIZE - 1) / CHUNK_SIZE ))
|
||||
echo "==> Uploading artifact data (${ZIP_SIZE} bytes in ${NUM_CHUNKS} chunk(s) of up to ${CHUNK_SIZE} bytes)..."
|
||||
|
||||
# Split the zip into chunks
|
||||
CHUNK_PREFIX="${TMPDIR_UPLOAD}/chunk_"
|
||||
split -b "$CHUNK_SIZE" "$ZIP_FILE" "$CHUNK_PREFIX"
|
||||
|
||||
BLOCK_IDS=()
|
||||
CHUNK_INDEX=0
|
||||
|
||||
for CHUNK_FILE in "${CHUNK_PREFIX}"*; do
|
||||
THIS_CHUNK=$(wc -c < "$CHUNK_FILE" | tr -d ' ')
|
||||
|
||||
# Generate block ID: zero-padded index, base64-encoded
|
||||
BLOCK_ID_RAW=$(printf "%05d" "$CHUNK_INDEX")
|
||||
BLOCK_ID=$(echo -n "$BLOCK_ID_RAW" | base64)
|
||||
BLOCK_IDS+=("$BLOCK_ID")
|
||||
|
||||
# URL-encode the block ID for the query parameter
|
||||
BLOCK_ID_ENCODED=$(echo "$BLOCK_ID" | sed 's/+/%2B/g; s/\//%2F/g; s/=/%3D/g')
|
||||
|
||||
UPLOAD_URL="${SIGNED_URL}&comp=block&blockid=${BLOCK_ID_ENCODED}"
|
||||
|
||||
HTTP_CODE=$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \
|
||||
"$UPLOAD_URL" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
-H "Content-Length: ${THIS_CHUNK}" \
|
||||
--data-binary "@${CHUNK_FILE}")
|
||||
|
||||
if [[ "$HTTP_CODE" != "201" ]]; then
|
||||
echo "Error: Chunk upload failed with HTTP $HTTP_CODE (chunk $CHUNK_INDEX)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$CHUNK_FILE"
|
||||
echo " Uploaded chunk $((CHUNK_INDEX + 1))/${NUM_CHUNKS} ($THIS_CHUNK bytes)"
|
||||
CHUNK_INDEX=$((CHUNK_INDEX + 1))
|
||||
done
|
||||
|
||||
# 5. Commit block list
|
||||
echo "==> Committing block list (${#BLOCK_IDS[@]} blocks)..."
|
||||
|
||||
BLOCK_LIST_XML='<?xml version="1.0" encoding="UTF-8" standalone="yes"?><BlockList>'
|
||||
for bid in "${BLOCK_IDS[@]}"; do
|
||||
BLOCK_LIST_XML+="<Latest>${bid}</Latest>"
|
||||
done
|
||||
BLOCK_LIST_XML+='</BlockList>'
|
||||
|
||||
BLOCKLIST_URL="${SIGNED_URL}&comp=blocklist"
|
||||
|
||||
HTTP_CODE=$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \
|
||||
"$BLOCKLIST_URL" \
|
||||
-H "Content-Type: application/xml" \
|
||||
-d "$BLOCK_LIST_XML")
|
||||
|
||||
if [[ "$HTTP_CODE" != "201" ]]; then
|
||||
echo "Error: Block list commit failed with HTTP $HTTP_CODE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Block list committed"
|
||||
|
||||
# 6. FinalizeArtifact
|
||||
echo "==> Finalizing artifact..."
|
||||
|
||||
FINALIZE_BODY="{\"workflow_run_backend_id\":\"${WORKFLOW_RUN_BACKEND_ID}\",\"workflow_job_run_backend_id\":\"${WORKFLOW_JOB_RUN_BACKEND_ID}\",\"name\":\"${ARTIFACT_NAME}\",\"size\":\"${ZIP_SIZE}\",\"hash\":{\"value\":\"sha256:${ZIP_HASH}\"}}"
|
||||
|
||||
FINALIZE_RESPONSE=$(curl -sS -X POST \
|
||||
"${TWIRP_BASE}/FinalizeArtifact" \
|
||||
-H "Authorization: Bearer ${ACTIONS_RUNTIME_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$FINALIZE_BODY")
|
||||
|
||||
echo " Finalize response: $FINALIZE_RESPONSE"
|
||||
|
||||
# Check success
|
||||
if command -v jq &>/dev/null; then
|
||||
FINALIZE_OK=$(echo "$FINALIZE_RESPONSE" | jq -r '.ok // empty')
|
||||
ARTIFACT_ID=$(echo "$FINALIZE_RESPONSE" | jq -r '.artifact_id // .artifactId // empty')
|
||||
else
|
||||
FINALIZE_OK=$(echo "$FINALIZE_RESPONSE" | sed -n 's/.*"ok"[[:space:]]*:[[:space:]]*\(true\|false\).*/\1/p')
|
||||
ARTIFACT_ID=$(echo "$FINALIZE_RESPONSE" | sed -n 's/.*"artifact_id"[[:space:]]*:[[:space:]]*"\{0,1\}\([0-9]*\)"\{0,1\}.*/\1/p')
|
||||
fi
|
||||
|
||||
if [[ "$FINALIZE_OK" != "true" ]]; then
|
||||
echo "Error: Finalize failed" >&2
|
||||
echo "Response: $FINALIZE_RESPONSE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Artifact '$ARTIFACT_NAME' uploaded successfully (ID: ${ARTIFACT_ID:-unknown})"
|
||||
Reference in New Issue
Block a user