nrf_wifi : Update Wi-Fi FW blobs #66
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: 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 |