mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-09-26 01:12:43 -05:00
Compare commits
5 Commits
dependabot
...
fix-codeco
Author | SHA1 | Date | |
---|---|---|---|
![]() |
37409ee564 | ||
![]() |
8d53c9cd36 | ||
![]() |
f8189abd81 | ||
![]() |
f203a79dbf | ||
![]() |
84ff073695 |
449
.github/workflows/ci.yml
vendored
449
.github/workflows/ci.yml
vendored
@@ -322,6 +322,455 @@ jobs:
|
||||
run: cd src-ui && pnpm exec playwright install
|
||||
- name: Run Playwright e2e tests
|
||||
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||
codecov-comment:
|
||||
name: "Codecov PR Comment"
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- tests-backend
|
||||
- tests-frontend
|
||||
- tests-frontend-e2e
|
||||
if: github.event_name == 'pull_request'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Gather pull request context
|
||||
id: pr
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
if (!pr) {
|
||||
core.info('No associated pull request. Skipping.');
|
||||
core.setOutput('shouldRun', 'false');
|
||||
return;
|
||||
}
|
||||
|
||||
core.setOutput('shouldRun', 'true');
|
||||
core.setOutput('prNumber', pr.number.toString());
|
||||
core.setOutput('headSha', pr.head.sha);
|
||||
- name: Fetch Codecov coverage
|
||||
id: coverage
|
||||
if: steps.pr.outputs.shouldRun == 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
COMMIT_SHA: ${{ steps.pr.outputs.headSha }}
|
||||
PR_NUMBER: ${{ steps.pr.outputs.prNumber }}
|
||||
with:
|
||||
script: |
|
||||
const commitSha = process.env.COMMIT_SHA;
|
||||
const prNumber = process.env.PR_NUMBER;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const service = 'gh';
|
||||
const baseUrl = `https://api.codecov.io/api/v2/${service}/${owner}/repos/${repo}`;
|
||||
const commitUrl = `${baseUrl}/commits/${commitSha}`;
|
||||
const maxAttempts = 20;
|
||||
const waitMs = 15000;
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
let data;
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
core.info(`Fetching Codecov report (attempt ${attempt}/${maxAttempts})`);
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(commitUrl, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
core.warning(`Codecov fetch failed: ${error}. Waiting before retrying.`);
|
||||
await sleep(waitMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (response.status === 404) {
|
||||
core.info('Report not ready yet (404). Waiting before retrying.');
|
||||
await sleep(waitMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([429, 500, 502, 503, 504].includes(response.status)) {
|
||||
const text = await response.text().catch(() => '');
|
||||
core.info(`Codecov API transient error ${response.status}: ${text}. Waiting before retrying.`);
|
||||
await sleep(waitMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => '');
|
||||
core.warning(`Codecov API returned ${response.status}: ${text}. Skipping comment.`);
|
||||
core.setOutput('shouldComment', 'false');
|
||||
return;
|
||||
}
|
||||
|
||||
data = await response.json().catch((error) => {
|
||||
core.warning(`Failed to parse Codecov response: ${error}.`);
|
||||
return undefined;
|
||||
});
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
core.info('Report payload empty. Waiting before retrying.');
|
||||
await sleep(waitMs);
|
||||
}
|
||||
|
||||
if (!data && prNumber) {
|
||||
core.info('Attempting to retrieve coverage from PR endpoint.');
|
||||
const prUrl = `${baseUrl}/pulls/${prNumber}`;
|
||||
let prResponse;
|
||||
try {
|
||||
prResponse = await fetch(prUrl, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
core.warning(`Codecov PR fetch failed: ${error}.`);
|
||||
}
|
||||
|
||||
if (prResponse) {
|
||||
if ([429, 500, 502, 503, 504].includes(prResponse.status)) {
|
||||
const text = await prResponse.text().catch(() => '');
|
||||
core.info(`Codecov PR endpoint transient error ${prResponse.status}: ${text}.`);
|
||||
} else if (!prResponse.ok) {
|
||||
const text = await prResponse.text().catch(() => '');
|
||||
core.warning(`Codecov PR endpoint returned ${prResponse.status}: ${text}.`);
|
||||
} else {
|
||||
const prData = await prResponse.json().catch((error) => {
|
||||
core.warning(`Failed to parse Codecov PR response: ${error}.`);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (prData?.latest_report) {
|
||||
data = { report: prData.latest_report };
|
||||
} else if (prData?.head_totals) {
|
||||
const headTotals = prData.head_totals;
|
||||
const baseTotals = prData.base_totals;
|
||||
let compareTotals;
|
||||
if (baseTotals && headTotals) {
|
||||
const headCoverage = Number(headTotals.coverage);
|
||||
const baseCoverage = Number(baseTotals.coverage);
|
||||
if (Number.isFinite(headCoverage) && Number.isFinite(baseCoverage)) {
|
||||
compareTotals = {
|
||||
base_coverage: baseCoverage,
|
||||
coverage_change: headCoverage - baseCoverage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
data = {
|
||||
report: {
|
||||
totals: headTotals,
|
||||
compare: compareTotals ? { totals: compareTotals } : undefined,
|
||||
totals_by_flag: [],
|
||||
},
|
||||
head_totals: headTotals,
|
||||
base_totals: baseTotals,
|
||||
};
|
||||
} else {
|
||||
data = prData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
core.warning('Unable to retrieve Codecov report after multiple attempts.');
|
||||
core.setOutput('shouldComment', 'false');
|
||||
return;
|
||||
}
|
||||
|
||||
const toNumber = (value) => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return undefined;
|
||||
}
|
||||
const num = Number(value);
|
||||
return Number.isFinite(num) ? num : undefined;
|
||||
};
|
||||
|
||||
const reportData = data.report || data;
|
||||
const totals = reportData.totals ?? data.head_totals ?? data.totals;
|
||||
if (!totals) {
|
||||
core.warning('Codecov response does not contain coverage totals.');
|
||||
core.setOutput('shouldComment', 'false');
|
||||
return;
|
||||
}
|
||||
|
||||
let compareTotals = reportData.compare?.totals ?? data.compare?.totals;
|
||||
if (!compareTotals && data.base_totals) {
|
||||
const baseCoverageValue = toNumber(data.base_totals.coverage);
|
||||
if (baseCoverageValue !== undefined) {
|
||||
const headCoverageValue = toNumber((data.head_totals ?? {}).coverage);
|
||||
compareTotals = {
|
||||
base_coverage: baseCoverageValue,
|
||||
coverage_change:
|
||||
headCoverageValue !== undefined ? headCoverageValue - baseCoverageValue : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const coverage = toNumber(totals.coverage);
|
||||
const baseCoverage = toNumber(compareTotals?.base_coverage ?? compareTotals?.base);
|
||||
let delta = toNumber(
|
||||
compareTotals?.coverage_change ??
|
||||
compareTotals?.coverage_diff ??
|
||||
totals.delta ??
|
||||
totals.diff ??
|
||||
totals.change,
|
||||
);
|
||||
|
||||
if (delta === undefined && coverage !== undefined && baseCoverage !== undefined) {
|
||||
delta = coverage - baseCoverage;
|
||||
}
|
||||
|
||||
const formatPercent = (value) => {
|
||||
if (value === undefined) return '—';
|
||||
return `${value.toFixed(2)}%`;
|
||||
};
|
||||
|
||||
const formatDelta = (value) => {
|
||||
if (value === undefined) return '—';
|
||||
const sign = value >= 0 ? '+' : '';
|
||||
return `${sign}${value.toFixed(2)}%`;
|
||||
};
|
||||
|
||||
const shortSha = commitSha.slice(0, 7);
|
||||
const reportBaseUrl = `https://app.codecov.io/gh/${owner}/${repo}`;
|
||||
const commitReportUrl = `${reportBaseUrl}/commit/${commitSha}?src=pr&el=comment`;
|
||||
const prReportUrl = prNumber
|
||||
? `${reportBaseUrl}/pull/${prNumber}?src=pr&el=comment`
|
||||
: commitReportUrl;
|
||||
|
||||
const findBaseCommitSha = () =>
|
||||
data?.report?.compare?.base_commitid ??
|
||||
data?.report?.compare?.base?.commitid ??
|
||||
data?.report?.base_commitid ??
|
||||
data?.compare?.base_commitid ??
|
||||
data?.compare?.base?.commitid ??
|
||||
data?.base_commitid ??
|
||||
data?.base?.commitid;
|
||||
|
||||
const baseCommitSha = findBaseCommitSha();
|
||||
const baseCommitUrl = baseCommitSha
|
||||
? `${reportBaseUrl}/commit/${baseCommitSha}?src=pr&el=comment`
|
||||
: undefined;
|
||||
const baseShortSha = baseCommitSha ? baseCommitSha.slice(0, 7) : undefined;
|
||||
|
||||
const lines = ['<!-- codecov-coverage-comment -->'];
|
||||
lines.push(`## [Codecov](${prReportUrl}) Report`);
|
||||
lines.push('');
|
||||
|
||||
if (coverage !== undefined) {
|
||||
lines.push(`:white_check_mark: Project coverage for \`${shortSha}\` is ${formatPercent(coverage)}.`);
|
||||
} else {
|
||||
lines.push(':warning: Coverage for the head commit is unavailable.');
|
||||
}
|
||||
|
||||
if (baseCoverage !== undefined) {
|
||||
const changeEmoji = delta === undefined ? ':grey_question:' : delta >= 0 ? ':white_check_mark:' : ':small_red_triangle_down:';
|
||||
const baseCoverageText = `Base${baseShortSha ? ` \`${baseShortSha}\`` : ''} ${formatPercent(baseCoverage)}`;
|
||||
const baseLink = baseCommitUrl ? `[${baseCoverageText}](${baseCommitUrl})` : baseCoverageText;
|
||||
const changeText =
|
||||
delta !== undefined
|
||||
? `${baseLink} (${formatDelta(delta)})`
|
||||
: `${baseLink} (change unknown)`;
|
||||
lines.push(`${changeEmoji} ${changeText}.`);
|
||||
}
|
||||
|
||||
lines.push(`:clipboard: [View full report on Codecov](${commitReportUrl}).`);
|
||||
|
||||
const normalizeTotals = (value) => {
|
||||
if (!value) return undefined;
|
||||
if (value.totals && typeof value.totals === 'object') return value.totals;
|
||||
return value;
|
||||
};
|
||||
|
||||
const headTotals = normalizeTotals(totals) ?? {};
|
||||
const baseTotals =
|
||||
normalizeTotals(data.base_totals) ??
|
||||
normalizeTotals(reportData.base_totals) ??
|
||||
normalizeTotals(reportData.compare?.base_totals) ??
|
||||
normalizeTotals(reportData.compare?.base);
|
||||
|
||||
const formatInteger = (value) => {
|
||||
if (value === undefined) return '—';
|
||||
return value.toLocaleString('en-US');
|
||||
};
|
||||
|
||||
const formatIntegerDelta = (value) => {
|
||||
if (value === undefined) return '—';
|
||||
const sign = value >= 0 ? '+' : '';
|
||||
return `${sign}${value.toLocaleString('en-US')}`;
|
||||
};
|
||||
|
||||
const getInteger = (value) => {
|
||||
const num = toNumber(value);
|
||||
return Number.isFinite(num) ? Math.round(num) : undefined;
|
||||
};
|
||||
|
||||
const metrics = [];
|
||||
metrics.push({
|
||||
label: 'Coverage',
|
||||
base: baseCoverage,
|
||||
head: coverage,
|
||||
diff: delta,
|
||||
format: formatPercent,
|
||||
formatDiff: formatDelta,
|
||||
});
|
||||
|
||||
const pushIntegerMetric = (label, headValueRaw, baseValueRaw) => {
|
||||
const headValue = getInteger(headValueRaw);
|
||||
const baseValue = getInteger(baseValueRaw);
|
||||
if (headValue === undefined && baseValue === undefined) {
|
||||
return;
|
||||
}
|
||||
const diff = headValue !== undefined && baseValue !== undefined ? headValue - baseValue : undefined;
|
||||
metrics.push({
|
||||
label,
|
||||
base: baseValue,
|
||||
head: headValue,
|
||||
diff,
|
||||
format: formatInteger,
|
||||
formatDiff: formatIntegerDelta,
|
||||
});
|
||||
};
|
||||
|
||||
pushIntegerMetric('Files', headTotals.files, baseTotals?.files);
|
||||
pushIntegerMetric('Lines', headTotals.lines, baseTotals?.lines);
|
||||
pushIntegerMetric('Branches', headTotals.branches, baseTotals?.branches);
|
||||
pushIntegerMetric('Hits', headTotals.hits, baseTotals?.hits);
|
||||
pushIntegerMetric('Misses', headTotals.misses, baseTotals?.misses);
|
||||
|
||||
const hasMetricData = metrics.some((metric) => metric.base !== undefined || metric.head !== undefined);
|
||||
if (hasMetricData) {
|
||||
lines.push('');
|
||||
lines.push('<details><summary>Coverage summary</summary>');
|
||||
lines.push('');
|
||||
lines.push('| Metric | Base | Head | Δ |');
|
||||
lines.push('| --- | --- | --- | --- |');
|
||||
for (const metric of metrics) {
|
||||
const baseValue = metric.base !== undefined ? metric.format(metric.base) : '—';
|
||||
const headValue = metric.head !== undefined ? metric.format(metric.head) : '—';
|
||||
const diffValue = metric.diff !== undefined ? metric.formatDiff(metric.diff) : '—';
|
||||
lines.push(`| ${metric.label} | ${baseValue} | ${headValue} | ${diffValue} |`);
|
||||
}
|
||||
lines.push('');
|
||||
lines.push('</details>');
|
||||
}
|
||||
|
||||
const normalizeEntries = (raw) => {
|
||||
if (!raw) return [];
|
||||
if (Array.isArray(raw)) return raw;
|
||||
if (typeof raw === 'object') {
|
||||
return Object.entries(raw).map(([name, totals]) => ({ name, ...(typeof totals === 'object' ? totals : { coverage: totals }) }));
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const buildTableRows = (entries) => {
|
||||
const rows = [];
|
||||
for (const entry of entries) {
|
||||
const label = entry.flag ?? entry.name ?? entry.component ?? entry.id;
|
||||
const entryTotals = entry.totals ?? entry;
|
||||
const entryCoverage = toNumber(entryTotals?.coverage);
|
||||
if (!label || entryCoverage === undefined) {
|
||||
continue;
|
||||
}
|
||||
const entryDelta = toNumber(
|
||||
entryTotals?.coverage_change ??
|
||||
entryTotals?.coverage_diff ??
|
||||
entryTotals?.delta ??
|
||||
entryTotals?.diff ??
|
||||
entryTotals?.change,
|
||||
);
|
||||
const coverageText = entryCoverage !== undefined ? `\`${formatPercent(entryCoverage)}\`` : '—';
|
||||
const deltaText = entryDelta !== undefined ? `\`${formatDelta(entryDelta)}\`` : '—';
|
||||
rows.push(`| ${label} | ${coverageText} | ${deltaText} |`);
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
|
||||
const componentEntries = normalizeEntries(reportData.components ?? data.components);
|
||||
const flagEntries = normalizeEntries(reportData.totals_by_flag ?? data.totals_by_flag);
|
||||
|
||||
if (componentEntries.length) {
|
||||
const componentsLink = prNumber
|
||||
? `${reportBaseUrl}/pull/${prNumber}/components?src=pr&el=components`
|
||||
: `${commitReportUrl}`;
|
||||
const componentRows = buildTableRows(componentEntries);
|
||||
if (componentRows.length) {
|
||||
lines.push('');
|
||||
lines.push(`[Components report](${componentsLink})`);
|
||||
lines.push('');
|
||||
lines.push('| Component | Coverage | Δ |');
|
||||
lines.push('| --- | --- | --- |');
|
||||
lines.push(...componentRows);
|
||||
}
|
||||
}
|
||||
|
||||
if (flagEntries.length) {
|
||||
const flagsLink = prNumber
|
||||
? `${reportBaseUrl}/pull/${prNumber}/flags?src=pr&el=flags`
|
||||
: `${commitReportUrl}`;
|
||||
const flagRows = buildTableRows(flagEntries);
|
||||
if (flagRows.length) {
|
||||
lines.push('');
|
||||
lines.push(`[Flags report](${flagsLink})`);
|
||||
lines.push('');
|
||||
lines.push('| Flag | Coverage | Δ |');
|
||||
lines.push('| --- | --- | --- |');
|
||||
lines.push(...flagRows);
|
||||
}
|
||||
}
|
||||
|
||||
const commentBody = lines.join('\n');
|
||||
const shouldComment = coverage !== undefined;
|
||||
core.setOutput('shouldComment', shouldComment ? 'true' : 'false');
|
||||
if (shouldComment) {
|
||||
core.setOutput('commentBody', commentBody);
|
||||
}
|
||||
- name: Upsert coverage comment
|
||||
if: steps.pr.outputs.shouldRun == 'true' && steps.coverage.outputs.shouldComment == 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ steps.pr.outputs.prNumber }}
|
||||
COMMENT_BODY: ${{ steps.coverage.outputs.commentBody }}
|
||||
with:
|
||||
script: |
|
||||
const prNumber = Number(process.env.PR_NUMBER);
|
||||
const body = process.env.COMMENT_BODY;
|
||||
const marker = '<!-- codecov-coverage-comment -->';
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const existing = comments.find((comment) => comment.body?.includes(marker));
|
||||
if (existing) {
|
||||
core.info(`Updating existing coverage comment (id: ${existing.id}).`);
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
core.info('Creating new coverage comment.');
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body,
|
||||
});
|
||||
}
|
||||
frontend-bundle-analysis:
|
||||
name: "Frontend Bundle Analysis"
|
||||
runs-on: ubuntu-24.04
|
||||
|
34
uv.lock
generated
34
uv.lock
generated
@@ -3291,25 +3291,25 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.13.1"
|
||||
version = "0.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/33/c8e89216845615d14d2d42ba2bee404e7206a8db782f33400754f3799f05/ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51", size = 5397987, upload-time = "2025-09-18T19:52:44.33Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6e/1a/1f4b722862840295bcaba8c9e5261572347509548faaa99b2d57ee7bfe6a/ruff-0.13.0.tar.gz", hash = "sha256:5b4b1ee7eb35afae128ab94459b13b2baaed282b1fb0f472a73c82c996c8ae60", size = 5372863, upload-time = "2025-09-10T16:25:37.917Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/41/ca37e340938f45cfb8557a97a5c347e718ef34702546b174e5300dbb1f28/ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b", size = 12304308, upload-time = "2025-09-18T19:51:56.253Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/84/ba378ef4129415066c3e1c80d84e539a0d52feb250685091f874804f28af/ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334", size = 12937258, upload-time = "2025-09-18T19:52:00.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/b6/ec5e4559ae0ad955515c176910d6d7c93edcbc0ed1a3195a41179c58431d/ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae", size = 12214554, upload-time = "2025-09-18T19:52:02.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/d6/cb3e3b4f03b9b0c4d4d8f06126d34b3394f6b4d764912fe80a1300696ef6/ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e", size = 12448181, upload-time = "2025-09-18T19:52:05.279Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/ea/bf60cb46d7ade706a246cd3fb99e4cfe854efa3dfbe530d049c684da24ff/ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389", size = 12104599, upload-time = "2025-09-18T19:52:07.497Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/3e/05f72f4c3d3a69e65d55a13e1dd1ade76c106d8546e7e54501d31f1dc54a/ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c", size = 13791178, upload-time = "2025-09-18T19:52:10.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/e7/01b1fc403dd45d6cfe600725270ecc6a8f8a48a55bc6521ad820ed3ceaf8/ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0", size = 14814474, upload-time = "2025-09-18T19:52:12.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/92/d9e183d4ed6185a8df2ce9faa3f22e80e95b5f88d9cc3d86a6d94331da3f/ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36", size = 14217531, upload-time = "2025-09-18T19:52:15.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/4a/6ddb1b11d60888be224d721e01bdd2d81faaf1720592858ab8bac3600466/ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38", size = 13265267, upload-time = "2025-09-18T19:52:17.649Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/98/3f1d18a8d9ea33ef2ad508f0417fcb182c99b23258ec5e53d15db8289809/ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a", size = 13243120, upload-time = "2025-09-18T19:52:20.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/86/b6ce62ce9c12765fa6c65078d1938d2490b2b1d9273d0de384952b43c490/ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783", size = 13443084, upload-time = "2025-09-18T19:52:23.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/6e/af7943466a41338d04503fb5a81b2fd07251bd272f546622e5b1599a7976/ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a", size = 12295105, upload-time = "2025-09-18T19:52:25.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/97/0249b9a24f0f3ebd12f007e81c87cec6d311de566885e9309fcbac5b24cc/ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700", size = 12072284, upload-time = "2025-09-18T19:52:27.478Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/85/0b64693b2c99d62ae65236ef74508ba39c3febd01466ef7f354885e5050c/ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae", size = 12970314, upload-time = "2025-09-18T19:52:30.212Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/fc/342e9f28179915d28b3747b7654f932ca472afbf7090fc0c4011e802f494/ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317", size = 13422360, upload-time = "2025-09-18T19:52:32.676Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/fe/6f87b419dbe166fd30a991390221f14c5b68946f389ea07913e1719741e0/ruff-0.13.0-py3-none-linux_armv6l.whl", hash = "sha256:137f3d65d58ee828ae136a12d1dc33d992773d8f7644bc6b82714570f31b2004", size = 12187826, upload-time = "2025-09-10T16:24:39.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/25/c92296b1fc36d2499e12b74a3fdb230f77af7bdf048fad7b0a62e94ed56a/ruff-0.13.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:21ae48151b66e71fd111b7d79f9ad358814ed58c339631450c66a4be33cc28b9", size = 12933428, upload-time = "2025-09-10T16:24:43.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/cf/40bc7221a949470307d9c35b4ef5810c294e6cfa3caafb57d882731a9f42/ruff-0.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:64de45f4ca5441209e41742d527944635a05a6e7c05798904f39c85bafa819e3", size = 12095543, upload-time = "2025-09-10T16:24:46.638Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/03/8b5ff2a211efb68c63a1d03d157e924997ada87d01bebffbd13a0f3fcdeb/ruff-0.13.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b2c653ae9b9d46e0ef62fc6fbf5b979bda20a0b1d2b22f8f7eb0cde9f4963b8", size = 12312489, upload-time = "2025-09-10T16:24:49.556Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/fc/2336ef6d5e9c8d8ea8305c5f91e767d795cd4fc171a6d97ef38a5302dadc/ruff-0.13.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cec632534332062bc9eb5884a267b689085a1afea9801bf94e3ba7498a2d207", size = 11991631, upload-time = "2025-09-10T16:24:53.439Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/7f/f6d574d100fca83d32637d7f5541bea2f5e473c40020bbc7fc4a4d5b7294/ruff-0.13.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd628101d9f7d122e120ac7c17e0a0f468b19bc925501dbe03c1cb7f5415b24", size = 13720602, upload-time = "2025-09-10T16:24:56.392Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/c8/a8a5b81d8729b5d1f663348d11e2a9d65a7a9bd3c399763b1a51c72be1ce/ruff-0.13.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afe37db8e1466acb173bb2a39ca92df00570e0fd7c94c72d87b51b21bb63efea", size = 14697751, upload-time = "2025-09-10T16:24:59.89Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/f5/183ec292272ce7ec5e882aea74937f7288e88ecb500198b832c24debc6d3/ruff-0.13.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f96a8d90bb258d7d3358b372905fe7333aaacf6c39e2408b9f8ba181f4b6ef2", size = 14095317, upload-time = "2025-09-10T16:25:03.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/8d/7f9771c971724701af7926c14dab31754e7b303d127b0d3f01116faef456/ruff-0.13.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b5e3d883e4f924c5298e3f2ee0f3085819c14f68d1e5b6715597681433f153", size = 13144418, upload-time = "2025-09-10T16:25:06.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/a6/7985ad1778e60922d4bef546688cd8a25822c58873e9ff30189cfe5dc4ab/ruff-0.13.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03447f3d18479df3d24917a92d768a89f873a7181a064858ea90a804a7538991", size = 13370843, upload-time = "2025-09-10T16:25:09.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/1c/bafdd5a7a05a50cc51d9f5711da704942d8dd62df3d8c70c311e98ce9f8a/ruff-0.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fbc6b1934eb1c0033da427c805e27d164bb713f8e273a024a7e86176d7f462cf", size = 13321891, upload-time = "2025-09-10T16:25:12.969Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/3e/7817f989cb9725ef7e8d2cee74186bf90555279e119de50c750c4b7a72fe/ruff-0.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8ab6a3e03665d39d4a25ee199d207a488724f022db0e1fe4002968abdb8001b", size = 12119119, upload-time = "2025-09-10T16:25:16.621Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/07/9df080742e8d1080e60c426dce6e96a8faf9a371e2ce22eef662e3839c95/ruff-0.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2a5c62f8ccc6dd2fe259917482de7275cecc86141ee10432727c4816235bc41", size = 11961594, upload-time = "2025-09-10T16:25:19.49Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/f4/ae1185349197d26a2316840cb4d6c3fba61d4ac36ed728bf0228b222d71f/ruff-0.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b7b85ca27aeeb1ab421bc787009831cffe6048faae08ad80867edab9f2760945", size = 12933377, upload-time = "2025-09-10T16:25:22.371Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/39/e776c10a3b349fc8209a905bfb327831d7516f6058339a613a8d2aaecacd/ruff-0.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:79ea0c44a3032af768cabfd9616e44c24303af49d633b43e3a5096e009ebe823", size = 13418555, upload-time = "2025-09-10T16:25:25.681Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
Reference in New Issue
Block a user