fix(macos): verify repacked dmg contains signed app #35
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: Auto-fill PR Description | |
| on: | |
| pull_request: | |
| types: [opened, reopened, synchronize] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| models: read | |
| jobs: | |
| fill-pr: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Gather context | |
| id: ctx | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -euo pipefail | |
| # ── Commits ── | |
| COMMITS=$(git log --pretty=format:"- %s" "${BASE_SHA}..${HEAD_SHA}" 2>/dev/null || echo "- (unable to read commits)") | |
| echo "commits<<EOF_COMMITS" >> "$GITHUB_OUTPUT" | |
| echo "$COMMITS" >> "$GITHUB_OUTPUT" | |
| echo "EOF_COMMITS" >> "$GITHUB_OUTPUT" | |
| # ── Diff stat ── | |
| DIFFSTAT=$(git diff --stat "${BASE_SHA}..${HEAD_SHA}" 2>/dev/null | tail -1 || echo "") | |
| echo "diffstat=${DIFFSTAT}" >> "$GITHUB_OUTPUT" | |
| # ── Changed files grouped by directory ── | |
| FILES=$(git diff --name-only "${BASE_SHA}..${HEAD_SHA}" 2>/dev/null || echo "") | |
| FILE_SUMMARY="" | |
| if [ -n "$FILES" ]; then | |
| # Separate root files and directory files | |
| ROOT_FILES=$(echo "$FILES" | grep -v '/' || true) | |
| DIR_FILES=$(echo "$FILES" | grep '/' || true) | |
| # Group by top-level directory | |
| if [ -n "$DIR_FILES" ]; then | |
| DIRS=$(echo "$DIR_FILES" | sed 's|/.*||' | sort -u) | |
| while IFS= read -r dir; do | |
| [ -z "$dir" ] && continue | |
| COUNT=$(echo "$DIR_FILES" | grep "^${dir}/" | wc -l | tr -d ' ') | |
| NAMES=$(echo "$DIR_FILES" | grep "^${dir}/" | sed "s|^${dir}/||" | head -8 | tr '\n' ', ' | sed 's/,$//' | sed 's/,/, /g') | |
| FILE_SUMMARY="${FILE_SUMMARY}- \`${dir}/\`: ${COUNT} file(s) — ${NAMES}"$'\n' | |
| done <<< "$DIRS" | |
| fi | |
| # Root-level files | |
| if [ -n "$ROOT_FILES" ]; then | |
| ROOT_COUNT=$(echo "$ROOT_FILES" | wc -l | tr -d ' ') | |
| ROOT_NAMES=$(echo "$ROOT_FILES" | head -8 | tr '\n' ', ' | sed 's/,$//' | sed 's/,/, /g') | |
| FILE_SUMMARY="${FILE_SUMMARY}- \`./\` (root): ${ROOT_COUNT} file(s) — ${ROOT_NAMES}"$'\n' | |
| fi | |
| TOTAL=$(echo "$FILES" | wc -l | tr -d ' ') | |
| FILE_SUMMARY="${FILE_SUMMARY}- **Total:** ${TOTAL} file(s)" | |
| fi | |
| echo "files<<EOF_FILES" >> "$GITHUB_OUTPUT" | |
| echo "$FILE_SUMMARY" >> "$GITHUB_OUTPUT" | |
| echo "EOF_FILES" >> "$GITHUB_OUTPUT" | |
| # ── Linked issues ── | |
| ISSUES=$(git log --pretty=format:"%s %b" "${BASE_SHA}..${HEAD_SHA}" 2>/dev/null \ | |
| | grep -oE '(close[sd]?|fix(es|ed)?|resolve[sd]?) #[0-9]+' \ | |
| | grep -oE '#[0-9]+' | sort -u | tr '\n' ' ' || true) | |
| echo "issues=${ISSUES:-none}" >> "$GITHUB_OUTPUT" | |
| # ── Compact diff for AI (truncated to 12000 chars to fit context) ── | |
| DIFF=$(git diff "${BASE_SHA}..${HEAD_SHA}" -- . ':!package-lock.json' ':!*.svg' 2>/dev/null | head -c 12000 || echo "") | |
| echo "diff<<EOF_DIFF" >> "$GITHUB_OUTPUT" | |
| echo "$DIFF" >> "$GITHUB_OUTPUT" | |
| echo "EOF_DIFF" >> "$GITHUB_OUTPUT" | |
| - name: Generate AI summary | |
| id: ai | |
| uses: actions/github-script@v7 | |
| env: | |
| COMMITS: ${{ steps.ctx.outputs.commits }} | |
| FILES: ${{ steps.ctx.outputs.files }} | |
| DIFFSTAT: ${{ steps.ctx.outputs.diffstat }} | |
| ISSUES: ${{ steps.ctx.outputs.issues }} | |
| DIFF: ${{ steps.ctx.outputs.diff }} | |
| HEAD_REF: ${{ github.event.pull_request.head.ref }} | |
| BASE_REF: ${{ github.event.pull_request.base.ref }} | |
| COMMIT_COUNT: ${{ github.event.pull_request.commits }} | |
| with: | |
| script: | | |
| const commitsRaw = process.env.COMMITS || ''; | |
| const filesRaw = process.env.FILES || ''; | |
| const diffRaw = process.env.DIFF || ''; | |
| const diffStatRaw = process.env.DIFFSTAT || ''; | |
| const allText = [commitsRaw, filesRaw, diffRaw, diffStatRaw].join('\n').toLowerCase(); | |
| const hasAny = (patterns) => patterns.some((p) => allText.includes(p)); | |
| const isDoc = hasAny(['docs/', '.md', 'readme', 'documentation']); | |
| const isCi = hasAny(['.github/workflows', 'workflow', 'actions/', 'ci', 'pipeline']); | |
| const isFeature = hasAny(['feat', 'feature', 'add ', 'implement', 'new ']); | |
| const isBugFix = hasAny(['fix', 'bug', 'issue', 'resolve', 'repair']); | |
| const isRefactor = hasAny(['refactor', 'cleanup', 'restructure', 'rename']); | |
| const isBreaking = hasAny(['breaking', '!:']); | |
| const hasUi = hasAny(['frontend/', 'ui', 'index.html', 'styles.css', 'app.js', 'tauri']); | |
| const hasTestsHint = hasAny(['test', 'spec', 'ci.yml', 'cargo test', 'npm test']); | |
| const inferTypeLines = [ | |
| `- [${isBugFix ? 'x' : ' '}] Bug fix (non-breaking change that fixes an issue)`, | |
| `- [${isFeature ? 'x' : ' '}] New feature (non-breaking change that adds functionality)`, | |
| `- [${isBreaking ? 'x' : ' '}] Breaking change (fix or feature that would cause existing functionality to change)`, | |
| `- [${isDoc ? 'x' : ' '}] Documentation update`, | |
| `- [${isCi ? 'x' : ' '}] CI/CD or infrastructure change`, | |
| `- [${isRefactor ? 'x' : ' '}] Refactor (no functional change)`, | |
| ].join('\n'); | |
| const inferValidationLines = [ | |
| '- [ ] I tested my change locally', | |
| `- [${hasTestsHint ? 'x' : ' '}] All existing tests still pass`, | |
| '- [ ] I added or updated tests for new functionality', | |
| `- [${isDoc ? 'x' : ' '}] I updated documentation/templates if needed`, | |
| '- [x] I confirmed no secrets or sensitive data are included', | |
| '- [ ] I checked for security vulnerabilities (OWASP Top 10)', | |
| ].join('\n'); | |
| const inferHowToTest = [ | |
| '1. Check out this branch and review commit history for scope confirmation.', | |
| isCi ? '2. Run/inspect the changed GitHub Actions workflows in a PR branch to confirm successful execution.' : '2. Run the main project workflow for this change set.', | |
| hasUi ? '3. Launch the application/UI path affected by this PR and verify behavior matches the summary.' : '3. Validate affected commands or scripts from the updated files.', | |
| '4. Confirm no regressions in related areas and that generated artifacts/output are correct.', | |
| ].join('\n'); | |
| const prompt = `You are a PR description agent for the DevOpster project (Rust CLI + Tauri desktop app + GitHub Pages docs). | |
| Analyze the git diff and commit messages below. Produce a COMPLETE PR description filling EVERY section of the template. | |
| Be specific, detailed, and professional. Infer type of change, validation, and test steps from the diff. | |
| IMPORTANT: Do not leave placeholders. Output complete markdown only. | |
| Branch: ${process.env.HEAD_REF} → ${process.env.BASE_REF} (${process.env.COMMIT_COUNT} commits) | |
| Linked issues from commits: ${process.env.ISSUES} | |
| Commits: | |
| ${process.env.COMMITS} | |
| Diff stat: ${process.env.DIFFSTAT} | |
| Files touched: | |
| ${process.env.FILES} | |
| Diff (truncated): | |
| \`\`\` | |
| ${process.env.DIFF} | |
| \`\`\` | |
| Fill EVERY section below. Replace ALL placeholder text. Check the applicable boxes. Output ONLY the markdown: | |
| ## Summary | |
| <One paragraph: what this PR does, why, and the impact. Be specific about the feature/fix.> | |
| ## Type of change | |
| <Check ONLY the applicable boxes based on the diff. Use [x] for checked, [ ] for unchecked.> | |
| - [ ] Bug fix (non-breaking change that fixes an issue) | |
| - [ ] New feature (non-breaking change that adds functionality) | |
| - [ ] Breaking change (fix or feature that would cause existing functionality to change) | |
| - [ ] Documentation update | |
| - [ ] CI/CD or infrastructure change | |
| - [ ] Refactor (no functional change) | |
| ## Changes | |
| <Detailed bulleted list of EVERY specific change, grouped by area (e.g. "### Editor", "### CI/CD", "### Config"). Include file names where relevant.> | |
| ## Files changed | |
| <Fill this table from the file list above. Group by top-level directory.> | |
| | Directory | Files | Description | | |
| |-----------|-------|-------------| | |
| | ... | ... | ... | | |
| ## Linked issues | |
| <List any issues referenced in commits (Closes #X, Fixes #Y). If none, write "No linked issues."> | |
| ## How to test | |
| <Write specific step-by-step instructions a reviewer can follow to validate these changes. Infer from the diff what commands to run, what to look for, etc.> | |
| 1. ... | |
| ## Validation | |
| <Check the boxes that apply based on what you see in the diff.> | |
| - [ ] I tested my change locally | |
| - [ ] All existing tests still pass | |
| - [ ] I added or updated tests for new functionality | |
| - [ ] I updated documentation/templates if needed | |
| - [ ] I confirmed no secrets or sensitive data are included | |
| - [ ] I checked for security vulnerabilities (OWASP Top 10) | |
| ## Screenshots / Logs | |
| <If the diff includes UI changes, describe what the reviewer should see. Otherwise write "N/A — no visual changes."> | |
| ## Notes | |
| <Any context: limitations, follow-up work needed, trade-offs, migration notes. If none, write "No additional notes.">`; | |
| let body = ''; | |
| try { | |
| const response = await fetch('https://models.github.ai/inference/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`, | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| model: 'openai/gpt-4o-mini', | |
| messages: [{ role: 'user', content: prompt }], | |
| max_tokens: 2500, | |
| temperature: 0.3, | |
| }), | |
| }); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| body = data.choices?.[0]?.message?.content || ''; | |
| } | |
| if (!body) throw new Error('Empty AI response'); | |
| core.info('AI summary generated successfully'); | |
| } catch (err) { | |
| core.warning(`AI summary failed (${err.message}), using structured fallback`); | |
| const issues = process.env.ISSUES === 'none' ? 'No linked issues.' : process.env.ISSUES; | |
| body = [ | |
| '## Summary', | |
| '', | |
| `Automated summary for \`${process.env.HEAD_REF}\` -> \`${process.env.BASE_REF}\` (${process.env.COMMIT_COUNT} commits), generated from commit metadata and changed files.`, | |
| '', | |
| '## Type of change', | |
| '', | |
| inferTypeLines, | |
| '', | |
| '## Changes', | |
| '', | |
| process.env.COMMITS, | |
| '', | |
| '## Files changed', | |
| '', | |
| process.env.FILES, | |
| '', | |
| '## Linked issues', | |
| '', | |
| issues, | |
| '', | |
| '## How to test', | |
| '', | |
| inferHowToTest, | |
| '', | |
| '## Validation', | |
| '', | |
| inferValidationLines, | |
| '', | |
| '## Screenshots / Logs', | |
| '', | |
| hasUi ? 'UI-related changes detected. Include screenshots or a short recording from the updated screens before merge.' : 'N/A - no visual changes.', | |
| '', | |
| '## Notes', | |
| '', | |
| 'Generated by fallback mode because AI completion was unavailable for this run. Review and refine where needed.', | |
| ].join('\n'); | |
| } | |
| core.setOutput('body', body); | |
| - name: Update PR description | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh pr edit "${{ github.event.pull_request.number }}" \ | |
| --repo "${{ github.repository }}" \ | |
| --body "${{ steps.ai.outputs.body }}" |