Add Multi Platform Sales Monitor #1083
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: Lint | |
| on: | |
| pull_request_target: | |
| branches: [dev, main] | |
| paths: | |
| - '**.py' | |
| jobs: | |
| lint: | |
| name: lint | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| statuses: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| ref: ${{ github.event.pull_request.head.ref }} | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install linters | |
| run: pip install flake8 autopep8 autoflake | |
| # ββ Get EXACT PR-changed files via GitHub API ββββββββββββββ | |
| - name: Get changed Python files | |
| id: changed | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Fetch exact file list from the PR (not git diff) | |
| ALL_FILES=$(curl -s \ | |
| -H "Authorization: token $GH_TOKEN" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files?per_page=100" \ | |
| | jq -r '.[].filename' \ | |
| | grep '\.py$' \ | |
| | grep -E '^(community|official|templates)/' || true) | |
| echo "π PR changed Python files:" | |
| echo "$ALL_FILES" | |
| if [ -z "$ALL_FILES" ]; then | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| echo "No Python files changed β skipping" | |
| exit 0 | |
| fi | |
| INIT_FILES="" | |
| LINT_FILES="" | |
| while IFS= read -r file; do | |
| # Skip files that no longer exist (deleted in PR) | |
| if [ ! -f "$file" ]; then | |
| continue | |
| fi | |
| if [[ "$(basename "$file")" == "__init__.py" ]]; then | |
| INIT_FILES="$INIT_FILES $file" | |
| else | |
| LINT_FILES="$LINT_FILES $file" | |
| fi | |
| done <<< "$ALL_FILES" | |
| INIT_FILES=$(echo "$INIT_FILES" | xargs) | |
| LINT_FILES=$(echo "$LINT_FILES" | xargs) | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| echo "lint_files=$LINT_FILES" >> $GITHUB_OUTPUT | |
| echo "init_files=$INIT_FILES" >> $GITHUB_OUTPUT | |
| echo "π Lint files: ${LINT_FILES:-none}" | |
| echo "π¦ __init__.py files: ${INIT_FILES:-none}" | |
| # ββ __init__.py must be empty check ββββββββββββββββββββββββββ | |
| - name: Check __init__.py files are empty | |
| if: steps.changed.outputs.skip == 'false' && steps.changed.outputs.init_files != '' | |
| id: init_check | |
| run: | | |
| INIT_FILES="${{ steps.changed.outputs.init_files }}" | |
| > init_violations.txt | |
| has_violations=false | |
| for file in $INIT_FILES; do | |
| if [ ! -f "$file" ]; then | |
| continue | |
| fi | |
| CONTENT=$(sed '/^\s*$/d' "$file") | |
| if [ -n "$CONTENT" ]; then | |
| echo "$file" >> init_violations.txt | |
| has_violations=true | |
| fi | |
| done | |
| echo "has_violations=$has_violations" >> $GITHUB_OUTPUT | |
| # ββ Auto-fix with autoflake (remove unused imports & vars) ββ | |
| - name: Auto-fix with autoflake | |
| if: steps.changed.outputs.skip == 'false' && steps.changed.outputs.lint_files != '' | |
| run: | | |
| autoflake --in-place \ | |
| --remove-all-unused-imports \ | |
| --remove-unused-variables \ | |
| ${{ steps.changed.outputs.lint_files }} | |
| # ββ Auto-format with autopep8 βββββββββββββββββββββββββββββββ | |
| - name: Auto-format with autopep8 | |
| if: steps.changed.outputs.skip == 'false' && steps.changed.outputs.lint_files != '' | |
| id: autoformat | |
| run: | | |
| autopep8 --in-place --max-line-length=120 --ignore=E501,W503 \ | |
| ${{ steps.changed.outputs.lint_files }} | |
| if git diff --quiet; then | |
| echo "has_fixes=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_fixes=true" >> $GITHUB_OUTPUT | |
| fi | |
| # ββ Commit ONLY the specific PR files ββββββββββββββββββββββ | |
| - name: Commit auto-format fixes | |
| if: steps.autoformat.outputs.has_fixes == 'true' | |
| continue-on-error: true | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Stage ONLY the exact files from this PR β nothing else | |
| for file in ${{ steps.changed.outputs.lint_files }}; do | |
| git add "$file" | |
| done | |
| git commit -m "style: auto-format Python files with autoflake + autopep8" | |
| git push | |
| # ββ Flake8 (check remaining issues after auto-format) ββββββ | |
| - name: Run Flake8 | |
| if: always() && steps.changed.outputs.skip == 'false' && steps.changed.outputs.lint_files != '' | |
| id: flake8 | |
| continue-on-error: true | |
| run: | | |
| OUTPUT=$(flake8 ${{ steps.changed.outputs.lint_files }} \ | |
| --max-line-length=120 --ignore=E501,W503 2>&1) || true | |
| echo "$OUTPUT" | |
| echo "$OUTPUT" > flake8_output.txt | |
| if [ -n "$OUTPUT" ]; then | |
| echo "has_errors=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_errors=false" >> $GITHUB_OUTPUT | |
| fi | |
| # ββ Create empty fallback files if steps were skipped βββββββ | |
| - name: Ensure output files exist | |
| if: always() && steps.changed.outputs.skip == 'false' | |
| run: | | |
| touch flake8_output.txt init_violations.txt | |
| # ββ PR Comment ββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: Comment on PR | |
| if: always() && steps.changed.outputs.skip == 'false' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const flake8 = fs.readFileSync('flake8_output.txt', 'utf8').trim(); | |
| const initViolations = fs.readFileSync('init_violations.txt', 'utf8').trim(); | |
| const flake8Err = '${{ steps.flake8.outputs.has_errors }}' === 'true'; | |
| const initErr = '${{ steps.init_check.outputs.has_violations }}' === 'true'; | |
| const autoFixed = '${{ steps.autoformat.outputs.has_fixes }}' === 'true'; | |
| const lintFiles = '${{ steps.changed.outputs.lint_files }}'; | |
| const initFiles = '${{ steps.changed.outputs.init_files }}'; | |
| const allPassed = !flake8Err && !initErr; | |
| let body = '<!-- lint-check-result -->\n'; | |
| body += '## π Lint Results\n\n'; | |
| if (autoFixed) { | |
| body += '### π§ Auto-formatted\n'; | |
| body += 'Some files were automatically cleaned and formatted with `autoflake` + `autopep8` and committed.\n'; | |
| body += '- β **Unused imports removed** (autoflake)\n'; | |
| body += '- β **Unused variables removed** (autoflake)\n'; | |
| body += '- β **PEP8 formatting applied** (autopep8)\n\n'; | |
| } | |
| if (initFiles) { | |
| if (initErr) { | |
| body += '### β `__init__.py` Must Be Empty\n\n'; | |
| body += 'The following `__init__.py` files must be **completely empty** (no code, no comments, no imports):\n\n'; | |
| body += '```\n' + initViolations + '\n```\n\n'; | |
| body += '> `__init__.py` in ability folders is only used as a package marker. Remove all content from these files.\n\n'; | |
| } else { | |
| body += '### β `__init__.py` β Empty as expected\n\n'; | |
| } | |
| } | |
| if (lintFiles) { | |
| body += `**Files linted:** \`${lintFiles}\`\n\n`; | |
| if (flake8Err) { | |
| body += '### β Flake8 Errors (could not be auto-fixed)\n```\n' + flake8 + '\n```\n\n'; | |
| } else { | |
| body += '### β Flake8 β Passed\n\n'; | |
| } | |
| } else { | |
| body += '_No non-init Python files to lint._\n\n'; | |
| } | |
| if (allPassed) { | |
| body += '> β All checks passed!'; | |
| } else { | |
| body += '> Fix the remaining issues and push again. The lint will re-run automatically.'; | |
| } | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number | |
| }); | |
| const existing = comments.find(c => | |
| c.user.type === 'Bot' && c.body.includes('<!-- lint-check-result -->') | |
| ); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: body | |
| }); | |
| } | |
| # ββ Fail the workflow if any check failed ββββββββββββββββββ | |
| - name: Fail if errors | |
| if: | | |
| steps.flake8.outputs.has_errors == 'true' || | |
| steps.init_check.outputs.has_violations == 'true' | |
| run: | | |
| echo "β Lint failed β check the PR comment for details" | |
| exit 1 | |
| - name: Report status | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const conclusion = '${{ job.status }}'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: context.payload.pull_request.head.sha, | |
| state: conclusion === 'success' ? 'success' : 'failure', | |
| context: 'lint', | |
| description: conclusion === 'success' ? 'Passed' : 'Failed' | |
| }); |