diff --git a/.gitea/workflows/build-iso.yaml b/.gitea/workflows/build-iso.yaml index fbe9b97..f5b4496 100644 --- a/.gitea/workflows/build-iso.yaml +++ b/.gitea/workflows/build-iso.yaml @@ -29,7 +29,7 @@ jobs: - name: Install archiso and dependencies run: | set -euo pipefail - pacman -Syu --noconfirm --needed archiso jq + pacman -Syu --noconfirm --needed archiso jq git-lfs zstd - name: Build Arch ISO run: | @@ -73,12 +73,14 @@ jobs: echo "tag_name=${TAG_NAME}" >> "$GITHUB_OUTPUT" echo "release_name=Arch Linux ISO (${BASE_DATE} Rev ${REVISION})" >> "$GITHUB_OUTPUT" - - name: Create Release and Upload ISO + - name: Create Release and Upload ISO via Git LFS env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} # Use internal Gitea URL to bypass traefik proxy timeout on large uploads GITEA_API: http://192.168.1.168:3000/api/v1 + GITEA_PUBLIC_URL: https://git.vilaythong.us REPOSITORY: ${{ github.repository }} + RELEASES_REPO: alvinlollo/AlvinArch-releases TAG_NAME: ${{ steps.tag.outputs.tag_name }} RELEASE_NAME: ${{ steps.tag.outputs.release_name }} run: | @@ -94,15 +96,14 @@ jobs: exit 1 fi - # Create the release via Gitea API (internal, no traefik) - RELEASE_ID=$(curl -sf -X POST \ - -H "Authorization: token $GITEA_TOKEN" \ - -H "Content-Type: application/json" \ - "$GITEA_API/repos/$REPOSITORY/releases" \ - -d "{\"tag_name\": \"$TAG_NAME\", \"name\": \"$RELEASE_NAME\"}" \ - | jq -er '.id') - - echo "Created release ID: $RELEASE_ID" + RELEASES_OWNER="${RELEASES_REPO%%/*}" + RELEASES_NAME="${RELEASES_REPO#*/}" + GITEA_BASE="${GITEA_API%/api/v1}" + GITEA_SCHEME="${GITEA_BASE%%://*}" + GITEA_HOST="${GITEA_BASE#*://}" + CLONE_URL="${GITEA_SCHEME}://${RELEASES_OWNER}:${GITEA_TOKEN}@${GITEA_HOST}/${RELEASES_REPO}.git" + REPO_DIR=/tmp/alvinarch-releases + trap 'rm -rf "$REPO_DIR"' EXIT # Collect the ISOs produced by the build isos=(out/*.iso) @@ -111,6 +112,9 @@ jobs: exit 1 fi + # Compress each ISO with zstd and record its SHA-256 + zst_files=() + sha256sums=() for iso in "${isos[@]}"; do fname=$(basename "$iso") size=$(stat -c %s "$iso") @@ -120,42 +124,93 @@ jobs: exit 1 fi - echo "Uploading $fname ($(( size / 1024 / 1024 )) MiB)..." - - http_code="" - for attempt in 1 2 3; do - http_code=$(curl -sS --max-time 1800 \ - -o /dev/null -w "%{http_code}" \ - -H "Authorization: token $GITEA_TOKEN" \ - -F "attachment=@$iso;filename=$fname" \ - "$GITEA_API/repos/$REPOSITORY/releases/$RELEASE_ID/assets" || true) - - if [[ "$http_code" == "201" ]]; then - echo " ✅ Upload OK (HTTP 201) on attempt $attempt" - break - fi - echo " ⚠️ Upload attempt $attempt returned HTTP $http_code — retrying..." >&2 - sleep 5 - done - - if [[ "$http_code" != "201" ]]; then - echo "❌ Failed to upload '$fname' (last HTTP code: $http_code)" >&2 - exit 1 - fi - - # Verify the exact asset made it: correct name AND byte size - confirmed=$(curl -sf -H "Authorization: token $GITEA_TOKEN" \ - "$GITEA_API/repos/$REPOSITORY/releases/$RELEASE_ID/assets" \ - | jq -er --arg n "$fname" --argjson s "$size" \ - 'first(.[] | select(.name == $n and .size == $s) | .name) // empty') - if [[ -z "$confirmed" ]]; then - echo "❌ Verification failed: asset '$fname' ($size bytes) not found on the release." >&2 - exit 1 - fi - echo " ✅ Verified '$confirmed' ($size bytes) attached to $TAG_NAME" + echo "Compressing $fname ($(( size / 1024 / 1024 )) MiB) with zstd..." + zstd -T0 -19 -f "$iso" -o "$iso.zst" + sha=$(sha256sum "$iso.zst" | awk '{print $1}') + echo " ✅ ${fname}.zst: $(( $(stat -c %s "$iso.zst") / 1024 / 1024 )) MiB, sha256=$sha" + zst_files+=("$iso.zst") + sha256sums+=("$sha") done - echo "Release $TAG_NAME created and ISOs uploaded." + # Make sure the releases repo exists (create it via API if missing) + if ! curl -sf -o /dev/null -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_API/repos/$RELEASES_REPO"; then + echo "Creating releases repo '$RELEASES_REPO'..." + curl -sf -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_API/user/repos" \ + -d "{\"name\": \"$RELEASES_NAME\", \"description\": \"Compressed Arch Linux live ISOs (Git LFS)\", \"auto_init\": true}" > /dev/null + fi + + # Push the compressed ISOs to the releases repo via Git LFS + if ! git clone --quiet "$CLONE_URL" "$REPO_DIR" 2>/dev/null; then + echo "❌ Failed to clone $RELEASES_REPO (check token access)." >&2 + exit 1 + fi + + # Work on the 'main' branch (create it if the remote default differs) + if git -C "$REPO_DIR" rev-parse --verify -q origin/main >/dev/null 2>&1; then + git -C "$REPO_DIR" checkout -q -B main origin/main + else + git -C "$REPO_DIR" checkout -q -B main + fi + + git -C "$REPO_DIR" lfs install --local + git -C "$REPO_DIR" lfs track "*.iso.zst" + mkdir -p "$REPO_DIR/releases/$TAG_NAME" + for zst in "${zst_files[@]}"; do + cp "$zst" "$REPO_DIR/releases/$TAG_NAME/" + done + git -C "$REPO_DIR" config user.name "Gitea Actions" + git -C "$REPO_DIR" config user.email "actions@gitea.local" + git -C "$REPO_DIR" add .gitattributes "releases/$TAG_NAME" + if git -C "$REPO_DIR" diff --cached --quiet; then + echo "⚠️ No changes to commit for $TAG_NAME — releasing as-is." >&2 + else + git -C "$REPO_DIR" commit --quiet -m "Add $TAG_NAME" + fi + if ! git -C "$REPO_DIR" push --quiet origin main 2>/dev/null; then + echo "❌ Failed to push $TAG_NAME to $RELEASES_REPO." >&2 + exit 1 + fi + + # Verify each LFS object exists server-side (oid == the sha256 we recorded) + for i in "${!zst_files[@]}"; do + fname=$(basename "${zst_files[$i]}") + size=$(stat -c %s "${zst_files[$i]}") + oid=${sha256sums[$i]} + lfs_payload=$(jq -nc --arg oid "$oid" --argjson sz "$size" \ + '{operation:"download",transfers:["basic"],objects:[{oid:$oid,size:$sz}]}') + obj=$(curl -sf -u "$RELEASES_OWNER:$GITEA_TOKEN" \ + -H "Content-Type: application/vnd.git-lfs+json" \ + -d "$lfs_payload" \ + "$GITEA_BASE/$RELEASES_REPO.git/info/lfs/objects/batch") + if [[ "$(jq -r '.objects[0] | has("actions")' <<< "$obj")" != "true" ]]; then + echo "❌ Verification failed: LFS object for '$fname' ($size bytes) missing on server." >&2 + exit 1 + fi + echo " ✅ Verified '$fname' ($size bytes) on $RELEASES_REPO" + done + + # Create the release with a link to the file directory + SHA-256 checksums + DOWNLOAD_URL="$GITEA_PUBLIC_URL/$RELEASES_REPO/src/branch/main/releases/$TAG_NAME" + printf -v body 'Arch Linux live ISO %s\n\nDownload: %s\n\nChecksums (SHA-256):\n' \ + "$RELEASE_NAME" "$DOWNLOAD_URL" + for i in "${!zst_files[@]}"; do + printf -v line -- '- `%s`: sha256 `%s`\n' "$(basename "${zst_files[$i]}")" "${sha256sums[$i]}" + body+="$line" + done + + payload=$(jq -nc --arg tag "$TAG_NAME" --arg name "$RELEASE_NAME" --arg body "$body" \ + '{tag_name:$tag,name:$name,body:$body}') + RELEASE_ID=$(curl -sf -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_API/repos/$REPOSITORY/releases" \ + -d "$payload" | jq -er '.id') + + echo " ✅ Released $TAG_NAME (ID: $RELEASE_ID) with download link + SHA-256 checksums" - name: Keep only latest 3 releases if: success()