diff --git a/.gitea/workflows/github-release-sync.yml b/.gitea/workflows/github-release-sync.yml deleted file mode 100644 index 33f4688..0000000 --- a/.gitea/workflows/github-release-sync.yml +++ /dev/null @@ -1,70 +0,0 @@ -# After pushing a tag like release_* to Gitea, wait for GitHub Actions to finish -# (Build release + Build Linux release), then create/update the GitHub release and upload assets. -# -# Repository secrets (Gitea): -# GH_PAT GitHub PAT: contents:write, actions:read (upload release assets, list workflow runs) -# GH_REPOSITORY GitHub repo as owner/name (example: Sudo-Ivan/MeshChatX) -# -# The tag must exist on GitHub (mirror) so workflows run there for the same ref. - -name: Sync tag release to GitHub - -on: - push: - tags: - - "release_*" - workflow_dispatch: - inputs: - tag: - description: Tag to sync to GitHub (required when not run from a tag ref) - required: false - type: string - -permissions: - contents: read - -jobs: - publish-github: - runs-on: ubuntu-latest - timeout-minutes: 240 - steps: - - name: Install tools - run: | - set -eu - sudo apt-get update -y - sudo apt-get install -y --no-install-recommends git curl jq ca-certificates unzip - - - name: Checkout - run: | - set -eu - SERVER="${GITEA_SERVER_URL:-${GITHUB_SERVER_URL:-}}" - REPO="${GITEA_REPOSITORY:-${GITHUB_REPOSITORY:-}}" - if [ -z "$SERVER" ] || [ -z "$REPO" ]; then - echo "Set GITEA_SERVER_URL/GITEA_REPOSITORY (or GitHub equivalents)." >&2 - exit 1 - fi - if [ -n "${GITEA_TOKEN:-}" ] || [ -n "${GITHUB_TOKEN:-}" ]; then - TOKEN="${GITEA_TOKEN:-$GITHUB_TOKEN}" - git config --global credential.helper "!f() { echo username=x-access-token; echo password=${TOKEN}; }; f" - fi - git init -q && git remote add origin "${SERVER}/${REPO}.git" - git fetch -q --depth=1 origin "${GITHUB_SHA}" && git checkout -q FETCH_HEAD - - - name: Publish GitHub release from Actions artifacts - env: - GH_REPOSITORY: ${{ secrets.GH_REPOSITORY }} - GH_PAT: ${{ secrets.GH_PAT }} - TIMEOUT_SEC: "14400" - POLL_INTERVAL: "60" - run: | - set -eu - TAG="${{ github.event.inputs.tag }}" - if [ -z "$TAG" ]; then - TAG="${{ github.ref_name }}" - fi - if [ -z "$TAG" ] || [ "$TAG" = "master" ] || [ "$TAG" = "dev" ]; then - echo "Set workflow input tag= to the GitHub tag, or run this workflow from a tag ref." >&2 - exit 1 - fi - export TAG - bash scripts/ci/wait-github-workflows-and-upload-release.sh diff --git a/scripts/ci/sync-github-release-assets.sh b/scripts/ci/sync-github-release-assets.sh deleted file mode 100644 index 5c3a5b4..0000000 --- a/scripts/ci/sync-github-release-assets.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env bash -# Download Windows/macOS build artifacts from GitHub Actions (build-release.yml) -# and attach them to an existing Gitea release. Best-effort: missing platforms are skipped. -# -# Primary CI and Linux release binaries live under .github/workflows/ (see README.md). -# Linux artifacts are produced by build-linux-release.yml on GitHub if you extend this script. -# -# Required env: TAG, GITHUB_REPOSITORY, GITHUB_PAT, GITEA_API_URL, GITEA_REPOSITORY, GITEA_TOKEN -set -euo pipefail - -TAG="${TAG:?set TAG to the release tag (e.g. v1.2.3)}" -GITHUB_REPOSITORY="${GITHUB_REPOSITORY:?}" -GITHUB_PAT="${GITHUB_PAT:?}" -GITEA_API_URL="${GITEA_API_URL:?}" -GITEA_REPOSITORY="${GITEA_REPOSITORY:?}" -GITEA_TOKEN="${GITEA_TOKEN:?}" - -GITEA_API_URL="${GITEA_API_URL%/}" -GH_API="https://api.github.com/repos/${GITHUB_REPOSITORY}" -AUTH_GH=(-H "Authorization: Bearer ${GITHUB_PAT}" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28") -AUTH_GITEA=(-H "Authorization: token ${GITEA_TOKEN}" -H "Accept: application/json") - -WORKDIR=$(mktemp -d) -trap 'rm -rf "${WORKDIR}"' EXIT - -enc_tag() { - printf '%s' "$1" | jq -sRr @uri -} - -log() { - printf '%s\n' "$*" >&2 -} - -if ! command -v jq >/dev/null 2>&1; then - log "Error: jq is required." - exit 1 -fi - -COMMIT_SHA="" -if COMMIT_JSON=$(curl -sS -f "${AUTH_GH[@]}" "${GH_API}/commits/$(enc_tag "$TAG")" 2>/dev/null); then - COMMIT_SHA=$(printf '%s' "$COMMIT_JSON" | jq -r '.sha // empty') -fi - -TAG_ENC=$(enc_tag "$TAG") -RUNS_URL="${GH_API}/actions/workflows/build-release.yml/runs?event=push&branch=${TAG_ENC}&per_page=30" -RUN_ID="" -if RUNS_JSON=$(curl -sS -f "${AUTH_GH[@]}" "$RUNS_URL" 2>/dev/null); then - RUN_ID=$(printf '%s' "$RUNS_JSON" | jq -r ' - [.workflow_runs[] | select(.conclusion == "success")] - | sort_by(.created_at) | reverse | .[0].id // empty - ') -fi - -if [ -z "$RUN_ID" ] && [ -n "$COMMIT_SHA" ]; then - if RUNS_JSON=$(curl -sS -f "${AUTH_GH[@]}" "${GH_API}/actions/workflows/build-release.yml/runs?per_page=50" 2>/dev/null); then - RUN_ID=$(printf '%s' "$RUNS_JSON" | jq -r --arg sha "$COMMIT_SHA" ' - [.workflow_runs[] | select(.head_sha == $sha and .conclusion == "success")] - | sort_by(.created_at) | reverse | .[0].id // empty - ') - fi -fi - -if [ -z "$RUN_ID" ]; then - log "No successful GitHub Actions run found for build-release.yml (tag=${TAG}). Skipping GitHub artifact sync." - exit 0 -fi - -log "Using GitHub workflow run id=${RUN_ID} for tag ${TAG}" - -ART_JSON=$(curl -sS "${AUTH_GH[@]}" "${GH_API}/actions/runs/${RUN_ID}/artifacts?per_page=100" || true) -N=$(printf '%s' "${ART_JSON:-{}}" | jq -r '(.artifacts // []) | length') -if [ "${N:-0}" -eq 0 ]; then - log "No artifacts on GitHub run ${RUN_ID} (or API error). Nothing to download." - exit 0 -fi - -STAGE="${WORKDIR}/stage" -mkdir -p "$STAGE" - -printf '%s' "$ART_JSON" | jq -r '.artifacts[] | "\(.id)|\(.name)|\(.archive_download_url)"' | while IFS='|' read -r _art_id art_name dl_url; do - case "$art_name" in - meshchatx-windows-*|meshchatx-macos-*) ;; - *) - log "Skipping artifact with unexpected name: ${art_name}" - continue - ;; - esac - ZIP="${WORKDIR}/$(echo "$art_name" | tr '/' '_').zip" - log "Downloading ${art_name}..." - if ! curl -sS -fL "${AUTH_GH[@]}" -o "$ZIP" "$dl_url"; then - log "Warning: failed to download ${art_name}; continuing." - continue - fi - EX="${WORKDIR}/ex-${art_name}" - mkdir -p "$EX" - if ! unzip -q -o "$ZIP" -d "$EX" 2>/dev/null; then - log "Warning: unzip failed for ${art_name}; continuing." - continue - fi - find "$EX" -type f \( -name '*.exe' -o -name '*.dmg' -o -name '*.blockmap' -o -name '*.yml' -o -name '*.yaml' \) -print0 2>/dev/null \ - | while IFS= read -r -d '' f; do - base=$(basename "$f") - cp -f "$f" "${STAGE}/${base}" - log "Staged ${base}" - done -done - -STAGED_N=$(find "$STAGE" -mindepth 1 -maxdepth 1 -type f 2>/dev/null | wc -l) -STAGED_N=${STAGED_N//[[:space:]]/} -if [ "${STAGED_N:-0}" -eq 0 ]; then - log "No .exe/.dmg (or related) files extracted from GitHub artifacts. Nothing to upload." - exit 0 -fi - -REL_JSON=$(curl -sS "${AUTH_GITEA[@]}" "${GITEA_API_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/tags/${TAG}") -REL_ID=$(printf '%s' "$REL_JSON" | jq -r '.id // empty') -if [ -z "$REL_ID" ] || [ "$REL_ID" = "null" ]; then - log "Error: No Gitea release for tag '${TAG}'. Create the Gitea release first, then re-run this script." - exit 1 -fi - -log "Uploading to Gitea release id=${REL_ID} (${GITEA_REPOSITORY}@${TAG})" - -find "$STAGE" -maxdepth 1 -type f -print0 | while IFS= read -r -d '' f; do - base=$(basename "$f") - NAME_ENC=$(printf '%s' "$base" | jq -sRr @uri) - if curl -sS -f "${AUTH_GITEA[@]}" -F "attachment=@${f}" \ - "${GITEA_API_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/${REL_ID}/assets?name=${NAME_ENC}" >/dev/null; then - log "Uploaded ${base}" - else - log "Warning: upload failed or duplicate for ${base}; continuing." - fi -done - -log "Done." diff --git a/scripts/ci/wait-github-workflows-and-upload-release.sh b/scripts/ci/wait-github-workflows-and-upload-release.sh deleted file mode 100644 index d10d1fd..0000000 --- a/scripts/ci/wait-github-workflows-and-upload-release.sh +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env bash -# Wait for successful GitHub Actions runs for TAG, download artifacts from listed workflows, -# then create or update a GitHub release and upload binaries. -# -# Required env: TAG, GH_REPOSITORY (owner/repo), GH_PAT -# Optional: WORKFLOWS (space-separated, default: build-release.yml build-linux-release.yml) -# TIMEOUT_SEC (default 14400), POLL_INTERVAL (default 60), DRAFT (default false) -# RELEASE_BODY_FILE (path to markdown; default tries ./release-body.md from cwd) -set -euo pipefail - -TAG="${TAG:?set TAG (e.g. v1.2.3 or release_1.2.3)}" -GH_REPOSITORY="${GH_REPOSITORY:?set GH_REPOSITORY to owner/repo on github.com}" -GH_PAT="${GH_PAT:?set GH_PAT (fine-grained or classic PAT with contents:write, actions:read)}" -WORKFLOWS="${WORKFLOWS:-build-release.yml build-linux-release.yml}" -TIMEOUT_SEC="${TIMEOUT_SEC:-14400}" -POLL_INTERVAL="${POLL_INTERVAL:-60}" -DRAFT="${DRAFT:-false}" - -GH_API="https://api.github.com/repos/${GH_REPOSITORY}" -AUTH=(-H "Authorization: Bearer ${GH_PAT}" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28") - -WORKDIR=$(mktemp -d) -trap 'rm -rf "${WORKDIR}"' EXIT - -log() { - printf '%s\n' "$*" >&2 -} - -if ! command -v jq >/dev/null 2>&1; then - log "Error: jq is required." - exit 1 -fi - -enc_tag() { - printf '%s' "$1" | jq -sRr @uri -} - -latest_success_run_id() { - local wf="$1" - local tag="$2" - local tag_enc run_id runs_json commit_sha runs_json2 - - tag_enc=$(enc_tag "$tag") - run_id="" - if runs_json=$(curl -sS -f "${AUTH[@]}" "${GH_API}/actions/workflows/${wf}/runs?event=push&branch=${tag_enc}&per_page=30" 2>/dev/null); then - run_id=$(printf '%s' "$runs_json" | jq -r ' - [.workflow_runs[] | select(.conclusion == "success")] - | sort_by(.created_at) | reverse | .[0].id // empty - ') - fi - - if [ -z "$run_id" ]; then - if ! commit_sha=$(curl -sS -f "${AUTH[@]}" "${GH_API}/commits/${tag_enc}" | jq -r '.sha // empty'); then - commit_sha="" - fi - if [ -n "$commit_sha" ] && runs_json2=$(curl -sS -f "${AUTH[@]}" "${GH_API}/actions/workflows/${wf}/runs?per_page=80" 2>/dev/null); then - run_id=$(printf '%s' "$runs_json2" | jq -r --arg sha "$commit_sha" ' - [.workflow_runs[] | select(.head_sha == $sha and .conclusion == "success")] - | sort_by(.created_at) | reverse | .[0].id // empty - ') - fi - fi - printf '%s' "$run_id" -} - -deadline=$(( $(date +%s) + TIMEOUT_SEC )) -declare -A RUN_IDS=() - -log "Waiting for workflows (${WORKFLOWS}) on tag ${TAG} (timeout ${TIMEOUT_SEC}s)..." - -while [ "$(date +%s)" -lt "$deadline" ]; do - all_set=1 - for wf in $WORKFLOWS; do - rid=$(latest_success_run_id "$wf" "$TAG") - if [ -n "$rid" ]; then - RUN_IDS["$wf"]=$rid - else - all_set=0 - fi - done - if [ "$all_set" = 1 ]; then - break - fi - log "Not all workflows succeeded yet; sleeping ${POLL_INTERVAL}s..." - sleep "$POLL_INTERVAL" -done - -for wf in $WORKFLOWS; do - if [ -z "${RUN_IDS[$wf]:-}" ]; then - log "Timeout or missing successful run for ${wf} (tag=${TAG})." - exit 1 - fi - log "Using ${wf} run_id=${RUN_IDS[$wf]}" -done - -STAGE="${WORKDIR}/stage" -mkdir -p "$STAGE" - -download_and_stage_run() { - local wf="$1" - local rid="$2" - local art_json n - - art_json=$(curl -sS "${AUTH[@]}" "${GH_API}/actions/runs/${rid}/artifacts?per_page=100" || true) - n=$(printf '%s' "${art_json:-{}}" | jq -r '(.artifacts // []) | length') - if [ "${n:-0}" -eq 0 ]; then - log "No artifacts for ${wf} run ${rid}." - return - fi - - printf '%s' "$art_json" | jq -r '.artifacts[] | "\(.name)|\(.archive_download_url)"' | while IFS='|' read -r art_name dl_url; do - case "$wf" in - build-release.yml) - case "$art_name" in - meshchatx-windows-*|meshchatx-macos-*) ;; - *) - log "Skipping artifact ${art_name} for ${wf}" - continue - ;; - esac - ;; - build-linux-release.yml) - case "$art_name" in - meshchatx-linux-release-*) ;; - *) - log "Skipping artifact ${art_name} for ${wf}" - continue - ;; - esac - ;; - *) - log "Unknown workflow file in download filter: ${wf}" >&2 - exit 1 - ;; - esac - - zip_path="${WORKDIR}/$(echo "$art_name" | tr '/' '_').zip" - log "Downloading ${art_name}..." - if ! curl -sS -fL "${AUTH[@]}" -o "$zip_path" "$dl_url"; then - log "Warning: download failed for ${art_name}" - continue - fi - ex="${WORKDIR}/ex-${art_name}" - mkdir -p "$ex" - if ! unzip -q -o "$zip_path" -d "$ex" 2>/dev/null; then - log "Warning: unzip failed for ${art_name}" - continue - fi - find "$ex" -type f \( \ - -name '*.AppImage' -o -name '*.deb' -o -name '*.rpm' -o -name '*.whl' -o \ - -name '*.exe' -o -name '*.dmg' -o -name '*.blockmap' -o \ - -name 'latest*.yml' -o -name 'latest*.yaml' -o -name '*-linux.yml' -o -name '*-linux.yaml' -o \ - -name 'meshchatx-frontend.zip' -o -name 'sbom.cyclonedx.json' -o -name '*.cosign.bundle' -o -name '*.intoto.jsonl' \ - \) -print0 2>/dev/null | while IFS= read -r -d '' f; do - cp -f "$f" "${STAGE}/$(basename "$f")" - done - done -} - -for wf in $WORKFLOWS; do - download_and_stage_run "$wf" "${RUN_IDS[$wf]}" -done - -file_count=$(find "$STAGE" -mindepth 1 -maxdepth 1 -type f 2>/dev/null | wc -l) -file_count=${file_count//[[:space:]]/} -if [ "${file_count:-0}" -eq 0 ]; then - log "No release files staged after downloads." - exit 1 -fi - -BODY_FILE="${RELEASE_BODY_FILE:-}" -if [ -z "$BODY_FILE" ] && [ -f "./release-body.md" ]; then - BODY_FILE="./release-body.md" -fi -BODY_JSON=$(jq -n '""') -if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then - BODY_JSON=$(jq -Rs . < "$BODY_FILE") -fi - -log "Creating or locating GitHub release for tag ${TAG}..." -REL_GET=$(curl -sS -w '%{http_code}' -o "${WORKDIR}/rel.json" "${AUTH[@]}" "${GH_API}/releases/tags/${TAG}" || true) -HTTP="${REL_GET: -3}" -REL_ID="" -if [ "$HTTP" = "200" ]; then - REL_ID=$(jq -r '.id // empty' "${WORKDIR}/rel.json") - UPLOAD_URL=$(jq -r '.upload_url // empty' "${WORKDIR}/rel.json") - log "Release exists id=${REL_ID}" -else - if [ "$DRAFT" = "true" ] || [ "$DRAFT" = "1" ]; then - draft_json="true" - else - draft_json="false" - fi - payload=$(jq -n \ - --arg tag "$TAG" \ - --arg name "$TAG" \ - --argjson draft "$draft_json" \ - --argjson body "$BODY_JSON" \ - '{tag_name: $tag, name: $name, body: body, draft: draft}') - if ! curl -sS -f "${AUTH[@]}" -X POST -H "Content-Type: application/json" \ - -d "$payload" "${GH_API}/releases" -o "${WORKDIR}/newrel.json"; then - log "Failed to create release for ${TAG}" - exit 1 - fi - REL_ID=$(jq -r '.id // empty' "${WORKDIR}/newrel.json") - UPLOAD_URL=$(jq -r '.upload_url // empty' "${WORKDIR}/newrel.json") - log "Created release id=${REL_ID}" -fi - -if [ -z "$REL_ID" ] || [ -z "$UPLOAD_URL" ]; then - log "Could not resolve release id or upload_url." - exit 1 -fi - -BASE_UPLOAD="${UPLOAD_URL%\{*}" - -EXISTING_NAMES=$(curl -sS "${AUTH[@]}" "${GH_API}/releases/${REL_ID}/assets" | jq -c '[.[].name]' 2>/dev/null || echo '[]') - -while IFS= read -r -d '' f; do - base=$(basename "$f") - if jq -n -e --argjson names "$EXISTING_NAMES" --arg n "$base" '$names | index($n) != null' >/dev/null 2>&1; then - log "Skipping existing asset: ${base}" - continue - fi - enc=$(printf '%s' "$base" | jq -sRr @uri) - log "Uploading ${base}..." - if ! curl -sS -f "${AUTH[@]}" -X POST \ - -H "Content-Type: application/octet-stream" \ - --data-binary @"$f" \ - "${BASE_UPLOAD}?name=${enc}" >/dev/null; then - log "Upload failed for ${base}" - exit 1 - fi -done < <(find "$STAGE" -maxdepth 1 -type f -print0) - -log "Done publishing assets to GitHub release ${TAG}."