Compare commits

..
Author SHA1 Message Date
Kevin Stillhammer eef6392152 Test exit-zero 2025-01-26 17:49:55 +01:00
45 changed files with 9320 additions and 13647 deletions
+4 -4
View File
@@ -4,12 +4,12 @@
"owner": "ruff-check", "owner": "ruff-check",
"pattern": [ "pattern": [
{ {
"code": 4, "regexp": "^Error: (.*):(\\d+):(\\d+): (\\w+) (.*)$",
"column": 3,
"file": 1, "file": 1,
"line": 2, "line": 2,
"message": 5, "column": 3,
"regexp": "^Error: (.*):(\\d+):(\\d+): (\\w+) (.*)$" "code": 4,
"message": 5
} }
] ]
} }
+2 -2
View File
@@ -4,9 +4,9 @@
"owner": "ruff-format", "owner": "ruff-format",
"pattern": [ "pattern": [
{ {
"file": 2, "regexp": "^(Would reformat):\\s*(.+)$",
"message": 1, "message": 1,
"regexp": "^(Would reformat):\\s*(.+)$" "file": 2
} }
] ]
} }
@@ -1,35 +0,0 @@
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.",
);
+49
View File
@@ -0,0 +1,49 @@
# `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/
+4 -7
View File
@@ -12,7 +12,6 @@
name: "CodeQL" name: "CodeQL"
on: on:
workflow_dispatch:
push: push:
branches: [main] branches: [main]
pull_request: pull_request:
@@ -39,13 +38,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@v4
with:
persist-credentials: false
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 uses: github/codeql-action/init@v3
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
source-root: src source-root: src
@@ -57,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # 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) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 uses: github/codeql-action/autobuild@v3
# ️ Command-line programs to run using the OS shell. # ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -71,4 +68,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 uses: github/codeql-action/analyze@v3
+2 -5
View File
@@ -3,20 +3,17 @@ name: Release Drafter
# yamllint disable-line rule:truthy # yamllint disable-line rule:truthy
on: on:
workflow_dispatch:
push: push:
branches: branches:
- main - main
workflow_dispatch:
jobs: jobs:
update_release_draft: update_release_draft:
name: ✏️ Draft release name: ✏️ Draft release
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps: steps:
- name: 🚀 Run Release Drafter - name: 🚀 Run Release Drafter
uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 uses: release-drafter/release-drafter@v6.0.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+34 -238
View File
@@ -1,7 +1,7 @@
name: "test" name: "test"
on: on:
workflow_dispatch:
pull_request: pull_request:
workflow_dispatch:
push: push:
branches: branches:
- main - main
@@ -10,106 +10,51 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: {}
jobs: jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Actionlint - name: Actionlint
uses: eifinger/actionlint-action@03ff1f78c0670b71017616a37170f327df932030 # v1.9.2 uses: eifinger/actionlint-action@8ebeb40569f3c15ca66fe36d1b75f9b70c6c4f6e # v1.9.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 - uses: actions/setup-node@v4
with: with:
node-version: "20" node-version: "20"
- run: | - run: |
npm install npm install
- run: | - run: |
npm run all 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 - name: Make sure no changes from linters are detected
run: | run: |
git diff --exit-code || (echo "::error::Please run 'npm run all' to fix the issues" && exit 1) git diff --exit-code
test-latest-version: test-latest-version:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, macos-14, windows-latest] os: [ubuntu-latest, macos-latest, macos-14, windows-latest]
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use latest version - name: Use latest version
uses: ./ uses: ./
with: with:
src: __tests__/fixtures/python-project src: __tests__/fixtures/python-project
version: latest version: latest
test-specific-version: test-specific-version:
runs-on: ${{ matrix.os }} runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
ruff-version: ["0.1.7", "0.1.8", "0.4.7", "0.4.10", "0.7", "0.7.x", ">=0.7.0"] ruff-version: ["0.7.4", "0.7", "0.7.x", ">=0.7.0"]
os: [ ubuntu-latest, ubuntu-24.04-arm, macos-latest, macos-14, windows-latest ]
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use version ${{ matrix.ruff-version }} - name: Use version ${{ matrix.ruff-version }}
uses: ./ uses: ./
with: with:
version: ${{ matrix.ruff-version }} version: ${{ matrix.ruff-version }}
src: __tests__/fixtures/python-project 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: test-default-version-from-pyproject:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use default version from pyproject.toml - name: Use default version from pyproject.toml
id: ruff-action id: ruff-action
uses: ./ uses: ./
@@ -125,9 +70,7 @@ jobs:
test-default-version-from-pyproject-dev-group: test-default-version-from-pyproject-dev-group:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use default version from pyproject.toml dev group - name: Use default version from pyproject.toml dev group
id: ruff-action id: ruff-action
uses: ./ uses: ./
@@ -141,107 +84,10 @@ jobs:
fi fi
env: env:
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }} 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: test-semver-range:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use version 0.7 - name: Use version 0.7
id: ruff-action id: ruff-action
uses: ./ uses: ./
@@ -255,50 +101,31 @@ jobs:
fi fi
env: env:
RUFF_VERSION: ${{ steps.ruff-action.outputs.ruff-version }} 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: test-checksum:
runs-on: ${{ matrix.inputs.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
inputs: os: [ubuntu-latest, macos-latest]
- os: ubuntu-latest checksum:
["0de731c669b9ece77e799ac3f4a160c30849752714d9775c94cc4cfaf326860c"]
exclude:
- os: macos-latest
checksum: "0de731c669b9ece77e799ac3f4a160c30849752714d9775c94cc4cfaf326860c" checksum: "0de731c669b9ece77e799ac3f4a160c30849752714d9775c94cc4cfaf326860c"
include:
- os: macos-latest - os: macos-latest
checksum: "af9583bff12afbca5d5670334e0187dd60c4d91bc71317d1b2dde70cb1200ba9" checksum: "af9583bff12afbca5d5670334e0187dd60c4d91bc71317d1b2dde70cb1200ba9"
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Checksum matches expected - name: Checksum matches expected
uses: ./ uses: ./
with: with:
version: "0.7.4" version: "0.7.4"
checksum: ${{ matrix.inputs.checksum }} checksum: ${{ matrix.checksum }}
src: __tests__/fixtures/python-project src: __tests__/fixtures/python-project
test-with-explicit-token: test-with-explicit-token:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use default version - name: Use default version
uses: ./ uses: ./
with: with:
@@ -310,9 +137,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, windows-latest] os: [ubuntu-latest, windows-latest]
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use args - name: Use args
uses: ./ uses: ./
with: with:
@@ -321,9 +146,7 @@ jobs:
test-failure: test-failure:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Format should fail - name: Format should fail
id: format-should-fail id: format-should-fail
continue-on-error: true continue-on-error: true
@@ -333,11 +156,9 @@ jobs:
src: __tests__/fixtures/malformed-python-project src: __tests__/fixtures/malformed-python-project
- name: Check if the action failed - name: Check if the action failed
run: | run: |
if [ "${FORMAT_SHOULD_FAIL_OUTCOME}" == "success" ]; then if [ ${{ steps.format-should-fail.outcome }} == "success" ]; then
exit 1 exit 1
fi fi
env:
FORMAT_SHOULD_FAIL_OUTCOME: ${{ steps.format-should-fail.outcome }}
- name: Check should fail - name: Check should fail
id: check-should-fail id: check-should-fail
continue-on-error: true continue-on-error: true
@@ -346,20 +167,16 @@ jobs:
src: __tests__/fixtures/malformed-python-project src: __tests__/fixtures/malformed-python-project
- name: Check if the action failed - name: Check if the action failed
run: | run: |
if [ "${CHECK_SHOULD_FAIL_OUTCOME}" == "success" ]; then if [ ${{ steps.check-should-fail.outcome }} == "success" ]; then
exit 1 exit 1
fi fi
env:
CHECK_SHOULD_FAIL_OUTCOME: ${{ steps.check-should-fail.outcome }}
test-multiple-src: test-multiple-src:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ ubuntu-latest, windows-latest ] os: [ ubuntu-latest, windows-latest ]
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use args - name: Use args
uses: ./ uses: ./
with: with:
@@ -367,33 +184,12 @@ jobs:
src: >- src: >-
__tests__/fixtures/python-project/src/python_project/__init__.py __tests__/fixtures/python-project/src/python_project/__init__.py
__tests__/fixtures/python-project/src/python_project/hello_world.py __tests__/fixtures/python-project/src/python_project/hello_world.py
test-exit-zero:
all-tests-passed:
runs-on: ubuntu-latest 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: steps:
- name: All tests passed - uses: actions/checkout@v4
run: | - name: Debug this
echo "All jobs passed: ${{ !(contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}" uses: ./
# shellcheck disable=SC2242 with:
exit ${{ (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) && 1 || 0 }} args: "--exit-zero"
src: __tests__/fixtures/python-project
+3 -10
View File
@@ -1,20 +1,13 @@
name: "Update known checksums" name: "Update known checksums"
on: on:
workflow_dispatch: workflow_dispatch:
schedule:
- cron: "0 4 * * *" # Run every day at 4am UTC
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with: - uses: actions/setup-node@v4
persist-credentials: false
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with: with:
node-version: "20" node-version: "20"
- name: Update known checksums - name: Update known checksums
@@ -24,7 +17,7 @@ jobs:
src/download/checksum/known-checksums.ts ${{ secrets.GITHUB_TOKEN }} src/download/checksum/known-checksums.ts ${{ secrets.GITHUB_TOKEN }}
- run: npm install && npm run all - run: npm install && npm run all
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with: with:
commit-message: "chore: update known checksums" commit-message: "chore: update known checksums"
title: title:
+3 -34
View File
@@ -13,38 +13,7 @@ jobs:
update_major_minor_tags: update_major_minor_tags:
name: Make sure major and minor tags are up to date on a patch release name: Make sure major and minor tags are up to date on a patch release
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with: - name: Run Update semver
persist-credentials: false uses: haya14busa/action-update-semver@v1.2.1
- 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}"
-3
View File
@@ -1,3 +0,0 @@
{
"recommendations": ["biomejs.biome"]
}
-16
View File
@@ -1,16 +0,0 @@
{
"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"
}
+9 -41
View File
@@ -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 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 clean, abiding the [rules](https://docs.astral.sh/ruff/rules/) specified in your
configuration. Though it runs `ruff check` by default, the action can do configuration. Though it runs `ruff`, the action can do anything `ruff` can (ex,
anything `ruff` can (ex, fix). fix).
## Contents ## Contents
@@ -16,9 +16,9 @@ anything `ruff` can (ex, fix).
- [Use to install ruff](#use-to-install-ruff) - [Use to install ruff](#use-to-install-ruff)
- [Use `ruff format`](#use-ruff-format) - [Use `ruff format`](#use-ruff-format)
- [Install specific versions](#install-specific-versions) - [Install specific versions](#install-specific-versions)
- [Install the latest version](#install-the-latest-version) - [Install the latest version (default)](#install-the-latest-version-default)
- [Install a specific version](#install-a-specific-version) - [Install a specific version](#install-a-specific-version)
- [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 by supplying a semver range](#install-a-version-by-supplying-a-semver-range)
- [Install a version from a specified version file](#install-a-version-from-a-specified-version-file) - [Install a version from a specified version file](#install-a-version-from-a-specified-version-file)
- [Validate checksum](#validate-checksum) - [Validate checksum](#validate-checksum)
- [GitHub authentication token](#github-authentication-token) - [GitHub authentication token](#github-authentication-token)
@@ -69,34 +69,19 @@ This action adds ruff to the PATH, so you can use it in subsequent steps.
- run: ruff format - 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` ### Use `ruff format`
```yaml ```yaml
- uses: astral-sh/ruff-action@v3 - uses: astral-sh/ruff-action@v3
with: with:
args: "format --check --diff" args: "format --check"
``` ```
### Install specific versions ### Install specific versions
By default this action looks for a pyproject.toml file in the root of the repository to determine 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 the ruff version to install. If no pyproject.toml file is found, or no ruff version is defined in
`project.dependencies`, `project.optional-dependencies`, or `dependency-groups`, either `dependencies` or `dependency-groups.dev` the latest version is installed.
the latest version is installed.
> [!NOTE]
> This action does only support ruff versions v0.0.247 and above.
#### Install the latest version #### Install the latest version
@@ -116,10 +101,9 @@ the latest version is installed.
version: "0.4.4" version: "0.4.4"
``` ```
#### Install a version by supplying a semver range or pep440 specifier #### Install a version by supplying a semver range
You can specify a [semver range](https://github.com/npm/node-semver?tab=readme-ov-file#ranges) 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. to install the latest version that satisfies the range.
```yaml ```yaml
@@ -136,23 +120,16 @@ to install the latest version that satisfies the range.
version: "0.4.x" 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 #### Install a version from a specified version file
You can specify a file to read the version from. You can specify a file to read the version from.
Currently `pyproject.toml` and `requirements.txt` are supported. Currently `pyproject.toml` is supported.
```yaml ```yaml
- name: Install a version from a specified version file - name: Install a version from a specified version file
uses: astral-sh/ruff-action@v3 uses: astral-sh/ruff-action@v3
with: with:
version-file: "my-path/to/pyproject.toml-or-requirements.txt" version-file: "my-path/to/pyproject.toml"
``` ```
### Validate checksum ### Validate checksum
@@ -186,15 +163,6 @@ are not sufficient, you can provide a custom GitHub token with the necessary per
github-token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} 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 ## Outputs
| Output | Description | | Output | Description |
@@ -1,22 +0,0 @@
[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"
@@ -1,2 +0,0 @@
def hello() -> str:
return "Hello from python-project!"
@@ -1,14 +0,0 @@
[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"
@@ -1,2 +0,0 @@
def hello() -> str:
return "Hello from python-project!"
@@ -1,14 +0,0 @@
[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"
@@ -1,2 +0,0 @@
def hello() -> str:
return "Hello from python-project!"
@@ -1,15 +0,0 @@
[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"
@@ -1,2 +0,0 @@
def hello() -> str:
return "Hello from python-project!"
-13
View File
@@ -1,13 +0,0 @@
[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"
-1
View File
@@ -1 +0,0 @@
ruff==0.9.0
+13 -26
View File
@@ -1,34 +1,20 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.1.4/schema.json", "$schema": "https://biomejs.dev/schemas/1.9.2/schema.json",
"assist": { "vcs": {
"actions": { "enabled": true,
"source": { "clientKind": "git",
"organizeImports": "on", "useIgnoreFile": false
"useSortedAttributes": "on",
"useSortedKeys": "on"
}
}
}, },
"files": { "files": {
"ignoreUnknown": false, "ignoreUnknown": false,
"includes": [ "ignore": ["dist", "lib", "node_modules"]
"**",
"!**/dist",
"!**/lib",
"!**/node_modules",
"!**/package*.json",
"!**/known-checksums.*"
]
}, },
"formatter": { "formatter": {
"enabled": true, "enabled": true,
"indentStyle": "space" "indentStyle": "space"
}, },
"javascript": { "organizeImports": {
"formatter": { "enabled": true
"quoteStyle": "double",
"trailingCommas": "all"
}
}, },
"linter": { "linter": {
"enabled": true, "enabled": true,
@@ -36,9 +22,10 @@
"recommended": true "recommended": true
} }
}, },
"vcs": { "javascript": {
"clientKind": "git", "formatter": {
"enabled": true, "quoteStyle": "double",
"useIgnoreFile": false "trailingCommas": "all"
}
} }
} }
Generated Vendored
+4496 -5956
View File
File diff suppressed because it is too large Load Diff
+4425 -4977
View File
File diff suppressed because it is too large Load Diff
+200 -196
View File
@@ -11,21 +11,17 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/tool-cache": "^2.0.2", "@actions/github": "^6.0.0",
"@octokit/core": "^7.0.3", "@actions/tool-cache": "^2.0.1",
"@octokit/plugin-paginate-rest": "^13.1.1", "smol-toml": "^1.3.1"
"@octokit/plugin-rest-endpoint-methods": "^16.0.0",
"@renovatebot/pep440": "^4.1.0",
"smol-toml": "^1.4.1"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.1.4", "@biomejs/biome": "1.9.4",
"@types/js-yaml": "^4.0.9", "@types/node": "^22.10.5",
"@types/node": "^24.2.1", "@types/semver": "^7.5.8",
"@types/semver": "^7.7.0",
"@vercel/ncc": "^0.38.3", "@vercel/ncc": "^0.38.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"typescript": "^5.9.2" "typescript": "^5.6.3"
} }
}, },
"node_modules/@actions/core": { "node_modules/@actions/core": {
@@ -45,6 +41,17 @@
"@actions/io": "^1.0.1" "@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": { "node_modules/@actions/http-client": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
@@ -60,23 +67,24 @@
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
}, },
"node_modules/@actions/tool-cache": { "node_modules/@actions/tool-cache": {
"version": "2.0.2", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.1.tgz",
"integrity": "sha512-fBhNNOWxuoLxztQebpOaWu6WeVmuwa77Z+DxIZ1B+OYvGkGQon6kTVg6Z32Cb13WCuw0szqonK+hh03mJV7Z6w==", "integrity": "sha512-iPU+mNwrbA8jodY8eyo/0S/QqCKDajiR8OxWTnSk/SnYg0sj8Hp4QcUEVC1YFpHWXtrfbQrE13Jz4k4HXJQKcA==",
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0", "@actions/exec": "^1.0.0",
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1",
"@actions/io": "^1.1.1", "@actions/io": "^1.1.1",
"semver": "^6.1.0" "semver": "^6.1.0",
"uuid": "^3.3.2"
} }
}, },
"node_modules/@biomejs/biome": { "node_modules/@biomejs/biome": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz",
"integrity": "sha512-QWlrqyxsU0FCebuMnkvBIkxvPqH89afiJzjMl+z67ybutse590jgeaFdDurE9XYtzpjRGTI1tlUZPGWmbKsElA==", "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==",
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0", "hasInstallScript": true,
"bin": { "bin": {
"biome": "bin/biome" "biome": "bin/biome"
}, },
@@ -88,25 +96,24 @@
"url": "https://opencollective.com/biome" "url": "https://opencollective.com/biome"
}, },
"optionalDependencies": { "optionalDependencies": {
"@biomejs/cli-darwin-arm64": "2.1.4", "@biomejs/cli-darwin-arm64": "1.9.4",
"@biomejs/cli-darwin-x64": "2.1.4", "@biomejs/cli-darwin-x64": "1.9.4",
"@biomejs/cli-linux-arm64": "2.1.4", "@biomejs/cli-linux-arm64": "1.9.4",
"@biomejs/cli-linux-arm64-musl": "2.1.4", "@biomejs/cli-linux-arm64-musl": "1.9.4",
"@biomejs/cli-linux-x64": "2.1.4", "@biomejs/cli-linux-x64": "1.9.4",
"@biomejs/cli-linux-x64-musl": "2.1.4", "@biomejs/cli-linux-x64-musl": "1.9.4",
"@biomejs/cli-win32-arm64": "2.1.4", "@biomejs/cli-win32-arm64": "1.9.4",
"@biomejs/cli-win32-x64": "2.1.4" "@biomejs/cli-win32-x64": "1.9.4"
} }
}, },
"node_modules/@biomejs/cli-darwin-arm64": { "node_modules/@biomejs/cli-darwin-arm64": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz",
"integrity": "sha512-sCrNENE74I9MV090Wq/9Dg7EhPudx3+5OiSoQOkIe3DLPzFARuL1dOwCWhKCpA3I5RHmbrsbNSRfZwCabwd8Qg==", "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
@@ -116,14 +123,13 @@
} }
}, },
"node_modules/@biomejs/cli-darwin-x64": { "node_modules/@biomejs/cli-darwin-x64": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz",
"integrity": "sha512-gOEICJbTCy6iruBywBDcG4X5rHMbqCPs3clh3UQ+hRKlgvJTk4NHWQAyHOXvaLe+AxD1/TNX1jbZeffBJzcrOw==", "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
@@ -133,14 +139,13 @@
} }
}, },
"node_modules/@biomejs/cli-linux-arm64": { "node_modules/@biomejs/cli-linux-arm64": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz",
"integrity": "sha512-juhEkdkKR4nbUi5k/KRp1ocGPNWLgFRD4NrHZSveYrD6i98pyvuzmS9yFYgOZa5JhaVqo0HPnci0+YuzSwT2fw==", "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -150,14 +155,13 @@
} }
}, },
"node_modules/@biomejs/cli-linux-arm64-musl": { "node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz",
"integrity": "sha512-nYr7H0CyAJPaLupFE2cH16KZmRC5Z9PEftiA2vWxk+CsFkPZQ6dBRdcC6RuS+zJlPc/JOd8xw3uCCt9Pv41WvQ==", "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -167,14 +171,13 @@
} }
}, },
"node_modules/@biomejs/cli-linux-x64": { "node_modules/@biomejs/cli-linux-x64": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz",
"integrity": "sha512-Eoy9ycbhpJVYuR+LskV9s3uyaIkp89+qqgqhGQsWnp/I02Uqg2fXFblHJOpGZR8AxdB9ADy87oFVxn9MpFKUrw==", "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -184,14 +187,13 @@
} }
}, },
"node_modules/@biomejs/cli-linux-x64-musl": { "node_modules/@biomejs/cli-linux-x64-musl": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz",
"integrity": "sha512-lvwvb2SQQHctHUKvBKptR6PLFCM7JfRjpCCrDaTmvB7EeZ5/dQJPhTYBf36BE/B4CRWR2ZiBLRYhK7hhXBCZAg==", "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -201,14 +203,13 @@
} }
}, },
"node_modules/@biomejs/cli-win32-arm64": { "node_modules/@biomejs/cli-win32-arm64": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz",
"integrity": "sha512-3WRYte7orvyi6TRfIZkDN9Jzoogbv+gSvR+b9VOXUg1We1XrjBg6WljADeVEaKTvOcpVdH0a90TwyOQ6ue4fGw==", "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@@ -218,14 +219,13 @@
} }
}, },
"node_modules/@biomejs/cli-win32-x64": { "node_modules/@biomejs/cli-win32-x64": {
"version": "2.1.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz",
"integrity": "sha512-tBc+W7anBPSFXGAoQW+f/+svkpt8/uXfRwDzN1DvnatkRMt16KIYpEi/iw8u9GahJlFv98kgHcIrSsZHZTR0sw==", "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@@ -243,165 +243,163 @@
} }
}, },
"node_modules/@octokit/auth-token": { "node_modules/@octokit/auth-token": {
"version": "6.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
} }
}, },
"node_modules/@octokit/core": { "node_modules/@octokit/core": {
"version": "7.0.3", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.3.tgz", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz",
"integrity": "sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ==", "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/auth-token": "^6.0.0", "@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^9.0.1", "@octokit/graphql": "^7.1.0",
"@octokit/request": "^10.0.2", "@octokit/request": "^8.3.1",
"@octokit/request-error": "^7.0.0", "@octokit/request-error": "^5.1.0",
"@octokit/types": "^14.0.0", "@octokit/types": "^13.0.0",
"before-after-hook": "^4.0.0", "before-after-hook": "^2.2.0",
"universal-user-agent": "^7.0.0" "universal-user-agent": "^6.0.0"
}, },
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
} }
}, },
"node_modules/@octokit/endpoint": { "node_modules/@octokit/endpoint": {
"version": "11.0.0", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz",
"integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/types": "^14.0.0", "@octokit/types": "^13.1.0",
"universal-user-agent": "^7.0.2" "universal-user-agent": "^6.0.0"
}, },
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
} }
}, },
"node_modules/@octokit/graphql": { "node_modules/@octokit/graphql": {
"version": "9.0.1", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz",
"integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==", "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/request": "^10.0.2", "@octokit/request": "^8.3.0",
"@octokit/types": "^14.0.0", "@octokit/types": "^13.0.0",
"universal-user-agent": "^7.0.0" "universal-user-agent": "^6.0.0"
}, },
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
} }
}, },
"node_modules/@octokit/openapi-types": { "node_modules/@octokit/openapi-types": {
"version": "25.1.0", "version": "22.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
"integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
"license": "MIT"
}, },
"node_modules/@octokit/plugin-paginate-rest": { "node_modules/@octokit/plugin-paginate-rest": {
"version": "13.1.1", "version": "9.2.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz",
"integrity": "sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw==", "integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/types": "^14.1.0" "@octokit/types": "^12.6.0"
}, },
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
}, },
"peerDependencies": { "peerDependencies": {
"@octokit/core": ">=6" "@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"
} }
}, },
"node_modules/@octokit/plugin-rest-endpoint-methods": { "node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "16.0.0", "version": "10.4.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.0.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
"integrity": "sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g==", "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/types": "^14.1.0" "@octokit/types": "^12.6.0"
}, },
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
}, },
"peerDependencies": { "peerDependencies": {
"@octokit/core": ">=6" "@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"
} }
}, },
"node_modules/@octokit/request": { "node_modules/@octokit/request": {
"version": "10.0.3", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
"integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==", "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/endpoint": "^11.0.0", "@octokit/endpoint": "^9.0.1",
"@octokit/request-error": "^7.0.0", "@octokit/request-error": "^5.1.0",
"@octokit/types": "^14.0.0", "@octokit/types": "^13.1.0",
"fast-content-type-parse": "^3.0.0", "universal-user-agent": "^6.0.0"
"universal-user-agent": "^7.0.2"
}, },
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
} }
}, },
"node_modules/@octokit/request-error": { "node_modules/@octokit/request-error": {
"version": "7.0.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
"integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/types": "^14.0.0" "@octokit/types": "^13.1.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}, },
"engines": { "engines": {
"node": ">= 20" "node": ">= 18"
} }
}, },
"node_modules/@octokit/types": { "node_modules/@octokit/types": {
"version": "14.1.0", "version": "13.6.1",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz",
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==",
"license": "MIT",
"dependencies": { "dependencies": {
"@octokit/openapi-types": "^25.1.0" "@octokit/openapi-types": "^22.2.0"
} }
}, },
"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": { "node_modules/@types/node": {
"version": "24.2.1", "version": "22.10.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz",
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~7.10.0" "undici-types": "~6.20.0"
} }
}, },
"node_modules/@types/semver": { "node_modules/@types/semver": {
"version": "7.7.0", "version": "7.5.8",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
"integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
"dev": true, "dev": true
"license": "MIT"
}, },
"node_modules/@vercel/ncc": { "node_modules/@vercel/ncc": {
"version": "0.38.3", "version": "0.38.3",
@@ -419,26 +417,14 @@
"dev": true "dev": true
}, },
"node_modules/before-after-hook": { "node_modules/before-after-hook": {
"version": "4.0.0", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
"license": "Apache-2.0"
}, },
"node_modules/fast-content-type-parse": { "node_modules/deprecation": {
"version": "3.0.0", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "MIT"
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.0",
@@ -452,6 +438,14 @@
"js-yaml": "bin/js-yaml.js" "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": { "node_modules/semver": {
"version": "6.3.1", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -461,10 +455,9 @@
} }
}, },
"node_modules/smol-toml": { "node_modules/smol-toml": {
"version": "1.4.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.1.tgz", "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz",
"integrity": "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==", "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==",
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">= 18" "node": ">= 18"
}, },
@@ -481,11 +474,10 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.9.2", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"dev": true, "dev": true,
"license": "Apache-2.0",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -495,10 +487,9 @@
} }
}, },
"node_modules/undici": { "node_modules/undici": {
"version": "5.29.0", "version": "5.28.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
"license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^2.0.0" "@fastify/busboy": "^2.0.0"
}, },
@@ -507,17 +498,30 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.10.0", "version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/universal-user-agent": { "node_modules/universal-user-agent": {
"version": "7.0.3", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
"license": "ISC" },
"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=="
} }
} }
} }
+11 -13
View File
@@ -6,11 +6,13 @@
"main": "dist/ruff-action/index.js", "main": "dist/ruff-action/index.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"check": "biome check --write", "format": "biome format --fix",
"format-check": "biome format",
"lint": "biome lint --fix",
"package": "ncc build -o dist/ruff-action src/ruff-action.ts && ncc build -o dist/update-known-checksums src/update-known-checksums.ts", "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)\"", "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)\"", "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 install && npm run build && npm run check && npm run package" "all": "npm run build && npm run format && npm run lint && npm run package"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -22,20 +24,16 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/tool-cache": "^2.0.2", "@actions/github": "^6.0.0",
"@octokit/core": "^7.0.3", "@actions/tool-cache": "^2.0.1",
"@octokit/plugin-paginate-rest": "^13.1.1", "smol-toml": "^1.3.1"
"@octokit/plugin-rest-endpoint-methods": "^16.0.0",
"@renovatebot/pep440": "^4.1.0",
"smol-toml": "^1.4.1"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.1.4", "@biomejs/biome": "1.9.4",
"@types/js-yaml": "^4.0.9", "@types/node": "^22.10.5",
"@types/node": "^24.2.1", "@types/semver": "^7.5.8",
"@types/semver": "^7.7.0",
"@vercel/ncc": "^0.38.3", "@vercel/ncc": "^0.38.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"typescript": "^5.9.2" "typescript": "^5.6.3"
} }
} }
+3 -3
View File
@@ -1,9 +1,9 @@
import * as crypto from "node:crypto";
import * as fs from "node:fs"; import * as fs from "node:fs";
import * as crypto from "node:crypto";
import * as core from "@actions/core"; import * as core from "@actions/core";
import type { Architecture, Platform } from "../../utils/platforms";
import { KNOWN_CHECKSUMS } from "./known-checksums"; import { KNOWN_CHECKSUMS } from "./known-checksums";
import type { Architecture, Platform } from "../../utils/platforms";
export async function validateChecksum( export async function validateChecksum(
checkSum: string | undefined, checkSum: string | undefined,
@@ -12,7 +12,7 @@ export async function validateChecksum(
platform: Platform, platform: Platform,
version: string, version: string,
): Promise<void> { ): Promise<void> {
let isValid: boolean | undefined; let isValid: boolean | undefined = undefined;
if (checkSum !== undefined && checkSum !== "") { if (checkSum !== undefined && checkSum !== "") {
isValid = await validateFileCheckSum(downloadPath, checkSum); isValid = await validateFileCheckSum(downloadPath, checkSum);
} else { } else {
File diff suppressed because it is too large Load Diff
@@ -1,11 +1,5 @@
import { promises as fs } from "node:fs"; import { promises as fs } from "node:fs";
import * as tc from "@actions/tool-cache"; import * as tc from "@actions/tool-cache";
import {
isRetryableError,
NonRetryableError,
RetryableError,
withRetry,
} from "../../utils/retry";
import { KNOWN_CHECKSUMS } from "./known-checksums"; import { KNOWN_CHECKSUMS } from "./known-checksums";
export async function updateChecksums( export async function updateChecksums(
filePath: string, filePath: string,
@@ -66,27 +60,6 @@ async function getOrDownloadChecksum(
} }
async function downloadAssetContent(downloadUrl: string): Promise<string> { async function downloadAssetContent(downloadUrl: string): Promise<string> {
return await withRetry(
async () => {
try {
const downloadPath = await tc.downloadTool(downloadUrl); const downloadPath = await tc.downloadTool(downloadUrl);
return await fs.readFile(downloadPath, "utf8"); 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",
);
} }
+28 -250
View File
@@ -1,23 +1,11 @@
import { promises as fs } from "node:fs";
import * as path from "node:path";
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as tc from "@actions/tool-cache"; import * as tc from "@actions/tool-cache";
import { Octokit } from "@octokit/core"; import * as path from "node:path";
import { paginateRest } from "@octokit/plugin-paginate-rest"; import { promises as fs } from "node:fs";
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 { OWNER, REPO, TOOL_CACHE_NAME } from "../utils/constants";
import type { Architecture, Platform } from "../utils/platforms"; import type { Architecture, Platform } from "../utils/platforms";
import {
isRetryableError,
NonRetryableError,
RetryableError,
withRetry,
} from "../utils/retry";
import { validateChecksum } from "./checksum/checksum"; import { validateChecksum } from "./checksum/checksum";
import * as github from "@actions/github";
const PaginatingOctokit = Octokit.plugin(paginateRest, restEndpointMethods);
export function tryGetFromToolCache( export function tryGetFromToolCache(
arch: Architecture, arch: Architecture,
@@ -31,7 +19,7 @@ export function tryGetFromToolCache(
resolvedVersion = version; resolvedVersion = version;
} }
const installedPath = tc.find(TOOL_CACHE_NAME, resolvedVersion, arch); const installedPath = tc.find(TOOL_CACHE_NAME, resolvedVersion, arch);
return { installedPath, version: resolvedVersion }; return { version: resolvedVersion, installedPath };
} }
export async function downloadVersion( export async function downloadVersion(
@@ -46,77 +34,16 @@ export async function downloadVersion(
if (platform === "pc-windows-msvc") { if (platform === "pc-windows-msvc") {
extension = ".zip"; extension = ".zip";
} }
const downloadUrl = constructDownloadUrl(version, platform, arch); const downloadUrl = `https://github.com/${OWNER}/${REPO}/releases/download/${version}/${artifact}${extension}`;
core.debug(`Downloading ruff from "${downloadUrl}" ...`); core.info(`Downloading ruff from "${downloadUrl}" ...`);
const downloadPath = await withRetry( const downloadPath = await tc.downloadTool(
async () => { downloadUrl,
try { undefined,
return await tc.downloadTool(downloadUrl, undefined, githubToken); 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); 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; let ruffDir: string;
if (platform === "pc-windows-msvc") { if (platform === "pc-windows-msvc") {
const fullPathWithExtension = `${downloadPath}${extension}`; const fullPathWithExtension = `${downloadPath}${extension}`;
@@ -124,22 +51,22 @@ async function extractDownloadedArtifact(
ruffDir = await tc.extractZip(fullPathWithExtension); ruffDir = await tc.extractZip(fullPathWithExtension);
// On windows extracting the zip does not create an intermediate directory // On windows extracting the zip does not create an intermediate directory
} else { } else {
ruffDir = await tc.extractTar(downloadPath); const extractedDir = await tc.extractTar(downloadPath);
if (semver.gte(version, "v0.5.0")) { ruffDir = path.join(extractedDir, artifact);
// Since v0.5.0 an intermediate directory is created
ruffDir = path.join(ruffDir, artifact);
} }
} const cachedToolDir = await tc.cacheDir(
const files = await fs.readdir(ruffDir); ruffDir,
core.debug(`Contents of ${ruffDir}: ${files.join(", ")}`); TOOL_CACHE_NAME,
return ruffDir; version,
arch,
);
return { version: version, cachedToolDir };
} }
export async function resolveVersion( export async function resolveVersion(
versionInput: string, versionInput: string,
githubToken: string, githubToken: string,
): Promise<string> { ): Promise<string> {
core.debug(`Resolving ${versionInput}...`);
const version = const version =
versionInput === "latest" versionInput === "latest"
? await getLatestVersion(githubToken) ? await getLatestVersion(githubToken)
@@ -149,182 +76,33 @@ export async function resolveVersion(
return version; return version;
} }
const availableVersions = await getAvailableVersions(githubToken); const availableVersions = await getAvailableVersions(githubToken);
const resolvedVersion = maxSatisfying(availableVersions, version); const resolvedVersion = tc.evaluateVersions(availableVersions, version);
if (resolvedVersion === undefined) { if (resolvedVersion === "") {
throw new Error(`No version found for ${version}`); throw new Error(`No version found for ${version}`);
} }
core.debug(`Resolved version: ${resolvedVersion}`);
return resolvedVersion; return resolvedVersion;
} }
async function getAvailableVersions(githubToken: string): Promise<string[]> { async function getAvailableVersions(githubToken: string): Promise<string[]> {
return await withRetry( const octokit = github.getOctokit(githubToken);
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);
}
if (isRetryableError(error)) { const response = await octokit.paginate(octokit.rest.repos.listReleases, {
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, owner: OWNER,
repo: REPO, 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); return response.map((release) => release.tag_name);
} }
async function getLatestVersion(githubToken: string) { async function getLatestVersion(githubToken: string) {
return await withRetry( const octokit = github.getOctokit(githubToken);
async () => {
const octokit = new PaginatingOctokit({ const { data: latestRelease } = await octokit.rest.repos.getLatestRelease({
auth: githubToken, 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 (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) { if (!latestRelease) {
throw new Error("Could not determine latest release."); throw new Error("Could not determine latest release.");
} }
return latestRelease.tag_name; 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;
}
const maxPep440 = pep440.maxSatisfying(versions, version);
if (maxPep440 !== null) {
core.debug(
`Found a version that satisfies the pep440 specifier: ${maxPep440}`,
);
return maxPep440;
}
return undefined;
} }
+14 -27
View File
@@ -1,13 +1,18 @@
import * as fs from "node:fs";
import * as path from "node:path";
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as exec from "@actions/exec"; import * as exec from "@actions/exec";
import * as semver from "semver"; import * as path from "node:path";
import { import {
downloadVersion, downloadVersion,
resolveVersion, resolveVersion,
tryGetFromToolCache, tryGetFromToolCache,
} from "./download/download-version"; } from "./download/download-version";
import {
type Architecture,
getArch,
getPlatform,
type Platform,
} from "./utils/platforms";
import { import {
args, args,
checkSum, checkSum,
@@ -16,13 +21,8 @@ import {
version, version,
versionFile as versionFileInput, versionFile as versionFileInput,
} from "./utils/inputs"; } from "./utils/inputs";
import { import { getRuffVersionFromPyproject } from "./utils/pyproject";
type Architecture, import * as fs from "node:fs";
getArch,
getPlatform,
type Platform,
} from "./utils/platforms";
import { getRuffVersionFromRequirementsFile } from "./utils/pyproject";
async function run(): Promise<void> { async function run(): Promise<void> {
const platform = getPlatform(); const platform = getPlatform();
@@ -62,11 +62,6 @@ async function setupRuff(
githubToken: string, githubToken: string,
): Promise<{ ruffDir: string; version: string }> { ): Promise<{ ruffDir: string; version: string }> {
const resolvedVersion = await determineVersion(); 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); const toolCacheResult = tryGetFromToolCache(arch, resolvedVersion);
if (toolCacheResult.installedPath) { if (toolCacheResult.installedPath) {
core.info(`Found ruffDir in tool-cache for ${toolCacheResult.version}`); core.info(`Found ruffDir in tool-cache for ${toolCacheResult.version}`);
@@ -98,27 +93,19 @@ async function determineVersion(): Promise<string> {
return await resolveVersion(version, githubToken); return await resolveVersion(version, githubToken);
} }
if (versionFileInput !== "") { if (versionFileInput !== "") {
const versionFromPyproject = const versionFromPyproject = getRuffVersionFromPyproject(versionFileInput);
getRuffVersionFromRequirementsFile(versionFileInput);
if (versionFromPyproject === undefined) { if (versionFromPyproject === undefined) {
core.warning( core.warning(
`Could not parse version from ${versionFileInput}. Using latest version.`, "Could not parse version from supplied pyproject.toml. Using latest version.",
); );
return await resolveVersion("latest", githubToken);
} }
return await resolveVersion(versionFromPyproject || "latest", githubToken);
} }
const pyProjectPath = path.join(src, "pyproject.toml"); const pyProjectPath = path.join(src, "pyproject.toml");
if (!fs.existsSync(pyProjectPath)) { if (!fs.existsSync(pyProjectPath)) {
core.info(`Could not find ${pyProjectPath}. Using latest version.`);
return await resolveVersion("latest", githubToken); return await resolveVersion("latest", githubToken);
} }
const versionFromPyproject = const versionFromPyproject = getRuffVersionFromPyproject(pyProjectPath);
getRuffVersionFromRequirementsFile(pyProjectPath);
if (versionFromPyproject === undefined) {
core.info(
`Could not parse version from ${pyProjectPath}. Using latest version.`,
);
}
return await resolveVersion(versionFromPyproject || "latest", githubToken); return await resolveVersion(versionFromPyproject || "latest", githubToken);
} }
+8 -37
View File
@@ -1,50 +1,21 @@
import * as github from "@actions/github";
import * as core from "@actions/core"; import * as core from "@actions/core";
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); import { OWNER, REPO } from "./utils/constants";
import * as semver from "semver";
import { updateChecksums } from "./download/checksum/update-known-checksums";
async function run(): Promise<void> { async function run(): Promise<void> {
const checksumFilePath = process.argv.slice(2)[0]; const checksumFilePath = process.argv.slice(2)[0];
const github_token = process.argv.slice(2)[1]; const github_token = process.argv.slice(2)[1];
const response = await withRetry( const octokit = github.getOctokit(github_token);
async () => {
try { const response = await octokit.paginate(octokit.rest.repos.listReleases, {
const octokit = new PaginatingOctokit({ auth: github_token });
return await octokit.paginate(octokit.rest.repos.listReleases, {
owner: OWNER, owner: OWNER,
repo: REPO, 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 downloadUrls: string[] = response.flatMap((release) => const downloadUrls: string[] = response.flatMap((release) =>
release.assets release.assets
.filter((asset) => asset.name.endsWith(".sha256")) .filter((asset) => asset.name.endsWith(".sha256"))
+2 -2
View File
@@ -9,9 +9,9 @@ export type Architecture = "i686" | "x86_64" | "aarch64";
export function getArch(): Architecture | undefined { export function getArch(): Architecture | undefined {
const arch = process.arch; const arch = process.arch;
const archMapping: { [key: string]: Architecture } = { const archMapping: { [key: string]: Architecture } = {
arm64: "aarch64",
ia32: "i686", ia32: "i686",
x64: "x86_64", x64: "x86_64",
arm64: "aarch64",
}; };
if (arch in archMapping) { if (arch in archMapping) {
@@ -22,8 +22,8 @@ export function getArch(): Architecture | undefined {
export function getPlatform(): Platform | undefined { export function getPlatform(): Platform | undefined {
const platform = process.platform; const platform = process.platform;
const platformMapping: { [key: string]: Platform } = { const platformMapping: { [key: string]: Platform } = {
darwin: "apple-darwin",
linux: "unknown-linux-gnu", linux: "unknown-linux-gnu",
darwin: "apple-darwin",
win32: "pc-windows-msvc", win32: "pc-windows-msvc",
}; };
+27 -76
View File
@@ -2,12 +2,34 @@ import * as fs from "node:fs";
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as toml from "smol-toml"; import * as toml from "smol-toml";
function getRuffVersionFromAllDependencies( export function getRuffVersionFromPyproject(
allDependencies: string[], filePath: string,
): string | undefined { ): string | undefined {
const ruffVersionDefinition = allDependencies.find((dep: string) => if (!fs.existsSync(filePath)) {
dep.startsWith("ruff"), 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"));
if (ruffVersionDefinition) { if (ruffVersionDefinition) {
const ruffVersion = ruffVersionDefinition const ruffVersion = ruffVersionDefinition
@@ -22,74 +44,3 @@ function getRuffVersionFromAllDependencies(
return undefined; 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;
}
}
-176
View File
@@ -1,176 +0,0 @@
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
View File
@@ -1,12 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "target": "ES2022" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, "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. */, "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. */, "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. */, "strict": true /* Enable all strict type-checking options. */,
"target": "ES2022" /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "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'. */
}, },
"exclude": ["node_modules", "**/*.test.ts"] "exclude": ["node_modules", "**/*.test.ts"]
} }