Skip to content

Commit c993896

Browse files
committed
github workflow added to verify Package dependencies
Signed-off-by: ashish-jabble <[email protected]>
1 parent e814e32 commit c993896

File tree

1 file changed

+344
-0
lines changed

1 file changed

+344
-0
lines changed

.github/workflows/verify-deps.yml

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
name: 🧪 Verify Package Dependencies
2+
3+
on:
4+
push:
5+
branches: ['*']
6+
pull_request:
7+
branches: ['*']
8+
9+
env:
10+
DEBIAN_FRONTEND: noninteractive
11+
12+
jobs:
13+
verify-dependencies:
14+
name: 🛠️ ${{ matrix.os }}-${{ matrix.arch }}
15+
runs-on: ${{ matrix.runs_on }}
16+
timeout-minutes: 30
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
include:
21+
- os: ubuntu-22.04
22+
arch: x86_64
23+
runs_on: ubuntu-22.04
24+
codename: jammy
25+
- os: ubuntu-22.04
26+
arch: aarch64
27+
runs_on: ubuntu-22.04
28+
codename: jammy
29+
- os: ubuntu-24.04
30+
arch: x86_64
31+
runs_on: ubuntu-24.04
32+
codename: noble
33+
- os: ubuntu-24.04
34+
arch: aarch64
35+
runs_on: ubuntu-24.04
36+
codename: noble
37+
38+
steps:
39+
- name: Checkout repository
40+
uses: actions/checkout@v4
41+
with:
42+
fetch-depth: 1
43+
44+
- name: Set up environment
45+
run: |
46+
echo "CONTROL_FILE=packages/Debian/${{ matrix.arch }}/DEBIAN/control" >> $GITHUB_ENV
47+
echo "REPORT=dep_report_${{ matrix.os }}_${{ matrix.arch }}.md" >> $GITHUB_ENV
48+
49+
- name: Validate control file exists
50+
run: |
51+
if [ ! -f "$CONTROL_FILE" ]; then
52+
echo "❌ Control file missing: $CONTROL_FILE"
53+
echo "Available files in packages/Debian/:"
54+
find packages/Debian/ -name "control" 2>/dev/null || echo "No control files found"
55+
exit 1
56+
fi
57+
echo "✅ Control file found: $CONTROL_FILE"
58+
59+
- name: Verify dependencies & generate report
60+
run: |
61+
set -euo pipefail
62+
63+
# Initialize report
64+
cat > "$REPORT" << EOF
65+
## 📦 Dependency Verification Report
66+
67+
**Platform:** ${{ matrix.arch }} on ${{ matrix.os }} (${{ matrix.codename }})
68+
**Generated:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')
69+
**Workflow:** ${GITHUB_WORKFLOW} - Run #${GITHUB_RUN_NUMBER}
70+
71+
EOF
72+
73+
# Enhanced dependency extraction function
74+
extract_deps() {
75+
local field="$1"
76+
awk -v field="$field" '
77+
BEGIN { found=0; deps="" }
78+
{
79+
if ($0 ~ "^" field ":") {
80+
found=1
81+
gsub("^" field ":[[:space:]]*", "")
82+
deps = $0
83+
} else if (found && $0 ~ "^[[:space:]]") {
84+
# Continuation line (starts with whitespace)
85+
gsub("^[[:space:]]*", "")
86+
if ($0 != "") {
87+
deps = deps " " $0
88+
}
89+
} else if (found && $0 !~ "^[[:space:]]" && $0 != "") {
90+
# New field found, stop processing
91+
print deps
92+
exit
93+
}
94+
}
95+
END { if (found && deps != "") print deps }
96+
' "$CONTROL_FILE" | \
97+
tr ',' '\n' | \
98+
sed -E 's/\([^)]*\)//g' | \
99+
sed -E 's/\[[^]]*\]//g' | \
100+
awk '{gsub(/^[[:space:]]+|[[:space:]]+$/, ""); if ($1 != "") print $1}' | \
101+
sort -u
102+
}
103+
104+
# Function to detect and resolve version placeholders
105+
resolve_version_placeholders() {
106+
local control_file="$1"
107+
local temp_file=""
108+
109+
echo "🔍 Checking for version placeholders in $(basename "$control_file")..." >&2
110+
111+
# Check for BOOST_VER placeholder
112+
if grep -q "{{BOOST_VER}}" "$control_file"; then
113+
echo "📦 Found {{BOOST_VER}} placeholder, resolving Boost version..." >&2
114+
115+
local boost_version=""
116+
local boost_suffix=""
117+
118+
# Method 1: Check installed libboost-dev package
119+
if [ -z "$boost_version" ]; then
120+
boost_version=$(dpkg -l 2>/dev/null | grep "libboost-dev" | awk '{print $3}' | grep -oP '^\d+\.\d+\.\d+' | head -1 || true)
121+
[ -n "$boost_version" ] && echo "✅ Found installed Boost version: $boost_version" >&2
122+
fi
123+
124+
# Method 2: Check available package version
125+
if [ -z "$boost_version" ]; then
126+
boost_version=$(apt-cache policy libboost-dev 2>/dev/null | grep "Candidate:" | awk '{print $2}' | grep -oP '^\d+\.\d+\.\d+' | head -1 || true)
127+
[ -n "$boost_version" ] && echo "✅ Found available Boost version: $boost_version" >&2
128+
fi
129+
130+
# Determine the correct Boost package suffix format
131+
echo "🔍 Determining correct Boost package naming format..." >&2
132+
133+
local boost_major_minor=$(echo "$boost_version" | cut -d'.' -f1-2)
134+
local boost_major_minor_no_dot=$(echo "$boost_major_minor" | tr -d '.')
135+
136+
# Test different naming conventions
137+
if apt-cache show "libboost-system${boost_version}" >/dev/null 2>&1; then
138+
boost_suffix="$boost_version"
139+
echo "✅ Found format: libboost-system${boost_suffix}" >&2
140+
elif apt-cache show "libboost-system${boost_major_minor}" >/dev/null 2>&1; then
141+
boost_suffix="$boost_major_minor"
142+
echo "✅ Found format: libboost-system${boost_suffix}" >&2
143+
elif apt-cache show "libboost-system${boost_major_minor_no_dot}" >/dev/null 2>&1; then
144+
boost_suffix="$boost_major_minor_no_dot"
145+
echo "✅ Found format: libboost-system${boost_suffix}" >&2
146+
else
147+
# Fallback to the most common format
148+
boost_suffix="$boost_major_minor"
149+
echo "⚠️ Could not detect format, using default: libboost-system${boost_suffix}" >&2
150+
fi
151+
152+
echo "🔢 Final Boost package suffix: $boost_suffix" >&2
153+
154+
# Create temporary file if not already created
155+
if [ -z "$temp_file" ]; then
156+
temp_file=$(mktemp)
157+
cp "$control_file" "$temp_file"
158+
fi
159+
160+
# Replace BOOST_VER placeholder
161+
sed -i "s/{{BOOST_VER}}/${boost_suffix}/g" "$temp_file"
162+
echo "✅ Resolved {{BOOST_VER}} to ${boost_suffix}" >&2
163+
164+
# Show resolved dependencies for verification
165+
echo "🔍 Resolved Boost dependencies:" >&2
166+
grep -E "libboost.*${boost_suffix}" "$temp_file" >&2 || true
167+
fi
168+
169+
# TODO: Add more placeholder handlers here in the future
170+
# Example for future use:
171+
# if grep -q "{{PYTHON_VER}}" "$control_file"; then
172+
# echo "📦 Found {{PYTHON_VER}} placeholder, resolving Python version..."
173+
# # Add Python version detection logic here
174+
# fi
175+
176+
# Return the path to the resolved file (or original if no changes)
177+
if [ -n "$temp_file" ]; then
178+
echo "📝 Using temporary control file with resolved placeholders: $temp_file" >&2
179+
echo "$temp_file"
180+
else
181+
echo "ℹ️ No version placeholders found, using original file" >&2
182+
echo "$control_file"
183+
fi
184+
}
185+
186+
# Resolve version placeholders in control file
187+
RESOLVED_CONTROL_FILE=$(resolve_version_placeholders "$CONTROL_FILE")
188+
189+
# Store the temporary file path for cleanup later
190+
if [ "$RESOLVED_CONTROL_FILE" != "$CONTROL_FILE" ]; then
191+
TEMP_CONTROL_FILE="$RESOLVED_CONTROL_FILE"
192+
echo "🔄 Using resolved control file: $TEMP_CONTROL_FILE"
193+
else
194+
echo "📄 Using original control file: $CONTROL_FILE"
195+
fi
196+
197+
# Update CONTROL_FILE to point to the resolved file
198+
CONTROL_FILE="$RESOLVED_CONTROL_FILE"
199+
200+
# Extract dependencies
201+
echo "🔍 Extracting dependencies from control file..."
202+
echo "📄 Using control file: $CONTROL_FILE"
203+
204+
BUILD_DEPS=$(extract_deps "Build-Depends")
205+
RUNTIME_DEPS=$(extract_deps "Depends")
206+
RECOMMENDS=$(extract_deps "Recommends")
207+
SUGGESTS=$(extract_deps "Suggests")
208+
209+
# Validate extraction results
210+
BUILD_COUNT=$(echo "$BUILD_DEPS" | wc -w)
211+
RUNTIME_COUNT=$(echo "$RUNTIME_DEPS" | wc -w)
212+
213+
echo "📊 Extraction results:"
214+
echo " - Build dependencies: $BUILD_COUNT packages"
215+
echo " - Runtime dependencies: $RUNTIME_COUNT packages"
216+
echo " - Recommended packages: $(echo "$RECOMMENDS" | wc -w) packages"
217+
echo " - Suggested packages: $(echo "$SUGGESTS" | wc -w) packages"
218+
219+
if [ $BUILD_COUNT -eq 0 ] && [ $RUNTIME_COUNT -eq 0 ]; then
220+
echo "❌ No dependencies extracted! This might indicate a parsing error."
221+
echo "Control file content:"
222+
head -20 "$CONTROL_FILE"
223+
exit 1
224+
fi
225+
226+
# Update package cache with retry
227+
echo "📥 Updating package cache..."
228+
for i in {1..3}; do
229+
if sudo apt-get update -qq; then
230+
break
231+
elif [ $i -eq 3 ]; then
232+
echo "❌ Failed to update package cache after 3 attempts"
233+
exit 1
234+
else
235+
echo "⚠️ Package cache update failed, retrying in 10s..."
236+
sleep 10
237+
fi
238+
done
239+
240+
# Enhanced dependency checking function
241+
check_dependencies() {
242+
local label="$1"
243+
local pkgs="$2"
244+
local is_critical="$3"
245+
local fail=0
246+
local total=0
247+
local available=0
248+
249+
echo "" >> "$REPORT"
250+
echo "## $label" >> "$REPORT"
251+
252+
if [ -z "$pkgs" ]; then
253+
echo "- ℹ️ No $label specified" >> "$REPORT"
254+
return 0
255+
fi
256+
257+
for pkg in $pkgs; do
258+
total=$((total + 1))
259+
260+
# Skip empty package names
261+
[ -z "$pkg" ] && continue
262+
263+
# Check if package is available
264+
if apt-cache show "$pkg" > /dev/null 2>&1; then
265+
# Get package version info
266+
version=$(apt-cache policy "$pkg" 2>/dev/null | grep "Candidate:" | awk '{print $2}' || echo "unknown")
267+
echo "- ✅ **$pkg** (version: $version)" >> "$REPORT"
268+
available=$((available + 1))
269+
else
270+
echo "- ❌ **$pkg** - Package not found in repositories" >> "$REPORT"
271+
272+
# Try to find similar packages
273+
similar=$(apt-cache search "^$pkg" 2>/dev/null | head -3 | cut -d' ' -f1 | tr '\n' ', ' | sed 's/,$//')
274+
if [ -n "$similar" ]; then
275+
echo " - 💡 Similar packages: $similar" >> "$REPORT"
276+
fi
277+
278+
if [ "$is_critical" = "true" ]; then
279+
fail=1
280+
fi
281+
fi
282+
done
283+
284+
# Add summary
285+
echo "" >> "$REPORT"
286+
echo "**Summary:** $available/$total packages available" >> "$REPORT"
287+
288+
if [ $fail -eq 1 ]; then
289+
echo "⚠️ **Critical dependencies missing!**" >> "$REPORT"
290+
fi
291+
292+
return $fail
293+
}
294+
295+
# Check all dependency types
296+
check_dependencies "🔨 Build Dependencies" "$BUILD_DEPS" "true" || exit 1
297+
check_dependencies "🏃 Runtime Dependencies" "$RUNTIME_DEPS" "true" || exit 1
298+
check_dependencies "💡 Recommended Packages" "$RECOMMENDS" "false" || true
299+
check_dependencies "🔧 Suggested Packages" "$SUGGESTS" "false" || true
300+
301+
# Add final summary
302+
cat >> "$REPORT" << EOF
303+
304+
---
305+
306+
## ✅ Verification Complete
307+
308+
All dependencies are accessible for ${{ matrix.arch }} on ${{ matrix.os }} (${{ matrix.codename }}).
309+
310+
EOF
311+
312+
# Cleanup temporary file if created
313+
if [ -n "${TEMP_CONTROL_FILE:-}" ] && [ -f "$TEMP_CONTROL_FILE" ]; then
314+
rm -f "$TEMP_CONTROL_FILE"
315+
echo "🧹 Cleaned up temporary control file"
316+
fi
317+
318+
- name: Validate report was generated
319+
run: |
320+
if [ ! -f "$REPORT" ] || [ ! -s "$REPORT" ]; then
321+
echo "❌ Report file is missing or empty"
322+
exit 1
323+
fi
324+
echo "✅ Report generated successfully ($(wc -l < "$REPORT") lines)"
325+
326+
- name: Add summary to job
327+
if: always()
328+
run: |
329+
echo "# 📦 Summary" >> $GITHUB_STEP_SUMMARY
330+
echo "" >> $GITHUB_STEP_SUMMARY
331+
if [ -f "$REPORT" ]; then
332+
cat "$REPORT" >> $GITHUB_STEP_SUMMARY
333+
else
334+
echo "❌ Report generation failed" >> $GITHUB_STEP_SUMMARY
335+
fi
336+
337+
- name: Check for failures
338+
if: failure()
339+
run: |
340+
echo "❌ Dependency verification failed for ${{ matrix.os }} (${{ matrix.arch }})"
341+
echo "This indicates build or runtime dependencies are missing."
342+
echo "Please review the generated report and update the control file or repository configuration."
343+
exit 1
344+

0 commit comments

Comments
 (0)