Skip to content

nrf_wifi : Update Wi-Fi FW blobs #68

nrf_wifi : Update Wi-Fi FW blobs

nrf_wifi : Update Wi-Fi FW blobs #68

Workflow file for this run

name: Module Monitor
on:
pull_request_target:
paths:
- 'zephyr/module.yml'
permissions:
contents: read
pull-requests: write
issues: write
jobs:
module-monitor:
runs-on: ubuntu-24.04
name: Monitor Module Changes
steps:
- name: Checkout the code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Fetch PR head
run: |
git fetch origin pull/${{ github.event.number }}/head:pr-head
- name: Setup sdk-nrfxlib repository
run: |
git clone --depth=1000 https://github.com/nrfconnect/sdk-nrfxlib.git sdk-nrfxlib-repo
cd sdk-nrfxlib-repo
git fetch --depth=1000
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.12
- name: Install PyYAML
run: |
pip install pyyaml
- name: Get module.yml changes and blob info
id: changes
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_BASE_SHA: ${{ github.event.pull_request_target.base.sha || '' }}
GITHUB_HEAD_SHA: ${{ github.event.pull_request_target.head.sha || '' }}
run: |
# Compare with base branch for pull requests
BASE_REF="$GITHUB_BASE_SHA"
HEAD_REF="$GITHUB_HEAD_SHA"
echo "DEBUG: Comparing $BASE_REF to $HEAD_REF" >&2
echo "DEBUG: Current HEAD is $(git rev-parse HEAD)" >&2
echo "DEBUG: Available commits:" >&2
git log --oneline -5 >&2
# Get old and new module.yml content
echo "DEBUG: Getting old module.yml from $BASE_REF" >&2
git show $BASE_REF:zephyr/module.yml > old_module.yml
echo "DEBUG: Getting new module.yml from pr-head" >&2
git show pr-head:zephyr/module.yml > new_module.yml
echo "DEBUG: Old module.yml content:" >&2
head -5 old_module.yml >&2
echo "DEBUG: New module.yml content:" >&2
head -5 new_module.yml >&2
# Parse YAML and generate comparison table
echo "DEBUG: Starting Python script..." >&2
python3 << 'PYTHON_SCRIPT_EOF'
import yaml
import sys
import os
import re
from urllib.parse import urlparse
def extract_commit_from_url(url):
"""Extract commit hash from GitHub raw URL"""
if 'raw/' in url:
parts = url.split('raw/')
if len(parts) > 1:
commit_part = parts[1].split('/')[0]
if len(commit_part) == 40: # Full SHA
return commit_part
elif len(commit_part) >= 7: # Short SHA
return commit_part[:7]
return "Unknown"
def parse_version(version_str):
"""Parse and fix version string format"""
if version_str == 'N/A':
return version_str
# Version format should be FAMILY.MAJOR.MINOR.PATCH
# If the version appears reversed (PATCH.MINOR.MAJOR.FAMILY), fix it
parts = version_str.split('.')
if len(parts) >= 4:
# Check if this looks like a reversed version (patch.min.maj.fam -> fam.maj.min.patch)
# If the first part is larger than the last part, it's likely reversed
try:
first_part = int(parts[0])
last_part = int(parts[-1])
if first_part > last_part:
# Reverse the parts to get FAMILY.MAJOR.MINOR.PATCH
parts = parts[::-1]
return '.'.join(parts)
except ValueError:
# If we can't parse as integers, return as is
pass
return version_str
def get_commit_info(commit_hash):
"""Get additional commit information like branch and PR references"""
try:
import subprocess
import json
import os
# Use the cloned sdk-nrfxlib repository
sdk_repo_dir = 'sdk-nrfxlib-repo'
if not os.path.exists(sdk_repo_dir):
return {'hash': commit_hash[:7] if commit_hash != "Unknown" else "Unknown"}
# Get commit details from the sdk-nrfxlib repository
result = subprocess.run(['git', 'log', '--format=%H|%s|%an|%ad', '--date=short', '-1', commit_hash],
cwd=sdk_repo_dir, capture_output=True, text=True, timeout=10)
if result.returncode == 0 and result.stdout.strip():
parts = result.stdout.strip().split('|')
if len(parts) >= 4:
return {
'hash': parts[0][:7],
'subject': parts[1],
'author': parts[2],
'date': parts[3]
}
except:
pass
# Fallback: return basic info
return {'hash': commit_hash[:7] if commit_hash != "Unknown" else "Unknown"}
def get_commit_status(commit_hash, new_module_file='new_module.yml'):
"""Get commit status and branch information"""
try:
import subprocess
import os
import re
# First, check if we have PR metadata in the new module.yml file
try:
with open(new_module_file, 'r') as f:
content = f.read()
print(f"DEBUG: Looking for PR metadata in module.yml for commit {commit_hash}", file=sys.stderr)
print(f"DEBUG: Module.yml content starts with: {content[:200]}", file=sys.stderr)
# Look for PR metadata comment (more flexible pattern)
pr_match = re.search(r'# Generated from PR #(\d+) \(commit: ([a-f0-9]+)\)', content)
if pr_match:
print(f"DEBUG: Found PR match: PR #{pr_match.group(1)}, commit {pr_match.group(2)}", file=sys.stderr)
if (pr_match.group(2) == commit_hash or pr_match.group(2).startswith(commit_hash) or commit_hash.startswith(pr_match.group(2))):
pr_number = pr_match.group(1)
print(f"DEBUG: Commit {commit_hash} matches PR #{pr_number}", file=sys.stderr)
return {
'display': f"[{commit_hash[:7]} (PR #{pr_number})](https://github.com/nrfconnect/sdk-nrfxlib/pull/{pr_number})",
'is_pr': True,
'pr_number': pr_number,
'pr_url': f"https://github.com/nrfconnect/sdk-nrfxlib/pull/{pr_number}"
}
else:
print(f"DEBUG: Commit {commit_hash} does not match PR commit {pr_match.group(2)}", file=sys.stderr)
else:
print(f"DEBUG: No PR metadata found in module.yml", file=sys.stderr)
except Exception as e:
print(f"DEBUG: Error reading module.yml: {e}", file=sys.stderr)
pass
# Change to sdk-nrfxlib repository directory
sdk_repo_dir = 'sdk-nrfxlib-repo'
if not os.path.exists(sdk_repo_dir):
return {'display': commit_hash[:7] if commit_hash != "Unknown" else "Unknown"}
# Get all branches containing this commit
result = subprocess.run(['git', 'branch', '-r', '--contains', commit_hash],
cwd=sdk_repo_dir, capture_output=True, text=True, timeout=10)
if result.returncode == 0:
branches = result.stdout.strip().split('\n')
branch_names = []
is_on_main = False
for branch in branches:
branch = branch.strip()
if branch:
branch_name = branch.replace('origin/', '')
branch_names.append(branch_name)
if branch_name in ['main', 'master']:
is_on_main = True
if is_on_main:
return {
'display': f"[{commit_hash[:7]} (main)](https://github.com/nrfconnect/sdk-nrfxlib/commit/{commit_hash})" if commit_hash != "Unknown" else "Unknown (main)"
}
elif branch_names:
# Show the first branch found
return {
'display': f"[{commit_hash[:7]} ({branch_names[0]})](https://github.com/nrfconnect/sdk-nrfxlib/commit/{commit_hash})" if commit_hash != "Unknown" else f"Unknown ({branch_names[0]})"
}
except:
pass
# Default: show short commit hash with clickable link
if commit_hash != "Unknown":
return {
'display': f"[{commit_hash[:7]} (commit)](https://github.com/nrfconnect/sdk-nrfxlib/commit/{commit_hash})"
}
else:
return {
'display': "Unknown"
}
def generate_diff_link(old_url, new_url):
"""Generate diff link for the source repository"""
old_commit = extract_commit_from_url(old_url)
new_commit = extract_commit_from_url(new_url)
if old_commit != "Unknown" and new_commit != "Unknown" and old_commit != new_commit:
return f"https://github.com/nrfconnect/sdk-nrfxlib/compare/{old_commit}...{new_commit}"
elif new_commit != "Unknown":
return f"https://github.com/nrfconnect/sdk-nrfxlib/commit/{new_commit}"
return "N/A"
try:
print("DEBUG: Loading old_module.yml...", file=sys.stderr)
# Load old and new module.yml
with open('old_module.yml', 'r') as f:
old_data = yaml.safe_load(f)
print("DEBUG: Loading new_module.yml...", file=sys.stderr)
with open('new_module.yml', 'r') as f:
new_data = yaml.safe_load(f)
old_blobs = {blob['path']: blob for blob in old_data.get('blobs', [])}
new_blobs = {blob['path']: blob for blob in new_data.get('blobs', [])}
# Generate comparison table
table_rows = []
table_rows.append("| Variant | Old Version | New Version | Old Commit | New Commit | Diff Link |")
table_rows.append("|---------|-------------|-------------|------------|------------|-----------|")
all_paths = set(old_blobs.keys()) | set(new_blobs.keys())
for path in sorted(all_paths):
old_blob = old_blobs.get(path, {})
new_blob = new_blobs.get(path, {})
old_version = parse_version(old_blob.get('version', 'N/A'))
new_version = parse_version(new_blob.get('version', 'N/A'))
old_commit_hash = extract_commit_from_url(old_blob.get('url', ''))
new_commit_hash = extract_commit_from_url(new_blob.get('url', ''))
# Get additional commit information
old_commit_info = get_commit_info(old_commit_hash)
new_commit_info = get_commit_info(new_commit_hash)
# Check commit status (merged vs PR)
old_commit_status = get_commit_status(old_commit_hash)
new_commit_status = get_commit_status(new_commit_hash)
# Format commit display with PR detection
print(f"DEBUG: Processing {path} - old_commit: {old_commit_hash}, new_commit: {new_commit_hash}", file=sys.stderr)
old_commit_status = get_commit_status(old_commit_hash, 'old_module.yml')
new_commit_status = get_commit_status(new_commit_hash, 'new_module.yml')
old_commit_display = old_commit_status['display']
new_commit_display = new_commit_status['display']
print(f"DEBUG: {path} - old_display: {old_commit_display}, new_display: {new_commit_display}", file=sys.stderr)
diff_link = generate_diff_link(old_blob.get('url', ''), new_blob.get('url', ''))
# Extract variant name from path
variant = path.split('/')[-2] if '/' in path else path
table_rows.append(f"| {variant} | {old_version} | {new_version} | {old_commit_display} | {new_commit_display} | {diff_link} |")
# Check if there are any actual changes
has_changes = False
for path in sorted(all_paths):
old_blob = old_blobs.get(path, {})
new_blob = new_blobs.get(path, {})
if (old_blob.get('version') != new_blob.get('version') or
old_blob.get('sha256') != new_blob.get('sha256') or
old_blob.get('url') != new_blob.get('url')):
has_changes = True
break
# Output the table
diff_content = []
if has_changes:
diff_content.append("## Firmware Blob Changes")
diff_content.append("")
diff_content.extend(table_rows)
else:
diff_content.append("## No Changes Detected")
diff_content.append("")
diff_content.append("All firmware blobs remain unchanged in this PR.")
diff_content.append("")
diff_content.append("### Current Firmware Blob Summary:")
# Create a simplified summary table without the comparison columns
summary_rows = []
summary_rows.append("| Variant | Version | Commit |")
summary_rows.append("|---------|---------|--------|")
for path in sorted(all_paths):
new_blob = new_blobs.get(path, {})
version = parse_version(new_blob.get('version', 'N/A'))
commit_hash = extract_commit_from_url(new_blob.get('url', ''))
commit_display = commit_hash[:7] if commit_hash != "Unknown" else "Unknown"
# Extract variant name from path
variant = path.split('/')[-2] if '/' in path else path
summary_rows.append(f"| {variant} | {version} | {commit_display} |")
diff_content.extend(summary_rows)
# Check for PR references to determine if DNM label should be added/removed
has_pr_reference = False
try:
with open('new_module.yml', 'r') as f:
content = f.read()
# Look for PR metadata comment
pr_match = re.search(r'# Generated from PR #(\d+) \(commit: ([a-f0-9]+)\)', content)
if pr_match:
has_pr_reference = True
print(f"DEBUG: Found PR reference: PR #{pr_match.group(1)}", file=sys.stderr)
except Exception as e:
print(f"DEBUG: Error checking for PR reference: {e}", file=sys.stderr)
# Write PR reference status to file for GitHub Actions
with open('pr_reference_status.txt', 'w') as f:
f.write('true' if has_pr_reference else 'false')
# Write to file for GitHub Actions output
print(f"DEBUG: Writing {len(diff_content)} lines to diff_output.txt", file=sys.stderr)
with open('diff_output.txt', 'w') as f:
f.write('\n'.join(diff_content))
print(f"DEBUG: Successfully wrote diff_output.txt", file=sys.stderr)
except Exception as e:
print(f"Error generating comparison: {e}", file=sys.stderr)
print("DEBUG: Python script completed successfully", file=sys.stderr)
PYTHON_SCRIPT_EOF
- name: Set output variables
id: set-output
run: |
echo "DEBUG: Checking if files exist..." >&2
ls -la *.txt >&2 || echo "No .txt files found" >&2
if [ -f diff_output.txt ]; then
echo "DEBUG: diff_output.txt exists, size: $(wc -c < diff_output.txt)" >&2
# Use base64 encoding to avoid delimiter conflicts
DIFF_CONTENT=$(cat diff_output.txt | base64 -w 0)
echo "diff_output_encoded=$DIFF_CONTENT" >> $GITHUB_OUTPUT
else
echo "DEBUG: diff_output.txt not found" >&2
echo "diff_output_encoded=" >> $GITHUB_OUTPUT
fi
if [ -f pr_reference_status.txt ]; then
echo "DEBUG: pr_reference_status.txt exists" >&2
PR_REFERENCE_STATUS=$(cat pr_reference_status.txt)
echo "has_pr_reference=$PR_REFERENCE_STATUS" >> $GITHUB_OUTPUT
else
echo "DEBUG: pr_reference_status.txt not found" >&2
echo "has_pr_reference=false" >> $GITHUB_OUTPUT
fi
- name: Create or update comment
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Decode the base64 content
let diffOutput = 'No changes detected';
if (process.env.diff_output_encoded) {
try {
diffOutput = Buffer.from(process.env.diff_output_encoded, 'base64').toString('utf8');
} catch (e) {
console.log('Error decoding diff_output:', e.message);
diffOutput = 'Error decoding output';
}
}
console.log('DEBUG: diff_output length:', diffOutput.length);
const commentBody = '## Module Monitor\n\nChanges detected in module.yml\n\n' +
diffOutput + '\n\n' +
'This comment was automatically generated.';
// Handle DNM label management
const hasPrReference = process.env.has_pr_reference === 'true';
console.log('DEBUG: has_pr_reference:', hasPrReference);
try {
// Get current labels
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const hasDnmLabel = currentLabels.some(label => label.name === 'DNM');
console.log('DEBUG: Current DNM label status:', hasDnmLabel);
// Add DNM label if PR reference is found and label doesn't exist
if (hasPrReference && !hasDnmLabel) {
console.log('Adding DNM label due to PR reference');
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['DNM']
});
}
// Remove DNM label if PR reference is gone and label exists
else if (!hasPrReference && hasDnmLabel) {
console.log('Removing DNM label as PR reference is gone');
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
name: 'DNM'
});
}
else {
console.log('No DNM label action needed');
}
} catch (error) {
console.log('Could not manage DNM label due to permissions or other error.');
console.log('Error:', error.message);
}
try {
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const existingComment = comments.find(comment =>
comment.body.includes('Module Monitor')
);
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body: commentBody
});
console.log('Successfully updated existing comment');
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody
});
console.log('Successfully created new comment');
}
} catch (error) {
console.log('Could not create/update comment due to permissions.');
console.log('Error:', error.message);
console.log('Comment body that would have been posted:');
console.log('---');
console.log(commentBody);
console.log('---');
// Don't fail the workflow - the PR detection worked correctly
console.log('Workflow completed successfully - PR detection and diff generation worked correctly.');
}
env:
diff_output_encoded: ${{ steps.set-output.outputs.diff_output_encoded }}
has_pr_reference: ${{ steps.set-output.outputs.has_pr_reference }}
- name: Output summary
run: |
echo "## Module Monitor Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Changes in zephyr/module.yml:" >> $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.set-output.outputs.diff_output_encoded }}" ]; then
echo "${{ steps.set-output.outputs.diff_output_encoded }}" | base64 -d >> $GITHUB_STEP_SUMMARY
else
echo "No changes detected" >> $GITHUB_STEP_SUMMARY
fi
echo '```' >> $GITHUB_STEP_SUMMARY