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: | |
| 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 | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - name: Fetch PR head | |
| run: | | |
| git fetch origin pull/${{ github.event.pull_request.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.base.sha || '' }} | |
| GITHUB_HEAD_SHA: ${{ github.event.pull_request.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 | |
| git show $BASE_REF:zephyr/module.yml > old_module.yml | |
| git show $HEAD_REF:zephyr/module.yml > new_module.yml | |
| # Parse YAML and generate comparison table | |
| echo "DEBUG: Starting Python script..." >&2 | |
| python3 << '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 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): | |
| """Get commit status and branch information""" | |
| try: | |
| import subprocess | |
| import os | |
| import re | |
| # First, check if we have PR metadata in the module.yml file | |
| try: | |
| with open('zephyr/module.yml', 'r') as f: | |
| content = f.read() | |
| # Look for PR metadata comment (more flexible pattern) | |
| pr_match = re.search(r'# Generated from PR #(\d+).*?commit: ([a-f0-9]+)', content, re.DOTALL) | |
| if pr_match and pr_match.group(2) == commit_hash: | |
| pr_number = pr_match.group(1) | |
| return { | |
| 'display': f"[{commit_hash[:7]}](PR #{pr_number})", | |
| 'is_pr': True, | |
| 'pr_number': pr_number, | |
| 'pr_url': f"https://github.com/nrfconnect/sdk-nrfxlib/pull/{pr_number}" | |
| } | |
| except: | |
| 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)" if commit_hash != "Unknown" else "Unknown (main)" | |
| } | |
| elif branch_names: | |
| # Show the first branch found | |
| return { | |
| 'display': f"{commit_hash[:7]} ({branch_names[0]})" if commit_hash != "Unknown" else f"Unknown ({branch_names[0]})" | |
| } | |
| except: | |
| pass | |
| # Default: show short commit hash | |
| return { | |
| 'display': commit_hash[:7] if commit_hash != "Unknown" else "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 = old_blob.get('version', 'N/A') | |
| new_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 | |
| old_commit_display = old_commit_status['display'] | |
| new_commit_display = new_commit_status['display'] | |
| 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:") | |
| diff_content.extend(table_rows[2:]) # Skip header rows | |
| # 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) | |
| print("diff_output<<DIFF_EOF", file=sys.stdout) | |
| print('\n'.join(diff_content), file=sys.stdout) | |
| print("DIFF_EOF", file=sys.stdout) | |
| except Exception as e: | |
| print("diff_output<<DIFF_EOF", file=sys.stdout) | |
| print(f"Error generating comparison: {e}", file=sys.stdout) | |
| print("DIFF_EOF", file=sys.stdout) | |
| print("DEBUG: Python script completed successfully", file=sys.stderr) | |
| 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 | |
| echo "diff_output<<DIFF_EOF" >> $GITHUB_OUTPUT | |
| cat diff_output.txt >> $GITHUB_OUTPUT | |
| echo "DIFF_EOF" >> $GITHUB_OUTPUT | |
| else | |
| echo "DEBUG: diff_output.txt not found" >&2 | |
| echo "diff_output<<DIFF_EOF" >> $GITHUB_OUTPUT | |
| echo "No changes detected" >> $GITHUB_OUTPUT | |
| echo "DIFF_EOF" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create or update comment | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| console.log('DEBUG: diff_output length:', process.env.diff_output ? process.env.diff_output.length : 0); | |
| const commentBody = '## Module Monitor\n\nChanges detected in module.yml\n\n' + | |
| (process.env.diff_output || 'No changes detected') + '\n\n' + | |
| 'This comment was automatically generated.'; | |
| 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: ${{ steps.set-output.outputs.diff_output }} | |
| - 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 | |
| echo "${{ steps.set-output.outputs.diff_output }}" >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY |