Files
tor-guard-relay/.github/workflows/release.yml
rE-Bo0t.bx1 a28ce0a4e6 feat: add gen-auth tool and refactor compose templates
Major refactor of Docker Compose configurations and tooling enhancements.

-  Add `gen-auth` script for generating Tor Control Port credentials
- 🐳 Refactor Docker Compose templates:
  - Add native healthcheck configurations to all relay/bridge files
  - Standardize security capabilities (drop ALL, add SETUID/SETGID)
  - Remove verbose comments to streamline template usage
  - Update volume definitions for better data persistence
- 🔧 Update base dependencies:
  - Alpine Linux -> 3.23.0
  - Golang -> 1.25.5-alpine
- 🧹 Standardize ENV variable names across all configurations
2025-12-05 04:37:19 +08:00

609 lines
25 KiB
YAML

name: 🚀✨ Build
on:
workflow_dispatch:
inputs:
build_mode:
description: 'Build mode'
required: true
default: 'rebuild'
type: choice
options:
- rebuild
- version_bump
variants:
description: 'Which variants to build'
required: true
default: 'both'
type: choice
options:
- both
- latest
- edge
release_type:
description: 'Release type (only for version_bump mode)'
required: false
default: 'patch'
type: choice
options:
- major
- minor
- patch
schedule:
- cron: '30 18 * * 0'
- cron: '0 12 */3 * *'
push:
tags:
- 'v*.*.*'
permissions:
contents: read
env:
GHCR_REGISTRY: ghcr.io
GHCR_IMAGE_NAME: ${{ github.repository_owner }}/onion-relay
DOCKERHUB_IMAGE_NAME: r3bo0tbx1/onion-relay
jobs:
determine-version:
name: 🏷️ Determine Version and Build Type
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: ${{ steps.version.outputs.version }}
build_type: ${{ steps.version.outputs.build_type }}
is_release: ${{ steps.version.outputs.is_release }}
build_date: ${{ steps.version.outputs.build_date }}
short_sha: ${{ steps.version.outputs.short_sha }}
build_variants: ${{ steps.version.outputs.build_variants }}
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: 🔍 Detect Version and Build Type
id: version
run: |
set -e
echo "🔍 Determining version context..."
BUILD_VARIANTS="both" # Default: build both variants
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
BUILD_TYPE="release"
IS_RELEASE="true"
BUILD_VARIANTS="both"
echo "🏷️ Release tag detected: v${VERSION}"
elif [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
BUILD_MODE="${{ github.event.inputs.build_mode }}"
BUILD_VARIANTS="${{ github.event.inputs.variants }}"
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v1.0.0")
if [[ "${BUILD_MODE}" == "rebuild" ]]; then
# Rebuild mode: Use last release version (same as weekly)
VERSION="${LATEST_TAG#v}"
BUILD_TYPE="manual-rebuild"
IS_RELEASE="false"
echo "🔄 Manual rebuild of last release: ${VERSION} (with updated packages)"
echo " Variants: ${BUILD_VARIANTS}"
else
# Version bump mode: Create new version with suffix
VERSION="${LATEST_TAG#v}-manual-${GITHUB_RUN_NUMBER}"
BUILD_TYPE="manual"
IS_RELEASE="false"
echo "👤 Manual build version: ${VERSION}"
echo " Variants: ${BUILD_VARIANTS}"
fi
elif [[ "${GITHUB_EVENT_NAME}" == "schedule" ]]; then
# Scheduled rebuild: Determine which schedule based on time
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v1.0.0")
VERSION="${LATEST_TAG#v}"
IS_RELEASE="false"
CURRENT_HOUR=$(date -u +%H)
if [[ "${CURRENT_HOUR}" == "18" ]]; then
# Weekly rebuild (Sundays 18:30 UTC): Build stable only
BUILD_TYPE="weekly"
BUILD_VARIANTS="latest"
echo "📅 Weekly rebuild of last release: ${VERSION} (stable variant with updated packages)"
else
# Edge-only rebuild (Every 3 days at 12:00 UTC): Build edge only
BUILD_TYPE="edge-rebuild"
BUILD_VARIANTS="edge"
echo "⚡ Edge-only rebuild of last release: ${VERSION} (edge variant with updated packages)"
fi
else
# Fallback (shouldn't happen)
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v1.0.0")
VERSION="${LATEST_TAG#v}"
BUILD_TYPE="unknown"
IS_RELEASE="false"
BUILD_VARIANTS="both"
echo "⚠️ Unknown trigger: ${VERSION}"
fi
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
SHORT_SHA=$(git rev-parse --short HEAD)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "build_type=${BUILD_TYPE}" >> "$GITHUB_OUTPUT"
echo "is_release=${IS_RELEASE}" >> "$GITHUB_OUTPUT"
echo "build_date=${BUILD_DATE}" >> "$GITHUB_OUTPUT"
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
echo "build_variants=${BUILD_VARIANTS}" >> "$GITHUB_OUTPUT"
- name: 📋 Version Information
run: |
echo "📦 Build Info:"
echo " 🏷️ Version: ${{ steps.version.outputs.version }}"
echo " 📝 Build Type: ${{ steps.version.outputs.build_type }}"
echo " ✅ Release: ${{ steps.version.outputs.is_release }}"
echo " 📅 Date: ${{ steps.version.outputs.build_date }}"
echo " 🔑 SHA: ${{ steps.version.outputs.short_sha }}"
build-and-push:
name: 🏗️ Multi-Arch Build and Push (${{ matrix.variant.name }})
runs-on: ubuntu-latest
needs: determine-version
permissions:
contents: read
packages: write
if: |
github.event_name != 'workflow_run' ||
github.event.workflow_run.conclusion == 'success'
strategy:
fail-fast: false
matrix:
variant:
- name: stable
dockerfile: Dockerfile
suffix: ""
is_latest: "true"
base: "Alpine 3.22.2"
push_dockerhub: "true"
- name: edge
dockerfile: Dockerfile.edge
suffix: "-edge"
is_latest: "false"
base: "Alpine edge"
push_dockerhub: "true"
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v5
- name: 🎯 Check if variant should be built
id: should_build
run: |
BUILD_VARIANTS="${{ needs.determine-version.outputs.build_variants }}"
VARIANT_NAME="${{ matrix.variant.name }}"
# Determine if this variant should be built
SHOULD_BUILD="false"
if [ "$BUILD_VARIANTS" = "both" ]; then
SHOULD_BUILD="true"
elif [ "$BUILD_VARIANTS" = "latest" ] && [ "$VARIANT_NAME" = "stable" ]; then
SHOULD_BUILD="true"
elif [ "$BUILD_VARIANTS" = "edge" ] && [ "$VARIANT_NAME" = "edge" ]; then
SHOULD_BUILD="true"
fi
echo "should_build=${SHOULD_BUILD}" >> "$GITHUB_OUTPUT"
echo "🔍 Variant: ${VARIANT_NAME}, Build Variants: ${BUILD_VARIANTS}, Should Build: ${SHOULD_BUILD}"
- name: 🎯 Verify Tools Directory
if: steps.should_build.outputs.should_build == 'true'
run: |
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Pre-Build: Verifying Tools"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ ! -d "tools" ]; then
echo "❌ tools/ directory not found"
exit 1
fi
TOOL_COUNT=0
for file in tools/*; do
[ -f "$file" ] || continue
filename=$(basename "$file")
if head -1 "$file" 2>/dev/null | grep -q "^#!/bin/sh"; then
echo "✅ $filename (busybox sh)"
TOOL_COUNT=$((TOOL_COUNT + 1))
fi
done
echo ""
if [ $TOOL_COUNT -lt 1 ]; then
echo "❌ Build blocked: No tools found in tools/"
exit 1
else
echo "🎉 Found $TOOL_COUNT diagnostic tools (busybox-only, no .sh extension)"
fi
- name: 🔧 Normalize scripts before build
if: steps.should_build.outputs.should_build == 'true'
run: |
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔧 Normalizing Line Endings and Permissions"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
sudo apt-get update -qq && sudo apt-get install -y dos2unix
echo "📄 Processing main scripts..."
for script in docker-entrypoint.sh healthcheck.sh Dockerfile Dockerfile.edge; do
if [ -f "$script" ]; then
dos2unix "$script" 2>/dev/null || true
echo " ✅ Normalized: $script"
fi
done
echo ""
echo "📁 Processing tools/*..."
if [ -d "tools" ]; then
find tools -type f -exec dos2unix {} \; 2>/dev/null || true
TOOL_COUNT=$(find tools -type f | wc -l)
echo " ✅ Normalized: $TOOL_COUNT tool scripts"
fi
echo ""
echo "🔐 Setting execute permissions..."
chmod +x docker-entrypoint.sh healthcheck.sh 2>/dev/null || true
[ -d "tools" ] && chmod +x tools/* 2>/dev/null || true
echo " ✅ Permissions verified"
echo ""
echo "🎉 Normalization complete"
- name: 🐳 Login to Docker Hub
if: steps.should_build.outputs.should_build == 'true' && matrix.variant.push_dockerhub == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: 📦 Login to GitHub Container Registry
if: steps.should_build.outputs.should_build == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 🖥️ Set up QEMU
if: steps.should_build.outputs.should_build == 'true'
uses: docker/setup-qemu-action@v3
with:
platforms: arm64,amd64
- name: 🔨 Set up Docker Buildx
if: steps.should_build.outputs.should_build == 'true'
uses: docker/setup-buildx-action@v3
- name: 🏷️ Generate Docker Tags
if: steps.should_build.outputs.should_build == 'true'
id: tags
run: |
VERSION="${{ needs.determine-version.outputs.version }}"
BUILD_TYPE="${{ needs.determine-version.outputs.build_type }}"
VARIANT_NAME="${{ matrix.variant.name }}"
SUFFIX="${{ matrix.variant.suffix }}"
IS_LATEST="${{ matrix.variant.is_latest }}"
PUSH_DOCKERHUB="${{ matrix.variant.push_dockerhub }}"
echo "🏷️ Generating tags for variant: ${VARIANT_NAME}"
echo " Version: ${VERSION}${SUFFIX}"
echo " Build Type: ${BUILD_TYPE}"
echo " Is Latest: ${IS_LATEST}"
echo " Push to Docker Hub: ${PUSH_DOCKERHUB}"
echo ""
TAGS=()
# Always add GHCR versioned tag
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}${SUFFIX}")
if [ "$BUILD_TYPE" = "release" ]; then
# New release: Add special tags
if [ "$IS_LATEST" = "true" ]; then
# Stable variant gets :latest
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest")
else
# Edge variant gets :edge
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:edge")
fi
# Add Docker Hub tags
if [ "$PUSH_DOCKERHUB" = "true" ]; then
if [ "$IS_LATEST" = "true" ]; then
# Stable: versioned tag + :latest
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}")
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:latest")
else
# Edge: only :edge (no versioned tag for Docker Hub)
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:edge")
fi
fi
elif [ "$BUILD_TYPE" = "weekly" ] || [ "$BUILD_TYPE" = "manual-rebuild" ] || [ "$BUILD_TYPE" = "edge-rebuild" ]; then
# Weekly rebuild, manual rebuild, or edge-only rebuild: Update version tag with fresh packages
if [ "$IS_LATEST" = "true" ]; then
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest")
else
TAGS+=("${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:edge")
fi
if [ "$PUSH_DOCKERHUB" = "true" ]; then
if [ "$IS_LATEST" = "true" ]; then
# Stable: versioned tag + :latest
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}")
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:latest")
else
# Edge: only :edge (no versioned tag for Docker Hub)
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:edge")
fi
fi
else
# Manual/validated builds: version tag only
if [ "$PUSH_DOCKERHUB" = "true" ]; then
if [ "$IS_LATEST" = "true" ]; then
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:${VERSION}")
else
# Edge manual builds: only :edge for Docker Hub
TAGS+=("${{ env.DOCKERHUB_IMAGE_NAME }}:edge")
fi
fi
fi
TAGS_STR=$(IFS=','; echo "${TAGS[*]}")
echo "tags=${TAGS_STR}" >> "$GITHUB_OUTPUT"
echo "📋 Generated tags:"
for tag in "${TAGS[@]}"; do
echo " - ${tag}"
done
- name: 🚀 Build and Push Multi-Arch Image
if: steps.should_build.outputs.should_build == 'true'
uses: docker/build-push-action@v6
with:
context: .
file: ./${{ matrix.variant.dockerfile }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.tags.outputs.tags }}
build-args: |
BUILD_DATE=${{ needs.determine-version.outputs.build_date }}
BUILD_VERSION=${{ needs.determine-version.outputs.version }}
cache-from: type=gha,scope=${{ matrix.variant.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.variant.name }}
labels: |
org.opencontainers.image.title=Tor Guard Relay (${{ matrix.variant.name }})
org.opencontainers.image.description=Hardened Tor Guard Relay (${{ matrix.variant.base }})
org.opencontainers.image.version=${{ needs.determine-version.outputs.version }}${{ matrix.variant.suffix }}
org.opencontainers.image.created=${{ needs.determine-version.outputs.build_date }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
sbom: true
provenance: true
- name: 📋 Generate SBOM (CycloneDX & SPDX)
if: steps.should_build.outputs.should_build == 'true' && needs.determine-version.outputs.is_release == 'true'
run: |
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📋 Generating Software Bill of Materials (SBOM)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Install syft for SBOM generation
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
VERSION="${{ needs.determine-version.outputs.version }}"
SUFFIX="${{ matrix.variant.suffix }}"
VARIANT="${{ matrix.variant.name }}"
IMAGE="${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${VERSION}${SUFFIX}"
echo "📦 Generating SBOM for ${VARIANT} variant"
echo " Image: ${IMAGE}"
echo ""
# Generate CycloneDX JSON
echo "📄 Generating CycloneDX JSON format..."
syft "${IMAGE}" -o cyclonedx-json > "sbom-${VARIANT}-cyclonedx-v${VERSION}.json"
echo " ✅ sbom-${VARIANT}-cyclonedx-v${VERSION}.json"
# Generate CycloneDX XML
echo "📄 Generating CycloneDX XML format..."
syft "${IMAGE}" -o cyclonedx-xml > "sbom-${VARIANT}-cyclonedx-v${VERSION}.xml"
echo " ✅ sbom-${VARIANT}-cyclonedx-v${VERSION}.xml"
# Generate SPDX JSON
echo "📄 Generating SPDX JSON format..."
syft "${IMAGE}" -o spdx-json > "sbom-${VARIANT}-spdx-v${VERSION}.json"
echo " ✅ sbom-${VARIANT}-spdx-v${VERSION}.json"
# Generate SPDX tag-value
echo "📄 Generating SPDX tag-value format..."
syft "${IMAGE}" -o spdx-tag-value > "sbom-${VARIANT}-spdx-v${VERSION}.spdx"
echo " ✅ sbom-${VARIANT}-spdx-v${VERSION}.spdx"
# Generate human-readable table
echo "📄 Generating human-readable table..."
syft "${IMAGE}" -o table > "sbom-${VARIANT}-table-v${VERSION}.txt"
echo " ✅ sbom-${VARIANT}-table-v${VERSION}.txt"
echo ""
echo "✅ SBOM generation complete for ${VARIANT} variant"
echo ""
echo "📊 Package Statistics:"
jq '.components | length' "sbom-${VARIANT}-cyclonedx-v${VERSION}.json" | xargs echo " Total packages:"
- name: 📤 Upload SBOM Artifacts
if: steps.should_build.outputs.should_build == 'true' && needs.determine-version.outputs.is_release == 'true'
uses: actions/upload-artifact@v4
with:
name: sbom-${{ matrix.variant.name }}-v${{ needs.determine-version.outputs.version }}
path: |
sbom-${{ matrix.variant.name }}-*.json
sbom-${{ matrix.variant.name }}-*.xml
sbom-${{ matrix.variant.name }}-*.spdx
sbom-${{ matrix.variant.name }}-*.txt
retention-days: 90
release-notes:
name: 📝 Generate Release Notes
runs-on: ubuntu-latest
needs: [determine-version, build-and-push]
permissions:
contents: write
if: needs.determine-version.outputs.is_release == 'true'
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v5
- name: 📝 Generate Notes
run: |
VERSION="${{ needs.determine-version.outputs.version }}"
GHCR_IMAGE="${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}"
DOCKERHUB_IMAGE="${{ env.DOCKERHUB_IMAGE_NAME }}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Generating Release Notes for v${VERSION}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Try to extract from CHANGELOG.md first
CHANGELOG_FOUND=0
if [ -f CHANGELOG.md ]; then
echo "🔍 Checking CHANGELOG.md for v${VERSION}..."
awk -v version="${VERSION}" '
$0 ~ "^##[[:space:]]*(\\[v?" version "\\]|v" version ")([[:space:]]*-.*)?$" {p=1; next}
p && /^##[[:space:]]*\[/ && !($0 ~ version) {p=0}
p
' CHANGELOG.md > tmp_notes.txt
sed -i '/^$/N;/^\n$/D' tmp_notes.txt 2>/dev/null || true
if [ -s tmp_notes.txt ]; then
echo "✅ Found changelog section for v${VERSION} in CHANGELOG.md"
CHANGELOG_FOUND=1
echo "## 🧅 Tor Guard Relay v${VERSION}" > release_notes.md
echo "" >> release_notes.md
cat tmp_notes.txt >> release_notes.md
else
echo "⚠️ No changelog section found for v${VERSION} in CHANGELOG.md"
fi
else
echo "⚠️ CHANGELOG.md not found"
fi
# Fall back to auto-generated notes from commits
if [ "$CHANGELOG_FOUND" = "0" ]; then
echo "📋 Auto-generating release notes from commits..."
if [ -x scripts/release/generate-release-notes.sh ]; then
# Use auto-generation script
chmod +x scripts/release/generate-release-notes.sh
./scripts/release/generate-release-notes.sh --format github "${VERSION}" > release_notes.md
echo "✅ Auto-generated release notes from conventional commits"
else
# Simple fallback
echo "## 🧅 Tor Guard Relay v${VERSION}" > release_notes.md
echo "" >> release_notes.md
echo "### Changes" >> release_notes.md
echo "" >> release_notes.md
git log --pretty=format:"- %s (\`%h\`) by %an" "$(git describe --tags --abbrev=0)..HEAD" >> release_notes.md || echo "- Initial release" >> release_notes.md
echo "" >> release_notes.md
echo "⚠️ **Note:** Release notes were auto-generated from commit history." >> release_notes.md
echo "For detailed changes, see the commit history below." >> release_notes.md
echo "✅ Generated basic release notes from commit history"
fi
fi
# Append Docker images and SBOM info
echo "" >> release_notes.md
echo "---" >> release_notes.md
echo "" >> release_notes.md
echo "### 🐳 Docker Images" >> release_notes.md
echo "" >> release_notes.md
echo "**Stable Variant (Recommended for Production)**" >> release_notes.md
echo "" >> release_notes.md
echo "- Base: Alpine 3.22.2" >> release_notes.md
echo "- Proven stability with weekly security updates" >> release_notes.md
echo "" >> release_notes.md
echo "\`\`\`bash" >> release_notes.md
echo "# From GitHub Container Registry (GHCR)" >> release_notes.md
echo "docker pull ${GHCR_IMAGE}:${VERSION}" >> release_notes.md
echo "docker pull ${GHCR_IMAGE}:latest" >> release_notes.md
echo "" >> release_notes.md
echo "# From Docker Hub" >> release_notes.md
echo "docker pull ${DOCKERHUB_IMAGE}:${VERSION}" >> release_notes.md
echo "docker pull ${DOCKERHUB_IMAGE}:latest" >> release_notes.md
echo "\`\`\`" >> release_notes.md
echo "" >> release_notes.md
echo "**Edge Variant (Testing Only)**" >> release_notes.md
echo "" >> release_notes.md
echo "- Base: Alpine edge (bleeding edge)" >> release_notes.md
echo "- ⚠️ **NOT recommended for production** - faster updates, less stable" >> release_notes.md
echo "" >> release_notes.md
echo "\`\`\`bash" >> release_notes.md
echo "# From GitHub Container Registry (GHCR) - versioned + simple tags" >> release_notes.md
echo "docker pull ${GHCR_IMAGE}:${VERSION}-edge" >> release_notes.md
echo "docker pull ${GHCR_IMAGE}:edge" >> release_notes.md
echo "" >> release_notes.md
echo "# From Docker Hub - simple tag only" >> release_notes.md
echo "docker pull ${DOCKERHUB_IMAGE}:edge" >> release_notes.md
echo "\`\`\`" >> release_notes.md
echo "" >> release_notes.md
echo "### 📋 Software Bill of Materials (SBOM)" >> release_notes.md
echo "" >> release_notes.md
echo "This release includes comprehensive SBOM files for **both variants** for supply chain security:" >> release_notes.md
echo "" >> release_notes.md
echo "- **CycloneDX**: JSON and XML formats (stable + edge)" >> release_notes.md
echo "- **SPDX**: JSON and tag-value formats (stable + edge)" >> release_notes.md
echo "- **Human-readable**: Table format (stable + edge)" >> release_notes.md
echo "" >> release_notes.md
echo "Download SBOM files from the release assets below (prefixed with \`stable-\` or \`edge-\`)." >> release_notes.md
echo "" >> release_notes.md
echo "---" >> release_notes.md
echo "" >> release_notes.md
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/$(git describe --tags --abbrev=0 2>/dev/null || echo 'v1.0.0')...v${VERSION}" >> release_notes.md
echo ""
echo "✅ Release notes generation complete"
- name: 📦 Download SBOM Artifacts (Stable)
uses: actions/download-artifact@v4
with:
name: sbom-stable-v${{ needs.determine-version.outputs.version }}
path: ./sbom
- name: 📦 Download SBOM Artifacts (Edge)
uses: actions/download-artifact@v4
with:
name: sbom-edge-v${{ needs.determine-version.outputs.version }}
path: ./sbom
- name: 🏷️ Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.determine-version.outputs.version }}
name: "🧅 Tor Guard Relay v${{ needs.determine-version.outputs.version }}"
body_path: release_notes.md
files: |
sbom/*.json
sbom/*.xml
sbom/*.spdx
sbom/*.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}