mirror of
https://github.com/r3bo0tbx1/tor-guard-relay.git
synced 2026-04-06 00:32:04 +02:00
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
609 lines
25 KiB
YAML
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 }}
|