github workflow added to verify Package dependencies #1
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: 🧪 Verify Package Dependencies | |
on: | |
push: | |
branches: ['*'] | |
pull_request: | |
branches: ['*'] | |
env: | |
DEBIAN_FRONTEND: noninteractive | |
jobs: | |
verify-dependencies: | |
name: 🛠️ ${{ matrix.os }}-${{ matrix.arch }} | |
runs-on: ${{ matrix.runs_on }} | |
timeout-minutes: 30 | |
strategy: | |
fail-fast: false | |
matrix: | |
include: | |
- os: ubuntu-22.04 | |
arch: x86_64 | |
runs_on: ubuntu-22.04 | |
codename: jammy | |
- os: ubuntu-22.04 | |
arch: aarch64 | |
runs_on: ubuntu-22.04 | |
codename: jammy | |
- os: ubuntu-24.04 | |
arch: x86_64 | |
runs_on: ubuntu-24.04 | |
codename: noble | |
- os: ubuntu-24.04 | |
arch: aarch64 | |
runs_on: ubuntu-24.04 | |
codename: noble | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 1 | |
- name: Set up environment | |
run: | | |
echo "CONTROL_FILE=packages/Debian/${{ matrix.arch }}/DEBIAN/control" >> $GITHUB_ENV | |
echo "REPORT=dep_report_${{ matrix.os }}_${{ matrix.arch }}.md" >> $GITHUB_ENV | |
- name: Validate control file exists | |
run: | | |
if [ ! -f "$CONTROL_FILE" ]; then | |
echo "❌ Control file missing: $CONTROL_FILE" | |
echo "Available files in packages/Debian/:" | |
find packages/Debian/ -name "control" 2>/dev/null || echo "No control files found" | |
exit 1 | |
fi | |
echo "✅ Control file found: $CONTROL_FILE" | |
- name: Verify dependencies & generate report | |
run: | | |
set -euo pipefail | |
# Initialize report | |
cat > "$REPORT" << EOF | |
## 📦 Dependency Verification Report | |
**Platform:** ${{ matrix.arch }} on ${{ matrix.os }} (${{ matrix.codename }}) | |
**Generated:** $(date -u '+%Y-%m-%d %H:%M:%S UTC') | |
**Workflow:** ${GITHUB_WORKFLOW} - Run #${GITHUB_RUN_NUMBER} | |
EOF | |
# Enhanced dependency extraction function | |
extract_deps() { | |
local field="$1" | |
awk -v field="$field" ' | |
BEGIN { found=0; deps="" } | |
{ | |
if ($0 ~ "^" field ":") { | |
found=1 | |
gsub("^" field ":[[:space:]]*", "") | |
deps = $0 | |
} else if (found && $0 ~ "^[[:space:]]") { | |
# Continuation line (starts with whitespace) | |
gsub("^[[:space:]]*", "") | |
if ($0 != "") { | |
deps = deps " " $0 | |
} | |
} else if (found && $0 !~ "^[[:space:]]" && $0 != "") { | |
# New field found, stop processing | |
print deps | |
exit | |
} | |
} | |
END { if (found && deps != "") print deps } | |
' "$CONTROL_FILE" | \ | |
tr ',' '\n' | \ | |
sed -E 's/\([^)]*\)//g' | \ | |
sed -E 's/\[[^]]*\]//g' | \ | |
awk '{gsub(/^[[:space:]]+|[[:space:]]+$/, ""); if ($1 != "") print $1}' | \ | |
sort -u | |
} | |
# Function to detect and resolve version placeholders | |
resolve_version_placeholders() { | |
local control_file="$1" | |
local temp_file="" | |
echo "🔍 Checking for version placeholders in $(basename "$control_file")..." >&2 | |
# Check for BOOST_VER placeholder | |
if grep -q "{{BOOST_VER}}" "$control_file"; then | |
echo "📦 Found {{BOOST_VER}} placeholder, resolving Boost version..." >&2 | |
local boost_version="" | |
local boost_suffix="" | |
# Method 1: Check installed libboost-dev package | |
if [ -z "$boost_version" ]; then | |
boost_version=$(dpkg -l 2>/dev/null | grep "libboost-dev" | awk '{print $3}' | grep -oP '^\d+\.\d+\.\d+' | head -1 || true) | |
[ -n "$boost_version" ] && echo "✅ Found installed Boost version: $boost_version" >&2 | |
fi | |
# Method 2: Check available package version | |
if [ -z "$boost_version" ]; then | |
boost_version=$(apt-cache policy libboost-dev 2>/dev/null | grep "Candidate:" | awk '{print $2}' | grep -oP '^\d+\.\d+\.\d+' | head -1 || true) | |
[ -n "$boost_version" ] && echo "✅ Found available Boost version: $boost_version" >&2 | |
fi | |
# Determine the correct Boost package suffix format | |
echo "🔍 Determining correct Boost package naming format..." >&2 | |
local boost_major_minor=$(echo "$boost_version" | cut -d'.' -f1-2) | |
local boost_major_minor_no_dot=$(echo "$boost_major_minor" | tr -d '.') | |
# Test different naming conventions | |
if apt-cache show "libboost-system${boost_version}" >/dev/null 2>&1; then | |
boost_suffix="$boost_version" | |
echo "✅ Found format: libboost-system${boost_suffix}" >&2 | |
elif apt-cache show "libboost-system${boost_major_minor}" >/dev/null 2>&1; then | |
boost_suffix="$boost_major_minor" | |
echo "✅ Found format: libboost-system${boost_suffix}" >&2 | |
elif apt-cache show "libboost-system${boost_major_minor_no_dot}" >/dev/null 2>&1; then | |
boost_suffix="$boost_major_minor_no_dot" | |
echo "✅ Found format: libboost-system${boost_suffix}" >&2 | |
else | |
# Fallback to the most common format | |
boost_suffix="$boost_major_minor" | |
echo "⚠️ Could not detect format, using default: libboost-system${boost_suffix}" >&2 | |
fi | |
echo "🔢 Final Boost package suffix: $boost_suffix" >&2 | |
# Create temporary file if not already created | |
if [ -z "$temp_file" ]; then | |
temp_file=$(mktemp) | |
cp "$control_file" "$temp_file" | |
fi | |
# Replace BOOST_VER placeholder | |
sed -i "s/{{BOOST_VER}}/${boost_suffix}/g" "$temp_file" | |
echo "✅ Resolved {{BOOST_VER}} to ${boost_suffix}" >&2 | |
# Show resolved dependencies for verification | |
echo "🔍 Resolved Boost dependencies:" >&2 | |
grep -E "libboost.*${boost_suffix}" "$temp_file" >&2 || true | |
fi | |
# TODO: Add more placeholder handlers here in the future | |
# Example for future use: | |
# if grep -q "{{PYTHON_VER}}" "$control_file"; then | |
# echo "📦 Found {{PYTHON_VER}} placeholder, resolving Python version..." | |
# # Add Python version detection logic here | |
# fi | |
# Return the path to the resolved file (or original if no changes) | |
if [ -n "$temp_file" ]; then | |
echo "📝 Using temporary control file with resolved placeholders: $temp_file" >&2 | |
echo "$temp_file" | |
else | |
echo "ℹ️ No version placeholders found, using original file" >&2 | |
echo "$control_file" | |
fi | |
} | |
# Resolve version placeholders in control file | |
RESOLVED_CONTROL_FILE=$(resolve_version_placeholders "$CONTROL_FILE") | |
# Store the temporary file path for cleanup later | |
if [ "$RESOLVED_CONTROL_FILE" != "$CONTROL_FILE" ]; then | |
TEMP_CONTROL_FILE="$RESOLVED_CONTROL_FILE" | |
echo "🔄 Using resolved control file: $TEMP_CONTROL_FILE" | |
else | |
echo "📄 Using original control file: $CONTROL_FILE" | |
fi | |
# Update CONTROL_FILE to point to the resolved file | |
CONTROL_FILE="$RESOLVED_CONTROL_FILE" | |
# Extract dependencies | |
echo "🔍 Extracting dependencies from control file..." | |
echo "📄 Using control file: $CONTROL_FILE" | |
BUILD_DEPS=$(extract_deps "Build-Depends") | |
RUNTIME_DEPS=$(extract_deps "Depends") | |
RECOMMENDS=$(extract_deps "Recommends") | |
SUGGESTS=$(extract_deps "Suggests") | |
# Validate extraction results | |
BUILD_COUNT=$(echo "$BUILD_DEPS" | wc -w) | |
RUNTIME_COUNT=$(echo "$RUNTIME_DEPS" | wc -w) | |
echo "📊 Extraction results:" | |
echo " - Build dependencies: $BUILD_COUNT packages" | |
echo " - Runtime dependencies: $RUNTIME_COUNT packages" | |
echo " - Recommended packages: $(echo "$RECOMMENDS" | wc -w) packages" | |
echo " - Suggested packages: $(echo "$SUGGESTS" | wc -w) packages" | |
if [ $BUILD_COUNT -eq 0 ] && [ $RUNTIME_COUNT -eq 0 ]; then | |
echo "❌ No dependencies extracted! This might indicate a parsing error." | |
echo "Control file content:" | |
head -20 "$CONTROL_FILE" | |
exit 1 | |
fi | |
# Update package cache with retry | |
echo "📥 Updating package cache..." | |
for i in {1..3}; do | |
if sudo apt-get update -qq; then | |
break | |
elif [ $i -eq 3 ]; then | |
echo "❌ Failed to update package cache after 3 attempts" | |
exit 1 | |
else | |
echo "⚠️ Package cache update failed, retrying in 10s..." | |
sleep 10 | |
fi | |
done | |
# Enhanced dependency checking function | |
check_dependencies() { | |
local label="$1" | |
local pkgs="$2" | |
local is_critical="$3" | |
local fail=0 | |
local total=0 | |
local available=0 | |
echo "" >> "$REPORT" | |
echo "## $label" >> "$REPORT" | |
if [ -z "$pkgs" ]; then | |
echo "- ℹ️ No $label specified" >> "$REPORT" | |
return 0 | |
fi | |
for pkg in $pkgs; do | |
total=$((total + 1)) | |
# Skip empty package names | |
[ -z "$pkg" ] && continue | |
# Check if package is available | |
if apt-cache show "$pkg" > /dev/null 2>&1; then | |
# Get package version info | |
version=$(apt-cache policy "$pkg" 2>/dev/null | grep "Candidate:" | awk '{print $2}' || echo "unknown") | |
echo "- ✅ **$pkg** (version: $version)" >> "$REPORT" | |
available=$((available + 1)) | |
else | |
echo "- ❌ **$pkg** - Package not found in repositories" >> "$REPORT" | |
# Try to find similar packages | |
similar=$(apt-cache search "^$pkg" 2>/dev/null | head -3 | cut -d' ' -f1 | tr '\n' ', ' | sed 's/,$//') | |
if [ -n "$similar" ]; then | |
echo " - 💡 Similar packages: $similar" >> "$REPORT" | |
fi | |
if [ "$is_critical" = "true" ]; then | |
fail=1 | |
fi | |
fi | |
done | |
# Add summary | |
echo "" >> "$REPORT" | |
echo "**Summary:** $available/$total packages available" >> "$REPORT" | |
if [ $fail -eq 1 ]; then | |
echo "⚠️ **Critical dependencies missing!**" >> "$REPORT" | |
fi | |
return $fail | |
} | |
# Check all dependency types | |
check_dependencies "🔨 Build Dependencies" "$BUILD_DEPS" "true" || exit 1 | |
check_dependencies "🏃 Runtime Dependencies" "$RUNTIME_DEPS" "true" || exit 1 | |
check_dependencies "💡 Recommended Packages" "$RECOMMENDS" "false" || true | |
check_dependencies "🔧 Suggested Packages" "$SUGGESTS" "false" || true | |
# Add final summary | |
cat >> "$REPORT" << EOF | |
--- | |
## ✅ Verification Complete | |
All dependencies are accessible for ${{ matrix.arch }} on ${{ matrix.os }} (${{ matrix.codename }}). | |
EOF | |
# Cleanup temporary file if created | |
if [ -n "${TEMP_CONTROL_FILE:-}" ] && [ -f "$TEMP_CONTROL_FILE" ]; then | |
rm -f "$TEMP_CONTROL_FILE" | |
echo "🧹 Cleaned up temporary control file" | |
fi | |
- name: Validate report was generated | |
run: | | |
if [ ! -f "$REPORT" ] || [ ! -s "$REPORT" ]; then | |
echo "❌ Report file is missing or empty" | |
exit 1 | |
fi | |
echo "✅ Report generated successfully ($(wc -l < "$REPORT") lines)" | |
- name: Add summary to job | |
if: always() | |
run: | | |
echo "# 📦 Summary" >> $GITHUB_STEP_SUMMARY | |
echo "" >> $GITHUB_STEP_SUMMARY | |
if [ -f "$REPORT" ]; then | |
cat "$REPORT" >> $GITHUB_STEP_SUMMARY | |
else | |
echo "❌ Report generation failed" >> $GITHUB_STEP_SUMMARY | |
fi | |
- name: Check for failures | |
if: failure() | |
run: | | |
echo "❌ Dependency verification failed for ${{ matrix.os }} (${{ matrix.arch }})" | |
echo "This indicates build or runtime dependencies are missing." | |
echo "Please review the generated report and update the control file or repository configuration." | |
exit 1 | |