Files
tor-guard-relay/.github/workflows/validate.yml
rE-Bo0t.bx1 be4f2bc125 feat(v1.1.7): Happy Family support (Tor 0.4.9+ FamilyId)
🔧 New tool: gen-family - generate/view Happy Family keys
  - Supports --force flag to overwrite existing keys without backup prompt

🐳 Dockerfiles: gen-family in both Dockerfile and Dockerfile.edge

🔧 Entrypoint:
- Phase 2: detect *.secret_family_key, log found keys (informational only)
- Guard/exit config gen: append FamilyId + MyFamily from ENV vars
- Bridge intentionally excluded

📊 Status tool: show family key count + Happy Family config state

📚 Docs:
- README: Happy Family section (generate / import), persistence table, flowchart
- ARCHITECTURE: all mermaid diagrams updated (Phase 2, config gen, tools, dirs)
- TOOLS: full gen-family reference with examples and exit codes
- DEPLOYMENT, MIGRATION, MIGRATION-V1.1.X, TROUBLESHOOTING: 5 -> 6 tools
- FAQ, example configs: version bump + FamilyId/MyFamily placeholders
- Directory authority voting: how 9 dirauths vote on relay flags (5/9 consensus)
- CIISS v2 ContactInfo: field reference, generator link, proof:uri-rsa verification
- All TOR_CONTACT_INFO examples updated to CIISS v2 format across templates and docs

📋 Templates:
- Guard/exit/multi-relay compose: TOR_FAMILY_ID + TOR_MY_FAMILY env vars
- All cosmos-compose + docker-compose versions -> 1.1.7

👷 CI: validate.yml gen-family in 8 spots (threshold 6), security tests, quick-test

🛡️ SECURITY.md: 1.1.7 active, 1.1.6 maintenance, gen-family in tools list

🔖 Version bump 1.1.6 -> 1.1.7 across 30+ files, tool count 5 -> 6, CHANGELOG entry

No breaking changes. TOR_FAMILY_ID and TOR_MY_FAMILY are optional.
2026-03-02 16:23:10 +08:00

678 lines
24 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: ✅⌛
on:
workflow_dispatch:
pull_request:
branches:
- main
- develop
push:
branches:
- main
- develop
paths:
- 'Dockerfile'
- 'docker-entrypoint.sh'
- 'tools/*'
- '.github/workflows/release.yml'
- '.github/workflows/validate.yml'
- 'templates/**'
- 'compose*.yml'
- 'docker-compose*.yml'
- '.github/dependabot.yml'
- '.github/renovate.json'
- '!**/*.md'
- '!**/*.txt'
- '!docs/**'
- '!LICENSE*'
- '!CONTRIBUTING*'
- '!CHANGELOG.md'
- '!SECURITY.md'
- '!CODE_OF_CONDUCT.md'
permissions:
contents: read
jobs:
lint:
name: 🔍 Lint and Validate
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v5
- name: 🐳 Lint Dockerfile with Hadolint
uses: hadolint/hadolint-action@v3.3.0
with:
dockerfile: Dockerfile
failure-threshold: warning
continue-on-error: true
- name: 🔍 Validate Dockerfile Syntax
run: |
echo "🐳 Validating Dockerfile build context..."
docker build --no-cache -t tor-relay-test .
echo "✅ Dockerfile syntax valid"
- name: 📝 Lint Shell Scripts
run: |
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔍 Checking Shell Script Syntax"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Check main entrypoint script
if [ -f docker-entrypoint.sh ]; then
echo "📄 Checking docker-entrypoint.sh..."
sh -n docker-entrypoint.sh || exit 1
echo " ✅ docker-entrypoint.sh syntax valid"
fi
echo ""
echo "📁 Checking tools directory (no .sh extension)..."
# Check if tools directory exists
if [ ! -d "tools" ]; then
echo " ⚠️ tools/ directory not found"
else
# Check all files in tools/ (no .sh extension)
TOOL_COUNT=0
for script in tools/status tools/health tools/fingerprint tools/bridge-line tools/gen-auth tools/gen-family; do
if [ -f "$script" ]; then
echo "📄 Checking $(basename "$script")..."
sh -n "$script" || exit 1
echo " ✅ $script syntax valid"
TOOL_COUNT=$((TOOL_COUNT + 1))
fi
done
echo ""
echo " ✅ All $TOOL_COUNT tool scripts validated"
fi
echo ""
echo "🎉 Shell script syntax validation complete"
- name: 🔧 Install ShellCheck
run: |
sudo apt-get update
sudo apt-get install -y shellcheck
- name: 🔎 Run ShellCheck on Scripts
run: |
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔎 Running ShellCheck Static Analysis"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# ShellCheck main scripts
if [ -f docker-entrypoint.sh ]; then
echo "🔍 ShellCheck: docker-entrypoint.sh"
shellcheck -S warning docker-entrypoint.sh || true
fi
# ShellCheck all tools (no .sh extension)
if [ -d "tools" ]; then
echo ""
echo "🔍 ShellCheck: tools/*"
for tool in tools/status tools/health tools/fingerprint tools/bridge-line tools/gen-auth tools/gen-family; do
if [ -f "$tool" ]; then
echo " Checking $(basename $tool)..."
shellcheck -S warning "$tool" || true
fi
done
fi
echo ""
echo "✅ ShellCheck analysis complete"
- name: 🎯 Verify Tool Extensions
run: |
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Verifying Tool File Extensions (NO .sh)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ ! -d "tools" ]; then
echo "⚠️ tools/ directory not found - skipping check"
exit 0
fi
HAS_SH_EXT=0
NO_EXT_COUNT=0
# Check all files in tools/
for file in tools/*; do
[ -f "$file" ] || continue
filename=$(basename "$file")
# Check if it's a shell script (has shebang)
if head -1 "$file" 2>/dev/null | grep -q "^#!/"; then
# Tools should NOT have .sh extension
if echo "$filename" | grep -q '\.sh$'; then
echo "❌ Tool should NOT have .sh extension: $filename"
HAS_SH_EXT=1
else
NO_EXT_COUNT=$((NO_EXT_COUNT + 1))
echo "✅ Tool has correct format (no .sh): $filename"
fi
fi
done
echo ""
echo "📊 Summary:"
echo " • Tools without .sh extension: $NO_EXT_COUNT"
echo ""
if [ $HAS_SH_EXT -eq 1 ]; then
echo "❌ Some tools have .sh extension (should not have it)"
exit 1
elif [ $NO_EXT_COUNT -lt 5 ]; then
echo "❌ Expected 6 tools (status, health, fingerprint, bridge-line, gen-auth, gen-family)"
exit 1
else
echo "✅ All tools have correct format (no .sh extension)"
fi
- name: 📋 Lint YAML Files
run: |
echo "🔧 Installing yamllint..."
pip install yamllint
echo "📋 Validating YAML files..."
echo "📁 Checking templates (lenient rules)..."
for yaml in templates/*.yml; do
if [ -f "$yaml" ]; then
yamllint -d "{extends: relaxed, rules: {line-length: {max: 200}, \
trailing-spaces: disable, new-line-at-end-of-file: disable, \
comments: disable}}" "$yaml" && echo "✅ $yaml valid" || \
echo "⚠️ $yaml has warnings (non-blocking)"
fi
done
echo "🔄 Checking other YAML files (strict rules)..."
for yaml in .github/*.yml; do
if [ -f "$yaml" ]; then
yamllint -d relaxed "$yaml" || exit 1
echo "✅ $yaml syntax valid"
fi
done
if [ -d "docs" ]; then
echo "📚 Checking docs (lenient)..."
for yaml in docs/*.yml; do
if [ -f "$yaml" ]; then
yamllint -d "{extends: relaxed, rules: {line-length: {max: 200}, \
trailing-spaces: disable}}" "$yaml" && echo "✅ $yaml valid" || \
echo "⚠️ $yaml has warnings"
fi
done
fi
echo " Skipping workflow files validation (complex format)"
- name: 🔖 Validate JSON Files
run: |
echo "🔍 Validating JSON files..."
for json in templates/*.json examples/*.json .github/*.json; do
if [ -f "$json" ]; then
python3 -m json.tool "$json" > /dev/null || exit 1
echo "✅ $json syntax valid"
fi
done
- name: 📚 Check Documentation
run: |
echo "📖 Verifying required documentation files..."
required_docs=(
"README.md"
"CHANGELOG.md"
"SECURITY.md"
"CONTRIBUTING.md"
"CODE_OF_CONDUCT.md"
"LICENSE.txt"
)
for doc in "${required_docs[@]}"; do
if [ ! -f "$doc" ]; then
echo "❌ Missing documentation: $doc"
exit 1
fi
echo "✅ $doc exists"
done
if [ -d "docs" ]; then
echo "📁 Checking docs directory..."
for doc in ARCHITECTURE.md BACKUP.md CONTROL-PORT.md DEPLOYMENT.md FAQ.md LEGAL.md LOCAL-TESTING.md MIGRATION-V1.1.X.md MIGRATION.md MONITORING.md MULTI-MODE.md PERFORMANCE.md TOOLS.md TROUBLESHOOTING-BRIDGE-MIGRATION.md; do
if [ -f "docs/$doc" ]; then
echo "✅ docs/$doc exists"
else
echo " docs/$doc missing (optional)"
fi
done
fi
- name: ✅ Lint Summary
run: echo "🎉 All linting checks passed"
build:
name: 🏗️ Build Docker Image
runs-on: ubuntu-latest
needs: lint
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v5
- name: 🖥️ Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64,amd64
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 🐳 Build Image (amd64 for testing)
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
load: true
tags: tor-relay:test
pull: true
build-args: |
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
BUILD_VERSION=test-${{ github.run_number }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: 💾 Save Image for Testing
run: |
docker save tor-relay:test -o /tmp/tor-relay-test.tar
echo "📦 Image size: $(du -h /tmp/tor-relay-test.tar | cut -f1)"
- name: ⬆️ Upload Image Artifact
uses: actions/upload-artifact@v5
with:
name: docker-image
path: /tmp/tor-relay-test.tar
retention-days: 1
integration:
name: 🧪 Integration Tests
runs-on: ubuntu-latest
needs: build
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v5
- name: ⬇️ Download Docker Image
uses: actions/download-artifact@v6
with:
name: docker-image
path: /tmp
- name: 📦 Load Docker Image
run: |
docker load -i /tmp/tor-relay-test.tar
docker image ls tor-relay
- name: 🚀 Run Container Smoke Test
run: |
echo "🚀 Starting container for smoke test..."
docker run -d --name tor-test --entrypoint /bin/sh tor-relay:test \
-c "echo '✅ Container started successfully'; sleep 60" || true
sleep 5
- name: 🔍 Check Container Status
run: |
echo "📊 Container status:"
docker ps -a | grep tor-test || true
echo ""
echo "📋 Container logs:"
docker logs tor-test 2>&1 | head -30 || true
- name: 🔧 Verify Tools Installation
run: |
echo "🔍 Checking installed tools..."
docker run --rm --entrypoint /bin/sh tor-relay:test -c "ls -la /usr/local/bin/" || exit 1
echo ""
echo "🛠️ Available diagnostic tools:"
docker run --rm --entrypoint /bin/sh tor-relay:test -c "ls -1 /usr/local/bin/status /usr/local/bin/health /usr/local/bin/fingerprint /usr/local/bin/bridge-line /usr/local/bin/gen-auth /usr/local/bin/gen-family" || exit 1
- name: 🧅 Verify Tor Installation
run: |
echo "🔍 Checking Tor version..."
TOR_VERSION=$(docker run --rm --entrypoint tor tor-relay:test --version | head -1)
if [ -z "$TOR_VERSION" ]; then
echo "❌ Tor installation failed"
exit 1
fi
echo "✅ Tor installed: $TOR_VERSION"
- name: 📁 Verify File Structure
run: |
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
echo '📂 === Directory Structure ===' && \
ls -la / | grep -E 'var|etc|usr' && \
echo '' && \
echo '🛠️ === Tools Directory ===' && \
ls -la /usr/local/bin/ && \
echo '' && \
echo '💾 === Data Directory ===' && \
ls -la /var/lib/tor 2>/dev/null || echo '(data dir empty, expected)' && \
echo '' && \
echo '📝 === Log Directory ===' && \
ls -la /var/log/tor 2>/dev/null || echo '(log dir empty, expected)'
"
- name: 📋 Check Build Metadata
run: |
echo "🔍 Checking build metadata..."
docker run --rm --entrypoint cat tor-relay:test /build-info.txt || exit 1
- name: 🔐 Verify Permissions
run: |
echo "🔐 Verifying directory permissions..."
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
DATA_OWNER=\$(stat -c '%u %g' /var/lib/tor)
if [ \"\$DATA_OWNER\" = '100 101' ]; then
echo '✅ /var/lib/tor ownership correct (100:101)'
else
echo '❌ /var/lib/tor ownership incorrect: \$DATA_OWNER'
exit 1
fi
LOG_OWNER=\$(stat -c '%u %g' /var/log/tor)
if [ \"\$LOG_OWNER\" = '100 101' ]; then
echo '✅ /var/log/tor ownership correct (100:101)'
else
echo '❌ /var/log/tor ownership incorrect: \$LOG_OWNER'
exit 1
fi
"
- name: ✅ Integration Tests Summary
run: echo "🎉 Integration tests completed successfully"
- name: 🧹 Cleanup
if: always()
run: |
docker stop tor-test 2>/dev/null || true
docker rm tor-test 2>/dev/null || true
security:
name: 🛡️ Security Scan
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
security-events: write
actions: read
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v5
- name: ⬇️ Download Docker Image
uses: actions/download-artifact@v6
with:
name: docker-image
path: /tmp
- name: 📦 Load Docker Image
run: docker load -i /tmp/tor-relay-test.tar
- name: 🔒 Trivy - Comprehensive Vulnerability Scan
uses: aquasecurity/trivy-action@0.34.1
with:
image-ref: 'tor-relay:test'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH,MEDIUM'
vuln-type: 'os,library'
ignore-unfixed: false
scanners: 'vuln,secret,config'
- name: ⬆️ Upload Trivy Results to GitHub Security
id: upload-sarif
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: 'trivy-results.sarif'
continue-on-error: true
- name: 📝 SARIF Upload Status
if: always()
run: |
if [ "${{ steps.upload-sarif.outcome }}" = "success" ]; then
echo "✅ SARIF results successfully uploaded to GitHub Security tab"
echo " View at: ${{ github.server_url }}/${{ github.repository }}/security/code-scanning"
else
echo "⚠️ SARIF upload skipped or failed (this is non-blocking)"
echo ""
echo "Possible reasons:"
echo " • Private repository without GitHub Advanced Security"
echo " • Insufficient permissions"
echo " • GitHub API rate limiting"
echo ""
echo "Security scan results are still available in:"
echo " ✅ Human-readable table output (see steps below)"
echo " ✅ JSON artifact (trivy-security-report)"
fi
- name: 📊 Trivy - Human Readable Report (Critical & High)
uses: aquasecurity/trivy-action@0.34.1
with:
image-ref: 'tor-relay:test'
format: 'table'
severity: 'CRITICAL,HIGH'
vuln-type: 'os,library'
ignore-unfixed: false
- name: 🔍 Trivy - Full Vulnerability List (All Severities)
uses: aquasecurity/trivy-action@0.34.1
with:
image-ref: 'tor-relay:test'
format: 'json'
output: 'trivy-full-report.json'
severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL'
vuln-type: 'os,library'
continue-on-error: true
- name: 🔐 Trivy - Secret Scanning
uses: aquasecurity/trivy-action@0.34.1
with:
image-ref: 'tor-relay:test'
scanners: 'secret'
format: 'table'
continue-on-error: true
- name: ⚙️ Trivy - Configuration Audit
uses: aquasecurity/trivy-action@0.34.1
with:
image-ref: 'tor-relay:test'
scanners: 'config'
format: 'table'
continue-on-error: true
- name: 🗂️ Trivy - Filesystem Scan
uses: aquasecurity/trivy-action@0.34.1
with:
scan-type: 'fs'
scan-ref: '.'
format: 'table'
severity: 'CRITICAL,HIGH'
scanners: 'vuln,secret,config,license'
skip-dirs: '.git,docs,examples,templates'
continue-on-error: true
- name: ⬆️ Upload Full Report Artifact
uses: actions/upload-artifact@v5
with:
name: trivy-security-report
path: trivy-full-report.json
retention-days: 7
continue-on-error: true
- name: 📋 Generate Security Summary
run: |
echo "## 🛡️ Security Scan Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Scans Performed:" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Image vulnerability scan (OS packages & libraries)" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Secret scanning (API keys, tokens, credentials)" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Configuration audit (Dockerfile, security best practices)" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Filesystem scan (source code vulnerabilities)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Report Locations:" >> $GITHUB_STEP_SUMMARY
echo "- **GitHub Security Tab:** Detailed SARIF results uploaded" >> $GITHUB_STEP_SUMMARY
echo "- **Artifacts:** Full JSON report available for download" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Scan Coverage:" >> $GITHUB_STEP_SUMMARY
echo "- **Severity Levels:** CRITICAL, HIGH, MEDIUM, LOW" >> $GITHUB_STEP_SUMMARY
echo "- **Scan Types:** Vulnerabilities, Secrets, Configs, Licenses" >> $GITHUB_STEP_SUMMARY
echo "- **Databases:** Alpine, NVD, GitHub Advisory" >> $GITHUB_STEP_SUMMARY
- name: ✅ Security Scan Complete
run: echo "🎉 Security scan completed - check GitHub Security tab for detailed results"
test-matrix:
name: 🧪 Test Matrix
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false
matrix:
test-case:
- 'help-flags'
- 'file-permissions'
- 'alpine-compatibility'
- 'tool-executability'
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v5
- name: ⬇️ Download Docker Image
uses: actions/download-artifact@v6
with:
name: docker-image
path: /tmp
- name: 📦 Load Docker Image
run: docker load -i /tmp/tor-relay-test.tar
- name: "🔍 Test: Help Flags"
if: matrix.test-case == 'help-flags'
run: |
echo "🧪 Testing tool existence..."
for tool in status health fingerprint bridge-line gen-auth gen-family; do
TOOL_PATH="/usr/local/bin/$tool"
if docker run --rm --entrypoint /bin/sh tor-relay:test -c "test -f $TOOL_PATH"; then
echo "✅ $tool exists at $TOOL_PATH"
else
echo "❌ $tool not found at $TOOL_PATH"
exit 1
fi
done
- name: "🔐 Test: File Permissions"
if: matrix.test-case == 'file-permissions'
run: |
echo "🔐 Verifying file permissions for tools..."
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
for tool in /usr/local/bin/status /usr/local/bin/health /usr/local/bin/fingerprint /usr/local/bin/bridge-line /usr/local/bin/gen-auth /usr/local/bin/gen-family; do
if [ -f \"\$tool\" ]; then
test -x \"\$tool\" && echo \"✅ \$(basename \$tool) is executable\" || exit 1
fi
done
"
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
test -x /usr/local/bin/docker-entrypoint.sh && \
echo '✅ docker-entrypoint.sh is executable' || exit 1
"
- name: "🏔️ Test: Alpine Compatibility"
if: matrix.test-case == 'alpine-compatibility'
run: |
echo "🏔️ Verifying Alpine base image..."
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
cat /etc/os-release | grep -i alpine && \
echo '✅ Alpine base confirmed' || exit 1
test -x /bin/sh && echo '✅ /bin/sh available' || exit 1
apk --version > /dev/null 2>&1 && echo '✅ apk available' || exit 1
command -v tor > /dev/null 2>&1 && echo '✅ tor available' || exit 1
"
- name: "🔧 Test: Tool Executability"
if: matrix.test-case == 'tool-executability'
run: |
echo "🔧 Testing tool execution..."
docker run --rm --entrypoint /bin/sh tor-relay:test -c "
for tool in /usr/local/bin/status /usr/local/bin/health /usr/local/bin/fingerprint /usr/local/bin/bridge-line /usr/local/bin/gen-auth /usr/local/bin/gen-family; do
if [ -f \"\$tool\" ]; then
BASENAME=\$(basename \"\$tool\")
echo \"🔍 Testing \$BASENAME...\"
# Tools require Tor to be running, just verify they're executable
test -x \"\$tool\" && echo \" ✅ \$BASENAME is executable\" || exit 1
fi
done
"
summary:
name: 📊 Build Summary
runs-on: ubuntu-latest
needs: [lint, build, integration, security, test-matrix]
if: always()
steps:
- name: 📋 Generate Build Summary
run: |
cat >> $GITHUB_STEP_SUMMARY << EOF
# 🧅 Build and Validation Pipeline
## 📊 Pipeline Status
- **🔍 Lint:** \`${{ needs.lint.result }}\`
- **🏗️ Build:** \`${{ needs.build.result }}\`
- **🧪 Integration:** \`${{ needs.integration.result }}\`
- **🛡️ Security:** \`${{ needs.security.result }}\`
- **🧪 Test Matrix:** \`${{ needs.test-matrix.result }}\`
## 📦 Build Information
- **🌿 Branch:** \`${{ github.ref }}\`
- **📝 Commit:** \`${{ github.sha }}\`
- **🔢 Run Number:** \`${{ github.run_number }}\`
- **📅 Date:** \`$(date -u +'%Y-%m-%dT%H:%M:%SZ')\`
## ✅ Checks Performed
- 🐳 Dockerfile validation with Hadolint
- 📝 Shell script syntax checking (POSIX sh)
- 🎯 Tool extension verification (no .sh)
- 🔎 ShellCheck static analysis
- 📋 YAML validation (lenient for templates)
- 🔖 JSON validation
- 📚 Documentation verification
- 🏗️ Docker image build (multi-arch ready)
- 🚀 Container smoke test
- 🧅 Tor installation verification
- 🔧 Tool availability check (status, health, fingerprint, bridge-line, gen-auth, gen-family)
- 🔐 Permission verification
- 🛡️ Security scanning with Trivy
- 🧪 Test matrix execution
## 🚀 Next Steps
If all checks pass:
- ✅ Image is ready for production use
- 📦 Release workflow will handle publishing to GHCR and Docker Hub
- 🏷️ Tag with semantic version to trigger release
EOF
- name: ✅ Build Pipeline Complete
if: success()
run: echo "🎉 All build and validation checks passed successfully"
- name: ❌ Build Pipeline Failed
if: failure()
run: |
echo "❌ Build pipeline failed - check logs above for details"
exit 1