8000 TYP: use mypy_primer to surface type checking regressions · numpy/numpy@ea116fa · GitHub
[go: up one dir, main page]

Skip to con 10000 tent

Commit ea116fa

Browse files
committed
TYP: use mypy_primer to surface type checking regressions
Resolves #28054
1 parent ebbd944 commit ea116fa

File tree

2 files changed

+195
-0
lines changed

2 files changed

+195
-0
lines changed

.github/workflows/mypy_primer.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Run mypy_primer
2+
3+
on:
4+
# Only run on PR, since we diff against main
5+
pull_request:
6+
paths:
7+
- "**/*.pyi"
8+
- ".github/workflows/mypy_primer.yml"
9+
- ".github/workflows/mypy_primer_comment.yml"
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
13+
cancel-in-progress: true
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
mypy_primer:
20+
name: Run
21+
runs-on: ubuntu-latest
22+
strategy:
23+
matrix:
24+
shard-index: [0, 1, 2, 3]
25+
fail-fast: false
26+
steps:
27+
- uses: actions/checkout@v4
28+
with:
29+
path: numpy_to_test
30+
fetch-depth: 0
31+
- uses: actions/setup-python@v5
32+
with:
33+
python-version: "3.12"
34+
- name: Install dependencies
35+
run: pip install git+https://github.com/hauntsaninja/mypy_primer.git
36+
- name: Run mypy_primer
37+
shell: bash
38+
run: |
39+
cd numpy_to_test
40+
MYPY_VERSION=$(grep mypy== requirements/test_requirements.txt | sed -n 's/mypy==\([^;]*\).*/\1/p')
41+
42+
echo "new commit"
43+
git checkout $GITHUB_SHA
44+
git rev-list --format=%s --max-count=1
45+
46+
MERGE_BASE=$(git merge-base $GITHUB_SHA origin/$GITHUB_BASE_REF)
47+
git worktree add ../numpy_base $MERGE_BASE
48+
cd ../numpy_base
49+
50+
echo "base commit"
51+
git rev-list --format=%s --max-count=1
52+
53+
echo ''
54+
cd ..
55+
# fail action if exit code isn't zero or one
56+
(
57+
mypy_primer \
58+
--new v${MYPY_VERSION} --old v${MYPY_VERSION} \
59+
--known-dependency-selector numpy \
60+
--old-prepend-path numpy_base --new-prepend-path numpy_to_test \
61+
--num-shards 4 --shard-index ${{ matrix.shard-index }} \
62+
--debug \
63+
--output concise \
64+
| tee diff_${{ matrix.shard-index }}.txt
65+
) || [ $? -eq 1 ]
66+
- if: ${{ matrix.shard-index == 0 }}
67+
name: Save PR number
68+
run: |
69+
echo ${{ github.event.pull_request.number }} | tee pr_number.txt
70+
- name: Upload mypy_primer diff + PR number
71+
uses: actions/upload-artifact@v4
72+
if: ${{ matrix.shard-index == 0 }}
73+
with:
74+
name: mypy_primer_diffs-${{ matrix.shard-index }}
75+
path: |
76+
diff_${{ matrix.shard-index }}.txt
77+
pr_number.txt
78+
- name: Upload mypy_primer diff
8000 79+
uses: actions/upload-artifact@v4
80+
if: ${{ matrix.shard-index != 0 }}
81+
with:
82+
name: mypy_primer_diffs-${{ matrix.shard-index }}
83+
path: diff_${{ matrix.shard-index }}.txt
84+
85+
join_artifacts:
86+
name: Join artifacts
87+
runs-on: ubuntu-latest
88+
needs: [mypy_primer]
89+
permissions:
90+
contents: read
91+
steps:
92+
- name: Merge artifacts
93+
uses: actions/upload-artifact/merge@v4
94+
with:
95+
name: mypy_primer_diffs
96+
pattern: mypy_primer_diffs-*
97+
delete-merged: true
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Comment with mypy_primer diff
2+
3+
on:
4+
workflow_run:
5+
workflows:
6+
- Run mypy_primer
7+
types:
8+
- completed
9+
10+
permissions:
11+
contents: read
12+
pull-requests: write
13+
14+
jobs:
15+
comment:
16+
name: Comment PR from mypy_primer
17+
runs-on: ubuntu-latest
18+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
19+
steps:
20+
- name: Download diffs
21+
uses: actions/github-script@v7
22+
with:
23+
script: |
24+
const fs = require('fs');
25+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
26+
owner: context.repo.owner,
27+
repo: context.repo.repo,
28+
run_id: ${{ github.event.workflow_run.id }},
29+
});
30+
const [matchArtifact] = artifacts.data.artifacts.filter((artifact) =>
31+
artifact.name == "mypy_primer_diffs");
32+
33+
const download = await github.rest.actions.downloadArtifact({
34+
owner: context.repo.owner,
35+
repo: context.repo.repo,
36+
artifact_id: matchArtifact.id,
37+
archive_format: "zip",
38+
});
39+
fs.writeFileSync("diff.zip", Buffer.from(download.data));
40+
41+
- run: unzip diff.zip
42+
- run: |
43+
cat diff_*.txt | tee fulldiff.txt
44+
45+
- name: Post comment
46+
id: post-comment
47+
uses: actions/github-script@v7
48+
with:
49+
github-token: ${{ secrets.GITHUB_TOKEN }}
50+
script: |
51+
const MAX_CHARACTERS = 50000
52+
const MAX_CHARACTERS_PER_PROJECT = MAX_CHARACTERS / 3
53+
54+
const fs = require('fs')
55+
let data = fs.readFileSync('fulldiff.txt', { encoding: 'utf8' })
56+
57+
function truncateIfNeeded(original, maxLength) {
58+
if (original.length <= maxLength) {
59+
return original
60+
}
61+
let truncated = original.substring(0, maxLength)
62+
// further, remove last line that might be truncated
63+
truncated = truncated.substring(0, truncated.lastIndexOf('\n'))
64+
let lines_truncated = original.split('\n').length - truncated.split('\n').length
65+
return `${truncated}\n\n... (truncated ${lines_truncated} lines) ...`
66+
}
67+
68+
const projects = data.split('\n\n')
69+
// don't let one project dominate
70+
data = projects.map(project => truncateIfNeeded(project, MAX_CHARACTERS_PER_PROJECT)).join('\n\n')
71+
// posting comment fails if too long, so truncate
72+
data = truncateIfNeeded(data, MAX_CHARACTERS)
73+
74+
console.log("Diff from mypy_primer:")
75+
console.log(data)
76+
77+
let body
78+
if (data.trim()) {
79+
body = 'Diff from [mypy_primer](https://github.com/hauntsaninja/mypy_primer), showing the effect of this PR on type check results on a corpus of open source code:\n```diff\n' + data + '```'
80+
} else {
81+
body = "According to [mypy_primer](https://github.com/hauntsaninja/mypy_primer), this change doesn't affect type check results on a corpus of open source code. ✅"
82+
}
83+
const prNumber = parseInt(fs.readFileSync("pr_number.txt", { encoding: "utf8" }))
84+
await github.rest.issues.createComment({
85+
issue_number: prNumber,
86+
owner: context.repo.owner,
87+
repo: context.repo.repo,
88+
body
89+
})
90+
return prNumber
91+
92+
- name: Hide old comments
93+
# v0.4.0
94+
uses: kanga333/comment-hider@c12bb20b48aeb8fc098e35967de8d4f8018fffdf
95+
with:
96+
github_token: ${{ secrets.GITHUB_TOKEN }}
97+
leave_visible: 1
98+
issue_number: ${{ steps.post-comment.outputs.result }}

0 commit comments

Comments
 (0)
0