mirror of
https://github.com/astral-sh/ruff-action.git
synced 2026-08-08 10:17:03 +00:00
Compare commits
85
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12172e3cfe | ||
|
|
0e7e01552a | ||
|
|
03f689c6c5 | ||
|
|
5ad29210b1 | ||
|
|
e46bd63c5f | ||
|
|
bfff570296 | ||
|
|
6576144cee | ||
|
|
7af5f13130 | ||
|
|
c48d3eaf66 | ||
|
|
f18f95d449 | ||
|
|
15a82c0309 | ||
|
|
5071c37a33 | ||
|
|
c397d0cf88 | ||
|
|
16fb2a8927 | ||
|
|
421486e990 | ||
|
|
6d8460e4e6 | ||
|
|
74b1866cee | ||
|
|
fbc7e4525b | ||
|
|
57714a7c8a | ||
|
|
f46fd74e35 | ||
|
|
6afcb280fd | ||
|
|
232c1eca56 | ||
|
|
6b45781b82 | ||
|
|
a099b88c70 | ||
|
|
b63c059bc7 | ||
|
|
0c50076f12 | ||
|
|
966d2455d2 | ||
|
|
b9e360afba | ||
|
|
2b785e352d | ||
|
|
8c3aa952c0 | ||
|
|
52ac3ada91 | ||
|
|
813f619ec2 | ||
|
|
110a15d08b | ||
|
|
6228face02 | ||
|
|
eaf0ecdd66 | ||
|
|
78b7b9921e | ||
|
|
9b2efb7fd8 | ||
|
|
da0c801a2d | ||
|
|
257550e917 | ||
|
|
94b3590e87 | ||
|
|
7a9e8edffe | ||
|
|
84f83ecf9e | ||
|
|
ea12b8ac41 | ||
|
|
b259b0b299 | ||
|
|
c6bea5606c | ||
|
|
400ab365db | ||
|
|
719c6e52c1 | ||
|
|
9a7114796b | ||
|
|
07c64b80af | ||
|
|
f800ba97f6 | ||
|
|
fd7f7e1868 | ||
|
|
faac838a0b | ||
|
|
94d4d711a6 | ||
|
|
9828f49eb4 | ||
|
|
8bfebd2812 | ||
|
|
b08b0d12d5 | ||
|
|
aecd4a32f6 | ||
|
|
8b3439f688 | ||
|
|
708db45010 | ||
|
|
9bdf1198e7 | ||
|
|
736d6639d0 | ||
|
|
bba8eb68c5 | ||
|
|
140ba44f2c | ||
|
|
5b3aacfc8f | ||
|
|
b11c1acffb | ||
|
|
19e5e84cff | ||
|
|
39f75e526a | ||
|
|
e0aedf576b | ||
|
|
160796f1a1 | ||
|
|
a7b1296fb5 | ||
|
|
097d5252c8 | ||
|
|
5f8cbe30d7 | ||
|
|
d8f577dec4 | ||
|
|
f173943ec8 | ||
|
|
d34edb0565 | ||
|
|
f14634c415 | ||
|
|
47de3deae8 | ||
|
|
d8281c74d4 | ||
|
|
a634044659 | ||
|
|
2993ff4a65 | ||
|
|
20a3b171f4 | ||
|
|
1c1aef9e3d | ||
|
|
0ceb04d9a0 | ||
|
|
18db80c954 | ||
|
|
0a5dfb89f1 |
@@ -4,12 +4,12 @@
|
||||
"owner": "ruff-check",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^Error: (.*):(\\d+):(\\d+): (\\w+) (.*)$",
|
||||
"code": 4,
|
||||
"column": 3,
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"code": 4,
|
||||
"message": 5
|
||||
"message": 5,
|
||||
"regexp": "^Error: (.*):(\\d+):(\\d+): (\\w+) (.*)$"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"owner": "ruff-format",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(Would reformat):\\s*(.+)$",
|
||||
"file": 2,
|
||||
"message": 1,
|
||||
"file": 2
|
||||
"regexp": "^(Would reformat):\\s*(.+)$"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
interface WorkflowJob {
|
||||
needs?: string[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface Workflow {
|
||||
jobs: Record<string, WorkflowJob>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const workflow = yaml.load(
|
||||
fs.readFileSync("../workflows/test.yml", "utf8"),
|
||||
) as Workflow;
|
||||
const jobs = Object.keys(workflow.jobs);
|
||||
const allTestsPassed = workflow.jobs["all-tests-passed"];
|
||||
const needs: string[] = allTestsPassed.needs || [];
|
||||
|
||||
const expectedNeeds = jobs.filter((j) => j !== "all-tests-passed");
|
||||
const missing = expectedNeeds.filter((j) => !needs.includes(j));
|
||||
|
||||
if (missing.length > 0) {
|
||||
console.error(
|
||||
`Missing jobs in all-tests-passed needs: ${missing.join(", ")}`,
|
||||
);
|
||||
console.info(
|
||||
"Please add the missing jobs to the needs section of all-tests-passed in test.yml.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(
|
||||
"All jobs in test.yml are in the needs section of all-tests-passed.",
|
||||
);
|
||||
@@ -1,49 +0,0 @@
|
||||
# `dist/index.js` is a special file in Actions.
|
||||
# When you reference an action with `uses:` in a workflow,
|
||||
# `index.js` is the code that will run.
|
||||
# For our project, we generate this file through a build process from other source files.
|
||||
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
||||
name: Check dist/
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-dist:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Rebuild the dist/ directory
|
||||
run: |
|
||||
npm run build
|
||||
npm run package
|
||||
|
||||
- name: Compare the expected and actual dist/ directories
|
||||
run: |
|
||||
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
||||
echo "Detected uncommitted changes after build. See status below:"
|
||||
git diff --text -v
|
||||
exit 1
|
||||
fi
|
||||
id: diff
|
||||
|
||||
# If index.js was different than expected, upload the expected version as an artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
@@ -12,6 +12,7 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
@@ -38,11 +39,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
source-root: src
|
||||
@@ -54,7 +57,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -68,4 +71,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
|
||||
@@ -3,17 +3,20 @@ name: Release Drafter
|
||||
|
||||
# yamllint disable-line rule:truthy
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
name: ✏️ Draft release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: 🚀 Run Release Drafter
|
||||
uses: release-drafter/release-drafter@v6.0.0
|
||||
uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
+238
-34
@@ -1,7 +1,7 @@
|
||||
name: "test"
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -10,51 +10,106 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Actionlint
|
||||
uses: eifinger/actionlint-action@8ebeb40569f3c15ca66fe36d1b75f9b70c6c4f6e # v1.9.0
|
||||
- uses: actions/setup-node@v4
|
||||
uses: eifinger/actionlint-action@03ff1f78c0670b71017616a37170f327df932030 # v1.9.2
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: "20"
|
||||
- run: |
|
||||
npm install
|
||||
- run: |
|
||||
npm run all
|
||||
- name: Check all jobs are in all-tests-passed.needs
|
||||
run: |
|
||||
tsc check-all-tests-passed-needs.ts
|
||||
node check-all-tests-passed-needs.js
|
||||
working-directory: .github/scripts
|
||||
- name: Make sure no changes from linters are detected
|
||||
run: |
|
||||
git diff --exit-code
|
||||
git diff --exit-code || (echo "::error::Please run 'npm run all' to fix the issues" && exit 1)
|
||||
test-latest-version:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, macos-14, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use latest version
|
||||
uses: ./
|
||||
with:
|
||||
src: __tests__/fixtures/python-project
|
||||
version: latest
|
||||
test-specific-version:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
ruff-version: ["0.7.4", "0.7", "0.7.x", ">=0.7.0"]
|
||||
ruff-version: ["0.1.7", "0.1.8", "0.4.7", "0.4.10", "0.7", "0.7.x", ">=0.7.0"]
|
||||
os: [ ubuntu-latest, ubuntu-24.04-arm, macos-latest, macos-14, windows-latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use version ${{ matrix.ruff-version }}
|
||||
uses: ./
|
||||
with:
|
||||
version: ${{ matrix.ruff-version }}
|
||||
src: __tests__/fixtures/python-project
|
||||
test-unsupported-version:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Try install old version
|
||||
id: install
|
||||
continue-on-error: true
|
||||
uses: ./
|
||||
with:
|
||||
version: 0.0.246
|
||||
src: __tests__/fixtures/python-project
|
||||
- name: Check if the action failed
|
||||
run: |
|
||||
if [ "${INSTALL_OUTCOME}" == "success" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
INSTALL_OUTCOME: ${{ steps.install.outcome }}
|
||||
test-version-from-version-file-pyproject:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use version from pyproject.toml
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
with:
|
||||
src: __tests__/fixtures/python-project
|
||||
version-file: __tests__/fixtures/pyproject.toml
|
||||
- name: Correct version gets installed
|
||||
run: |
|
||||
if [ "$RUFF_VERSION" != "0.9.3" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-default-version-from-pyproject:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version from pyproject.toml
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
@@ -70,7 +125,9 @@ jobs:
|
||||
test-default-version-from-pyproject-dev-group:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version from pyproject.toml dev group
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
@@ -84,10 +141,107 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-default-version-from-pyproject-dependency-groups:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version from pyproject.toml dependency groups
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
with:
|
||||
src: __tests__/fixtures/pyproject-dependency-groups-project
|
||||
version-file: __tests__/fixtures/pyproject-dependency-groups-project/pyproject.toml
|
||||
- name: Correct version gets installed
|
||||
run: |
|
||||
if [ "$RUFF_VERSION" != "0.8.3" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-default-version-from-pyproject-poetry-groups:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version from pyproject.toml poetry group dependencies
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
with:
|
||||
src: __tests__/fixtures/pyproject-dependency-poetry-grouped-project
|
||||
version-file: __tests__/fixtures/pyproject-dependency-poetry-grouped-project/pyproject.toml
|
||||
- name: Correct version gets installed
|
||||
run: |
|
||||
if [ "$RUFF_VERSION" != "0.8.3" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-default-version-from-pyproject-poetry:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version from pyproject.toml poetry dependencies
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
with:
|
||||
src: __tests__/fixtures/pyproject-dependency-poetry-project
|
||||
version-file: __tests__/fixtures/pyproject-dependency-poetry-project/pyproject.toml
|
||||
- name: Correct version gets installed
|
||||
run: |
|
||||
if [ "$RUFF_VERSION" != "0.8.6" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-default-version-from-pyproject-optional-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version from pyproject.toml optional dependencies
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
with:
|
||||
src: __tests__/fixtures/pyproject-optional-dependencies-project
|
||||
version-file: __tests__/fixtures/pyproject-optional-dependencies-project/pyproject.toml
|
||||
- name: Correct version gets installed
|
||||
run: |
|
||||
if [ "$RUFF_VERSION" != "0.8.3" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-default-version-from-requirements:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version from requirements.txt
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
with:
|
||||
src: __tests__/fixtures/python-project
|
||||
version-file: __tests__/fixtures/requirements.txt
|
||||
- name: Correct version gets installed
|
||||
run: |
|
||||
if [ "$RUFF_VERSION" != "0.9.0" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-semver-range:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use version 0.7
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
@@ -101,31 +255,50 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-pep440-version-specifier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install version 0.9.10
|
||||
id: ruff-action
|
||||
uses: ./
|
||||
with:
|
||||
version: ">=0.9.9,<0.10.0"
|
||||
src: __tests__/fixtures/python-project
|
||||
- name: Correct version gets installed
|
||||
run: |
|
||||
if [ "$RUFF_VERSION" != "0.9.10" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }}
|
||||
test-checksum:
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.inputs.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
checksum:
|
||||
["0de731c669b9ece77e799ac3f4a160c30849752714d9775c94cc4cfaf326860c"]
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
inputs:
|
||||
- os: ubuntu-latest
|
||||
checksum: "0de731c669b9ece77e799ac3f4a160c30849752714d9775c94cc4cfaf326860c"
|
||||
include:
|
||||
- os: macos-latest
|
||||
checksum: "af9583bff12afbca5d5670334e0187dd60c4d91bc71317d1b2dde70cb1200ba9"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Checksum matches expected
|
||||
uses: ./
|
||||
with:
|
||||
version: "0.7.4"
|
||||
checksum: ${{ matrix.checksum }}
|
||||
checksum: ${{ matrix.inputs.checksum }}
|
||||
src: __tests__/fixtures/python-project
|
||||
test-with-explicit-token:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use default version
|
||||
uses: ./
|
||||
with:
|
||||
@@ -137,7 +310,9 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use args
|
||||
uses: ./
|
||||
with:
|
||||
@@ -146,7 +321,9 @@ jobs:
|
||||
test-failure:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Format should fail
|
||||
id: format-should-fail
|
||||
continue-on-error: true
|
||||
@@ -156,9 +333,11 @@ jobs:
|
||||
src: __tests__/fixtures/malformed-python-project
|
||||
- name: Check if the action failed
|
||||
run: |
|
||||
if [ ${{ steps.format-should-fail.outcome }} == "success" ]; then
|
||||
if [ "${FORMAT_SHOULD_FAIL_OUTCOME}" == "success" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
FORMAT_SHOULD_FAIL_OUTCOME: ${{ steps.format-should-fail.outcome }}
|
||||
- name: Check should fail
|
||||
id: check-should-fail
|
||||
continue-on-error: true
|
||||
@@ -167,16 +346,20 @@ jobs:
|
||||
src: __tests__/fixtures/malformed-python-project
|
||||
- name: Check if the action failed
|
||||
run: |
|
||||
if [ ${{ steps.check-should-fail.outcome }} == "success" ]; then
|
||||
if [ "${CHECK_SHOULD_FAIL_OUTCOME}" == "success" ]; then
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
CHECK_SHOULD_FAIL_OUTCOME: ${{ steps.check-should-fail.outcome }}
|
||||
test-multiple-src:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use args
|
||||
uses: ./
|
||||
with:
|
||||
@@ -184,12 +367,33 @@ jobs:
|
||||
src: >-
|
||||
__tests__/fixtures/python-project/src/python_project/__init__.py
|
||||
__tests__/fixtures/python-project/src/python_project/hello_world.py
|
||||
test-exit-zero:
|
||||
|
||||
all-tests-passed:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- lint
|
||||
- test-latest-version
|
||||
- test-specific-version
|
||||
- test-unsupported-version
|
||||
- test-version-from-version-file-pyproject
|
||||
- test-default-version-from-pyproject
|
||||
- test-default-version-from-pyproject-dev-group
|
||||
- test-default-version-from-pyproject-dependency-groups
|
||||
- test-default-version-from-pyproject-poetry-groups
|
||||
- test-default-version-from-pyproject-poetry
|
||||
- test-default-version-from-pyproject-optional-dependencies
|
||||
- test-default-version-from-requirements
|
||||
- test-semver-range
|
||||
- test-pep440-version-specifier
|
||||
- test-checksum
|
||||
- test-with-explicit-token
|
||||
- test-args
|
||||
- test-failure
|
||||
- test-multiple-src
|
||||
if: always()
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Debug this
|
||||
uses: ./
|
||||
with:
|
||||
args: "--exit-zero"
|
||||
src: __tests__/fixtures/python-project
|
||||
- name: All tests passed
|
||||
run: |
|
||||
echo "All jobs passed: ${{ !(contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}"
|
||||
# shellcheck disable=SC2242
|
||||
exit ${{ (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) && 1 || 0 }}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
name: "Update known checksums"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 4 * * *" # Run every day at 4am UTC
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
with:
|
||||
node-version: "20"
|
||||
- name: Update known checksums
|
||||
@@ -17,7 +24,7 @@ jobs:
|
||||
src/download/checksum/known-checksums.ts ${{ secrets.GITHUB_TOKEN }}
|
||||
- run: npm install && npm run all
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
commit-message: "chore: update known checksums"
|
||||
title:
|
||||
|
||||
@@ -13,7 +13,38 @@ jobs:
|
||||
update_major_minor_tags:
|
||||
name: Make sure major and minor tags are up to date on a patch release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run Update semver
|
||||
uses: haya14busa/action-update-semver@v1.2.1
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Update Major Minor Tags
|
||||
run: |
|
||||
set -x
|
||||
|
||||
cd "${GITHUB_WORKSPACE}" || exit
|
||||
|
||||
# Set up variables.
|
||||
TAG="${GITHUB_REF#refs/tags/}" # v1.2.3
|
||||
MINOR="${TAG%.*}" # v1.2
|
||||
MAJOR="${MINOR%.*}" # v1
|
||||
|
||||
if [ "${GITHUB_REF}" = "${TAG}" ]; then
|
||||
echo "This workflow is not triggered by tag push: GITHUB_REF=${GITHUB_REF}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MESSAGE="Release ${TAG}"
|
||||
|
||||
# Set up Git.
|
||||
git config user.name "${GITHUB_ACTOR}"
|
||||
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
||||
|
||||
# Update MAJOR/MINOR tag
|
||||
git tag -fa "${MAJOR}" -m "${MESSAGE}"
|
||||
git tag -fa "${MINOR}" -m "${MESSAGE}"
|
||||
|
||||
# Push
|
||||
git push --force origin "${MINOR}"
|
||||
git push --force origin "${MAJOR}"
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["biomejs.biome"]
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.action.useSortedAttributes.biome": "explicit",
|
||||
"source.action.useSortedKeys.biome": "explicit",
|
||||
"source.fixAll.biome": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true,
|
||||
"explorer.excludeGitIgnore": false,
|
||||
"search.defaultViewMode": "list",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true
|
||||
},
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
@@ -4,8 +4,8 @@ A GitHub Action to run [ruff](https://github.com/astral-sh/ruff).
|
||||
|
||||
This action is commonly used as a pass/fail test to ensure your repository stays
|
||||
clean, abiding the [rules](https://docs.astral.sh/ruff/rules/) specified in your
|
||||
configuration. Though it runs `ruff`, the action can do anything `ruff` can (ex,
|
||||
fix).
|
||||
configuration. Though it runs `ruff check` by default, the action can do
|
||||
anything `ruff` can (ex, fix).
|
||||
|
||||
## Contents
|
||||
|
||||
@@ -16,9 +16,9 @@ fix).
|
||||
- [Use to install ruff](#use-to-install-ruff)
|
||||
- [Use `ruff format`](#use-ruff-format)
|
||||
- [Install specific versions](#install-specific-versions)
|
||||
- [Install the latest version (default)](#install-the-latest-version-default)
|
||||
- [Install the latest version](#install-the-latest-version)
|
||||
- [Install a specific version](#install-a-specific-version)
|
||||
- [Install a version by supplying a semver range](#install-a-version-by-supplying-a-semver-range)
|
||||
- [Install a version by supplying a semver range or pep440 specifier](#install-a-version-by-supplying-a-semver-range-or-pep440-specifier)
|
||||
- [Install a version from a specified version file](#install-a-version-from-a-specified-version-file)
|
||||
- [Validate checksum](#validate-checksum)
|
||||
- [GitHub authentication token](#github-authentication-token)
|
||||
@@ -69,19 +69,34 @@ This action adds ruff to the PATH, so you can use it in subsequent steps.
|
||||
- run: ruff format
|
||||
```
|
||||
|
||||
By default, this action runs `ruff check` after installation.
|
||||
If you do not want to run any `ruff` command but only install it,
|
||||
you can use the `args` input to overwrite the default value (`check`):
|
||||
|
||||
```yaml
|
||||
- name: Install ruff without running check or format
|
||||
uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
args: "--version"
|
||||
```
|
||||
|
||||
### Use `ruff format`
|
||||
|
||||
```yaml
|
||||
- uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
args: "format --check"
|
||||
args: "format --check --diff"
|
||||
```
|
||||
|
||||
### Install specific versions
|
||||
|
||||
By default this action looks for a pyproject.toml file in the root of the repository to determine
|
||||
the ruff version to install. If no pyproject.toml file is found, or no ruff version is defined in
|
||||
either `dependencies` or `dependency-groups.dev` the latest version is installed.
|
||||
`project.dependencies`, `project.optional-dependencies`, or `dependency-groups`,
|
||||
the latest version is installed.
|
||||
|
||||
> [!NOTE]
|
||||
> This action does only support ruff versions v0.0.247 and above.
|
||||
|
||||
#### Install the latest version
|
||||
|
||||
@@ -101,9 +116,10 @@ either `dependencies` or `dependency-groups.dev` the latest version is installed
|
||||
version: "0.4.4"
|
||||
```
|
||||
|
||||
#### Install a version by supplying a semver range
|
||||
#### Install a version by supplying a semver range or pep440 specifier
|
||||
|
||||
You can specify a [semver range](https://github.com/npm/node-semver?tab=readme-ov-file#ranges)
|
||||
or [pep440 specifier](https://peps.python.org/pep-0440/#version-specifiers)
|
||||
to install the latest version that satisfies the range.
|
||||
|
||||
```yaml
|
||||
@@ -120,16 +136,23 @@ to install the latest version that satisfies the range.
|
||||
version: "0.4.x"
|
||||
```
|
||||
|
||||
```yaml
|
||||
- name: Install a pep440-specifier-satisfying version of ruff
|
||||
uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
version: ">=0.11.10,<0.12.0"
|
||||
```
|
||||
|
||||
#### Install a version from a specified version file
|
||||
|
||||
You can specify a file to read the version from.
|
||||
Currently `pyproject.toml` is supported.
|
||||
Currently `pyproject.toml` and `requirements.txt` are supported.
|
||||
|
||||
```yaml
|
||||
- name: Install a version from a specified version file
|
||||
uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
version-file: "my-path/to/pyproject.toml"
|
||||
version-file: "my-path/to/pyproject.toml-or-requirements.txt"
|
||||
```
|
||||
|
||||
### Validate checksum
|
||||
@@ -163,6 +186,15 @@ are not sufficient, you can provide a custom GitHub token with the necessary per
|
||||
github-token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
## Reliability
|
||||
|
||||
This action includes built-in retry mechanisms to handle intermittent network issues that can occur when downloading Ruff or fetching version information from GitHub's API. The action will automatically retry failed operations up to 3 times with exponential backoff, which significantly reduces failure rates due to temporary connectivity issues.
|
||||
|
||||
- **GitHub API calls**: 30-second timeout with 3 retries
|
||||
- **Binary downloads**: 60-second timeout with 3 retries
|
||||
- **Smart error detection**: Automatically identifies retryable errors (timeouts, network issues, 5xx HTTP responses)
|
||||
- **Detailed logging**: Provides comprehensive error information for debugging
|
||||
|
||||
## Outputs
|
||||
|
||||
| Output | Description |
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[project]
|
||||
name = "pyproject-dependency-groups-project"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
{ include-group = "docs" },
|
||||
{ include-group = "lint" },
|
||||
]
|
||||
docs = [
|
||||
"sphinx",
|
||||
]
|
||||
lint = [
|
||||
"ruff==0.8.3",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def hello() -> str:
|
||||
return "Hello from python-project!"
|
||||
+1
@@ -0,0 +1 @@
|
||||
print("Hello world!")
|
||||
@@ -0,0 +1,14 @@
|
||||
[project]
|
||||
name = "pyproject-dependency-poetry-grouped-project"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
sphinx = "*"
|
||||
ruff = "0.8.3"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def hello() -> str:
|
||||
return "Hello from python-project!"
|
||||
+1
@@ -0,0 +1 @@
|
||||
print("Hello world!")
|
||||
@@ -0,0 +1,14 @@
|
||||
[project]
|
||||
name = "pyproject-dependency-poetry-project"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
sphinx = "*"
|
||||
ruff = "~0.8.2"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def hello() -> str:
|
||||
return "Hello from python-project!"
|
||||
+1
@@ -0,0 +1 @@
|
||||
print("Hello world!")
|
||||
@@ -0,0 +1,15 @@
|
||||
[project]
|
||||
name = "pyproject-optional-dependencies-project"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[project.optional-dependencies]
|
||||
lint = [
|
||||
"ruff==0.8.3",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def hello() -> str:
|
||||
return "Hello from python-project!"
|
||||
+1
@@ -0,0 +1 @@
|
||||
print("Hello world!")
|
||||
@@ -0,0 +1,13 @@
|
||||
[project]
|
||||
name = "pyython-project"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"ruff==0.9.3",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
@@ -0,0 +1 @@
|
||||
ruff==0.9.0
|
||||
+26
-13
@@ -1,20 +1,34 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.2/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": false
|
||||
"$schema": "https://biomejs.dev/schemas/2.1.4/schema.json",
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on",
|
||||
"useSortedAttributes": "on",
|
||||
"useSortedKeys": "on"
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": ["dist", "lib", "node_modules"]
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/dist",
|
||||
"!**/lib",
|
||||
"!**/node_modules",
|
||||
"!**/package*.json",
|
||||
"!**/known-checksums.*"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space"
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double",
|
||||
"trailingCommas": "all"
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
@@ -22,10 +36,9 @@
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double",
|
||||
"trailingCommas": "all"
|
||||
}
|
||||
"vcs": {
|
||||
"clientKind": "git",
|
||||
"enabled": true,
|
||||
"useIgnoreFile": false
|
||||
}
|
||||
}
|
||||
|
||||
+5938
-4478
File diff suppressed because it is too large
Load Diff
+4982
-4430
File diff suppressed because it is too large
Load Diff
Generated
+197
-201
@@ -11,17 +11,21 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^6.0.0",
|
||||
"@actions/tool-cache": "^2.0.1",
|
||||
"smol-toml": "^1.3.1"
|
||||
"@actions/tool-cache": "^2.0.2",
|
||||
"@octokit/core": "^7.0.3",
|
||||
"@octokit/plugin-paginate-rest": "^13.1.1",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^16.0.0",
|
||||
"@renovatebot/pep440": "^4.1.0",
|
||||
"smol-toml": "^1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@biomejs/biome": "2.1.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^24.2.1",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@vercel/ncc": "^0.38.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"typescript": "^5.6.3"
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
@@ -41,17 +45,6 @@
|
||||
"@actions/io": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.0.tgz",
|
||||
"integrity": "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.2.0",
|
||||
"@octokit/core": "^5.0.1",
|
||||
"@octokit/plugin-paginate-rest": "^9.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
|
||||
@@ -67,24 +60,23 @@
|
||||
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
|
||||
},
|
||||
"node_modules/@actions/tool-cache": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.1.tgz",
|
||||
"integrity": "sha512-iPU+mNwrbA8jodY8eyo/0S/QqCKDajiR8OxWTnSk/SnYg0sj8Hp4QcUEVC1YFpHWXtrfbQrE13Jz4k4HXJQKcA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.2.tgz",
|
||||
"integrity": "sha512-fBhNNOWxuoLxztQebpOaWu6WeVmuwa77Z+DxIZ1B+OYvGkGQon6kTVg6Z32Cb13WCuw0szqonK+hh03mJV7Z6w==",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/exec": "^1.0.0",
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@actions/io": "^1.1.1",
|
||||
"semver": "^6.1.0",
|
||||
"uuid": "^3.3.2"
|
||||
"semver": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/biome": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz",
|
||||
"integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.4.tgz",
|
||||
"integrity": "sha512-QWlrqyxsU0FCebuMnkvBIkxvPqH89afiJzjMl+z67ybutse590jgeaFdDurE9XYtzpjRGTI1tlUZPGWmbKsElA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"bin": {
|
||||
"biome": "bin/biome"
|
||||
},
|
||||
@@ -96,24 +88,25 @@
|
||||
"url": "https://opencollective.com/biome"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@biomejs/cli-darwin-arm64": "1.9.4",
|
||||
"@biomejs/cli-darwin-x64": "1.9.4",
|
||||
"@biomejs/cli-linux-arm64": "1.9.4",
|
||||
"@biomejs/cli-linux-arm64-musl": "1.9.4",
|
||||
"@biomejs/cli-linux-x64": "1.9.4",
|
||||
"@biomejs/cli-linux-x64-musl": "1.9.4",
|
||||
"@biomejs/cli-win32-arm64": "1.9.4",
|
||||
"@biomejs/cli-win32-x64": "1.9.4"
|
||||
"@biomejs/cli-darwin-arm64": "2.1.4",
|
||||
"@biomejs/cli-darwin-x64": "2.1.4",
|
||||
"@biomejs/cli-linux-arm64": "2.1.4",
|
||||
"@biomejs/cli-linux-arm64-musl": "2.1.4",
|
||||
"@biomejs/cli-linux-x64": "2.1.4",
|
||||
"@biomejs/cli-linux-x64-musl": "2.1.4",
|
||||
"@biomejs/cli-win32-arm64": "2.1.4",
|
||||
"@biomejs/cli-win32-x64": "2.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz",
|
||||
"integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.4.tgz",
|
||||
"integrity": "sha512-sCrNENE74I9MV090Wq/9Dg7EhPudx3+5OiSoQOkIe3DLPzFARuL1dOwCWhKCpA3I5RHmbrsbNSRfZwCabwd8Qg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@@ -123,13 +116,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-darwin-x64": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz",
|
||||
"integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.4.tgz",
|
||||
"integrity": "sha512-gOEICJbTCy6iruBywBDcG4X5rHMbqCPs3clh3UQ+hRKlgvJTk4NHWQAyHOXvaLe+AxD1/TNX1jbZeffBJzcrOw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@@ -139,13 +133,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-arm64": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz",
|
||||
"integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.4.tgz",
|
||||
"integrity": "sha512-juhEkdkKR4nbUi5k/KRp1ocGPNWLgFRD4NrHZSveYrD6i98pyvuzmS9yFYgOZa5JhaVqo0HPnci0+YuzSwT2fw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -155,13 +150,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz",
|
||||
"integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.4.tgz",
|
||||
"integrity": "sha512-nYr7H0CyAJPaLupFE2cH16KZmRC5Z9PEftiA2vWxk+CsFkPZQ6dBRdcC6RuS+zJlPc/JOd8xw3uCCt9Pv41WvQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -171,13 +167,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-x64": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz",
|
||||
"integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.4.tgz",
|
||||
"integrity": "sha512-Eoy9ycbhpJVYuR+LskV9s3uyaIkp89+qqgqhGQsWnp/I02Uqg2fXFblHJOpGZR8AxdB9ADy87oFVxn9MpFKUrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -187,13 +184,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz",
|
||||
"integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.4.tgz",
|
||||
"integrity": "sha512-lvwvb2SQQHctHUKvBKptR6PLFCM7JfRjpCCrDaTmvB7EeZ5/dQJPhTYBf36BE/B4CRWR2ZiBLRYhK7hhXBCZAg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -203,13 +201,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-win32-arm64": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz",
|
||||
"integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.4.tgz",
|
||||
"integrity": "sha512-3WRYte7orvyi6TRfIZkDN9Jzoogbv+gSvR+b9VOXUg1We1XrjBg6WljADeVEaKTvOcpVdH0a90TwyOQ6ue4fGw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@@ -219,13 +218,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-win32-x64": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz",
|
||||
"integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.4.tgz",
|
||||
"integrity": "sha512-tBc+W7anBPSFXGAoQW+f/+svkpt8/uXfRwDzN1DvnatkRMt16KIYpEi/iw8u9GahJlFv98kgHcIrSsZHZTR0sw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@@ -243,163 +243,165 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
|
||||
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
|
||||
"integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz",
|
||||
"integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==",
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.3.tgz",
|
||||
"integrity": "sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^4.0.0",
|
||||
"@octokit/graphql": "^7.1.0",
|
||||
"@octokit/request": "^8.3.1",
|
||||
"@octokit/request-error": "^5.1.0",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.1",
|
||||
"@octokit/request": "^10.0.2",
|
||||
"@octokit/request-error": "^7.0.0",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"before-after-hook": "^4.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz",
|
||||
"integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==",
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz",
|
||||
"integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
"@octokit/types": "^14.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz",
|
||||
"integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==",
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz",
|
||||
"integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^8.3.0",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
"@octokit/request": "^10.0.2",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
"integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
|
||||
"version": "25.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
|
||||
"integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz",
|
||||
"integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==",
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz",
|
||||
"integrity": "sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^12.6.0"
|
||||
"@octokit/types": "^14.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": "5"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
|
||||
"version": "20.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
|
||||
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
|
||||
"version": "12.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
|
||||
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^20.0.0"
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
|
||||
"integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.0.0.tgz",
|
||||
"integrity": "sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^12.6.0"
|
||||
"@octokit/types": "^14.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": "5"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
|
||||
"version": "20.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
|
||||
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
|
||||
"version": "12.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
|
||||
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^20.0.0"
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
|
||||
"integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz",
|
||||
"integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^9.0.1",
|
||||
"@octokit/request-error": "^5.1.0",
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
"@octokit/endpoint": "^11.0.0",
|
||||
"@octokit/request-error": "^7.0.0",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"fast-content-type-parse": "^3.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
|
||||
"integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz",
|
||||
"integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
"@octokit/types": "^14.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "13.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz",
|
||||
"integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==",
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
|
||||
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^22.2.0"
|
||||
"@octokit/openapi-types": "^25.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz",
|
||||
"integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==",
|
||||
"node_modules/@renovatebot/pep440": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@renovatebot/pep440/-/pep440-4.1.0.tgz",
|
||||
"integrity": "sha512-mo2RxnOSp78Njt1HmgMwjl6FapP4OyIS8HypJlymCvN7AIV2Xf5PmZfl/E3O1WWZ6IjKrfsEAaPWFMi8tnkq3g==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^20.9.0 || ^22.11.0",
|
||||
"pnpm": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/js-yaml": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
|
||||
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
|
||||
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
"undici-types": "~7.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
|
||||
"dev": true
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vercel/ncc": {
|
||||
"version": "0.38.3",
|
||||
@@ -417,14 +419,26 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
|
||||
"integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
|
||||
"integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
@@ -438,14 +452,6 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
@@ -455,9 +461,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/smol-toml": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz",
|
||||
"integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.1.tgz",
|
||||
"integrity": "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
@@ -474,10 +481,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
|
||||
"integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -487,9 +495,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.28.4",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
|
||||
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
|
||||
"version": "5.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
|
||||
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
@@ -498,30 +507,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"version": "7.10.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
||||
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
||||
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-11
@@ -6,13 +6,11 @@
|
||||
"main": "dist/ruff-action/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"format": "biome format --fix",
|
||||
"format-check": "biome format",
|
||||
"lint": "biome lint --fix",
|
||||
"check": "biome check --write",
|
||||
"package": "ncc build -o dist/ruff-action src/ruff-action.ts && ncc build -o dist/update-known-checksums src/update-known-checksums.ts",
|
||||
"act": "act pull_request -W .github/workflows/test.yml --container-architecture linux/amd64 -s GITHUB_TOKEN=\"$(gh auth token)\"",
|
||||
"update-known-checksums": "RUNNER_TEMP=known_checksums node dist/update-known-checksums/index.js src/download/checksum/known-checksums.ts \"$(gh auth token)\"",
|
||||
"all": "npm run build && npm run format && npm run lint && npm run package"
|
||||
"all": "npm install && npm run build && npm run check && npm run package"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,16 +22,20 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^6.0.0",
|
||||
"@actions/tool-cache": "^2.0.1",
|
||||
"smol-toml": "^1.3.1"
|
||||
"@actions/tool-cache": "^2.0.2",
|
||||
"@octokit/core": "^7.0.3",
|
||||
"@octokit/plugin-paginate-rest": "^13.1.1",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^16.0.0",
|
||||
"@renovatebot/pep440": "^4.1.0",
|
||||
"smol-toml": "^1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@biomejs/biome": "2.1.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^24.2.1",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@vercel/ncc": "^0.38.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"typescript": "^5.6.3"
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as crypto from "node:crypto";
|
||||
import * as fs from "node:fs";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import { KNOWN_CHECKSUMS } from "./known-checksums";
|
||||
import type { Architecture, Platform } from "../../utils/platforms";
|
||||
import { KNOWN_CHECKSUMS } from "./known-checksums";
|
||||
|
||||
export async function validateChecksum(
|
||||
checkSum: string | undefined,
|
||||
@@ -12,7 +12,7 @@ export async function validateChecksum(
|
||||
platform: Platform,
|
||||
version: string,
|
||||
): Promise<void> {
|
||||
let isValid: boolean | undefined = undefined;
|
||||
let isValid: boolean | undefined;
|
||||
if (checkSum !== undefined && checkSum !== "") {
|
||||
isValid = await validateFileCheckSum(downloadPath, checkSum);
|
||||
} else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,11 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import * as tc from "@actions/tool-cache";
|
||||
import {
|
||||
isRetryableError,
|
||||
NonRetryableError,
|
||||
RetryableError,
|
||||
withRetry,
|
||||
} from "../../utils/retry";
|
||||
import { KNOWN_CHECKSUMS } from "./known-checksums";
|
||||
export async function updateChecksums(
|
||||
filePath: string,
|
||||
@@ -60,6 +66,27 @@ async function getOrDownloadChecksum(
|
||||
}
|
||||
|
||||
async function downloadAssetContent(downloadUrl: string): Promise<string> {
|
||||
const downloadPath = await tc.downloadTool(downloadUrl);
|
||||
return await fs.readFile(downloadPath, "utf8");
|
||||
return await withRetry(
|
||||
async () => {
|
||||
try {
|
||||
const downloadPath = await tc.downloadTool(downloadUrl);
|
||||
return await fs.readFile(downloadPath, "utf8");
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (isRetryableError(err)) {
|
||||
throw new RetryableError(
|
||||
`Failed to download checksum file: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`Failed to download checksum file: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ maxRetries: 3, timeoutMs: 30000 },
|
||||
"download checksum file",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import * as core from "@actions/core";
|
||||
import * as tc from "@actions/tool-cache";
|
||||
import * as path from "node:path";
|
||||
import { promises as fs } from "node:fs";
|
||||
import { Octokit } from "@octokit/core";
|
||||
import { paginateRest } from "@octokit/plugin-paginate-rest";
|
||||
import { restEndpointMethods } from "@octokit/plugin-rest-endpoint-methods";
|
||||
import * as pep440 from "@renovatebot/pep440";
|
||||
import * as semver from "semver";
|
||||
import { OWNER, REPO, TOOL_CACHE_NAME } from "../utils/constants";
|
||||
import type { Architecture, Platform } from "../utils/platforms";
|
||||
import {
|
||||
isRetryableError,
|
||||
NonRetryableError,
|
||||
RetryableError,
|
||||
withRetry,
|
||||
} from "../utils/retry";
|
||||
import { validateChecksum } from "./checksum/checksum";
|
||||
import * as github from "@actions/github";
|
||||
|
||||
const PaginatingOctokit = Octokit.plugin(paginateRest, restEndpointMethods);
|
||||
|
||||
export function tryGetFromToolCache(
|
||||
arch: Architecture,
|
||||
@@ -19,7 +31,7 @@ export function tryGetFromToolCache(
|
||||
resolvedVersion = version;
|
||||
}
|
||||
const installedPath = tc.find(TOOL_CACHE_NAME, resolvedVersion, arch);
|
||||
return { version: resolvedVersion, installedPath };
|
||||
return { installedPath, version: resolvedVersion };
|
||||
}
|
||||
|
||||
export async function downloadVersion(
|
||||
@@ -34,16 +46,77 @@ export async function downloadVersion(
|
||||
if (platform === "pc-windows-msvc") {
|
||||
extension = ".zip";
|
||||
}
|
||||
const downloadUrl = `https://github.com/${OWNER}/${REPO}/releases/download/${version}/${artifact}${extension}`;
|
||||
core.info(`Downloading ruff from "${downloadUrl}" ...`);
|
||||
const downloadUrl = constructDownloadUrl(version, platform, arch);
|
||||
core.debug(`Downloading ruff from "${downloadUrl}" ...`);
|
||||
|
||||
const downloadPath = await tc.downloadTool(
|
||||
downloadUrl,
|
||||
undefined,
|
||||
githubToken,
|
||||
const downloadPath = await withRetry(
|
||||
async () => {
|
||||
try {
|
||||
return await tc.downloadTool(downloadUrl, undefined, githubToken);
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (isRetryableError(err)) {
|
||||
throw new RetryableError(
|
||||
`Failed to download ruff binary: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`Failed to download ruff binary: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ maxRetries: 3, timeoutMs: 60000 }, // 60 second timeout for downloads
|
||||
"download ruff binary",
|
||||
);
|
||||
|
||||
core.debug(`Downloaded ruff to "${downloadPath}"`);
|
||||
await validateChecksum(checkSum, downloadPath, arch, platform, version);
|
||||
|
||||
const extractedDir = await extractDownloadedArtifact(
|
||||
version,
|
||||
downloadPath,
|
||||
extension,
|
||||
platform,
|
||||
artifact,
|
||||
);
|
||||
|
||||
const cachedToolDir = await tc.cacheDir(
|
||||
extractedDir,
|
||||
TOOL_CACHE_NAME,
|
||||
version,
|
||||
arch,
|
||||
);
|
||||
return { cachedToolDir, version: version };
|
||||
}
|
||||
|
||||
function constructDownloadUrl(
|
||||
version: string,
|
||||
platform: Platform,
|
||||
arch: Architecture,
|
||||
): string {
|
||||
const artifactVersionSuffix =
|
||||
semver.lte(version, "v0.4.10") && semver.gte(version, "v0.1.8")
|
||||
? `-${version}`
|
||||
: "";
|
||||
const artifact = `ruff${artifactVersionSuffix}-${arch}-${platform}`;
|
||||
let extension = ".tar.gz";
|
||||
if (platform === "pc-windows-msvc") {
|
||||
extension = ".zip";
|
||||
}
|
||||
const versionPrefix = semver.lte(version, "v0.4.10") ? "v" : "";
|
||||
return `https://github.com/${OWNER}/${REPO}/releases/download/${versionPrefix}${version}/${artifact}${extension}`;
|
||||
}
|
||||
|
||||
async function extractDownloadedArtifact(
|
||||
version: string,
|
||||
downloadPath: string,
|
||||
extension: string,
|
||||
platform: Platform,
|
||||
artifact: string,
|
||||
): Promise<string> {
|
||||
let ruffDir: string;
|
||||
if (platform === "pc-windows-msvc") {
|
||||
const fullPathWithExtension = `${downloadPath}${extension}`;
|
||||
@@ -51,22 +124,22 @@ export async function downloadVersion(
|
||||
ruffDir = await tc.extractZip(fullPathWithExtension);
|
||||
// On windows extracting the zip does not create an intermediate directory
|
||||
} else {
|
||||
const extractedDir = await tc.extractTar(downloadPath);
|
||||
ruffDir = path.join(extractedDir, artifact);
|
||||
ruffDir = await tc.extractTar(downloadPath);
|
||||
if (semver.gte(version, "v0.5.0")) {
|
||||
// Since v0.5.0 an intermediate directory is created
|
||||
ruffDir = path.join(ruffDir, artifact);
|
||||
}
|
||||
}
|
||||
const cachedToolDir = await tc.cacheDir(
|
||||
ruffDir,
|
||||
TOOL_CACHE_NAME,
|
||||
version,
|
||||
arch,
|
||||
);
|
||||
return { version: version, cachedToolDir };
|
||||
const files = await fs.readdir(ruffDir);
|
||||
core.debug(`Contents of ${ruffDir}: ${files.join(", ")}`);
|
||||
return ruffDir;
|
||||
}
|
||||
|
||||
export async function resolveVersion(
|
||||
versionInput: string,
|
||||
githubToken: string,
|
||||
): Promise<string> {
|
||||
core.debug(`Resolving ${versionInput}...`);
|
||||
const version =
|
||||
versionInput === "latest"
|
||||
? await getLatestVersion(githubToken)
|
||||
@@ -76,33 +149,182 @@ export async function resolveVersion(
|
||||
return version;
|
||||
}
|
||||
const availableVersions = await getAvailableVersions(githubToken);
|
||||
const resolvedVersion = tc.evaluateVersions(availableVersions, version);
|
||||
if (resolvedVersion === "") {
|
||||
const resolvedVersion = maxSatisfying(availableVersions, version);
|
||||
if (resolvedVersion === undefined) {
|
||||
throw new Error(`No version found for ${version}`);
|
||||
}
|
||||
core.debug(`Resolved version: ${resolvedVersion}`);
|
||||
return resolvedVersion;
|
||||
}
|
||||
|
||||
async function getAvailableVersions(githubToken: string): Promise<string[]> {
|
||||
const octokit = github.getOctokit(githubToken);
|
||||
return await withRetry(
|
||||
async () => {
|
||||
try {
|
||||
const octokit = new PaginatingOctokit({
|
||||
auth: githubToken,
|
||||
});
|
||||
return await getReleaseTagNames(octokit);
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
if (error.message.includes("Bad credentials")) {
|
||||
core.info(
|
||||
"No (valid) GitHub token provided. Falling back to anonymous. Requests might be rate limited.",
|
||||
);
|
||||
const octokit = new PaginatingOctokit();
|
||||
return await getReleaseTagNames(octokit);
|
||||
}
|
||||
|
||||
const response = await octokit.paginate(octokit.rest.repos.listReleases, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
});
|
||||
if (isRetryableError(error)) {
|
||||
throw new RetryableError(
|
||||
`Failed to get available versions: ${error.message}`,
|
||||
error,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`Failed to get available versions: ${error.message}`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ maxRetries: 3, timeoutMs: 30000 }, // 30 second timeout for API calls
|
||||
"get available versions",
|
||||
);
|
||||
}
|
||||
|
||||
async function getReleaseTagNames(
|
||||
octokit: InstanceType<typeof PaginatingOctokit>,
|
||||
): Promise<string[]> {
|
||||
const response = await withRetry(
|
||||
async () => {
|
||||
try {
|
||||
return await octokit.paginate(octokit.rest.repos.listReleases, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (isRetryableError(err)) {
|
||||
throw new RetryableError(
|
||||
`Failed to list GitHub releases: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`Failed to list GitHub releases: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ maxRetries: 3, timeoutMs: 30000 },
|
||||
"list GitHub releases",
|
||||
);
|
||||
|
||||
const releaseTagNames = response.map((release) => release.tag_name);
|
||||
if (releaseTagNames.length === 0) {
|
||||
throw Error(
|
||||
"Github API request failed while getting releases. Check the GitHub status page for outages. Try again later.",
|
||||
);
|
||||
}
|
||||
return response.map((release) => release.tag_name);
|
||||
}
|
||||
|
||||
async function getLatestVersion(githubToken: string) {
|
||||
const octokit = github.getOctokit(githubToken);
|
||||
return await withRetry(
|
||||
async () => {
|
||||
const octokit = new PaginatingOctokit({
|
||||
auth: githubToken,
|
||||
});
|
||||
|
||||
const { data: latestRelease } = await octokit.rest.repos.getLatestRelease({
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
});
|
||||
let latestRelease: { tag_name: string } | undefined;
|
||||
try {
|
||||
latestRelease = await getLatestRelease(octokit);
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
if (error.message.includes("Bad credentials")) {
|
||||
core.info(
|
||||
"No (valid) GitHub token provided. Falling back to anonymous. Requests might be rate limited.",
|
||||
);
|
||||
const octokit = new PaginatingOctokit();
|
||||
latestRelease = await getLatestRelease(octokit);
|
||||
} else {
|
||||
core.error(
|
||||
"Github API request failed while getting latest release. Check the GitHub status page for outages. Try again later.",
|
||||
);
|
||||
|
||||
if (!latestRelease) {
|
||||
throw new Error("Could not determine latest release.");
|
||||
if (isRetryableError(error)) {
|
||||
throw new RetryableError(
|
||||
`Failed to get latest version: ${error.message}`,
|
||||
error,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`Failed to get latest version: ${error.message}`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!latestRelease) {
|
||||
throw new Error("Could not determine latest release.");
|
||||
}
|
||||
return latestRelease.tag_name;
|
||||
},
|
||||
{ maxRetries: 3, timeoutMs: 30000 },
|
||||
"get latest version",
|
||||
);
|
||||
}
|
||||
|
||||
async function getLatestRelease(
|
||||
octokit: InstanceType<typeof PaginatingOctokit>,
|
||||
) {
|
||||
return await withRetry(
|
||||
async () => {
|
||||
try {
|
||||
const { data: latestRelease } =
|
||||
await octokit.rest.repos.getLatestRelease({
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
});
|
||||
return latestRelease;
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (isRetryableError(err)) {
|
||||
throw new RetryableError(
|
||||
`Failed to get latest release: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`Failed to get latest release: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ maxRetries: 3, timeoutMs: 30000 },
|
||||
"get latest release",
|
||||
);
|
||||
}
|
||||
|
||||
function maxSatisfying(
|
||||
versions: string[],
|
||||
version: string,
|
||||
): string | undefined {
|
||||
const maxSemver = tc.evaluateVersions(versions, version);
|
||||
if (maxSemver !== "") {
|
||||
core.debug(`Found a version that satisfies the semver range: ${maxSemver}`);
|
||||
return maxSemver;
|
||||
}
|
||||
return latestRelease.tag_name;
|
||||
const maxPep440 = pep440.maxSatisfying(versions, version);
|
||||
if (maxPep440 !== null) {
|
||||
core.debug(
|
||||
`Found a version that satisfies the pep440 specifier: ${maxPep440}`,
|
||||
);
|
||||
return maxPep440;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
+27
-14
@@ -1,18 +1,13 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import * as core from "@actions/core";
|
||||
import * as exec from "@actions/exec";
|
||||
import * as path from "node:path";
|
||||
import * as semver from "semver";
|
||||
import {
|
||||
downloadVersion,
|
||||
resolveVersion,
|
||||
tryGetFromToolCache,
|
||||
} from "./download/download-version";
|
||||
|
||||
import {
|
||||
type Architecture,
|
||||
getArch,
|
||||
getPlatform,
|
||||
type Platform,
|
||||
} from "./utils/platforms";
|
||||
import {
|
||||
args,
|
||||
checkSum,
|
||||
@@ -21,8 +16,13 @@ import {
|
||||
version,
|
||||
versionFile as versionFileInput,
|
||||
} from "./utils/inputs";
|
||||
import { getRuffVersionFromPyproject } from "./utils/pyproject";
|
||||
import * as fs from "node:fs";
|
||||
import {
|
||||
type Architecture,
|
||||
getArch,
|
||||
getPlatform,
|
||||
type Platform,
|
||||
} from "./utils/platforms";
|
||||
import { getRuffVersionFromRequirementsFile } from "./utils/pyproject";
|
||||
|
||||
async function run(): Promise<void> {
|
||||
const platform = getPlatform();
|
||||
@@ -62,6 +62,11 @@ async function setupRuff(
|
||||
githubToken: string,
|
||||
): Promise<{ ruffDir: string; version: string }> {
|
||||
const resolvedVersion = await determineVersion();
|
||||
if (semver.lt(resolvedVersion, "v0.0.247")) {
|
||||
throw Error(
|
||||
"This action does not support ruff versions older than 0.0.247",
|
||||
);
|
||||
}
|
||||
const toolCacheResult = tryGetFromToolCache(arch, resolvedVersion);
|
||||
if (toolCacheResult.installedPath) {
|
||||
core.info(`Found ruffDir in tool-cache for ${toolCacheResult.version}`);
|
||||
@@ -93,19 +98,27 @@ async function determineVersion(): Promise<string> {
|
||||
return await resolveVersion(version, githubToken);
|
||||
}
|
||||
if (versionFileInput !== "") {
|
||||
const versionFromPyproject = getRuffVersionFromPyproject(versionFileInput);
|
||||
const versionFromPyproject =
|
||||
getRuffVersionFromRequirementsFile(versionFileInput);
|
||||
if (versionFromPyproject === undefined) {
|
||||
core.warning(
|
||||
"Could not parse version from supplied pyproject.toml. Using latest version.",
|
||||
`Could not parse version from ${versionFileInput}. Using latest version.`,
|
||||
);
|
||||
return await resolveVersion("latest", githubToken);
|
||||
}
|
||||
return await resolveVersion(versionFromPyproject || "latest", githubToken);
|
||||
}
|
||||
const pyProjectPath = path.join(src, "pyproject.toml");
|
||||
if (!fs.existsSync(pyProjectPath)) {
|
||||
core.info(`Could not find ${pyProjectPath}. Using latest version.`);
|
||||
return await resolveVersion("latest", githubToken);
|
||||
}
|
||||
const versionFromPyproject = getRuffVersionFromPyproject(pyProjectPath);
|
||||
const versionFromPyproject =
|
||||
getRuffVersionFromRequirementsFile(pyProjectPath);
|
||||
if (versionFromPyproject === undefined) {
|
||||
core.info(
|
||||
`Could not parse version from ${pyProjectPath}. Using latest version.`,
|
||||
);
|
||||
}
|
||||
return await resolveVersion(versionFromPyproject || "latest", githubToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,50 @@
|
||||
import * as github from "@actions/github";
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import { OWNER, REPO } from "./utils/constants";
|
||||
import { Octokit } from "@octokit/core";
|
||||
import { paginateRest } from "@octokit/plugin-paginate-rest";
|
||||
import { restEndpointMethods } from "@octokit/plugin-rest-endpoint-methods";
|
||||
import * as semver from "semver";
|
||||
|
||||
import { updateChecksums } from "./download/checksum/update-known-checksums";
|
||||
import { OWNER, REPO } from "./utils/constants";
|
||||
import {
|
||||
isRetryableError,
|
||||
NonRetryableError,
|
||||
RetryableError,
|
||||
withRetry,
|
||||
} from "./utils/retry";
|
||||
|
||||
const PaginatingOctokit = Octokit.plugin(paginateRest, restEndpointMethods);
|
||||
|
||||
async function run(): Promise<void> {
|
||||
const checksumFilePath = process.argv.slice(2)[0];
|
||||
const github_token = process.argv.slice(2)[1];
|
||||
|
||||
const octokit = github.getOctokit(github_token);
|
||||
const response = await withRetry(
|
||||
async () => {
|
||||
try {
|
||||
const octokit = new PaginatingOctokit({ auth: github_token });
|
||||
return await octokit.paginate(octokit.rest.repos.listReleases, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (isRetryableError(err)) {
|
||||
throw new RetryableError(
|
||||
`Failed to list releases for checksum update: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`Failed to list releases for checksum update: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ maxRetries: 3, timeoutMs: 60000 },
|
||||
"list releases for checksum update",
|
||||
);
|
||||
|
||||
const response = await octokit.paginate(octokit.rest.repos.listReleases, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
});
|
||||
const downloadUrls: string[] = response.flatMap((release) =>
|
||||
release.assets
|
||||
.filter((asset) => asset.name.endsWith(".sha256"))
|
||||
|
||||
@@ -9,9 +9,9 @@ export type Architecture = "i686" | "x86_64" | "aarch64";
|
||||
export function getArch(): Architecture | undefined {
|
||||
const arch = process.arch;
|
||||
const archMapping: { [key: string]: Architecture } = {
|
||||
arm64: "aarch64",
|
||||
ia32: "i686",
|
||||
x64: "x86_64",
|
||||
arm64: "aarch64",
|
||||
};
|
||||
|
||||
if (arch in archMapping) {
|
||||
@@ -22,8 +22,8 @@ export function getArch(): Architecture | undefined {
|
||||
export function getPlatform(): Platform | undefined {
|
||||
const platform = process.platform;
|
||||
const platformMapping: { [key: string]: Platform } = {
|
||||
linux: "unknown-linux-gnu",
|
||||
darwin: "apple-darwin",
|
||||
linux: "unknown-linux-gnu",
|
||||
win32: "pc-windows-msvc",
|
||||
};
|
||||
|
||||
|
||||
+76
-27
@@ -2,34 +2,12 @@ import * as fs from "node:fs";
|
||||
import * as core from "@actions/core";
|
||||
import * as toml from "smol-toml";
|
||||
|
||||
export function getRuffVersionFromPyproject(
|
||||
filePath: string,
|
||||
function getRuffVersionFromAllDependencies(
|
||||
allDependencies: string[],
|
||||
): string | undefined {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
core.warning(`Could not find file: ${filePath}`);
|
||||
return undefined;
|
||||
}
|
||||
const pyprojectContent = fs.readFileSync(filePath, "utf-8");
|
||||
let pyproject:
|
||||
| {
|
||||
project?: { dependencies?: string[] };
|
||||
"dependency-groups"?: { dev?: string[] };
|
||||
}
|
||||
| undefined;
|
||||
try {
|
||||
pyproject = toml.parse(pyprojectContent);
|
||||
} catch (err) {
|
||||
const message = (err as Error).message;
|
||||
core.warning(`Error while parsing ${filePath}: ${message}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const dependencies: string[] = pyproject?.project?.dependencies || [];
|
||||
const devDependencies: string[] = pyproject?.["dependency-groups"]?.dev || [];
|
||||
|
||||
const ruffVersionDefinition =
|
||||
dependencies.find((dep: string) => dep.startsWith("ruff")) ||
|
||||
devDependencies.find((dep: string) => dep.startsWith("ruff"));
|
||||
const ruffVersionDefinition = allDependencies.find((dep: string) =>
|
||||
dep.startsWith("ruff"),
|
||||
);
|
||||
|
||||
if (ruffVersionDefinition) {
|
||||
const ruffVersion = ruffVersionDefinition
|
||||
@@ -44,3 +22,74 @@ export function getRuffVersionFromPyproject(
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
interface Pyproject {
|
||||
project?: {
|
||||
dependencies?: string[];
|
||||
"optional-dependencies"?: Record<string, string[]>;
|
||||
};
|
||||
"dependency-groups"?: Record<string, Array<string | object>>;
|
||||
tool?: {
|
||||
poetry?: {
|
||||
dependencies?: Record<string, string | object>;
|
||||
group?: Record<string, { dependencies: Record<string, string | object> }>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function parsePyproject(pyprojectContent: string): string | undefined {
|
||||
const pyproject: Pyproject = toml.parse(pyprojectContent);
|
||||
const dependencies: string[] = pyproject?.project?.dependencies || [];
|
||||
const optionalDependencies: string[] = Object.values(
|
||||
pyproject?.project?.["optional-dependencies"] || {},
|
||||
).flat();
|
||||
const devDependencies: string[] = Object.values(
|
||||
pyproject?.["dependency-groups"] || {},
|
||||
)
|
||||
.flat()
|
||||
.filter((item: string | object) => typeof item === "string");
|
||||
return (
|
||||
getRuffVersionFromAllDependencies(
|
||||
dependencies.concat(optionalDependencies, devDependencies),
|
||||
) || getRuffVersionFromPoetryGroups(pyproject)
|
||||
);
|
||||
}
|
||||
|
||||
function getRuffVersionFromPoetryGroups(
|
||||
pyproject: Pyproject,
|
||||
): string | undefined {
|
||||
// Special handling for Poetry until it supports PEP 735
|
||||
// See: <https://github.com/python-poetry/poetry/issues/9751>
|
||||
const poetry = pyproject?.tool?.poetry || {};
|
||||
const poetryGroups = Object.values(poetry.group || {});
|
||||
if (poetry.dependencies) {
|
||||
poetryGroups.unshift({ dependencies: poetry.dependencies });
|
||||
}
|
||||
return poetryGroups
|
||||
.flatMap((group) => Object.entries(group.dependencies))
|
||||
.map(([name, spec]) => {
|
||||
if (name === "ruff" && typeof spec === "string") return spec;
|
||||
return undefined;
|
||||
})
|
||||
.find((version) => version !== undefined);
|
||||
}
|
||||
|
||||
export function getRuffVersionFromRequirementsFile(
|
||||
filePath: string,
|
||||
): string | undefined {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
core.warning(`Could not find file: ${filePath}`);
|
||||
return undefined;
|
||||
}
|
||||
const pyprojectContent = fs.readFileSync(filePath, "utf-8");
|
||||
if (filePath.endsWith(".txt")) {
|
||||
return getRuffVersionFromAllDependencies(pyprojectContent.split("\n"));
|
||||
}
|
||||
try {
|
||||
return parsePyproject(pyprojectContent);
|
||||
} catch (err) {
|
||||
const message = (err as Error).message;
|
||||
core.warning(`Error while parsing ${filePath}: ${message}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import * as core from "@actions/core";
|
||||
|
||||
export interface RetryOptions {
|
||||
maxRetries: number;
|
||||
initialDelayMs: number;
|
||||
maxDelayMs: number;
|
||||
backoffMultiplier: number;
|
||||
timeoutMs?: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_RETRY_OPTIONS: RetryOptions = {
|
||||
backoffMultiplier: 2,
|
||||
initialDelayMs: 1000,
|
||||
maxDelayMs: 10000,
|
||||
maxRetries: 3,
|
||||
timeoutMs: 30000, // 30 second timeout
|
||||
};
|
||||
|
||||
export class RetryableError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly cause?: Error,
|
||||
) {
|
||||
super(message);
|
||||
this.name = "RetryableError";
|
||||
}
|
||||
}
|
||||
|
||||
export class NonRetryableError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly cause?: Error,
|
||||
) {
|
||||
super(message);
|
||||
this.name = "NonRetryableError";
|
||||
}
|
||||
}
|
||||
|
||||
export async function withRetry<T>(
|
||||
operation: () => Promise<T>,
|
||||
options: Partial<RetryOptions> = {},
|
||||
operationName = "operation",
|
||||
): Promise<T> {
|
||||
const opts = { ...DEFAULT_RETRY_OPTIONS, ...options };
|
||||
let lastError: Error | undefined;
|
||||
|
||||
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
||||
try {
|
||||
if (attempt > 0) {
|
||||
const delay = Math.min(
|
||||
opts.initialDelayMs * opts.backoffMultiplier ** (attempt - 1),
|
||||
opts.maxDelayMs,
|
||||
);
|
||||
core.info(
|
||||
`Retrying ${operationName} (attempt ${attempt + 1}/${opts.maxRetries + 1}) after ${delay}ms delay...`,
|
||||
);
|
||||
await sleep(delay);
|
||||
}
|
||||
|
||||
// Wrap operation with timeout if specified
|
||||
if (opts.timeoutMs) {
|
||||
return await withTimeout(operation(), opts.timeoutMs, operationName);
|
||||
} else {
|
||||
return await operation();
|
||||
}
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
|
||||
// Don't retry on non-retryable errors
|
||||
if (lastError instanceof NonRetryableError) {
|
||||
core.debug(
|
||||
`Non-retryable error in ${operationName}: ${lastError.message}`,
|
||||
);
|
||||
throw lastError.cause || lastError;
|
||||
}
|
||||
|
||||
// Log the error for debugging
|
||||
core.debug(
|
||||
`Attempt ${attempt + 1} failed for ${operationName}: ${lastError.message}`,
|
||||
);
|
||||
|
||||
// If this was the last attempt, throw the error
|
||||
if (attempt === opts.maxRetries) {
|
||||
core.error(
|
||||
`${operationName} failed after ${opts.maxRetries + 1} attempts. Last error: ${lastError.message}`,
|
||||
);
|
||||
throw lastError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError || new Error(`${operationName} failed for unknown reason`);
|
||||
}
|
||||
|
||||
export async function withTimeout<T>(
|
||||
promise: Promise<T>,
|
||||
timeoutMs: number,
|
||||
operationName = "operation",
|
||||
): Promise<T> {
|
||||
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(
|
||||
new RetryableError(`${operationName} timed out after ${timeoutMs}ms`),
|
||||
);
|
||||
}, timeoutMs);
|
||||
});
|
||||
|
||||
return Promise.race([promise, timeoutPromise]);
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function isRetryableError(error: Error): boolean {
|
||||
const message = error.message.toLowerCase();
|
||||
|
||||
// Network-related errors that should be retried
|
||||
const retryableMessages = [
|
||||
"connect timeout",
|
||||
"connection timeout",
|
||||
"timeout",
|
||||
"econnreset",
|
||||
"econnrefused",
|
||||
"enotfound",
|
||||
"network error",
|
||||
"request timeout",
|
||||
"socket timeout",
|
||||
"fetch failed",
|
||||
"connect etimedout",
|
||||
];
|
||||
|
||||
// HTTP status codes that should be retried
|
||||
const retryableStatusCodes = [408, 429, 500, 502, 503, 504];
|
||||
|
||||
// Check for retryable messages
|
||||
if (retryableMessages.some((msg) => message.includes(msg))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for HTTP status codes in error message
|
||||
const statusMatch = message.match(/status.*?(\d{3})/);
|
||||
if (statusMatch) {
|
||||
const statusCode = parseInt(statusMatch[1], 10);
|
||||
if (retryableStatusCodes.includes(statusCode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function wrapWithRetryLogic<T>(
|
||||
operation: () => Promise<T>,
|
||||
operationName: string,
|
||||
options: Partial<RetryOptions> = {},
|
||||
): () => Promise<T> {
|
||||
return async () => {
|
||||
try {
|
||||
return await withRetry(operation, options, operationName);
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (isRetryableError(err)) {
|
||||
throw new RetryableError(
|
||||
`${operationName} failed: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
throw new NonRetryableError(
|
||||
`${operationName} failed: ${err.message}`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
|
||||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
|
||||
"outDir": "./lib" /* Redirect output structure to the directory. */,
|
||||
"rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
"target": "ES2022" /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user