Skip to content

Commit ac53f1b

Browse files
committed
Merge branch 'main' into sort-elements
2 parents e4a0af4 + e19e94c commit ac53f1b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+4148
-1186
lines changed

.github/BENCHMARK_FAIL_TEMPLATE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
title: "{{ env.TITLE }}"
3+
labels: [bug]
4+
---
5+
6+
The {{ workflow }} workflow failed on {{ date | date("YYYY-MM-DD HH:mm") }} UTC
7+
8+
The most recent failing benchmark was on {{ env.PLATFORM }} py{{ env.PYTHON }} {{ env.BACKEND }}
9+
with commit: {{ sha }}
10+
11+
Full run: https://github.com/scverse/napari-spatialdata/actions/runs/{{ env.RUN_ID }}
12+
13+
(This post will be updated if another test fails, as long as this issue remains open.)

.github/dependabot.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2+
3+
version: 2
4+
updates:
5+
- package-ecosystem: "github-actions"
6+
directory: "/"
7+
schedule:
8+
interval: "monthly"
9+
commit-message:
10+
prefix: "ci(dependabot):"
11+
labels:
12+
- "maintenance"
13+
groups:
14+
actions:
15+
patterns:
16+
- "*"

.github/workflows/benchmarks.yml

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# This CI configuration for relative benchmarks is based on the research done
2+
# for scikit-image's implementation available here:
3+
# https://github.com/scikit-image/scikit-image/blob/9bdd010a8/.github/workflows/benchmarks.yml#L1
4+
# Blog post with the rationale: https://labs.quansight.org/blog/2021/08/github-actions-benchmarks/
5+
6+
name: Benchmarks
7+
8+
on:
9+
pull_request:
10+
types: [labeled]
11+
schedule:
12+
- cron: "6 6 * * 0" # every sunday
13+
workflow_dispatch:
14+
inputs:
15+
base_ref:
16+
description: "Baseline commit or git reference"
17+
required: true
18+
contender_ref:
19+
description: "Contender commit or git reference"
20+
required: true
21+
22+
# This is the main configuration section that needs to be fine tuned to napari's needs
23+
# All the *_THREADS options is just to make the benchmarks more robust by not using parallelism
24+
env:
25+
OPENBLAS_NUM_THREADS: "1"
26+
MKL_NUM_THREADS: "1"
27+
OMP_NUM_THREADS: "1"
28+
ASV_OPTIONS: "--split --show-stderr --factor 1.5 --attribute timeout=900"
29+
# --split -> split final reports in tables
30+
# --show-stderr -> print tracebacks if errors occur
31+
# --factor 1.5 -> report anomaly if tested timings are beyond 1.5x base timings
32+
# --attribute timeout=300 -> override timeout attribute (default=60s) to allow slow tests to run
33+
# see https://asv.readthedocs.io/en/stable/commands.html#asv-continuous for more details!
34+
35+
jobs:
36+
benchmark:
37+
if: ${{ github.event.label.name == 'run-benchmarks' && github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
38+
name: ${{ matrix.benchmark-name }}
39+
runs-on: ${{ matrix.runs-on }}
40+
permissions:
41+
contents: read
42+
issues: write
43+
strategy:
44+
fail-fast: false
45+
matrix:
46+
include:
47+
- benchmark-name: Qt
48+
asv-command: continuous
49+
selection-regex: "^benchmark_qt_.*"
50+
runs-on: macos-latest
51+
# Qt tests run on macOS to avoid using Xvfb business
52+
# xvfb makes everything run, but some tests segfault :shrug:
53+
# Fortunately, macOS graphics stack does not need xvfb!
54+
- benchmark-name: non-Qt
55+
asv-command: continuous
56+
selection-regex: "^benchmark_(?!qt_).*"
57+
runs-on: ubuntu-latest
58+
59+
steps:
60+
# We need the full repo to avoid this issue
61+
# https://github.com/actions/checkout/issues/23
62+
- uses: actions/checkout@v4
63+
with:
64+
fetch-depth: 0
65+
66+
- uses: actions/setup-python@v5
67+
name: Install Python
68+
with:
69+
python-version: "3.11"
70+
cache-dependency-path: pyproject.toml
71+
72+
- uses: tlambert03/setup-qt-libs@v1
73+
74+
- name: Setup asv
75+
run: python -m pip install "asv[virtualenv]"
76+
env:
77+
PIP_CONSTRAINT: ${{ github.workspace }}/benchmarks/benchmark.txt
78+
79+
- uses: octokit/request-action@v2.x
80+
id: latest_release
81+
with:
82+
route: GET /repos/{owner}/{repo}/releases/latest
83+
owner: scverse
84+
repo: napari-spatialdata
85+
env:
86+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
87+
88+
- name: Run ${{ matrix.benchmark-name }} benchmarks
89+
id: run_benchmark
90+
env:
91+
# asv will checkout commits, which might contain LFS artifacts; ignore those errors since
92+
# they are probably just documentation PNGs not needed here anyway
93+
GIT_LFS_SKIP_SMUDGE: 1
94+
HEAD_LABEL: ${{ github.event.pull_request.head.label }}
95+
PIP_CONSTRAINT: ${{ github.workspace }}/benchmarks/benchmark.txt
96+
run: |
97+
set -euxo pipefail
98+
read -ra cmd_options <<< "$ASV_OPTIONS"
99+
100+
# ID this runner
101+
asv machine --yes
102+
103+
if [[ $GITHUB_EVENT_NAME == pull_request ]]; then
104+
EVENT_NAME="PR #${{ github.event.pull_request.number }}"
105+
BASE_REF=${{ github.event.pull_request.base.sha }}
106+
CONTENDER_REF=${GITHUB_SHA}
107+
echo "Baseline: ${BASE_REF} (${{ github.event.pull_request.base.label }})"
108+
echo "Contender: ${CONTENDER_REF} ($HEAD_LABEL)"
109+
elif [[ $GITHUB_EVENT_NAME == schedule ]]; then
110+
EVENT_NAME="cronjob"
111+
BASE_REF="${{ fromJSON(steps.latest_release.outputs.data).target_commitish }}"
112+
CONTENDER_REF="${GITHUB_SHA}"
113+
echo "Baseline: ${BASE_REF} (${{ fromJSON(steps.latest_release.outputs.data).tag_name }})"
114+
echo "Contender: ${CONTENDER_REF} (current main)"
115+
elif [[ $GITHUB_EVENT_NAME == workflow_dispatch ]]; then
116+
EVENT_NAME="manual trigger"
117+
BASE_REF="${{ github.event.inputs.base_ref }}"
118+
CONTENDER_REF="${{ github.event.inputs.contender_ref }}"
119+
echo "Baseline: ${BASE_REF} (workflow input)"
120+
echo "Contender: ${CONTENDER_REF} (workflow input)"
121+
fi
122+
123+
echo "EVENT_NAME=$EVENT_NAME" >> "$GITHUB_ENV"
124+
echo "BASE_REF=$BASE_REF" >> "$GITHUB_ENV"
125+
echo "CONTENDER_REF=$CONTENDER_REF" >> "$GITHUB_ENV"
126+
127+
# Run benchmarks for current commit against base
128+
asv continuous "${cmd_options[@]}" -b "${{ matrix.selection-regex }}" "${BASE_REF}" "${CONTENDER_REF}" \
129+
| sed -E "/Traceback | failed$|PERFORMANCE DECREASED/ s/^/::error:: /" \
130+
| tee asv_continuous.log
131+
132+
# Report and export results for subsequent steps
133+
if grep "Traceback \|failed\|PERFORMANCE DECREASED" asv_continuous.log > /dev/null ; then
134+
exit 1
135+
fi
136+
137+
- name: Report Failures as Issue
138+
if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && failure() }}
139+
uses: JasonEtco/create-an-issue@v2
140+
env:
141+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
142+
PLATFORM: ${{ matrix.runs-on }}
143+
PYTHON: "3.9"
144+
BACKEND: ${{ matrix.benchmark-name }}
145+
RUN_ID: ${{ github.run_id }}
146+
TITLE: "[test-bot] Benchmark tests failing"
147+
with:
148+
filename: .github/BENCHMARK_FAIL_TEMPLATE.md
149+
update_existing: true
150+
151+
- name: Add more info to artifact
152+
if: always()
153+
run: |
154+
# Copy the full `asv continuous` log
155+
cp asv_continuous.log .asv/results/asv_continuous_${{ matrix.benchmark-name }}.log
156+
# ensure that even if this isn't a PR, the benchmark_report workflow can run without error
157+
touch .asv/results/message_${{ matrix.benchmark-name }}.txt
158+
159+
# Add the message that might be posted as a comment on the PR
160+
# We delegate the actual comment to `benchmarks_report.yml` due to
161+
# potential token permissions issues
162+
if [[ $GITHUB_EVENT_NAME == pull_request ]]; then
163+
164+
echo "${{ github.event.pull_request.number }}" > .asv/results/pr_number
165+
echo \
166+
"The ${{ matrix.benchmark-name }} benchmark run requested by $EVENT_NAME ($CONTENDER_REF vs $BASE_REF) has" \
167+
"finished with status '${{ steps.run_benchmark.outcome }}'. See the" \
168+
"[CI logs and artifacts](||BENCHMARK_CI_LOGS_URL||) for further details." \
169+
> .asv/results/message_${{ matrix.benchmark-name }}.txt
170+
171+
fi
172+
173+
- uses: actions/upload-artifact@v4
174+
if: always()
175+
with:
176+
name: asv-benchmark-results-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}-${{ matrix.benchmark-name }}
177+
path: .asv/results
178+
179+
combine-artifacts:
180+
runs-on: ubuntu-latest
181+
needs: benchmark
182+
if: always()
183+
steps:
184+
- name: Download artifact
185+
uses: actions/download-artifact@v4
186+
with:
187+
pattern: asv-benchmark-results*
188+
path: asv_result
189+
merge-multiple: true
190+
- name: Upload artifact
191+
uses: actions/upload-artifact@v4
192+
with:
193+
name: asv-benchmark-results-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}
194+
path: asv_result
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Report benchmark results to the PR
2+
# We need a dual workflow to make sure the token has the needed permissions to post comments
3+
# See https://stackoverflow.com/a/71683208 for more details
4+
5+
# When this workflow is triggered, it pulls the latest version of this file on
6+
# the default branch. Changes to this file won't be reflected until after the
7+
# PR is merged.
8+
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run
9+
10+
name: "Benchmarks - Report"
11+
12+
on:
13+
workflow_run:
14+
workflows: [Benchmarks]
15+
types:
16+
- completed
17+
18+
permissions:
19+
pull-requests: write
20+
issues: write
21+
22+
jobs:
23+
download:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: "Download artifact"
27+
uses: actions/github-script@v7
28+
with:
29+
script: |
30+
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
31+
owner: context.repo.owner,
32+
repo: context.repo.repo,
33+
run_id: context.payload.workflow_run.id,
34+
});
35+
let artifactName = `asv-benchmark-results-${context.payload.workflow_run.id}-${context.payload.workflow_run.run_number}-${context.payload.workflow_run.run_attempt}`
36+
console.log(`Artifact name: ${artifactName}`);
37+
console.log(`All artifacts: ${JSON.stringify(allArtifacts.data.artifacts)}`);
38+
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
39+
return artifact.name == artifactName
40+
})[0];
41+
if (matchArtifact === undefined) {
42+
throw TypeError('Build Artifact not found!');
43+
}
44+
let download = await github.rest.actions.downloadArtifact({
45+
owner: context.repo.owner,
46+
repo: context.repo.repo,
47+
artifact_id: matchArtifact.id,
48+
archive_format: 'zip',
49+
});
50+
let fs = require('fs');
51+
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/asv_results.zip`, Buffer.from(download.data));
52+
53+
- name: Unzip and prepare data
54+
run: |
55+
unzip asv_results.zip
56+
# combine the Qt and non-Qt messages
57+
cat message_Qt.txt message_non-Qt.txt > message.txt
58+
59+
- name: Replace URLs
60+
run: |
61+
sed -i 's@||BENCHMARK_CI_LOGS_URL||@${{ github.event.workflow_run.html_url }}@g' message.txt
62+
63+
- name: Collect PR number if available
64+
run: |
65+
if [[ -f pr_number ]]; then
66+
echo "PR_NUMBER=$(cat pr_number)" >> "$GITHUB_ENV"
67+
fi
68+
69+
- name: "Comment on PR"
70+
if: env.PR_NUMBER != ''
71+
uses: actions/github-script@v7
72+
with:
73+
github-token: ${{ secrets.GITHUB_TOKEN }}
74+
script: |
75+
let fs = require('fs');
76+
let issue_number = Number(process.env.PR_NUMBER);
77+
let body = fs.readFileSync('message.txt', 'utf8');
78+
await github.rest.issues.createComment({
79+
owner: context.repo.owner,
80+
repo: context.repo.repo,
81+
issue_number: issue_number,
82+
body: body,
83+
});
84+
85+
- name: "Remove run-benchmarks label"
86+
if: env.PR_NUMBER != ''
87+
uses: actions/github-script@v7
88+
with:
89+
github-token: ${{ secrets.GITHUB_TOKEN }}
90+
script: |
91+
let fs = require('fs');
92+
let issue_number = Number(process.env.PR_NUMBER);
93+
await github.rest.issues.removeLabel({
94+
owner: context.repo.owner,
95+
repo: context.repo.repo,
96+
issue_number: issue_number,
97+
name: 'run-benchmarks',
98+
});

.github/workflows/release.yml

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,50 @@
11
name: Release
22

33
on:
4-
release:
5-
types: [published]
4+
push:
5+
# Sequence of patterns matched against refs/tags
6+
tags:
7+
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
8+
pull_request:
9+
paths:
10+
- .github/workflows/release.yml
611

712
jobs:
813
release:
914
# requires that you have put your twine API key in your
1015
# github secrets (see readme for details)
1116
runs-on: ubuntu-latest
12-
if: contains(github.ref, 'tags')
17+
permissions:
18+
contents: write
1319
steps:
14-
- uses: actions/checkout@v2
15-
- name: Set up Python
16-
uses: actions/setup-python@v2
20+
- uses: actions/checkout@v4
1721
with:
18-
python-version: "3.x"
19-
- name: Install dependencies
20-
run: |
21-
python -m pip install --upgrade pip
22-
pip install -U setuptools setuptools_scm wheel twine build
23-
- name: Build and publish
24-
env:
25-
TWINE_USERNAME: __token__
26-
TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }}
22+
fetch-depth: 0
23+
- uses: hynek/build-and-inspect-python-package@v2
24+
id: build_dist
25+
- name: determine tag
26+
run: echo "tag=${GITHUB_REF/refs\/tags\/v/}" >> "$GITHUB_ENV"
27+
- name: debug dist
2728
run: |
28-
git tag
29-
python -m build .
30-
twine upload dist/*
29+
set -x
30+
echo "dist=${{ steps.build_dist.outputs.dist }}"
31+
ls -l ${{ steps.build_dist.outputs.dist }}
32+
mkdir -p dist
33+
cp ${{ steps.build_dist.outputs.dist }}/*.whl ./dist/
34+
cp ${{ steps.build_dist.outputs.dist }}/*.gz ./dist/
35+
36+
- name: Create Release
37+
uses: "softprops/action-gh-release@v2"
38+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
39+
with:
40+
tag_name: ${{ github.ref }}
41+
name: ${{ env.tag }}
42+
draft: false
43+
prerelease: ${{ contains(env.tag, 'rc') || contains(env.tag, 'a') || contains(env.tag, 'b') }}
44+
target_commitish: ${{ github.sha }}
45+
files: dist/*
46+
- name: Publish PyPI Package
47+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
48+
uses: pypa/gh-action-pypi-publish@release/v1
49+
with:
50+
password: ${{ secrets.TWINE_API_KEY }}

0 commit comments

Comments
 (0)