feat(skill): make aso_skill a real, tested, installable Python package #12
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: Claude Code Review | |
| # Runs on every PR to `dev` and `main` to provide AI-driven review feedback | |
| # and size-labeling. Heavier review happens for PRs to dev; main only sees | |
| # minimal validation via claude-main-check.yml. | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| branches: [dev, main] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| id-token: write | |
| concurrency: | |
| group: claude-code-review-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| pr-labeling: | |
| name: Label PR Size | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Label PR by size | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const changes = (pr.additions || 0) + (pr.deletions || 0); | |
| const sizeBuckets = [ | |
| { name: 'size: XS', max: 50, color: '00ff7f', description: 'Tiny PR (<50 changes)' }, | |
| { name: 'size: S', max: 200, color: '7fff00', description: 'Small PR (50-199 changes)' }, | |
| { name: 'size: M', max: 500, color: 'ffff00', description: 'Medium PR (200-499 changes)' }, | |
| { name: 'size: L', max: 1000, color: 'ffa500', description: 'Large PR (500-999 changes)' }, | |
| { name: 'size: XL', max: Infinity, color: 'ff0000', description: 'Extra large PR (>=1000 changes)' }, | |
| ]; | |
| const bucket = sizeBuckets.find(b => changes < b.max); | |
| // Ensure the label exists (idempotent) | |
| try { | |
| await github.rest.issues.getLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: bucket.name, | |
| }); | |
| } catch (e) { | |
| if (e.status === 404) { | |
| await github.rest.issues.createLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: bucket.name, | |
| color: bucket.color, | |
| description: bucket.description, | |
| }); | |
| } else { | |
| throw e; | |
| } | |
| } | |
| // Remove any stale size labels | |
| const { data: labels } = await github.rest.issues.listLabelsOnIssue({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| }); | |
| for (const label of labels) { | |
| if (label.name.startsWith('size: ') && label.name !== bucket.name) { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| name: label.name, | |
| }); | |
| } | |
| } | |
| // Add the new size label | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| labels: [bucket.name], | |
| }); | |
| core.info(`Labeled PR #${pr.number} as ${bucket.name} (${changes} changes)`); | |
| if (changes >= 500) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: `**Large PR alert**: this PR has ${changes} changes. Consider splitting it into smaller PRs (<200 changes) for faster review and fewer conflicts.`, | |
| }); | |
| } | |
| claude-review: | |
| name: Claude Code Review | |
| runs-on: ubuntu-latest | |
| # Heavier review only on PRs to dev. main gets minimal validation in claude-main-check.yml. | |
| if: github.event.pull_request.base.ref == 'dev' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Run Claude Code Review | |
| id: claude-review | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| prompt: | | |
| REPO: ${{ github.repository }} | |
| PR NUMBER: ${{ github.event.pull_request.number }} | |
| Review this ASO skill PR. Be concise and actionable. | |
| ASO-specific concerns: | |
| - Character limit validation | |
| (Apple: title 30, subtitle 30, keywords 100; Google: title 50, short desc 80) | |
| - Platform-specific logic correctness (Apple App Store vs Google Play Store) | |
| - Keyword analysis accuracy / relevance scoring | |
| - Metadata optimization quality | |
| - No external dependencies (standard library only) | |
| Code quality: | |
| - PEP 8 (ruff) | |
| - Type hints + docstrings for public functions | |
| - Error handling + edge cases | |
| - Performance (especially iTunes API calls) | |
| Security: | |
| - No hardcoded credentials or API keys | |
| - Safe URL handling and input validation | |
| - Proper data sanitization | |
| Documentation: | |
| - README updated for feature changes | |
| - CHANGELOG.md updated following Keep a Changelog | |
| - Inline comments for complex ASO logic | |
| Use CLAUDE.md for project context. Post your review with `gh pr comment`. | |
| claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh search:*)"' |