Skip to content

Commit 4ea94ea

Browse files
authored
Add mypy_primer workflow (#2819)
1 parent f3fa5d0 commit 4ea94ea

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

.github/workflows/mypy_primer.yml

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

0 commit comments

Comments
 (0)