diff --git a/.codespellrc b/.codespellrc
new file mode 100644
index 00000000..13c05a80
--- /dev/null
+++ b/.codespellrc
@@ -0,0 +1,5 @@
+[codespell]
+skip = .git,*.pdf,*.svg,pnpm-lock.yaml,yarn.lock
+# some modules, parts of regexes, and variable names to ignore, some
+# misspellings in fixtures/external responses we do not own
+ignore-words-list = caf,bu,nwo,nd,kernal,crate,unparseable,couldn,defintions
diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index e3cadb92..608e342b 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
- "version": "8.0.0",
+ "version": "8.0.7",
"commands": [
"dotnet-ef"
]
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 54c8cabf..6751f7bc 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,8 +1,8 @@
# These are supported funding model platforms
github: [mburumaxwell] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
+patreon: maxwellweru # Replace with a single Patreon username
+open_collective: maxwellweru # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 200289c3..f79572bf 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -10,6 +10,13 @@ assignees: ''
**Describe the bug**
A clear and concise description of what the bug is.
+**Categorization**
+- [ ] This is not a permissions issue (Seek help at https://github.com/tinglesoftware/dependabot-azure-devops/discussions/1245)
+
+**Repository**
+URL: e.g. https://dev.azure.com/tingle/dependabot/_git/repro-684
+
+
**To Reproduce**
Steps to reproduce the behavior:
1. ...
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 4a608938..1e614505 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -5,60 +5,80 @@
version: 2
updates:
-- package-ecosystem: "github-actions" # See documentation for possible values
- directory: "/" # Location of package manifests
- schedule:
- interval: "weekly"
- time: "04:00"
- open-pull-requests-limit: 10
+ - package-ecosystem: "github-actions" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ time: "02:00"
+ open-pull-requests-limit: 10
-- package-ecosystem: "bundler" # See documentation for possible values
- directory: "/updater" # Location of package manifests
- schedule:
- interval: "weekly"
- time: "04:00"
- open-pull-requests-limit: 10
- ignore:
- - dependency-name: "rubocop*"
- update-types: ["version-update:semver-patch"]
+ - package-ecosystem: "devcontainers" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ time: "02:00"
+ open-pull-requests-limit: 10
-- package-ecosystem: "docker" # See documentation for possible values
- directory: "/updater" # Location of package manifests
- schedule:
- interval: "weekly"
- time: "04:00"
- open-pull-requests-limit: 10
+ - package-ecosystem: "bundler" # See documentation for possible values
+ directory: "/updater" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ time: "02:00"
+ open-pull-requests-limit: 10
+ groups:
+ opentelemetry:
+ patterns: ["opentelemetry-*"]
+ rubocop:
+ patterns: ["*rubocop*"]
+ sentry:
+ patterns: ["sentry-*"]
-- package-ecosystem: "nuget" # See documentation for possible values
- directory: "/" # Location of package manifests
- schedule:
- interval: "weekly"
- time: "04:00"
- open-pull-requests-limit: 10
- groups:
- event-bus:
- patterns: ["Tingle.EventBus*"]
- microsoft:
- patterns: ["Microsoft.*"]
- system:
- patterns: ["System.*"]
- tingle:
- patterns:
- - "Tingle.AspNetCore.*"
- - "Tingle.Extensions.*"
- xuint:
- patterns: ["Xunit*"]
+ - package-ecosystem: "docker" # See documentation for possible values
+ directory: "/updater" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ time: "02:00"
+ open-pull-requests-limit: 10
-- package-ecosystem: "npm" # See documentation for possible values
- directory: "/extension" # Location of package manifests
- schedule:
- interval: "weekly"
- time: "04:00"
- open-pull-requests-limit: 10
- ignore:
- - dependency-name: "axios"
- update-types: ["version-update:semver-patch"]
- - dependency-name: "jest"
- update-types: ["version-update:semver-patch"]
- - dependency-name: "@types/*"
- update-types: ["version-update:semver-patch"]
+ - package-ecosystem: "nuget" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ time: "02:00"
+ open-pull-requests-limit: 10
+ groups:
+ azure:
+ patterns:
+ - 'Azure.*'
+ - 'Microsoft.Azure.*'
+ - 'Microsoft.Extensions.Configuration.AzureAppConfiguration'
+ event-bus:
+ patterns: ["Tingle.EventBus*"]
+ microsoft:
+ patterns: ["Microsoft*"]
+ exclude-patterns:
+ - 'Microsoft.Azure.*'
+ - 'Microsoft.Extensions.Configuration.AzureAppConfiguration'
+ - "Microsoft.VisualStudio.Azure.Containers.Tools.Targets"
+ system:
+ patterns: ["System*"]
+ tingle:
+ patterns:
+ - "Tingle.AspNetCore*"
+ - "Tingle.Extensions*"
+ xunit:
+ patterns: ["Xunit*"]
+
+ - package-ecosystem: "npm" # See documentation for possible values
+ directory: "/extension" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ time: "02:00"
+ open-pull-requests-limit: 10
+ groups:
+ jest:
+ patterns: ["*jest*"]
+ js-yaml:
+ patterns: ["*js-yaml*"]
+ js-ts-types:
+ patterns: ["@types/*"]
diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml
index af269959..49e810c4 100644
--- a/.github/workflows/cleanup.yml
+++ b/.github/workflows/cleanup.yml
@@ -28,11 +28,12 @@ jobs:
- { ecosystem: pub }
- { ecosystem: pip }
- { ecosystem: swift }
+ - { ecosystem: devcontainers }
- { ecosystem: terraform }
steps:
- name: Delete old dependabot-updater-${{ matrix.suite.ecosystem }} images
- uses: actions/delete-package-versions@v4
+ uses: actions/delete-package-versions@v5
with:
package-name: 'dependabot-updater-${{ matrix.suite.ecosystem }}'
package-type: 'container'
@@ -45,7 +46,7 @@ jobs:
steps:
- name: Delete old dependabot-server images
- uses: actions/delete-package-versions@v4
+ uses: actions/delete-package-versions@v5
with:
package-name: 'dependabot-server'
package-type: 'container'
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index 9f138b37..00000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches: [ "main" ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ "main" ]
- schedule:
- - cron: '45 20 * * 5'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'javascript', 'ruby', 'csharp' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
- # Use only 'java' to analyze code written in Java, Kotlin or both
- # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
- # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
-
- # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
- # queries: security-extended,security-and-quality
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, Go, 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@v2
-
- # âšī¸ Command-line programs to run using the OS shell.
- # đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
-
- # If the Autobuild fails above, remove it and uncomment the following three lines.
- # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
-
- # - run: |
- # echo "Run, Build Application using script"
- # ./location_of_script_within_repo/buildscript.sh
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
- with:
- category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
new file mode 100644
index 00000000..df18e8f6
--- /dev/null
+++ b/.github/workflows/codespell.yml
@@ -0,0 +1,21 @@
+name: Codespell
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+permissions:
+ contents: read
+
+jobs:
+ codespell:
+ name: Check for spelling errors
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Codespell
+ uses: codespell-project/actions-codespell@v2
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
deleted file mode 100644
index 4e751977..00000000
--- a/.github/workflows/dependency-review.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-# Dependency Review Action
-#
-# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
-#
-# Source repository: https://github.com/actions/dependency-review-action
-# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
-name: 'Dependency Review'
-on: [pull_request]
-
-permissions:
- contents: read
-
-jobs:
- dependency-review:
- runs-on: ubuntu-latest
- steps:
- - name: 'Checkout Repository'
- uses: actions/checkout@v4
- - name: 'Dependency Review'
- uses: actions/dependency-review-action@v3
diff --git a/.github/workflows/extension.yml b/.github/workflows/extension.yml
index f8cfbad1..e352ea05 100644
--- a/.github/workflows/extension.yml
+++ b/.github/workflows/extension.yml
@@ -25,6 +25,10 @@ jobs:
Build:
runs-on: ubuntu-latest
+ concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -32,13 +36,13 @@ jobs:
fetch-depth: 0 # Required for GitVersion
- name: Install GitVersion
- uses: gittools/actions/gitversion/setup@v0
+ uses: gittools/actions/gitversion/setup@v1
with:
versionSpec: '5.x'
- name: Determine Version
id: gitversion
- uses: gittools/actions/gitversion/execute@v0
+ uses: gittools/actions/gitversion/execute@v1
with:
useConfigFile: true
@@ -70,6 +74,9 @@ jobs:
uses: cschleiden/replace-tokens@v1
with:
files: '["${{ github.workspace }}/extension/overrides*.json"]'
+ env:
+ MAJOR_MINOR_PATCH: ${{ steps.gitversion.outputs.majorMinorPatch }}
+ BUILD_NUMBER: ${{ github.run_number }}
- name: Update values in extension/task/task.json
run: |
@@ -82,21 +89,21 @@ jobs:
tfx extension create
--root extension
--manifest-globs vss-extension.json
- --output-path $GITHUB_WORKSPACE/drop/dev
+ --output-path ${{ github.workspace }}/drop/dev
--json5
- --overrides-file $GITHUB_WORKSPACE/extension/overrides.dev.json
+ --overrides-file ${{ github.workspace }}/extension/overrides.dev.json
- name: Create Extension (prod)
run: >
tfx extension create
--root extension
--manifest-globs vss-extension.json
- --output-path $GITHUB_WORKSPACE/drop/prod
+ --output-path ${{ github.workspace }}/drop/prod
--json5
- --overrides-file $GITHUB_WORKSPACE/extension/overrides.prod.json
+ --overrides-file ${{ github.workspace }}/extension/overrides.prod.json
- - name: Publish Artifact
- uses: actions/upload-artifact@v3
+ - name: Upload Artifact (drop)
+ uses: actions/upload-artifact@v4
with:
path: ${{ github.workspace }}/drop/*
name: drop
@@ -105,9 +112,13 @@ jobs:
runs-on: ubuntu-latest
needs: [ Build ]
+ concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: false # wait for previous runs to complete
+
steps:
- name: Download Artifact
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: drop
@@ -132,7 +143,7 @@ jobs:
if: github.ref == 'refs/heads/main'
run: >
tfx extension publish
- --vsix $GITHUB_WORKSPACE/dev/*.vsix
+ --vsix ${{ github.workspace }}/dev/*.vsix
--auth-type pat
--token ${{ secrets.AZURE_DEVOPS_EXTENSION_TOKEN }}
--share-with tingle
@@ -141,6 +152,6 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
run: >
tfx extension publish
- --vsix $GITHUB_WORKSPACE/prod/*.vsix
+ --vsix ${{ github.workspace }}/prod/*.vsix
--auth-type pat
--token ${{ secrets.AZURE_DEVOPS_EXTENSION_TOKEN }}
diff --git a/.github/workflows/mispell.yml b/.github/workflows/mispell.yml
deleted file mode 100644
index 32b68116..00000000
--- a/.github/workflows/mispell.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: Misspell
-
-on:
- workflow_dispatch:
- schedule:
- - cron: '0 0 * * 1'
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
- - uses: sobolevn/misspell-fixer-action@0.1.0
- - uses: peter-evans/create-pull-request@v5
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- commit-message: 'Fixes by misspell-fixer'
- title: 'Typos fix by misspell-fixer'
diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml
index 03c5b0f3..82062d65 100644
--- a/.github/workflows/server.yml
+++ b/.github/workflows/server.yml
@@ -17,15 +17,22 @@ on:
- ".github/workflows/server.yml"
- "!docs/**"
+env:
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }}
+
jobs:
Build:
runs-on: ubuntu-latest
env:
- buildConfiguration: 'Release'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
IMAGE_NAME: 'dependabot-server'
DOCKER_BUILDKIT: 1 # Enable Docker BuildKit
+ concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -33,63 +40,93 @@ jobs:
fetch-depth: 0 # Required for GitVersion
- name: Install GitVersion
- uses: gittools/actions/gitversion/setup@v0
+ uses: gittools/actions/gitversion/setup@v1
with:
versionSpec: '5.x'
- name: Determine Version
- uses: gittools/actions/gitversion/execute@v0
+ uses: gittools/actions/gitversion/execute@v1
+ id: gitversion
with:
useConfigFile: true
- name: Setup .NET SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.x'
- name: Test
- run: dotnet test -c $buildConfiguration --verbosity normal --collect "Code coverage"
+ run: dotnet test -c Release --collect "Code coverage"
- name: Publish
run: |
dotnet publish \
- $GITHUB_WORKSPACE/server/Tingle.Dependabot/Tingle.Dependabot.csproj \
- -c $buildConfiguration \
- -o $GITHUB_WORKSPACE/drop/Tingle.Dependabot
+ ${{ github.workspace }}/server/Tingle.Dependabot/Tingle.Dependabot.csproj \
+ -c Release \
+ -o ${{ github.workspace }}/drop/Tingle.Dependabot
- name: Replace tokens
uses: cschleiden/replace-tokens@v1
with:
files: '["${{ github.workspace }}/server/main.bicep"]'
+ env:
+ IMAGE_TAG: ${{ steps.gitversion.outputs.nuGetVersionV2 }}
- name: Build bicep file
- uses: azure/CLI@v1
+ uses: azure/cli@v2
with:
- azcliversion: 2.45.0 # somehow 2.46.0 is failing
inlineScript: |
- cp $GITHUB_WORKSPACE/server/main.bicep $GITHUB_WORKSPACE/drop/main.bicep
- az bicep build --file server/main.bicep --outfile $GITHUB_WORKSPACE/drop/main.json
+ cp ${{ github.workspace }}/server/main.bicep ${{ github.workspace }}/drop/main.bicep && \
+ az bicep build --file server/main.bicep --outfile ${{ github.workspace }}/drop/main.json
+
+ - name: Upload Artifact (drop)
+ uses: actions/upload-artifact@v4
+ with:
+ path: ${{ github.workspace }}/drop/*
+ name: drop
+
+ - name: Create deploy folder
+ run: |
+ mkdir -p deploy
+ cp ${{ github.workspace }}/server/main.bicep ${{ github.workspace }}/deploy/main.bicep
+ cp ${{ github.workspace }}/server/main.parameters.json ${{ github.workspace }}/deploy/main.parameters.json
+
+ - name: Replace tokens in deploy folder
+ uses: cschleiden/replace-tokens@v1
+ with:
+ files: '["${{ github.workspace }}/deploy/main.parameters.json"]'
+ env:
+ DOCKER_IMAGE_TAG: ${{ steps.gitversion.outputs.shortSha }}
+ DEPENDABOT_PROJECT_TOKEN: ${{ secrets.DEPENDABOT_PROJECT_TOKEN }}
+ DEPENDABOT_GITHUB_TOKEN: ${{ secrets.DEPENDABOT_GITHUB_TOKEN }}
+
+ - name: Upload Artifact (deploy)
+ uses: actions/upload-artifact@v4
+ with:
+ path: deploy
+ name: deploy
+ retention-days: 1
- name: Pull Docker base image & warm Docker cache
- run: docker pull "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:latest"
+ run: docker pull "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest"
- name: Build image
run: |
docker build \
-f server/Tingle.Dependabot/Dockerfile.CI \
- --label com.github.image.run.id=$GITHUB_RUN_ID \
- --label com.github.image.run.number=$GITHUB_RUN_NUMBER \
- --label com.github.image.job.id=$GITHUB_JOB \
- --label com.github.image.source.sha=$GITHUB_SHA \
- --label com.github.image.source.branch=$GITHUB_REF \
- -t "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:latest" \
- -t "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_SHORTSHA" \
- -t "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_NUGETVERSIONV2" \
- -t "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_MAJOR.$GITVERSION_MINOR" \
- -t "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_MAJOR" \
- --cache-from ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:latest \
+ --label com.github.image.run.id=${{ github.run_id }} \
+ --label com.github.image.run.number=${{ github.run_number }} \
+ --label com.github.image.job.id=${{ github.job }} \
+ --label com.github.image.source.sha=${{ github.sha }} \
+ --label com.github.image.source.branch=${{ github.ref }} \
+ -t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest" \
+ -t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.shortSha }}" \
+ -t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.nuGetVersionV2 }}" \
+ -t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.major}}.${{ steps.gitversion.outputs.minor }}" \
+ -t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.major }}" \
+ --cache-from ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest \
--build-arg BUILDKIT_INLINE_CACHE=1 \
- $GITHUB_WORKSPACE/drop/Tingle.Dependabot
+ ${{ github.workspace }}/drop/Tingle.Dependabot
- name: Log into registry
if: ${{ (github.ref == 'refs/heads/main') || (!startsWith(github.ref, 'refs/pull')) || startsWith(github.ref, 'refs/tags') }}
@@ -98,24 +135,18 @@ jobs:
- name: Push image (latest, ShortSha)
if: ${{ (github.ref == 'refs/heads/main') || startsWith(github.ref, 'refs/tags') }}
run: |
- docker push "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:latest"
- docker push "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_SHORTSHA"
+ docker push "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest"
+ docker push "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.shortSha }}"
- name: Push image (NuGetVersionV2)
if: ${{ !startsWith(github.ref, 'refs/pull') }}
- run: docker push "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_NUGETVERSIONV2"
+ run: docker push "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.nuGetVersionV2 }}"
- name: Push image (major, minor)
if: startsWith(github.ref, 'refs/tags')
run: |
- docker push "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_MAJOR.$GITVERSION_MINOR"
- docker push "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$GITVERSION_MAJOR"
-
- - name: Publish Artifact
- uses: actions/upload-artifact@v3
- with:
- path: ${{ github.workspace }}/drop/*
- name: drop
+ docker push "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.major }}.${{ steps.gitversion.outputs.minor }}"
+ docker push "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.major }}"
- name: Upload Release
if: startsWith(github.ref, 'refs/tags/')
@@ -127,3 +158,33 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
allowUpdates: true
+
+ # Deploy:
+ # runs-on: ubuntu-latest
+ # needs: Build
+ # if: ${{ github.actor != 'dependabot[bot]' && ((github.ref == 'refs/heads/main') || startsWith(github.ref, 'refs/tags')) }}
+
+ # concurrency:
+ # group: ${{ github.workflow }}-${{ github.ref }}
+ # cancel-in-progress: false # wait for previous runs to complete
+
+ # steps:
+ # - name: Download Artifact
+ # uses: actions/download-artifact@v4
+ # with:
+ # name: deploy
+ # path: ${{ github.workspace }}/deploy
+
+ # - name: Azure Login
+ # uses: azure/login@v2
+ # with:
+ # creds: ${{ secrets.AZURE_CREDENTIALS }}
+
+ # - name: Deploy
+ # uses: azure/arm-deploy@v2
+ # with:
+ # subscriptionId: ${{ env.AZURE_SUBSCRIPTION_ID }}
+ # resourceGroupName: ${{ env.AZURE_RESOURCE_GROUP }}
+ # template: '${{ github.workspace }}/deploy/main.bicep'
+ # parameters: '${{ github.workspace }}/deploy/main.parameters.json'
+ # scope: 'resourcegroup'
diff --git a/.github/workflows/updater.yml b/.github/workflows/updater.yml
index 64142734..5ff4a5df 100644
--- a/.github/workflows/updater.yml
+++ b/.github/workflows/updater.yml
@@ -14,6 +14,9 @@ on:
- main
paths:
- "updater/**"
+ - '.rubocop*.yml'
+ - '.ruby-version'
+ - 'Rakefile'
- ".github/workflows/updater.yml"
- "!docs/**"
@@ -60,12 +63,12 @@ jobs:
fetch-depth: 0 # Required for GitVersion
- name: Install GitVersion
- uses: gittools/actions/gitversion/setup@v1
+ uses: gittools/actions/gitversion/setup@v3.0.0
with:
- versionSpec: '5.x'
+ versionSpec: '6.x'
- name: Determine Version
- uses: gittools/actions/gitversion/execute@v1
+ uses: gittools/actions/gitversion/execute@v3.0.0
id: gitversion
with:
useConfigFile: true
@@ -88,27 +91,33 @@ jobs:
# remove this after at least one release tagged 'latest'
continue-on-error: true
+ - name: Get dependabot-updater image tag version
+ id: docker-base-version
+ run: |
+ tag_name=$(grep -oP "(?<=gem \"dependabot-omnibus\", \"~>).*(?=\")" updater/Gemfile)
+ tag_sha=$(curl --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' --url "https://api.github.com/repos/dependabot/dependabot-core/tags" | jq -r "[.[]|select(.name==\"v$tag_name\")][0].commit.sha")
+ echo "Using dependabot-updater image tag '$tag_sha' (v$tag_name)"
+ echo "version=$tag_sha" >> $GITHUB_OUTPUT
+
+ - name: Remove + from fullSemVer
+ id: remove_plus
+ run: |
+ cleanedFullSemVer=$(echo "${{ steps.gitversion.outputs.fullSemVer }}" | tr -d '+')
+ echo "::set-output name=cleanedFullSemVer::$cleanedFullSemVer"
+
- name: Display Variables
run: |
- echo "ECOSYSTEM: ${{ matrix.suite.ecosystem }}"
- echo "GITHUB_RUN_ID: ${{ github.run_id }}"
- echo "GITHUB_RUN_NUMBER: ${{ github.run_number }}"
- echo "GITHUB_JOB: ${{ github.job }}"
- echo "GITHUB_SHA: ${{ github.sha }}"
- echo "GITHUB_REF: ${{ github.ref }}"
- echo "GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}"
- echo "IMAGE_NAME: ${{ env.IMAGE_NAME }}"
- echo "GITVERSION_SHORT_SHA: ${{ steps.gitversion.outputs.shortSha }}"
- echo "GITVERSION_NUGET_VERSION_V2: ${{ steps.gitversion.outputs.nuGetVersionV2 }}"
- echo "GITVERSION_MAJOR: ${{ steps.gitversion.outputs.major }}"
- echo "GITVERSION_MINOR: ${{ steps.gitversion.outputs.minor }}"
-
+ echo "fullSemVer: ${{ steps.gitversion.outputs.fullSemVer }}"
+ echo "cleanedFullSemVer: ${{ steps.remove_plus.outputs.cleanedFullSemVer }}"
+
- name: Build image
run: |
docker build \
-f updater/Dockerfile \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg ECOSYSTEM=${{ matrix.suite.ecosystem }} \
+ --build-arg BASE_VERSION=${{ steps.docker-base-version.outputs.version }} \
+ --build-arg DEPENDABOT_UPDATER_VERSION=${{ steps.remove_plus.outputs.cleanedFullSemVer }} \
--label com.github.image.run.id=${{ github.run_id }} \
--label com.github.image.run.number=${{ github.run_number }} \
--label com.github.image.job.id=${{ github.job }} \
@@ -116,7 +125,7 @@ jobs:
--label com.github.image.source.branch=${{ github.ref }} \
-t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest" \
-t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.shortSha }}" \
- -t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.nuGetVersionV2 }}" \
+ -t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.remove_plus.outputs.cleanedFullSemVer }}" \
-t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.major}}.${{ steps.gitversion.outputs.minor }}" \
-t "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.major }}" \
--cache-from ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest \
@@ -134,7 +143,7 @@ jobs:
- name: Push image (NuGetVersionV2)
if: ${{ !startsWith(github.ref, 'refs/pull') }}
- run: docker push "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.gitversion.outputs.nuGetVersionV2 }}"
+ run: docker push "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.remove_plus.outputs.cleanedFullSemVer }}"
- name: Push image (major, minor)
if: startsWith(github.ref, 'refs/tags')
diff --git a/.rubocop.yml b/.rubocop.yml
index 1c595291..3a9503f0 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,6 +1,10 @@
---
+inherit_from: .rubocop_todo.yml
+
require:
- rubocop-performance
+ - rubocop-rspec
+ - rubocop-sorbet
AllCops:
DisplayCopNames: true
@@ -10,7 +14,10 @@ AllCops:
- "*/spec/fixtures/**/*"
- "vendor/**/*"
- "dry-run/**/*"
+ - "bundler/helpers/v1/patched_bundler"
+ - "bundler/helpers/spec_helpers/*"
NewCops: enable
+ TargetRubyVersion: 3.1
SuggestExtensions: false
Gemspec/DeprecatedAttributeAssignment:
Enabled: true
@@ -224,10 +231,12 @@ Performance/UnfreezeString:
Enabled: true
Performance/UriDefaultParser:
Enabled: true
-Style/AccessorGrouping:
+RSpec/IndexedLet:
Enabled: false
+Style/AccessorGrouping:
+ EnforcedStyle: 'separated'
Style/ArgumentsForwarding:
- Enabled: true
+ Enabled: false
Style/ArrayCoercion:
Enabled: false
Style/BisectedAttrAccessor:
@@ -331,6 +340,19 @@ Style/SpecialGlobalVars:
Enabled: false
Style/SelectByRegexp:
Enabled: false
+Sorbet/TrueSigil:
+ Enabled: true
+ Exclude:
+ - "**/spec/**/*"
+Sorbet/StrictSigil:
+ Exclude:
+ - "**/spec/**/*"
+Sorbet/StrongSigil:
+ Exclude:
+ - "**/spec/**/*"
+RSpec:
+ Include:
+ - "**/spec/**/*"
# TODO these were temporarily disabled during the Ruby 2.7 -> 3.1 upgrade
# in order to keep the upgrade diff small, they will be enabled/fixed in
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 00000000..64ea1d09
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,192 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config`
+# on 2024-05-17 06:52:32 UTC using RuboCop version 1.63.2.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 39
+RSpec/AnyInstance:
+ Exclude:
+ - 'bundler/helpers/v1/spec/shared_contexts.rb'
+ - 'bundler/spec/dependabot/bundler/update_checker_spec.rb'
+ - 'common/spec/dependabot/clients/codecommit_spec.rb'
+ - 'common/spec/dependabot/file_fetchers/base_spec.rb'
+ - 'common/spec/dependabot/git_commit_checker_spec.rb'
+ - 'common/spec/dependabot/pull_request_creator/message_builder_spec.rb'
+ - 'gradle/spec/dependabot/gradle/metadata_finder_spec.rb'
+ - 'maven/spec/dependabot/maven/metadata_finder_spec.rb'
+ - 'npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb'
+ - 'python/spec/dependabot/python/file_updater_spec.rb'
+ - 'updater/spec/dependabot/dependency_change_builder_spec.rb'
+ - 'updater/spec/dependabot/file_fetcher_command_spec.rb'
+
+# Offense count: 1286
+# Configuration parameters: CountAsOne.
+RSpec/ExampleLength:
+ Max: 98
+
+# Offense count: 10
+# Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly.
+# Include: **/*_spec*rb*, **/spec/**/*
+RSpec/FilePath:
+ Exclude:
+ - 'bundler/helpers/v2/spec/ruby_version_spec.rb'
+ - 'bundler/spec/dependabot/bundler/helper_spec.rb'
+ - 'common/spec/dependabot/clients/codecommit_spec.rb'
+ - 'elm/spec/dependabot/elm/update_checker/elm_19_version_resolver_spec.rb'
+ - 'nuget/spec/dependabot/nuget/nuget_config_credential_helpers_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/compatibility_checker_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/nupkg_fetcher_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/nuspec_fetcher_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/repository_finder_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/tfm_finder_spec.rb'
+
+# Offense count: 29
+# Configuration parameters: AssignmentOnly.
+RSpec/InstanceVariable:
+ Exclude:
+ - 'go_modules/spec/dependabot/go_modules/file_updater_spec.rb'
+
+# Offense count: 22
+RSpec/IteratedExpectation:
+ Exclude:
+ - 'bundler/spec/dependabot/bundler/file_updater_spec.rb'
+ - 'cargo/spec/dependabot/cargo/file_updater_spec.rb'
+ - 'composer/spec/dependabot/composer/file_updater_spec.rb'
+ - 'docker/spec/dependabot/docker/file_updater_spec.rb'
+ - 'elm/spec/dependabot/elm/file_updater_spec.rb'
+ - 'git_submodules/spec/dependabot/git_submodules/file_updater_spec.rb'
+ - 'github_actions/spec/dependabot/github_actions/file_updater_spec.rb'
+ - 'go_modules/spec/dependabot/go_modules/file_updater_spec.rb'
+ - 'gradle/spec/dependabot/gradle/file_updater_spec.rb'
+ - 'hex/spec/dependabot/hex/file_updater_spec.rb'
+ - 'maven/spec/dependabot/maven/file_updater_spec.rb'
+ - 'npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater_spec.rb'
+ - 'python/spec/dependabot/python/file_updater/requirement_file_updater_spec.rb'
+ - 'python/spec/dependabot/python/file_updater_spec.rb'
+
+
+# Offense count: 3
+RSpec/MessageChain:
+ Exclude:
+ - 'updater/spec/dependabot/api_client_spec.rb'
+ - 'updater/spec/dependabot/sentry/exception_sanitizer_processor_spec.rb'
+ - 'updater/spec/dependabot/service_spec.rb'
+
+# Offense count: 395
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: have_received, receive
+RSpec/MessageSpies:
+ Enabled: false
+
+# Offense count: 1380
+RSpec/MultipleExpectations:
+ Max: 17
+
+# Offense count: 5339
+# Configuration parameters: AllowSubject.
+RSpec/MultipleMemoizedHelpers:
+ Max: 30
+
+# Offense count: 3871
+# Configuration parameters: AllowedGroups.
+RSpec/NestedGroups:
+ Max: 8
+
+# Offense count: 16
+RSpec/OverwritingSetup:
+ Exclude:
+ - 'bundler/spec/dependabot/bundler/update_checker_spec.rb'
+ - 'common/spec/dependabot/pull_request_creator/message_builder_spec.rb'
+ - 'docker/spec/dependabot/docker/file_updater_spec.rb'
+ - 'gradle/spec/dependabot/gradle/update_checker/version_finder_spec.rb'
+ - 'maven/spec/dependabot/maven/update_checker/version_finder_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/repository_finder_spec.rb'
+ - 'python/spec/dependabot/python/update_checker/pip_version_resolver_spec.rb'
+ - 'python/spec/dependabot/python/update_checker_spec.rb'
+ - 'terraform/spec/dependabot/terraform/file_parser_spec.rb'
+ - 'updater/spec/dependabot/dependency_snapshot_spec.rb'
+
+# Offense count: 4
+RSpec/RepeatedExample:
+ Exclude:
+ - 'nuget/spec/dependabot/nuget/update_checker/repository_finder_spec.rb'
+ - 'terraform/spec/dependabot/terraform/registry_client_spec.rb'
+
+# Offense count: 8
+RSpec/RepeatedExampleGroupBody:
+ Exclude:
+ - 'cargo/spec/dependabot/cargo/file_fetcher_spec.rb'
+ - 'composer/spec/dependabot/composer/update_checker/latest_version_finder_spec.rb'
+ - 'maven/spec/dependabot/maven_spec.rb'
+ - 'pub/spec/dependabot/pub/requirements_spec.rb'
+
+# Offense count: 45
+RSpec/RepeatedExampleGroupDescription:
+ Enabled: false
+
+# Offense count: 10
+# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
+# Include: **/*_spec.rb
+RSpec/SpecFilePathFormat:
+ Exclude:
+ - 'bundler/helpers/v2/spec/ruby_version_spec.rb'
+ - 'bundler/spec/dependabot/bundler/helper_spec.rb'
+ - 'common/spec/dependabot/clients/codecommit_spec.rb'
+ - 'elm/spec/dependabot/elm/update_checker/elm_19_version_resolver_spec.rb'
+ - 'nuget/spec/dependabot/nuget/nuget_config_credential_helpers_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/compatibility_checker_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/nupkg_fetcher_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/nuspec_fetcher_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/repository_finder_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker/tfm_finder_spec.rb'
+
+# Offense count: 50
+RSpec/StubbedMock:
+ Exclude:
+ - 'bundler/spec/dependabot/bundler/update_checker_spec.rb'
+ - 'common/spec/dependabot/pull_request_creator/branch_namer_spec.rb'
+ - 'common/spec/dependabot/pull_request_creator_spec.rb'
+ - 'common/spec/dependabot/pull_request_updater_spec.rb'
+ - 'docker/spec/dependabot/docker/utils/credentials_finder_spec.rb'
+ - 'go_modules/spec/dependabot/go_modules/file_updater_spec.rb'
+ - 'npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb'
+ - 'nuget/spec/dependabot/nuget/update_checker_spec.rb'
+ - 'python/spec/dependabot/python/file_updater_spec.rb'
+ - 'python/spec/dependabot/python/update_checker_spec.rb'
+ - 'updater/spec/dependabot/update_files_command_spec.rb'
+ - 'updater/spec/dependabot/updater_spec.rb'
+
+# Offense count: 28
+RSpec/SubjectStub:
+ Exclude:
+ - 'common/spec/dependabot/metadata_finders/base/commits_finder_spec.rb'
+ - 'common/spec/dependabot/metadata_finders/base_spec.rb'
+ - 'common/spec/dependabot/pull_request_updater/azure_spec.rb'
+ - 'updater/spec/dependabot/environment_spec.rb'
+ - 'updater/spec/dependabot/sentry/exception_sanitizer_processor_spec.rb'
+ - 'updater/spec/dependabot/sentry/sentry_context_processor_spec.rb'
+
+# Offense count: 2
+RSpec/UnspecifiedException:
+ Exclude:
+ - 'updater/spec/dependabot/job_spec.rb'
+
+# Offense count: 26
+# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
+RSpec/VerifiedDoubles:
+ Exclude:
+ - 'common/spec/dependabot/git_commit_checker_spec.rb'
+ - 'common/spec/dependabot/git_metadata_fetcher_spec.rb'
+ - 'common/spec/dependabot/metadata_finders/base/commits_finder_spec.rb'
+ - 'common/spec/dependabot/pull_request_creator/branch_namer_spec.rb'
+ - 'common/spec/dependabot/pull_request_creator/github_spec.rb'
+ - 'github_actions/spec/dependabot/github_actions/update_checker_spec.rb'
+ - 'go_modules/spec/dependabot/go_modules/file_updater/go_mod_updater_spec.rb'
+ - 'go_modules/spec/dependabot/go_modules/file_updater_spec.rb'
+ - 'python/spec/dependabot/python/file_updater_spec.rb'
+ - 'updater/spec/dependabot/file_fetcher_command_spec.rb'
+ - 'updater/spec/dependabot/sentry/sentry_context_processor_spec.rb'
+ - 'updater/spec/dependabot/update_files_command_spec.rb'
diff --git a/.ruby-version b/.ruby-version
index 0aec50e6..bea438e9 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-3.1.4
+3.3.1
diff --git a/.vscode/settings.json b/.vscode/settings.json
index a2546ddf..75b8bdd4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"azuredevops",
+ "devcontainers",
"fabrikam",
"Kubernetes"
]
diff --git a/GitVersion.yml b/GitVersion.yml
index 491b6000..9ee53ad6 100644
--- a/GitVersion.yml
+++ b/GitVersion.yml
@@ -1,3 +1,6 @@
-# Only changing the mode from 'ContinuousDelivery' to 'ContinuousDeployment' results in better numbers (no '+' in them)
-# To get the default configuration comment out all the contents of this file and run 'gitversion /showconfig'
-mode: ContinuousDeployment
+# To get the effective configuration run 'gitversion /showconfig'
+branches:
+ pull-request:
+ label: pr
+ main:
+ label: ci
diff --git a/README.md b/README.md
index f79dda94..5a87b695 100644
--- a/README.md
+++ b/README.md
@@ -11,13 +11,13 @@ In this repository you'll find:
1. Dependabot [updater](./updater) in Ruby. See [docs](./docs/updater.md).
2. Dockerfile and build/image for running the updater via Docker [here](./updater/Dockerfile).
3. Dependabot [server](./server/) in .NET/C#. See [docs](./docs/server.md).
-4. Azure DevOps [Extension](https://marketplace.visualstudio.com/items?itemName=tingle-software.dependabot) and [source](./extension).
+4. Azure DevOps [Extension](https://marketplace.visualstudio.com/items?itemName=tingle-software.dependabot) and [source](./extension). See [docs](./docs/extension.md).
> The hosted version is available to sponsors (most, but not all). It includes hustle free runs where the infrastructure is maintained for you. Much like the GitHub hosted version. Alternatively, you can run and host your own [server](./docs/server.md). Once you sponsor, you can send out an email to an maintainer or wait till they reach out. This is meant to ease the burden until GitHub/Azure/Microsoft can get it working natively (which could also be never) and hopefully for free.
## Using a configuration file
-Similar to the GitHub native version where you add a `.github/dependabot.yml` file, this repository adds support for the same official [configuration options](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file) via a file located at `.github/dependabot.yml`. This support is only available in the Azure DevOps extension and the [managed version](https://managd.dev). However, the extension does not currently support automatically picking up the file, a pipeline is still required. See [docs](./extension/README.md#usage).
+Similar to the GitHub native version where you add a `.azuredevops/dependabot.yml` or `.github/dependabot.yml` file, this repository adds support for the same official [configuration options](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file) via a file located at `.azuredevops/dependabot.yml` or `.github/dependabot.yml`. This support is only available in the Azure DevOps extension and the [managed version](https://managd.dev). However, the extension does not currently support automatically picking up the file, a pipeline is still required. See [docs](./extension/README.md#usage).
We are well aware that ignore conditions are not explicitly passed and passed on from the extension/server to the container. It is intentional. The ruby script in the docker container does it automatically. If you are having issues, search for related issues such as https://github.com/tinglesoftware/dependabot-azure-devops/pull/582 before creating a new issue. You can also test against various reproductions such as https://dev.azure.com/tingle/dependabot/_git/repro-582
@@ -41,26 +41,69 @@ registries:
my-analyzers:
type: nuget-feed
url: https://dev.azure.com/organization2/_packaging/my-analyzers/nuget/v3/index.json
- token: PAT:${{ANOTHER_PAT}}
+ token: PAT:${{MY_OTHER_PAT}}
artifactory:
type: nuget-feed
url: https://artifactory.com/api/nuget/v3/myfeed
- token: PAT:${{DEPENDABOT_ARTIFACTORY_PAT}}
+ token: PAT:${{MY_ARTIFACTORY_PAT}}
+ telerik:
+ type: nuget-feed
+ url: https://nuget.telerik.com/v3/index.json
+ username: ${{MY_TELERIK_USERNAME}}
+ password: ${{MY_TELERIK_PASSWORD}}
+ token: ${{MY_TELERIK_USERNAME}}:${{MY_TELERIK_PASSWORD}}
updates:
-...
+ ...
```
Note:
1. `${{VARIABLE_NAME}}` notation is used liked described [here](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/managing-encrypted-secrets-for-dependabot)
-BUT the values will be used from Environment Variables in the pipeline/environment. Template variables are not supported for this replacement. Replacement only works for values considered secret in the registries section i.e. `password`, `token`, and `key`
-
-2. When using a token the notation should be `PAT:${{VARIABLE_NAME}}`. Otherwise the wrong authentication mechanism is used by dependabot, see [here](https://github.com/tinglesoftware/dependabot-azure-devops/issues/50).
-
-When working with Azure Artifacts, some extra permission steps need to be done:
-
-1. The PAT should have *Packaging Read* permission.
-2. The user owning the PAT must be granted permissions to access the feed either directly or via a group. An easy way for this is to give `Contributor` permissions the `[{project_name}]\Contributors` group under the `Feed Settings -> Permissions` page. The page has the url format: `https://dev.azure.com/{organization}/{project}/_packaging?_a=settings&feed={feed-name}&view=permissions`.
+BUT the values will be used from Environment Variables in the pipeline/environment. Template variables are not supported for this replacement. Replacement only works for values considered secret in the registries section i.e. `username`, `password`, `token`, and `key`
+
+2. When using an Azure DevOps Artifact feed, only the `token` property is required. The token notation should be `PAT:${{VARIABLE_NAME}}` otherwise the wrong authentication mechanism is used by Dependabot, see [here](https://github.com/tinglesoftware/dependabot-azure-devops/issues/50) for more details.
+When working with Azure DevOps Artifacts, some extra permission steps need to be done:
+
+ 1. The PAT should have *Packaging Read* permission.
+ 2. The user owning the PAT must be granted permissions to access the feed either directly or via a group. An easy way for this is to give `Contributor` permissions the `[{project_name}]\Contributors` group under the `Feed Settings -> Permissions` page. The page has the url format: `https://dev.azure.com/{organization}/{project}/_packaging?_a=settings&feed={feed-name}&view=permissions`.
+
+3. When using a NuGet package server secured with basic auth, the `username`, `password`, and `token` properties are all required. The token notation should be `${{USERNAME}}:${{PASSWORD}}`, see [here](https://github.com/tinglesoftware/dependabot-azure-devops/issues/1232#issuecomment-2247616424) for more details.
+
+
+4. When your project contains a `nuget.config` file with custom package source configuration, the `key` property is required for each nuget-feed registry. The key must match between `dependabot.yml` and `nuget.config` otherwise the package source will be duplicated, package source mappings will be ignored, and auth errors will occur during dependency discovery.
+
+ If your `nuget.config` looks like this:
+ ```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ```
+
+ Then your `dependabot.yml` registry should look like this:
+
+ ```yml
+ version: 2
+ registries:
+ my-org:
+ type: nuget-feed
+ key: my-organisation1-nuget
+ url: https://dev.azure.com/my-organization/_packaging/my-nuget-feed/nuget/v3/index.json
+ token: PAT:${{MY_DEPENDABOT_ADO_PAT}}
+ ```
+
## Security Advisories, Vulnerabilities, and Updates
@@ -68,7 +111,14 @@ Security-only updates ia a mechanism to only create pull requests for dependenci
A GitHub access token with `public_repo` access is required to perform the GitHub GraphQL for `securityVulnerabilities`.
-### Acknowledgements
+## Development Guide
+
+If you'd like to contribute to the project or just run it locally, view our development guides for:
+
+- [Azure DevOps extension](./docs/extension.md#development-guide)
+- [Dependabot updater](./docs/updater.md#development-guide)
+
+## Acknowledgements
The work in this repository is based on inspired and occasionally guided by some predecessors in the same area:
@@ -78,6 +128,6 @@ The work in this repository is based on inspired and occasionally guided by some
4. andrcun's work on GitLab: [code](https://gitlab.com/dependabot-gitlab/dependabot)
5. WeWork's work for GitLab: [code](https://github.com/wemake-services/kira-dependencies)
-### Issues & Comments
+## Issues & Comments
Please leave all comments, bugs, requests, and issues on the Issues page. We'll respond to your request ASAP!
diff --git a/Rakefile b/Rakefile
index 5fad7232..7bd302a5 100644
--- a/Rakefile
+++ b/Rakefile
@@ -7,32 +7,34 @@ require "uri"
require "json"
require "rubygems/package"
require "bundler"
-# require "./common/lib/dependabot"
+require "./common/lib/dependabot"
require "yaml"
# ./dependabot-core.gemspec is purposefully excluded from this list
# because it's an empty gem as a placeholder to prevent namesquatting.
-# GEMSPECS = %w(
-# common/dependabot-common.gemspec
-# go_modules/dependabot-go_modules.gemspec
-# terraform/dependabot-terraform.gemspec
-# docker/dependabot-docker.gemspec
-# git_submodules/dependabot-git_submodules.gemspec
-# github_actions/dependabot-github_actions.gemspec
-# nuget/dependabot-nuget.gemspec
-# gradle/dependabot-gradle.gemspec
-# maven/dependabot-maven.gemspec
-# bundler/dependabot-bundler.gemspec
-# elm/dependabot-elm.gemspec
-# cargo/dependabot-cargo.gemspec
-# npm_and_yarn/dependabot-npm_and_yarn.gemspec
-# composer/dependabot-composer.gemspec
-# hex/dependabot-hex.gemspec
-# python/dependabot-python.gemspec
-# pub/dependabot-pub.gemspec
-# omnibus/dependabot-omnibus.gemspec
-# swift/dependabot-swift.gemspec
-# ).freeze
+GEMSPECS = %w(
+ common/dependabot-common.gemspec
+ go_modules/dependabot-go_modules.gemspec
+ terraform/dependabot-terraform.gemspec
+ docker/dependabot-docker.gemspec
+ git_submodules/dependabot-git_submodules.gemspec
+ github_actions/dependabot-github_actions.gemspec
+ nuget/dependabot-nuget.gemspec
+ gradle/dependabot-gradle.gemspec
+ maven/dependabot-maven.gemspec
+ bundler/dependabot-bundler.gemspec
+ elm/dependabot-elm.gemspec
+ cargo/dependabot-cargo.gemspec
+ npm_and_yarn/dependabot-npm_and_yarn.gemspec
+ composer/dependabot-composer.gemspec
+ hex/dependabot-hex.gemspec
+ python/dependabot-python.gemspec
+ pub/dependabot-pub.gemspec
+ omnibus/dependabot-omnibus.gemspec
+ silent/dependabot-silent.gemspec
+ swift/dependabot-swift.gemspec
+ devcontainers/dependabot-devcontainers.gemspec
+).freeze
def run_command(command)
puts "> #{command}"
@@ -102,8 +104,8 @@ end
namespace :rubocop do
task :sort do
File.write(
- ".rubocop.yml",
- YAML.load_file(".rubocop.yml").sort_by_key(true).to_yaml
+ "omnibus/.rubocop.yml",
+ YAML.load_file("omnibus/.rubocop.yml").sort_by_key(true).to_yaml
)
end
end
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index a9476130..00000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,125 +0,0 @@
-name: '$(Date:yyyy)-$(Date:MM)-$(Date:dd)$(Rev:.r)'
-
-trigger:
- batch: true
- branches:
- include:
- - main
- - refs/tags/*
-
-pr:
- branches:
- include:
- - main
- drafts: false
- autoCancel: true
- paths:
- include:
- - server/**
- - azure-pipelines.yml
- exclude:
- - docs/**
-
-stages:
-- stage: Build
- jobs:
- - job: Build
-
- pool:
- vmImage: 'ubuntu-latest'
-
- steps:
- - checkout: self
- fetchDepth: 0 # no shallow fetch, we need all the history for GitVersion to work
-
- - task: gitversion/setup@0
- displayName: Setup GitVersion
- inputs:
- versionSpec: '5.x'
-
- - task: gitversion/execute@0
- displayName: Determine Version
- name: GitVersion
- inputs:
- useConfigFile: true
- configFilePath: '$(Build.SourcesDirectory)/GitVersion.yml'
-
- - task: replacetokens@3
- displayName: 'Replace tokens in main.parameters.json'
- inputs:
- rootDirectory: '$(Build.SourcesDirectory)/server'
- targetFiles: 'main.parameters.json'
- actionOnMissing: fail
- verbosity: detailed
-
- # Compile bicep file to JSON to make it independent
- - script: |
- bicep build main.bicep --outfile main.json
- displayName: 'Compile bicep file'
- workingDirectory: $(Build.SourcesDirectory)/server
-
- - task: CopyFiles@2
- displayName: 'Copy files to drop folder'
- inputs:
- SourceFolder: $(Build.SourcesDirectory)/server
- Contents: |
- *.json
- TargetFolder: $(Build.ArtifactStagingDirectory)/drop
-
- - task: PublishPipelineArtifact@1
- displayName: "Publish drop artifact"
- inputs:
- targetPath: $(Build.ArtifactStagingDirectory)/drop
- artifactName: "drop"
-
- - task: AzureCLI@2
- displayName: "Validate Resources"
- condition: |
- and
- (
- succeeded(),
- startsWith(variables['Build.SourceBranch'], 'refs/pull/')
- )
- inputs:
- azureSubscription: $(azureConnection)
- scriptType: bash
- scriptLocation: inlineScript
- inlineScript: >
- az deployment group validate
- --resource-group '$(resourceGroupName)'
- --template-file $(Build.SourcesDirectory)/server/main.json
- --parameters $(Build.SourcesDirectory)/server/main.parameters.json
-
-- stage: Deploy
- displayName: Deploy
- dependsOn: Build
- # only deploy non pr branches
- condition: |
- and
- (
- succeeded(),
- not(startsWith(variables['Build.SourceBranch'], 'refs/pull/'))
- )
- jobs:
- - deployment: Deploy
- environment: Dependabot
-
- pool:
- vmImage: 'ubuntu-latest'
-
- strategy:
- runOnce:
- deploy:
- steps:
-
- - task: AzureCLI@2
- displayName: "Deploy Resources"
- inputs:
- azureSubscription: $(azureConnection)
- scriptType: bash
- scriptLocation: inlineScript
- inlineScript: >
- az deployment group create
- --resource-group '$(resourceGroupName)'
- --template-file $(Pipeline.Workspace)/drop/main.json
- --parameters $(Pipeline.Workspace)/drop/main.parameters.json
diff --git a/docs/extension.md b/docs/extension.md
new file mode 100644
index 00000000..0f02cac4
--- /dev/null
+++ b/docs/extension.md
@@ -0,0 +1,46 @@
+
+# Table of Contents
+
+- [Using the extension](#using-the-extension)
+- [Development guide](#development-guide)
+ - [Getting the development environment ready](#getting-the-development-environment-ready)
+ - [Building the extension](#building-the-extension)
+ - [Installing the extension](#installing-the-extension)
+ - [Running the unit tests](#running-the-unit-tests)
+
+# Using the extension
+
+See the extension [README.md](../extension/README.md).
+
+# Development guide
+
+## Getting the development environment ready
+First, ensure you have [Node.js](https://docs.docker.com/engine/install/) v18+ installed.
+Next, install project dependencies with npm:
+
+```bash
+cd extension
+npm install
+```
+
+## Building the extension
+```bash
+cd extension
+npm run build:prod
+npm run mv:prod
+```
+
+To generate the Azure DevOps `.vsix` extension package for testing, you'll first need to [create a publisher account](https://learn.microsoft.com/en-us/azure/devops/extend/publish/overview?view=azure-devops#create-a-publisher) on the [Visual Studio Marketplace Publishing Portal](https://marketplace.visualstudio.com/manage/createpublisher?managePageRedirect=true). After this, override your publisher ID below and generate the extension with:
+
+```bash
+npx tfx-cli extension create --overrides-file overrides.local.json --override "{\"publisher\": \"your-publisher-id-here\"}" --json5
+```
+
+## Installing the extension
+To test the extension in Azure DevOps, you'll first need to build the extension `.vsix` file (see above). After this, [publish your extension](https://learn.microsoft.com/en-us/azure/devops/extend/publish/overview?view=azure-devops#publish-your-extension), then [install your extension](https://learn.microsoft.com/en-us/azure/devops/extend/publish/overview?view=azure-devops#install-your-extension).
+
+## Running the unit tests
+```bash
+cd extension
+npm run test
+```
\ No newline at end of file
diff --git a/docs/server.md b/docs/server.md
index 99444912..f6352872 100644
--- a/docs/server.md
+++ b/docs/server.md
@@ -1,4 +1,16 @@
-# Running the server
+
+# Table of Contents
+
+- [Why should I use the server?](#why-should-i-use-the-server)
+- [Composition](#composition)
+- [Deployment](#deployment)
+ - [Single click deployment](#single-click-deployment)
+ - [Deployment Parameters](#deployment-parameters)
+ - [Deployment with CLI](#deployment-with-cli)
+ - [Service Hooks and Subscriptions](#service-hooks-and-subscriptions)
+- [Keeping updated](#keeping-updated)
+
+# Why should I use the server?
Running multiple pipelines in Azure DevOps can quickly become overwhelming especially when you have many repositories. In some cases, you might want to keep one pipeline to manage multiple repositories but that can quickly get opaque with over-generalization in templates.
@@ -11,17 +23,7 @@ The extension is a good place to start but when you need to roll out across all
5. Management UI similar to GitHub-hosted version. *Coming soon*
6. Zero maintenance, after initial deployment. Also cheap.
-## Documentation
-
-- [Composition](#composition)
-- [Deployment](#deployment)
- - [Deployment with Buttons](#single-click-deployment)
- - [Deployment Parameters](#deployment-parameters)
- - [Deployment with CLI](#deployment-with-cli)
- - [Service Hooks and Subscriptions](#service-hooks-and-subscriptions)
-- [Keeping updated](#keeping-updated)
-
-## Composition
+# Composition
The server is deployed in your Azure Subscription. To function properly, the server component is run as a single application in Azure Container Apps (Consumption Tier), backed by a single Azure SQL Server database (Basic tier), a single Service Bus namespace (Basic tier), and jobs scheduled using Azure Container Instances (Consumption Tier).
@@ -33,9 +35,9 @@ The current cost we have internally for this in the `westeurope` region:
- Azure Container Apps: approx $15/month given about 80% idle time
- **Total: approx $22/month** (expected to reduce when jobs are added to Azure Container Apps, see https://github.com/microsoft/azure-container-apps/issues/526)
-## Deployment
+# Deployment
-### Single click deployment
+## Single click deployment
The easiest means of deployment is to use the relevant button below.
@@ -45,7 +47,7 @@ The easiest means of deployment is to use the relevant button below.
You can also use the [server/main.json](../server/main.json) file, [server/main.bicep](../server/main.bicep) file, or pull either file from the [latest release](https://github.com/tinglesoftware/dependabot-azure-devops/releases/latest). You will need an Azure subscription and a resource group to deploy to.
-### Deployment Parameters
+## Deployment Parameters
The deployment exposes the following parameters that can be tuned to suit the setup.
@@ -59,7 +61,7 @@ The deployment exposes the following parameters that can be tuned to suit the se
> The template includes a User Assigned Managed Identity, which is used when performing Azure Resource Manager operations such as deletions. In the deployment it creates the role assignments that it needs. These role assignments are on the resource group that you deploy to.
-### Deployment with CLI
+## Deployment with CLI
> Ensure the Azure CLI tools are installed and that you are logged in.
@@ -81,11 +83,11 @@ The script becomes:
```bash
az deployment group create --resource-group DEPENDABOT \
--template-file main.bicep \
- --parameters dependabot.parameters.json \
+ --parameters main.parameters.json \
--confirm-with-what-if
```
-The parameters file (`dependabot.parameters.json`):
+The parameters file (`main.parameters.json`):
```json
{
@@ -105,11 +107,11 @@ The parameters file (`dependabot.parameters.json`):
}
```
-### Service Hooks and Subscriptions
+## Service Hooks and Subscriptions
To enable automatic pickup of configuration files, merge conflict resolution and commands via comments, subscriptions need to be setup on Azure DevOps. You should let the application create them on startup to because it is easier. See [code](https://github.com/tinglesoftware/dependabot-azure-devops/blob/b4e87bfeea133b8e9fa278c98157b7a0123bfdd3/server/Tingle.Dependabot/Workflow/AzureDevOpsProvider.cs#L18-L21) for the list of events subscribed to.
-## Keeping updated
+# Keeping updated
If you wish to keep your deployment updated, you can create a private repository with this one as a git submodule, configure dependabot to update it then add a new workflow that deploys to your preferred host using a manual trigger (or one of your choice).
diff --git a/docs/updater.md b/docs/updater.md
index f3ccfad5..03dc43b4 100644
--- a/docs/updater.md
+++ b/docs/updater.md
@@ -1,3 +1,15 @@
+
+# Table of Contents
+
+- [Running the updater](#running-the-updater)
+ - [Environment variables](#environment-variables)
+- [Development guide](#development-guide)
+ - [Getting the development environment ready](#getting-the-development-environment-ready)
+ - [Building the Docker image](#building-the-docker-image)
+ - [Running your code changes](#running-your-code-changes)
+ - [Running the code linter](#running-the-code-linter)
+ - [Running the unit tests](#running-the-unit-tests)
+
# Running the updater
First, you need to pull the docker image locally to your machine:
@@ -6,23 +18,16 @@ First, you need to pull the docker image locally to your machine:
docker pull ghcr.io/tinglesoftware/dependabot-updater-
```
-Next create and run a container from the image:
+Next create and run a container from the image. The full list of container options are detailed in [Environment variables](#environment-variables); at minimum the command should be:
+
```bash
docker run --rm -t \
-e GITHUB_ACCESS_TOKEN= \
-e DEPENDABOT_PACKAGE_MANAGER= \
- -e DEPENDABOT_DIRECTORY=/ \
+ -e DEPENDABOT_DIRECTORY= \
-e DEPENDABOT_TARGET_BRANCH= \
- -e DEPENDABOT_VERSIONING_STRATEGY= \
- -e DEPENDABOT_OPEN_PULL_REQUESTS_LIMIT=10 \
-e DEPENDABOT_EXTRA_CREDENTIALS= \
- -e DEPENDABOT_ALLOW_CONDITIONS= \
- -e DEPENDABOT_IGNORE_CONDITIONS= \
- -e DEPENDABOT_COMMIT_MESSAGE_OPTIONS= \
- -e DEPENDABOT_BRANCH_NAME_SEPARATOR= \
- -e DEPENDABOT_MILESTONE= \
- -e DEPENDABOT_UPDATER_OPTIONS= \
-e AZURE_PROTOCOL= \
-e AZURE_HOSTNAME= \
-e AZURE_PORT= \
@@ -31,9 +36,6 @@ docker run --rm -t \
-e AZURE_ORGANIZATION= \
-e AZURE_PROJECT= \
-e AZURE_REPOSITORY= \
- -e AZURE_SET_AUTO_COMPLETE= \
- -e AZURE_AUTO_APPROVE_PR= \
- -e AZURE_AUTO_APPROVE_USER_TOKEN= \
ghcr.io/tinglesoftware/dependabot-updater- update_script
```
@@ -45,23 +47,12 @@ docker run --rm -t \
-e DEPENDABOT_PACKAGE_MANAGER=nuget \
-e DEPENDABOT_DIRECTORY=/ \
-e DEPENDABOT_TARGET_BRANCH=main \
- -e DEPENDABOT_VERSIONING_STRATEGY=auto \
- -e DEPENDABOT_OPEN_PULL_REQUESTS_LIMIT=10 \
-e DEPENDABOT_EXTRA_CREDENTIALS='[{"type":"npm_registry","token":"","registry":"npm.fontawesome.com"}]' \
- -e DEPENDABOT_ALLOW_CONDITIONS='[{"dependency-name":"django*","dependency-type":"direct"}]' \
- -e DEPENDABOT_IGNORE_CONDITIONS='[{"dependency-name":"@types/*"}]' \
- -e DEPENDABOT_COMMIT_MESSAGE_OPTIONS='{"prefix":"(dependabot)"}' \
- -e DEPENDABOT_BRANCH_NAME_SEPARATOR='/' \
- -e DEPENDABOT_MILESTONE=123 \
- -e DEPENDABOT_UPDATER_OPTIONS='goprivate=true,kubernetes_updates=true' \
-e AZURE_HOSTNAME=dev.azure.com \
-e AZURE_ACCESS_TOKEN=abcd..efgh \
-e AZURE_ORGANIZATION=tinglesoftware \
-e AZURE_PROJECT=oss \
-e AZURE_REPOSITORY=repro-411 \
- -e AZURE_SET_AUTO_COMPLETE=true \
- -e AZURE_AUTO_APPROVE_PR=true \
- -e AZURE_AUTO_APPROVE_USER_TOKEN=ijkl..mnop \
ghcr.io/tinglesoftware/dependabot-updater-nuget update_script
```
@@ -73,15 +64,7 @@ docker run --rm -t \
-e DEPENDABOT_PACKAGE_MANAGER=nuget \
-e DEPENDABOT_DIRECTORY=/ \
-e DEPENDABOT_TARGET_BRANCH=main \
- -e DEPENDABOT_VERSIONING_STRATEGY=auto \
- -e DEPENDABOT_OPEN_PULL_REQUESTS_LIMIT=10 \
-e DEPENDABOT_EXTRA_CREDENTIALS='[{"type":"npm_registry","token":"","registry":"npm.fontawesome.com"}]' \
- -e DEPENDABOT_ALLOW_CONDITIONS='[{"dependency-name":"django*","dependency-type":"direct"}]' \
- -e DEPENDABOT_IGNORE_CONDITIONS='[{"dependency-name":"@types/*"}]' \
- -e DEPENDABOT_COMMIT_MESSAGE_OPTIONS='{"prefix":"(dependabot)"}' \
- -e DEPENDABOT_BRANCH_NAME_SEPARATOR='/' \
- -e DEPENDABOT_MILESTONE=123 \
- -e DEPENDABOT_UPDATER_OPTIONS='goprivate=true,kubernetes_updates=true' \
-e AZURE_PROTOCOL=http \
-e AZURE_HOSTNAME=my-devops.com \
-e AZURE_PORT=8080 \
@@ -90,9 +73,6 @@ docker run --rm -t \
-e AZURE_ORGANIZATION=tinglesoftware \
-e AZURE_PROJECT=oss \
-e AZURE_REPOSITORY=repro-411 \
- -e AZURE_SET_AUTO_COMPLETE=true \
- -e AZURE_AUTO_APPROVE_PR=true \
- -e AZURE_AUTO_APPROVE_USER_TOKEN=ijkl..mnop \
ghcr.io/tinglesoftware/dependabot-updater-nuget update_script
```
@@ -100,41 +80,99 @@ docker run --rm -t \
To run the script, some environment variables are required.
-|Variable Name|Description|
-|--|--|
-|GITHUB_ACCESS_TOKEN|**_Optional_**. The GitHub token (classic) for authenticating requests against GitHub public repositories. This is useful to avoid rate limiting errors. The token must include permissions to read public repositories. See the [documentation](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token) for more on Personal Access Tokens.|
-|DEPENDABOT_PACKAGE_MANAGER|**_Required_**. The type of packages to check for dependency upgrades. Examples: `nuget`, `maven`, `gradle`, `npm_and_yarn`, etc. See the [updated-script](./script/update_script.rb) or [docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem) for more.|
-|DEPENDABOT_DIRECTORY|**_Optional_**. The directory in which dependencies are to be checked. When not specified, the root of the repository (denoted as '/') is used.|
-|DEPENDABOT_TARGET_BRANCH|**_Optional_**. The branch to be targeted when creating a pull request. When not specified, Dependabot will resolve the default branch of the repository.|
-|DEPENDABOT_VERSIONING_STRATEGY|**_Optional_**. The versioning strategy to use. See [official docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#versioning-strategy) for the allowed values|
-|DEPENDABOT_OPEN_PULL_REQUESTS_LIMIT|**_Optional_**. The maximum number of open pull requests to have at any one time. Defaults to 5. Setting to 0 implies security only updates.|
-|DEPENDABOT_EXTRA_CREDENTIALS|**_Optional_**. The extra credentials in JSON format. Extra credentials can be used to access private NuGet feeds, docker registries, maven repositories, etc. For example a private registry authentication (For example FontAwesome Pro: `[{"type":"npm_registry","token":"","registry":"npm.fontawesome.com"}]`)|
-|DEPENDABOT_ALLOW_CONDITIONS|**_Optional_**. The dependencies whose updates are allowed, in JSON format. This can be used to control which packages can be updated. For example: `[{\"dependency-name\":"django*",\"dependency-type\":\"direct\"}]`. See [official docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#allow) for more.|
-|DEPENDABOT_IGNORE_CONDITIONS|**_Optional_**. The dependencies to be ignored, in JSON format. This can be used to control which packages can be updated. For example: `[{\"dependency-name\":\"express\",\"versions\":[\"4.x\",\"5.x\"]}]`. See [official docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#ignore) for more.|
-|DEPENDABOT_COMMIT_MESSAGE_OPTIONS|**_Optional_**. The commit message options, in JSON format. For example: `{\"prefix\":\"(dependabot)\"}`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#commit-message) for more.|
-|DEPENDABOT_LABELS|**_Optional_**. The custom labels to be used, in JSON format. This can be used to override the default values. For example: `[\"npm dependencies\",\"triage-board\"]`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/customizing-dependency-updates#setting-custom-labels) for more.|
-|DEPENDABOT_REVIEWERS|**_Optional_**. The identifiers of the users to review the pull requests, in JSON format. These shall be added as optional approvers. For example: `[\"23d9f23d-981e-4a0c-a975-8e5c665914ec\",\"62b67ef1-58e9-4be9-83d3-690a6fc67d6b\"]`.
-|DEPENDABOT_ASSIGNEES|**_Optional_**. The identifiers of the users to be assigned to the pull requests, in JSON format. These shall be added as required approvers. For example: `[\"be9321e2-f404-4ffa-8d6b-44efddb04865\"]`. |
-|DEPENDABOT_BRANCH_NAME_SEPARATOR|**_Optional_**. The separator to use in created branches. For example: `-`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#pull-request-branch-nameseparator) for more.|
-|DEPENDABOT_REJECT_EXTERNAL_CODE|**_Optional_**. Determines if the execution external code is allowed. Defaults to `false`.|
-|DEPENDABOT_FAIL_ON_EXCEPTION|**_Optional_**. Determines if the execution should fail when an exception occurs. Defaults to `true`.|
-|DEPENDABOT_SECURITY_ADVISORIES_FILE|**_Optional_**. The absolute file path containing security advisories in JSON format. For example: `/mnt/security_advisories/nuget-2022-12-13.json`|
-|DEPENDABOT_EXCLUDE_REQUIREMENTS_TO_UNLOCK|**_Optional_**. Exclude certain dependency updates requirements. See list of allowed values [here](https://github.com/dependabot/dependabot-core/issues/600#issuecomment-407808103). Useful if you have lots of dependencies and the update script too slow. The values provided are space-separated. Example: `own all` to only use the `none` version requirement.|
-|DEPENDABOT_MILESTONE|**_Optional_**. The identifier of the work item to be linked to the Pull Requests that dependabot creates.|
-|DEPENDABOT_UPDATER_OPTIONS|**_Optional_**. Comma separated list of updater options; available options depend on PACKAGE_MANAGER. Example: `goprivate=true,kubernetes_updates=true`.|
-|DEPENDABOT_SKIP_PULL_REQUESTS|**_Optional_**. Determines whether to skip creation and updating of pull requests. When set to `true` the logic to update the dependencies is executed but the actual Pull Requests are not created/updated. This is useful for debugging. Defaults to `false`.|
-|DEPENDABOT_AUTHOR_EMAIL|**_Optional_**. The email address to use for the change commit author, can be used e.g. in private Azure DevOps Server deployments to associate the committer with an existing account, to provide a profile picture.|
-|DEPENDABOT_AUTHOR_NAME|**_Optional_**. The display name to use for the change commit author.|
-|AZURE_PROTOCOL|**_Optional_**. The transport protocol (`http` or `https`) used by your Azure DevOps installation. Defaults to `https`.|
-|AZURE_HOSTNAME|**_Optional_**. The hostname of the where the organization is hosted. Defaults to `dev.azure.com` but for older organizations this may have the format `xxx.visualstudio.com`. Check the url on the browser. For Azure DevOps Server, this may be the unexposed one e.g. `localhost` or one that you have exposed publicly via DNS.|
-|AZURE_PORT|**_Optional_**. The TCP port used by your Azure DevOps installation. Defaults to `80` or `443`, depending on the indicated protocol.|
-|AZURE_VIRTUAL_DIRECTORY|**_Optional_**. Some Azure DevOps Server installations are hosted in an IIS virtual directory, traditionally named tfs. This variable can be used to define the name of that virtual directory. By default, this is not set.|
-|AZURE_ACCESS_USERNAME|**_Optional_**. This Variable can be used together with the User Password in the Access Token Variable to use basic Auth when connecting to Azure Dev Ops. By default, this is not set.|
-|AZURE_ACCESS_TOKEN|**_Required_**. The Personal Access in Azure DevOps for accessing the repository and creating pull requests. The required permissions are:
- Code (Full)
- Pull Requests Threads (Read & Write).
See the [documentation](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page#create-a-pat) to know more about creating a Personal Access Token|
-|AZURE_ORGANIZATION|**_Required_**. The name of the Azure DevOps Organization. This is can be extracted from the URL of the home page. https://dev.azure.com/{organization}/|
-|AZURE_PROJECT|**_Required_**. The name of the Azure DevOps Project within the above organization. This can be extracted them the URL too. https://dev.azure.com/{organization}/{project}/|
-|AZURE_REPOSITORY|**_Required_**. The name of the Azure DevOps Repository within the above project to run Dependabot against. This can be extracted from the URL of the repository. https://dev.azure.com/{organization}/{project}/_git/{repository}/|
-|AZURE_SET_AUTO_COMPLETE|**_Optional_**. Determines if the pull requests that dependabot creates should have auto complete set. When set to `true`, pull requests that pass all policies will be merged automatically|
-|AZURE_AUTO_COMPLETE_IGNORE_CONFIG_IDS|**_Optional_**. List of any policy configuration Id's which auto-complete should not wait for. Only applies to optional policies. Auto-complete always waits for required (blocking) policies.|
-|AZURE_AUTO_APPROVE_PR|**_Optional_**. Determines if the pull requests that dependabot creates should be automatically completed. When set to `true`, pull requests will be approved automatically.|
-|AZURE_AUTO_APPROVE_USER_TOKEN|**_Optional_**. A personal access token for the user to automatically approve the created PR. `AZURE_AUTO_APPROVE_PR` must be set to `true` for this to work.|
+|Variable Name|Supported Command(s)|Description|
+|--|--|--|
+|GITHUB_ACCESS_TOKEN|Update,
vNext|**_Optional_**. The GitHub token (classic) for authenticating requests against GitHub public repositories and the GitHub Advisory API (for vulnerability checking). This is useful to avoid rate limiting errors. The token must include permissions to read public repositories. See the [documentation](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token) for more on Personal Access Tokens.
Using a GitHub token allows for dependency vulnerabilities to be automatically checked using the GitHub Advisory API, in addition to any user-defined security advisories in `DEPENDABOT_SECURITY_ADVISORIES_FILE`.|
+|DEPENDABOT_PACKAGE_MANAGER|Update,
vNext|**_Required_**. The type of packages to check for dependency upgrades. Examples: `nuget`, `maven`, `gradle`, `npm_and_yarn`, etc. See the [updated-script](./script/update_script.rb) or [docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem) for more.|
+|DEPENDABOT_DIRECTORY|Update,
vNext|**_Optional_**. The directory in which dependencies are to be checked. When not specified, the root of the repository (denoted as '/') is used.|
+|DEPENDABOT_DIRECTORIES|vNext|**_Optional_**. The list of directories in which dependencies are to be checked, in JSON format. For example: `['/', '/src']`. When specified, it overrides `DEPENDABOT_DIRECTORY`. When not specified, `DEPENDABOT_DIRECTORY` is used instead. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#directories) for more.|
+|DEPENDABOT_TARGET_BRANCH|Update,
vNext|**_Optional_**. The branch to be targeted when creating a pull request. When not specified, Dependabot will resolve the default branch of the repository.|
+|DEPENDABOT_VERSIONING_STRATEGY|Update,
vNext|**_Optional_**. The versioning strategy to use. See [official docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#versioning-strategy) for the allowed values|
+|DEPENDABOT_OPEN_PULL_REQUESTS_LIMIT|Update,
vNext|**_Optional_**. The maximum number of open pull requests to have at any one time. Defaults to 5. Setting to 0 implies security only updates.|
+|DEPENDABOT_SECURITY_ADVISORIES_FILE|Update,
vNext|**_Optional_**. The absolute file path containing additional user-defined security advisories in JSON format. For example: `/mnt/security_advisories/nuget-2022-12-13.json`|
+|DEPENDABOT_EXTRA_CREDENTIALS|Update,
vNext|**_Optional_**. The extra credentials in JSON format. Extra credentials can be used to access private NuGet feeds, docker registries, maven repositories, etc. For example a private registry authentication (For example FontAwesome Pro: `[{"type":"npm_registry","token":"","registry":"npm.fontawesome.com"}]`)|
+|DEPENDABOT_ALLOW_CONDITIONS|Update,
vNext|**_Optional_**. The dependencies whose updates are allowed, in JSON format. This can be used to control which packages can be updated. For example: `[{\"dependency-name\":"django*",\"dependency-type\":\"direct\"}]`. See [official docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#allow) for more.|
+|DEPENDABOT_IGNORE_CONDITIONS|Update,
vNext|**_Optional_**. The dependencies to be ignored, in JSON format. This can be used to control which packages can be updated. For example: `[{\"dependency-name\":\"express\",\"versions\":[\"4.x\",\"5.x\"]}]`. See [official docs](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#ignore) for more.|
+|DEPENDABOT_EXCLUDE_REQUIREMENTS_TO_UNLOCK|Update|**_Optional_**. Exclude certain dependency updates requirements. See list of allowed values [here](https://github.com/dependabot/dependabot-core/issues/600#issuecomment-407808103). Useful if you have lots of dependencies and the update script too slow. The values provided are space-separated. Example: `own all` to only use the `none` version requirement.|
+|DEPENDABOT_VENDOR|vNext|**_Optional_** Determines if dependencies are vendored when updating them. Don't use this option if you're using `gomod` as Dependabot automatically detects vendoring. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#vendor) for more.|
+|DEPENDABOT_DEPENDENCY_GROUPS|vNext|**_Optional_**. The dependency group rule mappings, in JSON format. For example: `{"microsoft":{"applies-to":"version-updates","dependency-type":"production","patterns":["microsoft*"],"exclude-patterns":["*azure*"],"update-types":["minor","patch"]}}`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups) for more. |
+|DEPENDABOT_UPDATER_OPTIONS|Update,
vNext|**_Optional_**. Comma separated list of updater options (i.e. experiments); available options depend on `PACKAGE_MANAGER`. Example: `goprivate=true,kubernetes_updates=true`.|
+|DEPENDABOT_REJECT_EXTERNAL_CODE|Update,
vNext|**_Optional_**. Determines if the execution external code is allowed when cloning source repositories. Defaults to `false`.|
+|DEPENDABOT_AUTHOR_EMAIL|Update,
vNext|**_Optional_**. The email address to use for the change commit author, can be used e.g. in private Azure DevOps Server deployments to associate the committer with an existing account, to provide a profile picture.|
+|DEPENDABOT_AUTHOR_NAME|Update,
vNext|**_Optional_**. The display name to use for the change commit author.|
+|DEPENDABOT_SIGNATURE_KEY|vNext|**_Optional_**. The GPG signature key that git commits will be signed with. See [official docs](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) for more. By default, commits will not be signed. |
+|DEPENDABOT_BRANCH_NAME_SEPARATOR|Update,
vNext|**_Optional_**. The separator to use in created branches. For example: `-`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#pull-request-branch-nameseparator) for more.|
+|DEPENDABOT_BRANCH_NAME_PREFIX|vNext|**_Optional_**. The prefix used for Git branch names. Defaults to `dependabot`. |
+|DEPENDABOT_PR_NAME_PREFIX_STYLE|vNext|**_Optional_**. The pull request name prefix styling. Possible options are `none`, `angular`, `eslint`, `gitmoji`. If `DEPENDABOT_COMMIT_MESSAGE_OPTIONS` prefixes are also defined, this option does nothing. Defaults to `none`. |
+|DEPENDABOT_COMMIT_MESSAGE_OPTIONS|Update,
vNext|**_Optional_**. The commit message options, in JSON format. For example: `{\"prefix\":\"(dependabot)\"}`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#commit-message) for more.|
+|DEPENDABOT_COMPATIBILITY_SCORE_BADGE|vNext|**_Optional_**. Determines if compatibility score badges are shown in the pull request description for single dependency updates (but not group updates). This feature uses public information from GitHub and enabling it does **not** send any private information about your repository to GitHub other than the dependency name and version number(s) required to calculate to the compatibility score. Defaults to `false`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates#about-compatibility-scores) for more.|
+|DEPENDABOT_MESSAGE_HEADER|vNext|**_Optional_**. Additional pull request description text to shown before the dependency change info.|
+|DEPENDABOT_MESSAGE_FOOTER|vNext|**_Optional_**. Additional pull request description text to shown after the dependency change info. This text will not be truncated, even when the dependency change info exceeds the PR maximum description length. |
+|DEPENDABOT_REVIEWERS|Update,
vNext|**_Optional_**. The user id or email of the users to review the pull requests, in JSON format. These shall be added as optional approvers. For example: `[\"23d9f23d-981e-4a0c-a975-8e5c665914ec\",\"user@company.com\"]`.
+|DEPENDABOT_ASSIGNEES|Update,
vNext|**_Optional_**. The user ids or emails of the users to be assigned to the pull requests, in JSON format. These shall be added as required approvers. For example: `[\"be9321e2-f404-4ffa-8d6b-44efddb04865\", \"user@company.com\"]`. |
+|DEPENDABOT_LABELS|Update,
vNext|**_Optional_**. The custom labels to be used, in JSON format. This can be used to override the default values. For example: `[\"npm dependencies\",\"triage-board\"]`. See [official docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/customizing-dependency-updates#setting-custom-labels) for more.|
+|DEPENDABOT_MILESTONE|Update,
vNext|**_Optional_**. The identifier of the work item to be linked to the Pull Requests that dependabot creates.|
+|DEPENDABOT_SKIP_PULL_REQUESTS|Update,
vNext|**_Optional_**. Determines whether to skip creation and updating of pull requests. When set to `true` the logic to update the dependencies is executed but the actual Pull Requests are not created/updated. This is useful for debugging. Defaults to `false`.|
+|DEPENDABOT_CLOSE_PULL_REQUESTS|Update,
vNext|**_Optional_**. Determines whether to abandon unwanted pull requests. Defaults to `false`.|
+|DEPENDABOT_COMMENT_PULL_REQUESTS|vNext|**_Optional_**. Determines whether to comment on pull requests which an explanation of the reason for closing. Defaults to `false`.|
+|DEPENDABOT_FAIL_ON_EXCEPTION|Update|**_Optional_**. Determines if the execution should fail when an exception occurs. Defaults to `true`.|
+|DEPENDABOT_JOB_ID|vNext|**_Optional_**. The unique id for the update job run. Used for logging and auditing. When not specified, the current date/timestamp is used.|
+|DEPENDABOT_DEBUG|vNext|**_Optional_**. Determines if verbose log messages are logged. Useful for diagnosing issues. Defaults to `false`.|
+|AZURE_PROTOCOL|Update,
vNext|**_Optional_**. The transport protocol (`http` or `https`) used by your Azure DevOps installation. Defaults to `https`.|
+|AZURE_HOSTNAME|Update,
vNext|**_Optional_**. The hostname of the where the organization is hosted. Defaults to `dev.azure.com` but for older organizations this may have the format `xxx.visualstudio.com`. Check the url on the browser. For Azure DevOps Server, this may be the unexposed one e.g. `localhost` or one that you have exposed publicly via DNS.|
+|AZURE_PORT|Update,
vNext|**_Optional_**. The TCP port used by your Azure DevOps installation. Defaults to `80` or `443`, depending on the indicated protocol.|
+|AZURE_VIRTUAL_DIRECTORY|Update,
vNext|**_Optional_**. Some Azure DevOps Server installations are hosted in an IIS virtual directory, traditionally named tfs. This variable can be used to define the name of that virtual directory. By default, this is not set.|
+|AZURE_ACCESS_USERNAME|Update,
vNext|**_Optional_**. This Variable can be used together with the User Password in the Access Token Variable to use basic Auth when connecting to Azure Dev Ops. By default, this is not set.|
+|AZURE_ACCESS_TOKEN|Update,
vNext|**_Required_**. The Personal Access in Azure DevOps for accessing the repository and creating pull requests. The required permissions are:
- Code (Full)
- Pull Requests Threads (Read & Write).
See the [documentation](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page#create-a-pat) to know more about creating a Personal Access Token|
+|AZURE_ORGANIZATION|Update,
vNext|**_Required_**. The name of the Azure DevOps Organization. This is can be extracted from the URL of the home page. https://dev.azure.com/{organization}/|
+|AZURE_PROJECT|Update,
vNext|**_Required_**. The name of the Azure DevOps Project within the above organization. This can be extracted them the URL too. https://dev.azure.com/{organization}/{project}/|
+|AZURE_REPOSITORY|Update,
vNext|**_Required_**. The name of the Azure DevOps Repository within the above project to run Dependabot against. This can be extracted from the URL of the repository. https://dev.azure.com/{organization}/{project}/_git/{repository}/|
+|AZURE_SET_AUTO_COMPLETE|Update,
vNext|**_Optional_**. Determines if the pull requests that dependabot creates should have auto complete set. When set to `true`, pull requests that pass all policies will be merged automatically|
+|AZURE_AUTO_COMPLETE_IGNORE_CONFIG_IDS|Update,
vNext|**_Optional_**. List of any policy configuration Id's which auto-complete should not wait for. Only applies to optional policies. Auto-complete always waits for required (blocking) policies.|
+|AZURE_AUTO_APPROVE_PR|Update,
vNext|**_Optional_**. Determines if the pull requests that dependabot creates should be automatically completed. When set to `true`, pull requests will be approved automatically.|
+|AZURE_AUTO_APPROVE_USER_TOKEN|Update,
vNext|**_Optional_**. A personal access token for the user to automatically approve the created PR. `AZURE_AUTO_APPROVE_PR` must be set to `true` for this to work.|
+
+# Development guide
+
+## Getting the development environment ready
+First, ensure you have [Docker](https://docs.docker.com/engine/install/) and [Ruby](https://www.ruby-lang.org/en/documentation/installation/) installed.
+On Linux, you'll need the the build essentials and Ruby development packages too; These are typically `build-essentials` and `ruby-dev`.
+
+Next, install project build tools with bundle:
+
+```bash
+cd updater
+bundle install
+```
+
+## Building the Docker image
+Each package ecosystem must be built separately; You only need to build images for the ecosystems that you plan on testing.
+
+```bash
+docker build \
+ -f updater/Dockerfile \
+ --build-arg BUILDKIT_INLINE_CACHE=1 \
+ --build-arg ECOSYSTEM= \
+ --build-arg BASE_VERSION=latest \
+ -t "ghcr.io/tinglesoftware/dependabot-updater-:latest" \
+ .
+```
+
+In some scenarios, you may want to set `BASE_VERSION` to a specific version instead of "latest".
+See [updater/Dockerfile](../updater/Dockerfile) for a more detailed explanation.
+
+## Running your code changes
+To test run your code changes, you'll first need to build the updater Docker image (see above), then run the updater Docker image in a container with all the required environment variables (see above).
+
+## Running the code linter
+```bash
+cd updater
+bundle exec rubocop
+bundle exec rubocop -a # to automatically fix any correctable offenses
+```
+
+## Running the unit tests
+```bash
+cd updater
+bundle exec rspec spec
+```
diff --git a/extension/.gitignore b/extension/.gitignore
index b882c447..57c2ca3a 100644
--- a/extension/.gitignore
+++ b/extension/.gitignore
@@ -1,2 +1,4 @@
node_modules
.taskkey
+task/**/*.js
+*.vsix
\ No newline at end of file
diff --git a/extension/README.md b/extension/README.md
index bb2ebff5..6c7e9f8e 100644
--- a/extension/README.md
+++ b/extension/README.md
@@ -4,7 +4,7 @@ This is the unofficial [dependabot](https://github.com/Dependabot/dependabot-cor
## Usage
-Add a configuration file stored at `.github/dependabot.yml` or `.github/dependabot.yaml` conforming to the [official spec](https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates).
+Add a configuration file stored at `.azuredevops/dependabot.yml` or `.github/dependabot.yml` conforming to the [official spec](https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates).
To use in a YAML pipeline:
diff --git a/extension/overrides.dev.json b/extension/overrides.dev.json
index b1c04fc9..bf917f17 100644
--- a/extension/overrides.dev.json
+++ b/extension/overrides.dev.json
@@ -1,5 +1,5 @@
{
"id":"dependabot-dev",
- "version": "#{GITVERSION_MAJORMINORPATCH}#.#{GITHUB_RUN_NUMBER}#",
+ "version": "#{MAJOR_MINOR_PATCH}#.#{BUILD_NUMBER}#",
"name": "Dependabot (Dev)"
}
\ No newline at end of file
diff --git a/extension/overrides.prod.json b/extension/overrides.prod.json
index 391a4b3e..af2b7d36 100644
--- a/extension/overrides.prod.json
+++ b/extension/overrides.prod.json
@@ -1,4 +1,4 @@
{
- "version": "#{GITVERSION_MAJORMINORPATCH}#.#{GITHUB_RUN_NUMBER}#",
+ "version": "#{MAJOR_MINOR_PATCH}#.#{BUILD_NUMBER}#",
"public": true
}
\ No newline at end of file
diff --git a/extension/package-lock.json b/extension/package-lock.json
index 59206f42..163ad97e 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,7 +1,7 @@
{
"name": "dependabot-azure-devops",
"version": "1.0.0",
- "lockfileVersion": 2,
+ "lockfileVersion": 3,
"requires": true,
"packages": {
"": {
@@ -9,76 +9,77 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
- "axios": "^1.6.0",
- "azure-pipelines-task-lib": "^4.7.0",
- "js-yaml": "^4.1.0"
+ "axios": "1.7.3",
+ "azure-pipelines-task-lib": "4.15.0",
+ "js-yaml": "4.1.0"
},
"devDependencies": {
- "@types/jest": "^29.5.1",
- "@types/js-yaml": "^4.0.3",
- "@types/node": "^20.9.0",
- "@types/q": "^1.5.5",
- "jest": "^29.5.0",
- "ts-jest": "^29.1.1",
- "ts-node": "^10.9.1",
- "typescript": "^5.2.2"
+ "@types/jest": "29.5.12",
+ "@types/js-yaml": "4.0.9",
+ "@types/node": "22.1.0",
+ "@types/q": "1.5.8",
+ "jest": "29.7.0",
+ "ts-jest": "29.2.4",
+ "ts-node": "10.9.2",
+ "typescript": "5.5.4"
}
},
"node_modules/@ampproject/remapping": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
- "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dev": true,
"dependencies": {
- "@jridgewell/gen-mapping": "^0.1.0",
- "@jridgewell/trace-mapping": "^0.3.9"
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/code-frame": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
- "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
+ "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
"dev": true,
"dependencies": {
- "@babel/highlight": "^7.18.6"
+ "@babel/highlight": "^7.24.7",
+ "picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
- "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz",
+ "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
- "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
- "dev": true,
- "dependencies": {
- "@ampproject/remapping": "^2.1.0",
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.5",
- "@babel/helper-compilation-targets": "^7.20.0",
- "@babel/helper-module-transforms": "^7.20.2",
- "@babel/helpers": "^7.20.5",
- "@babel/parser": "^7.20.5",
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.20.5",
- "@babel/types": "^7.20.5",
- "convert-source-map": "^1.7.0",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz",
+ "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.24.7",
+ "@babel/helper-compilation-targets": "^7.24.7",
+ "@babel/helper-module-transforms": "^7.24.7",
+ "@babel/helpers": "^7.24.7",
+ "@babel/parser": "^7.24.7",
+ "@babel/template": "^7.24.7",
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7",
+ "convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
- "json5": "^2.2.1",
- "semver": "^6.3.0"
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
@@ -88,224 +89,208 @@
"url": "https://opencollective.com/babel"
}
},
- "node_modules/@babel/core/node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "dev": true
- },
"node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/generator": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz",
- "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz",
+ "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.20.5",
- "@jridgewell/gen-mapping": "^0.3.2",
+ "@babel/types": "^7.24.7",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^2.5.1"
},
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "dev": true,
- "dependencies": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz",
- "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz",
+ "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==",
"dev": true,
"dependencies": {
- "@babel/compat-data": "^7.20.0",
- "@babel/helper-validator-option": "^7.18.6",
- "browserslist": "^4.21.3",
- "semver": "^6.3.0"
+ "@babel/compat-data": "^7.24.7",
+ "@babel/helper-validator-option": "^7.24.7",
+ "browserslist": "^4.22.2",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/helper-environment-visitor": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
- "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
+ "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
"dev": true,
+ "dependencies": {
+ "@babel/types": "^7.24.7"
+ },
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
- "version": "7.19.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
- "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
+ "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
"dev": true,
"dependencies": {
- "@babel/template": "^7.18.10",
- "@babel/types": "^7.19.0"
+ "@babel/template": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-hoist-variables": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
+ "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.18.6"
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
- "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
+ "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.18.6"
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz",
- "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz",
+ "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==",
"dev": true,
"dependencies": {
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-module-imports": "^7.18.6",
- "@babel/helper-simple-access": "^7.20.2",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/helper-validator-identifier": "^7.19.1",
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.20.1",
- "@babel/types": "^7.20.2"
+ "@babel/helper-environment-visitor": "^7.24.7",
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-simple-access": "^7.24.7",
+ "@babel/helper-split-export-declaration": "^7.24.7",
+ "@babel/helper-validator-identifier": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
- "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz",
+ "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-simple-access": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
- "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
+ "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.20.2"
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-split-export-declaration": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
+ "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.18.6"
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.19.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
- "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz",
+ "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.19.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
- "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
+ "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
- "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz",
+ "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
- "version": "7.20.6",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
- "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz",
+ "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==",
"dev": true,
"dependencies": {
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.20.5",
- "@babel/types": "^7.20.5"
+ "@babel/template": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
- "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
+ "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
"dev": true,
"dependencies": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
@@ -383,9 +368,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.20.13",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz",
- "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
+ "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@@ -455,12 +440,12 @@
}
},
"node_modules/@babel/plugin-syntax-jsx": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
- "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz",
+ "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==",
"dev": true,
"dependencies": {
- "@babel/helper-plugin-utils": "^7.18.6"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -557,12 +542,12 @@
}
},
"node_modules/@babel/plugin-syntax-typescript": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz",
- "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz",
+ "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==",
"dev": true,
"dependencies": {
- "@babel/helper-plugin-utils": "^7.19.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -572,34 +557,34 @@
}
},
"node_modules/@babel/template": {
- "version": "7.18.10",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
- "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
+ "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
"dev": true,
"dependencies": {
- "@babel/code-frame": "^7.18.6",
- "@babel/parser": "^7.18.10",
- "@babel/types": "^7.18.10"
+ "@babel/code-frame": "^7.24.7",
+ "@babel/parser": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz",
- "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.5",
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.19.0",
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.20.5",
- "@babel/types": "^7.20.5",
- "debug": "^4.1.0",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz",
+ "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.24.7",
+ "@babel/helper-environment-visitor": "^7.24.7",
+ "@babel/helper-function-name": "^7.24.7",
+ "@babel/helper-hoist-variables": "^7.24.7",
+ "@babel/helper-split-export-declaration": "^7.24.7",
+ "@babel/parser": "^7.24.7",
+ "@babel/types": "^7.24.7",
+ "debug": "^4.3.1",
"globals": "^11.1.0"
},
"engines": {
@@ -607,13 +592,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
- "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz",
+ "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==",
"dev": true,
"dependencies": {
- "@babel/helper-string-parser": "^7.19.4",
- "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/helper-string-parser": "^7.24.7",
+ "@babel/helper-validator-identifier": "^7.24.7",
"to-fast-properties": "^2.0.0"
},
"engines": {
@@ -696,16 +681,16 @@
}
},
"node_modules/@jest/console": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz",
- "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
+ "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
"chalk": "^4.0.0",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
"slash": "^3.0.0"
},
"engines": {
@@ -713,37 +698,37 @@
}
},
"node_modules/@jest/core": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz",
- "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
+ "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
"dev": true,
"dependencies": {
- "@jest/console": "^29.5.0",
- "@jest/reporters": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/console": "^29.7.0",
+ "@jest/reporters": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
"ansi-escapes": "^4.2.1",
"chalk": "^4.0.0",
"ci-info": "^3.2.0",
"exit": "^0.1.2",
"graceful-fs": "^4.2.9",
- "jest-changed-files": "^29.5.0",
- "jest-config": "^29.5.0",
- "jest-haste-map": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-regex-util": "^29.4.3",
- "jest-resolve": "^29.5.0",
- "jest-resolve-dependencies": "^29.5.0",
- "jest-runner": "^29.5.0",
- "jest-runtime": "^29.5.0",
- "jest-snapshot": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
- "jest-watcher": "^29.5.0",
+ "jest-changed-files": "^29.7.0",
+ "jest-config": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-resolve-dependencies": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "jest-watcher": "^29.7.0",
"micromatch": "^4.0.4",
- "pretty-format": "^29.5.0",
+ "pretty-format": "^29.7.0",
"slash": "^3.0.0",
"strip-ansi": "^6.0.0"
},
@@ -760,89 +745,89 @@
}
},
"node_modules/@jest/environment": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz",
- "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
+ "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
"dev": true,
"dependencies": {
- "@jest/fake-timers": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
- "jest-mock": "^29.5.0"
+ "jest-mock": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/expect": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz",
- "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
"dev": true,
"dependencies": {
- "expect": "^29.5.0",
- "jest-snapshot": "^29.5.0"
+ "expect": "^29.7.0",
+ "jest-snapshot": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/expect-utils": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz",
- "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
+ "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
"dev": true,
"dependencies": {
- "jest-get-type": "^29.4.3"
+ "jest-get-type": "^29.6.3"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/fake-timers": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz",
- "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
+ "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"@sinonjs/fake-timers": "^10.0.2",
"@types/node": "*",
- "jest-message-util": "^29.5.0",
- "jest-mock": "^29.5.0",
- "jest-util": "^29.5.0"
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/globals": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz",
- "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
+ "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
"dev": true,
"dependencies": {
- "@jest/environment": "^29.5.0",
- "@jest/expect": "^29.5.0",
- "@jest/types": "^29.5.0",
- "jest-mock": "^29.5.0"
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "jest-mock": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/reporters": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz",
- "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
+ "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
"dev": true,
"dependencies": {
"@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@jridgewell/trace-mapping": "^0.3.15",
+ "@jest/console": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
"@types/node": "*",
"chalk": "^4.0.0",
"collect-v8-coverage": "^1.0.0",
@@ -850,13 +835,13 @@
"glob": "^7.1.3",
"graceful-fs": "^4.2.9",
"istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-instrument": "^5.1.0",
+ "istanbul-lib-instrument": "^6.0.0",
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-source-maps": "^4.0.0",
"istanbul-reports": "^3.1.3",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-worker": "^29.5.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
"slash": "^3.0.0",
"string-length": "^4.0.1",
"strip-ansi": "^6.0.0",
@@ -875,24 +860,24 @@
}
},
"node_modules/@jest/schemas": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
- "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
"dev": true,
"dependencies": {
- "@sinclair/typebox": "^0.25.16"
+ "@sinclair/typebox": "^0.27.8"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jest/source-map": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz",
- "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
+ "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
"dev": true,
"dependencies": {
- "@jridgewell/trace-mapping": "^0.3.15",
+ "@jridgewell/trace-mapping": "^0.3.18",
"callsites": "^3.0.0",
"graceful-fs": "^4.2.9"
},
@@ -901,13 +886,13 @@
}
},
"node_modules/@jest/test-result": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz",
- "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
+ "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
"dev": true,
"dependencies": {
- "@jest/console": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/console": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/istanbul-lib-coverage": "^2.0.0",
"collect-v8-coverage": "^1.0.0"
},
@@ -916,14 +901,14 @@
}
},
"node_modules/@jest/test-sequencer": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz",
- "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
+ "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
"dev": true,
"dependencies": {
- "@jest/test-result": "^29.5.0",
+ "@jest/test-result": "^29.7.0",
"graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
+ "jest-haste-map": "^29.7.0",
"slash": "^3.0.0"
},
"engines": {
@@ -931,22 +916,22 @@
}
},
"node_modules/@jest/transform": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz",
- "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
+ "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
"dev": true,
"dependencies": {
"@babel/core": "^7.11.6",
- "@jest/types": "^29.5.0",
- "@jridgewell/trace-mapping": "^0.3.15",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
"babel-plugin-istanbul": "^6.1.1",
"chalk": "^4.0.0",
"convert-source-map": "^2.0.0",
"fast-json-stable-stringify": "^2.1.0",
"graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
- "jest-regex-util": "^29.4.3",
- "jest-util": "^29.5.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
"micromatch": "^4.0.4",
"pirates": "^4.0.4",
"slash": "^3.0.0",
@@ -957,12 +942,12 @@
}
},
"node_modules/@jest/types": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
- "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.4.3",
+ "@jest/schemas": "^29.6.3",
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
@@ -974,80 +959,81 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
- "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dev": true,
"dependencies": {
- "@jridgewell/set-array": "^1.0.0",
- "@jridgewell/sourcemap-codec": "^1.4.10"
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
- "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
- "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
- "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.17",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
- "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"dependencies": {
- "@jridgewell/resolve-uri": "3.1.0",
- "@jridgewell/sourcemap-codec": "1.4.14"
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@sinclair/typebox": {
- "version": "0.25.24",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
- "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==",
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
"dev": true
},
"node_modules/@sinonjs/commons": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
- "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
"dev": true,
"dependencies": {
"type-detect": "4.0.8"
}
},
"node_modules/@sinonjs/fake-timers": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
- "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
+ "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
"dev": true,
"dependencies": {
- "@sinonjs/commons": "^2.0.0"
+ "@sinonjs/commons": "^3.0.0"
}
},
"node_modules/@tsconfig/node10": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
- "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"dev": true
},
"node_modules/@tsconfig/node12": {
@@ -1063,15 +1049,15 @@
"dev": true
},
"node_modules/@tsconfig/node16": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
- "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
},
"node_modules/@types/babel__core": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
- "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==",
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.20.7",
@@ -1082,18 +1068,18 @@
}
},
"node_modules/@types/babel__generator": {
- "version": "7.6.4",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
- "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
"dev": true,
"dependencies": {
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__template": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
- "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.1.0",
@@ -1101,51 +1087,51 @@
}
},
"node_modules/@types/babel__traverse": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz",
- "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==",
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
+ "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.3.0"
+ "@babel/types": "^7.20.7"
}
},
"node_modules/@types/graceful-fs": {
- "version": "4.1.6",
- "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
- "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
+ "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/istanbul-lib-coverage": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
"dev": true
},
"node_modules/@types/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
"dev": true,
"dependencies": {
"@types/istanbul-lib-coverage": "*"
}
},
"node_modules/@types/istanbul-reports": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
"dev": true,
"dependencies": {
"@types/istanbul-lib-report": "*"
}
},
"node_modules/@types/jest": {
- "version": "29.5.1",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz",
- "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==",
+ "version": "29.5.12",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
+ "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
"dev": true,
"dependencies": {
"expect": "^29.0.0",
@@ -1153,57 +1139,52 @@
}
},
"node_modules/@types/js-yaml": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
- "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true
},
"node_modules/@types/node": {
- "version": "20.9.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
- "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
+ "version": "22.1.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz",
+ "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "undici-types": "~5.26.4"
+ "undici-types": "~6.13.0"
}
},
- "node_modules/@types/prettier": {
- "version": "2.7.2",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
- "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==",
- "dev": true
- },
"node_modules/@types/q": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz",
- "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==",
+ "version": "1.5.8",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz",
+ "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==",
"dev": true
},
"node_modules/@types/stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
"dev": true
},
"node_modules/@types/yargs": {
- "version": "17.0.17",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz",
- "integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==",
+ "version": "17.0.32",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
+ "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
"dev": true,
"dependencies": {
"@types/yargs-parser": "*"
}
},
"node_modules/@types/yargs-parser": {
- "version": "21.0.0",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
- "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"dev": true
},
"node_modules/acorn": {
- "version": "8.8.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
- "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
+ "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@@ -1213,20 +1194,23 @@
}
},
"node_modules/acorn-walk": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "version": "8.3.3",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
+ "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
"dev": true,
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/adm-zip": {
- "version": "0.5.10",
- "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
- "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==",
+ "version": "0.5.14",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.14.tgz",
+ "integrity": "sha512-DnyqqifT4Jrcvb8USYjp6FHtBpEIz1mnXu6pTRHZ0RL69LbQYiO+0lDFg5+OKA7U29oWSs3a/i8fhn8ZcceIWg==",
"engines": {
- "node": ">=6.0"
+ "node": ">=12.0"
}
},
"node_modules/agent-base": {
@@ -1303,41 +1287,36 @@
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
+ "node_modules/async": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
+ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
- "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz",
+ "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==",
+ "license": "MIT",
"dependencies": {
- "follow-redirects": "^1.15.0",
+ "follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
- "node_modules/axios/node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/azure-pipelines-task-lib": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-4.7.0.tgz",
- "integrity": "sha512-5MctDC1Bt7eFi9tQTXlikuWRDc2MenCNruMsEwcKuXqBj1ZY+fA/D+E1DbE0Qi2u8kl1p6szT0we8k6RHSOe/w==",
+ "version": "4.15.0",
+ "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-4.15.0.tgz",
+ "integrity": "sha512-Y72FjLTE2CAM9KrBXzc6vjelTBCpdYb2NkyFB0hwksTrhA3q8nsF680dofuTeXztQ94UTpkK27hpgSHnqYf5ZA==",
+ "license": "MIT",
"dependencies": {
"adm-zip": "^0.5.10",
- "deasync": "^0.1.28",
"minimatch": "3.0.5",
"nodejs-file-downloader": "^4.11.1",
"q": "^1.5.1",
@@ -1347,15 +1326,15 @@
}
},
"node_modules/babel-jest": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz",
- "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
+ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
"dev": true,
"dependencies": {
- "@jest/transform": "^29.5.0",
+ "@jest/transform": "^29.7.0",
"@types/babel__core": "^7.1.14",
"babel-plugin-istanbul": "^6.1.1",
- "babel-preset-jest": "^29.5.0",
+ "babel-preset-jest": "^29.6.3",
"chalk": "^4.0.0",
"graceful-fs": "^4.2.9",
"slash": "^3.0.0"
@@ -1383,10 +1362,35 @@
"node": ">=8"
}
},
+ "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-istanbul/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/babel-plugin-jest-hoist": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz",
- "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
+ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
"dev": true,
"dependencies": {
"@babel/template": "^7.3.3",
@@ -1422,12 +1426,12 @@
}
},
"node_modules/babel-preset-jest": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz",
- "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
+ "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
"dev": true,
"dependencies": {
- "babel-plugin-jest-hoist": "^29.5.0",
+ "babel-plugin-jest-hoist": "^29.6.3",
"babel-preset-current-node-syntax": "^1.0.0"
},
"engines": {
@@ -1442,14 +1446,6 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
- "node_modules/bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
- "dependencies": {
- "file-uri-to-path": "1.0.0"
- }
- },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1460,21 +1456,21 @@
}
},
"node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/browserslist": {
- "version": "4.21.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
- "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
+ "version": "4.23.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz",
+ "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==",
"dev": true,
"funding": [
{
@@ -1484,13 +1480,17 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
- "caniuse-lite": "^1.0.30001400",
- "electron-to-chromium": "^1.4.251",
- "node-releases": "^2.0.6",
- "update-browserslist-db": "^1.0.9"
+ "caniuse-lite": "^1.0.30001629",
+ "electron-to-chromium": "^1.4.796",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.16"
},
"bin": {
"browserslist": "cli.js"
@@ -1545,9 +1545,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001439",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz",
- "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==",
+ "version": "1.0.30001636",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz",
+ "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==",
"dev": true,
"funding": [
{
@@ -1557,6 +1557,10 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
]
},
@@ -1586,18 +1590,24 @@
}
},
"node_modules/ci-info": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
- "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
"engines": {
"node": ">=8"
}
},
"node_modules/cjs-module-lexer": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
- "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
+ "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
"dev": true
},
"node_modules/cliui": {
@@ -1625,9 +1635,9 @@
}
},
"node_modules/collect-v8-coverage": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
- "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
+ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
"dev": true
},
"node_modules/color-convert": {
@@ -1670,6 +1680,27 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true
},
+ "node_modules/create-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
+ "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "prompts": "^2.0.1"
+ },
+ "bin": {
+ "create-jest": "bin/create-jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@@ -1690,23 +1721,10 @@
"node": ">= 8"
}
},
- "node_modules/deasync": {
- "version": "0.1.28",
- "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.28.tgz",
- "integrity": "sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==",
- "hasInstallScript": true,
- "dependencies": {
- "bindings": "^1.5.0",
- "node-addon-api": "^1.7.1"
- },
- "engines": {
- "node": ">=0.11.0"
- }
- },
"node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
+ "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
"dependencies": {
"ms": "2.1.2"
},
@@ -1720,15 +1738,23 @@
}
},
"node_modules/dedent": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
- "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
- "dev": true
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
+ "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
+ "dev": true,
+ "peerDependencies": {
+ "babel-plugin-macros": "^3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
},
"node_modules/deepmerge": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
- "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -1761,18 +1787,34 @@
}
},
"node_modules/diff-sequences": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
- "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
"dev": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/ejs": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jake": "^10.8.5"
+ },
+ "bin": {
+ "ejs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/electron-to-chromium": {
- "version": "1.4.284",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
- "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
+ "version": "1.4.805",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.805.tgz",
+ "integrity": "sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw==",
"dev": true
},
"node_modules/emittery": {
@@ -1803,9 +1845,9 @@
}
},
"node_modules/escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"dev": true,
"engines": {
"node": ">=6"
@@ -1866,16 +1908,16 @@
}
},
"node_modules/expect": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz",
- "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
"dev": true,
"dependencies": {
- "@jest/expect-utils": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "jest-matcher-utils": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0"
+ "@jest/expect-utils": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -1896,15 +1938,43 @@
"bser": "2.1.1"
}
},
- "node_modules/file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
+ "node_modules/filelist": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
+ "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.0.1"
+ }
+ },
+ "node_modules/filelist/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/filelist/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
},
"node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -1927,9 +1997,9 @@
}
},
"node_modules/follow-redirects": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
@@ -1945,15 +2015,28 @@
}
}
},
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
@@ -1965,9 +2048,12 @@
}
},
"node_modules/function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
@@ -2012,6 +2098,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -2048,22 +2135,11 @@
}
},
"node_modules/graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
- "node_modules/has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dependencies": {
- "function-bind": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4.0"
- }
- },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -2073,6 +2149,17 @@
"node": ">=8"
}
},
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -2132,6 +2219,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -2157,11 +2245,11 @@
"dev": true
},
"node_modules/is-core-module": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
- "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
"dependencies": {
- "has": "^1.0.3"
+ "hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -2213,51 +2301,54 @@
"dev": true
},
"node_modules/istanbul-lib-coverage": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
- "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+ "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/istanbul-lib-instrument": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
- "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz",
+ "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==",
"dev": true,
"dependencies": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
"istanbul-lib-coverage": "^3.2.0",
- "semver": "^6.3.0"
+ "semver": "^7.5.4"
},
"engines": {
- "node": ">=8"
+ "node": ">=10"
}
},
"node_modules/istanbul-lib-instrument/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
"node_modules/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
"dev": true,
"dependencies": {
"istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^3.0.0",
+ "make-dir": "^4.0.0",
"supports-color": "^7.1.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=10"
}
},
"node_modules/istanbul-lib-source-maps": {
@@ -2275,9 +2366,9 @@
}
},
"node_modules/istanbul-reports": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
- "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+ "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
"dev": true,
"dependencies": {
"html-escaper": "^2.0.0",
@@ -2287,16 +2378,48 @@
"node": ">=8"
}
},
+ "node_modules/jake": {
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz",
+ "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "async": "^3.2.3",
+ "chalk": "^4.0.2",
+ "filelist": "^1.0.4",
+ "minimatch": "^3.1.2"
+ },
+ "bin": {
+ "jake": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jake/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/jest": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz",
- "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
+ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
"dev": true,
"dependencies": {
- "@jest/core": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/core": "^29.7.0",
+ "@jest/types": "^29.6.3",
"import-local": "^3.0.2",
- "jest-cli": "^29.5.0"
+ "jest-cli": "^29.7.0"
},
"bin": {
"jest": "bin/jest.js"
@@ -2314,12 +2437,13 @@
}
},
"node_modules/jest-changed-files": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz",
- "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
+ "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
"dev": true,
"dependencies": {
"execa": "^5.0.0",
+ "jest-util": "^29.7.0",
"p-limit": "^3.1.0"
},
"engines": {
@@ -2327,28 +2451,28 @@
}
},
"node_modules/jest-circus": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz",
- "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
+ "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
"dev": true,
"dependencies": {
- "@jest/environment": "^29.5.0",
- "@jest/expect": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
"chalk": "^4.0.0",
"co": "^4.6.0",
- "dedent": "^0.7.0",
+ "dedent": "^1.0.0",
"is-generator-fn": "^2.0.0",
- "jest-each": "^29.5.0",
- "jest-matcher-utils": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-runtime": "^29.5.0",
- "jest-snapshot": "^29.5.0",
- "jest-util": "^29.5.0",
+ "jest-each": "^29.7.0",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
"p-limit": "^3.1.0",
- "pretty-format": "^29.5.0",
+ "pretty-format": "^29.7.0",
"pure-rand": "^6.0.0",
"slash": "^3.0.0",
"stack-utils": "^2.0.3"
@@ -2358,22 +2482,21 @@
}
},
"node_modules/jest-cli": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz",
- "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
+ "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
"dev": true,
"dependencies": {
- "@jest/core": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/core": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
"chalk": "^4.0.0",
+ "create-jest": "^29.7.0",
"exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
"import-local": "^3.0.2",
- "jest-config": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
- "prompts": "^2.0.1",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
"yargs": "^17.3.1"
},
"bin": {
@@ -2392,31 +2515,31 @@
}
},
"node_modules/jest-config": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz",
- "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
+ "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
"dev": true,
"dependencies": {
"@babel/core": "^7.11.6",
- "@jest/test-sequencer": "^29.5.0",
- "@jest/types": "^29.5.0",
- "babel-jest": "^29.5.0",
+ "@jest/test-sequencer": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "babel-jest": "^29.7.0",
"chalk": "^4.0.0",
"ci-info": "^3.2.0",
"deepmerge": "^4.2.2",
"glob": "^7.1.3",
"graceful-fs": "^4.2.9",
- "jest-circus": "^29.5.0",
- "jest-environment-node": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "jest-regex-util": "^29.4.3",
- "jest-resolve": "^29.5.0",
- "jest-runner": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
+ "jest-circus": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
"micromatch": "^4.0.4",
"parse-json": "^5.2.0",
- "pretty-format": "^29.5.0",
+ "pretty-format": "^29.7.0",
"slash": "^3.0.0",
"strip-json-comments": "^3.1.1"
},
@@ -2437,24 +2560,24 @@
}
},
"node_modules/jest-diff": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
- "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
+ "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
"dev": true,
"dependencies": {
"chalk": "^4.0.0",
- "diff-sequences": "^29.4.3",
- "jest-get-type": "^29.4.3",
- "pretty-format": "^29.5.0"
+ "diff-sequences": "^29.6.3",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-docblock": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz",
- "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
+ "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
"dev": true,
"dependencies": {
"detect-newline": "^3.0.0"
@@ -2464,62 +2587,62 @@
}
},
"node_modules/jest-each": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz",
- "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
+ "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"chalk": "^4.0.0",
- "jest-get-type": "^29.4.3",
- "jest-util": "^29.5.0",
- "pretty-format": "^29.5.0"
+ "jest-get-type": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "pretty-format": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-environment-node": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz",
- "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
+ "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
"dev": true,
"dependencies": {
- "@jest/environment": "^29.5.0",
- "@jest/fake-timers": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
- "jest-mock": "^29.5.0",
- "jest-util": "^29.5.0"
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-get-type": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
- "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
+ "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
"dev": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-haste-map": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz",
- "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
+ "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"@types/graceful-fs": "^4.1.3",
"@types/node": "*",
"anymatch": "^3.0.3",
"fb-watchman": "^2.0.0",
"graceful-fs": "^4.2.9",
- "jest-regex-util": "^29.4.3",
- "jest-util": "^29.5.0",
- "jest-worker": "^29.5.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
"micromatch": "^4.0.4",
"walker": "^1.0.8"
},
@@ -2531,46 +2654,46 @@
}
},
"node_modules/jest-leak-detector": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz",
- "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
+ "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
"dev": true,
"dependencies": {
- "jest-get-type": "^29.4.3",
- "pretty-format": "^29.5.0"
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-matcher-utils": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz",
- "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
+ "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
"dev": true,
"dependencies": {
"chalk": "^4.0.0",
- "jest-diff": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "pretty-format": "^29.5.0"
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-message-util": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz",
- "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
+ "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.12.13",
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"@types/stack-utils": "^2.0.0",
"chalk": "^4.0.0",
"graceful-fs": "^4.2.9",
"micromatch": "^4.0.4",
- "pretty-format": "^29.5.0",
+ "pretty-format": "^29.7.0",
"slash": "^3.0.0",
"stack-utils": "^2.0.3"
},
@@ -2579,14 +2702,14 @@
}
},
"node_modules/jest-mock": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz",
- "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
+ "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
- "jest-util": "^29.5.0"
+ "jest-util": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -2610,26 +2733,26 @@
}
},
"node_modules/jest-regex-util": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz",
- "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==",
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
+ "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
"dev": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-resolve": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz",
- "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
+ "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
"dev": true,
"dependencies": {
"chalk": "^4.0.0",
"graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
+ "jest-haste-map": "^29.7.0",
"jest-pnp-resolver": "^1.2.2",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
"resolve": "^1.20.0",
"resolve.exports": "^2.0.0",
"slash": "^3.0.0"
@@ -2639,43 +2762,43 @@
}
},
"node_modules/jest-resolve-dependencies": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz",
- "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
+ "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
"dev": true,
"dependencies": {
- "jest-regex-util": "^29.4.3",
- "jest-snapshot": "^29.5.0"
+ "jest-regex-util": "^29.6.3",
+ "jest-snapshot": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-runner": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz",
- "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
+ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
"dev": true,
"dependencies": {
- "@jest/console": "^29.5.0",
- "@jest/environment": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/console": "^29.7.0",
+ "@jest/environment": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
"chalk": "^4.0.0",
"emittery": "^0.13.1",
"graceful-fs": "^4.2.9",
- "jest-docblock": "^29.4.3",
- "jest-environment-node": "^29.5.0",
- "jest-haste-map": "^29.5.0",
- "jest-leak-detector": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-resolve": "^29.5.0",
- "jest-runtime": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-watcher": "^29.5.0",
- "jest-worker": "^29.5.0",
+ "jest-docblock": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-leak-detector": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-resolve": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-watcher": "^29.7.0",
+ "jest-worker": "^29.7.0",
"p-limit": "^3.1.0",
"source-map-support": "0.5.13"
},
@@ -2684,31 +2807,31 @@
}
},
"node_modules/jest-runtime": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz",
- "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^29.5.0",
- "@jest/fake-timers": "^29.5.0",
- "@jest/globals": "^29.5.0",
- "@jest/source-map": "^29.4.3",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
+ "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/globals": "^29.7.0",
+ "@jest/source-map": "^29.6.3",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
"chalk": "^4.0.0",
"cjs-module-lexer": "^1.0.0",
"collect-v8-coverage": "^1.0.0",
"glob": "^7.1.3",
"graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-mock": "^29.5.0",
- "jest-regex-util": "^29.4.3",
- "jest-resolve": "^29.5.0",
- "jest-snapshot": "^29.5.0",
- "jest-util": "^29.5.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
"slash": "^3.0.0",
"strip-bom": "^4.0.0"
},
@@ -2717,47 +2840,41 @@
}
},
"node_modules/jest-snapshot": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz",
- "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
+ "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
"dev": true,
"dependencies": {
"@babel/core": "^7.11.6",
"@babel/generator": "^7.7.2",
"@babel/plugin-syntax-jsx": "^7.7.2",
"@babel/plugin-syntax-typescript": "^7.7.2",
- "@babel/traverse": "^7.7.2",
"@babel/types": "^7.3.3",
- "@jest/expect-utils": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/babel__traverse": "^7.0.6",
- "@types/prettier": "^2.1.5",
+ "@jest/expect-utils": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
"babel-preset-current-node-syntax": "^1.0.0",
"chalk": "^4.0.0",
- "expect": "^29.5.0",
+ "expect": "^29.7.0",
"graceful-fs": "^4.2.9",
- "jest-diff": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "jest-matcher-utils": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
"natural-compare": "^1.4.0",
- "pretty-format": "^29.5.0",
- "semver": "^7.3.5"
+ "pretty-format": "^29.7.0",
+ "semver": "^7.5.3"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-snapshot/node_modules/semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
"dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
"bin": {
"semver": "bin/semver.js"
},
@@ -2766,12 +2883,12 @@
}
},
"node_modules/jest-util": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz",
- "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
"chalk": "^4.0.0",
"ci-info": "^3.2.0",
@@ -2783,17 +2900,17 @@
}
},
"node_modules/jest-validate": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz",
- "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
+ "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.5.0",
+ "@jest/types": "^29.6.3",
"camelcase": "^6.2.0",
"chalk": "^4.0.0",
- "jest-get-type": "^29.4.3",
+ "jest-get-type": "^29.6.3",
"leven": "^3.1.0",
- "pretty-format": "^29.5.0"
+ "pretty-format": "^29.7.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -2812,18 +2929,18 @@
}
},
"node_modules/jest-watcher": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz",
- "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
+ "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
"dev": true,
"dependencies": {
- "@jest/test-result": "^29.5.0",
- "@jest/types": "^29.5.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
"@types/node": "*",
"ansi-escapes": "^4.2.1",
"chalk": "^4.0.0",
"emittery": "^0.13.1",
- "jest-util": "^29.5.0",
+ "jest-util": "^29.7.0",
"string-length": "^4.0.1"
},
"engines": {
@@ -2831,13 +2948,13 @@
}
},
"node_modules/jest-worker": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz",
- "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
"dev": true,
"dependencies": {
"@types/node": "*",
- "jest-util": "^29.5.0",
+ "jest-util": "^29.7.0",
"merge-stream": "^2.0.0",
"supports-color": "^8.0.0"
},
@@ -2950,39 +3067,39 @@
"dev": true
},
"node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
+ "yallist": "^3.0.2"
}
},
"node_modules/make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
"dev": true,
"dependencies": {
- "semver": "^6.0.0"
+ "semver": "^7.5.3"
},
"engines": {
- "node": ">=8"
+ "node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-dir/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
"node_modules/make-error": {
@@ -3007,12 +3124,12 @@
"dev": true
},
"node_modules/micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
+ "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"dev": true,
"dependencies": {
- "braces": "^3.0.2",
+ "braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
@@ -3069,11 +3186,6 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
- "node_modules/node-addon-api": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
- "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="
- },
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -3081,17 +3193,17 @@
"dev": true
},
"node_modules/node-releases": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
- "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==",
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"dev": true
},
"node_modules/nodejs-file-downloader": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/nodejs-file-downloader/-/nodejs-file-downloader-4.12.1.tgz",
- "integrity": "sha512-LpfCTNhh805AlLnJnzt1PuEj+RmbrccbAQZ6hBRw2e6QPVR0Qntuo6qqyvPHG5s77/0w0IEKgRAD4nbSnr/X4w==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/nodejs-file-downloader/-/nodejs-file-downloader-4.13.0.tgz",
+ "integrity": "sha512-nI2fKnmJWWFZF6SgMPe1iBodKhfpztLKJTtCtNYGhm/9QXmWa/Pk9Sv00qHgzEvNLe1x7hjGDRor7gcm/ChaIQ==",
"dependencies": {
- "follow-redirects": "^1.15.1",
+ "follow-redirects": "^1.15.6",
"https-proxy-agent": "^5.0.0",
"mime-types": "^2.1.27",
"sanitize-filename": "^1.6.3"
@@ -3242,9 +3354,9 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"dev": true
},
"node_modules/picomatch": {
@@ -3260,9 +3372,9 @@
}
},
"node_modules/pirates": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
- "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
"dev": true,
"engines": {
"node": ">= 6"
@@ -3281,12 +3393,12 @@
}
},
"node_modules/pretty-format": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
- "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.4.3",
+ "@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
"react-is": "^18.0.0"
},
@@ -3325,9 +3437,9 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/pure-rand": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.0.tgz",
- "integrity": "sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
+ "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
"dev": true,
"funding": [
{
@@ -3344,15 +3456,16 @@
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
+ "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)",
"engines": {
"node": ">=0.6.0",
"teleport": ">=0.2.0"
}
},
"node_modules/react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"dev": true
},
"node_modules/rechoir": {
@@ -3376,11 +3489,11 @@
}
},
"node_modules/resolve": {
- "version": "1.22.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
- "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dependencies": {
- "is-core-module": "^2.9.0",
+ "is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
@@ -3413,9 +3526,9 @@
}
},
"node_modules/resolve.exports": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz",
- "integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
+ "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
"dev": true,
"engines": {
"node": ">=10"
@@ -3430,9 +3543,9 @@
}
},
"node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"bin": {
"semver": "bin/semver"
}
@@ -3674,12 +3787,14 @@
}
},
"node_modules/ts-jest": {
- "version": "29.1.1",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
- "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==",
+ "version": "29.2.4",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz",
+ "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"bs-logger": "0.x",
+ "ejs": "^3.1.10",
"fast-json-stable-stringify": "2.x",
"jest-util": "^29.0.0",
"json5": "^2.2.3",
@@ -3692,10 +3807,11 @@
"ts-jest": "cli.js"
},
"engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"@babel/core": ">=7.0.0-beta.0 <8",
+ "@jest/transform": "^29.0.0",
"@jest/types": "^29.0.0",
"babel-jest": "^29.0.0",
"jest": "^29.0.0",
@@ -3705,6 +3821,9 @@
"@babel/core": {
"optional": true
},
+ "@jest/transform": {
+ "optional": true
+ },
"@jest/types": {
"optional": true
},
@@ -3717,13 +3836,10 @@
}
},
"node_modules/ts-jest/node_modules/semver": {
- "version": "7.5.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
- "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
"dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
"bin": {
"semver": "bin/semver.js"
},
@@ -3732,9 +3848,9 @@
}
},
"node_modules/ts-node": {
- "version": "10.9.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
- "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
@@ -3796,10 +3912,11 @@
}
},
"node_modules/typescript": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
- "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
"dev": true,
+ "license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -3809,15 +3926,16 @@
}
},
"node_modules/undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz",
+ "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==",
+ "dev": true,
+ "license": "MIT"
},
"node_modules/update-browserslist-db": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
- "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
+ "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
"dev": true,
"funding": [
{
@@ -3827,23 +3945,27 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
},
"bin": {
- "browserslist-lint": "cli.js"
+ "update-browserslist-db": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/utf8-byte-length": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
- "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA=="
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
+ "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="
},
"node_modules/uuid": {
"version": "3.4.0",
@@ -3861,25 +3983,19 @@
"dev": true
},
"node_modules/v8-to-istanbul": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
- "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
+ "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.12",
"@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^1.6.0"
+ "convert-source-map": "^2.0.0"
},
"engines": {
"node": ">=10.12.0"
}
},
- "node_modules/v8-to-istanbul/node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "dev": true
- },
"node_modules/walker": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
@@ -3949,15 +4065,15 @@
}
},
"node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
"node_modules/yargs": {
- "version": "17.7.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
- "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"dependencies": {
"cliui": "^8.0.1",
@@ -4002,3004 +4118,5 @@
"url": "https://github.com/sponsors/sindresorhus"
}
}
- },
- "dependencies": {
- "@ampproject/remapping": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
- "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
- "dev": true,
- "requires": {
- "@jridgewell/gen-mapping": "^0.1.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- },
- "@babel/code-frame": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
- "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.18.6"
- }
- },
- "@babel/compat-data": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
- "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
- "dev": true
- },
- "@babel/core": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
- "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
- "dev": true,
- "requires": {
- "@ampproject/remapping": "^2.1.0",
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.5",
- "@babel/helper-compilation-targets": "^7.20.0",
- "@babel/helper-module-transforms": "^7.20.2",
- "@babel/helpers": "^7.20.5",
- "@babel/parser": "^7.20.5",
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.20.5",
- "@babel/types": "^7.20.5",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.1",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "@babel/generator": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz",
- "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.20.5",
- "@jridgewell/gen-mapping": "^0.3.2",
- "jsesc": "^2.5.1"
- },
- "dependencies": {
- "@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "dev": true,
- "requires": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- }
- }
- },
- "@babel/helper-compilation-targets": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz",
- "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==",
- "dev": true,
- "requires": {
- "@babel/compat-data": "^7.20.0",
- "@babel/helper-validator-option": "^7.18.6",
- "browserslist": "^4.21.3",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "@babel/helper-environment-visitor": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
- "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
- "dev": true
- },
- "@babel/helper-function-name": {
- "version": "7.19.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
- "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.18.10",
- "@babel/types": "^7.19.0"
- }
- },
- "@babel/helper-hoist-variables": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-module-imports": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
- "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-module-transforms": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz",
- "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==",
- "dev": true,
- "requires": {
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-module-imports": "^7.18.6",
- "@babel/helper-simple-access": "^7.20.2",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/helper-validator-identifier": "^7.19.1",
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.20.1",
- "@babel/types": "^7.20.2"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
- "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
- "dev": true
- },
- "@babel/helper-simple-access": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
- "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.20.2"
- }
- },
- "@babel/helper-split-export-declaration": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-string-parser": {
- "version": "7.19.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
- "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
- "dev": true
- },
- "@babel/helper-validator-identifier": {
- "version": "7.19.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
- "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
- "dev": true
- },
- "@babel/helper-validator-option": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
- "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
- "dev": true
- },
- "@babel/helpers": {
- "version": "7.20.6",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
- "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.20.5",
- "@babel/types": "^7.20.5"
- }
- },
- "@babel/highlight": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
- "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "@babel/parser": {
- "version": "7.20.13",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz",
- "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==",
- "dev": true
- },
- "@babel/plugin-syntax-async-generators": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
- "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-bigint": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
- "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-class-properties": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
- "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.12.13"
- }
- },
- "@babel/plugin-syntax-import-meta": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
- "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
- "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-jsx": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
- "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-syntax-logical-assignment-operators": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
- "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-numeric-separator": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
- "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-object-rest-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-top-level-await": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
- "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.14.5"
- }
- },
- "@babel/plugin-syntax-typescript": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz",
- "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.19.0"
- }
- },
- "@babel/template": {
- "version": "7.18.10",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
- "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.18.6",
- "@babel/parser": "^7.18.10",
- "@babel/types": "^7.18.10"
- }
- },
- "@babel/traverse": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz",
- "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.5",
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.19.0",
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.20.5",
- "@babel/types": "^7.20.5",
- "debug": "^4.1.0",
- "globals": "^11.1.0"
- }
- },
- "@babel/types": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
- "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
- "dev": true,
- "requires": {
- "@babel/helper-string-parser": "^7.19.4",
- "@babel/helper-validator-identifier": "^7.19.1",
- "to-fast-properties": "^2.0.0"
- }
- },
- "@bcoe/v8-coverage": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
- "dev": true
- },
- "@cspotcode/source-map-support": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
- "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
- "dev": true,
- "requires": {
- "@jridgewell/trace-mapping": "0.3.9"
- },
- "dependencies": {
- "@jridgewell/trace-mapping": {
- "version": "0.3.9",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
- "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
- "dev": true,
- "requires": {
- "@jridgewell/resolve-uri": "^3.0.3",
- "@jridgewell/sourcemap-codec": "^1.4.10"
- }
- }
- }
- },
- "@istanbuljs/load-nyc-config": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
- "dev": true,
- "requires": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "get-package-type": "^0.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
- "dependencies": {
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "requires": {
- "sprintf-js": "~1.0.2"
- }
- },
- "js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
- "dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- }
- }
- }
- },
- "@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true
- },
- "@jest/console": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz",
- "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0",
- "slash": "^3.0.0"
- }
- },
- "@jest/core": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz",
- "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==",
- "dev": true,
- "requires": {
- "@jest/console": "^29.5.0",
- "@jest/reporters": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "jest-changed-files": "^29.5.0",
- "jest-config": "^29.5.0",
- "jest-haste-map": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-regex-util": "^29.4.3",
- "jest-resolve": "^29.5.0",
- "jest-resolve-dependencies": "^29.5.0",
- "jest-runner": "^29.5.0",
- "jest-runtime": "^29.5.0",
- "jest-snapshot": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
- "jest-watcher": "^29.5.0",
- "micromatch": "^4.0.4",
- "pretty-format": "^29.5.0",
- "slash": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "@jest/environment": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz",
- "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "jest-mock": "^29.5.0"
- }
- },
- "@jest/expect": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz",
- "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==",
- "dev": true,
- "requires": {
- "expect": "^29.5.0",
- "jest-snapshot": "^29.5.0"
- }
- },
- "@jest/expect-utils": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz",
- "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==",
- "dev": true,
- "requires": {
- "jest-get-type": "^29.4.3"
- }
- },
- "@jest/fake-timers": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz",
- "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.5.0",
- "@sinonjs/fake-timers": "^10.0.2",
- "@types/node": "*",
- "jest-message-util": "^29.5.0",
- "jest-mock": "^29.5.0",
- "jest-util": "^29.5.0"
- }
- },
- "@jest/globals": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz",
- "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==",
- "dev": true,
- "requires": {
- "@jest/environment": "^29.5.0",
- "@jest/expect": "^29.5.0",
- "@jest/types": "^29.5.0",
- "jest-mock": "^29.5.0"
- }
- },
- "@jest/reporters": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz",
- "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==",
- "dev": true,
- "requires": {
- "@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@jridgewell/trace-mapping": "^0.3.15",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-instrument": "^5.1.0",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.1.3",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-worker": "^29.5.0",
- "slash": "^3.0.0",
- "string-length": "^4.0.1",
- "strip-ansi": "^6.0.0",
- "v8-to-istanbul": "^9.0.1"
- }
- },
- "@jest/schemas": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
- "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
- "dev": true,
- "requires": {
- "@sinclair/typebox": "^0.25.16"
- }
- },
- "@jest/source-map": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz",
- "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==",
- "dev": true,
- "requires": {
- "@jridgewell/trace-mapping": "^0.3.15",
- "callsites": "^3.0.0",
- "graceful-fs": "^4.2.9"
- }
- },
- "@jest/test-result": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz",
- "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==",
- "dev": true,
- "requires": {
- "@jest/console": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "collect-v8-coverage": "^1.0.0"
- }
- },
- "@jest/test-sequencer": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz",
- "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==",
- "dev": true,
- "requires": {
- "@jest/test-result": "^29.5.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
- "slash": "^3.0.0"
- }
- },
- "@jest/transform": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz",
- "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.11.6",
- "@jest/types": "^29.5.0",
- "@jridgewell/trace-mapping": "^0.3.15",
- "babel-plugin-istanbul": "^6.1.1",
- "chalk": "^4.0.0",
- "convert-source-map": "^2.0.0",
- "fast-json-stable-stringify": "^2.1.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
- "jest-regex-util": "^29.4.3",
- "jest-util": "^29.5.0",
- "micromatch": "^4.0.4",
- "pirates": "^4.0.4",
- "slash": "^3.0.0",
- "write-file-atomic": "^4.0.2"
- }
- },
- "@jest/types": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
- "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.4.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@jridgewell/gen-mapping": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
- "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
- "dev": true,
- "requires": {
- "@jridgewell/set-array": "^1.0.0",
- "@jridgewell/sourcemap-codec": "^1.4.10"
- }
- },
- "@jridgewell/resolve-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
- "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
- "dev": true
- },
- "@jridgewell/set-array": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
- "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
- "dev": true
- },
- "@jridgewell/sourcemap-codec": {
- "version": "1.4.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
- "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
- "dev": true
- },
- "@jridgewell/trace-mapping": {
- "version": "0.3.17",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
- "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
- "dev": true,
- "requires": {
- "@jridgewell/resolve-uri": "3.1.0",
- "@jridgewell/sourcemap-codec": "1.4.14"
- }
- },
- "@sinclair/typebox": {
- "version": "0.25.24",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
- "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==",
- "dev": true
- },
- "@sinonjs/commons": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
- "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
- "dev": true,
- "requires": {
- "type-detect": "4.0.8"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
- "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^2.0.0"
- }
- },
- "@tsconfig/node10": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
- "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
- "dev": true
- },
- "@tsconfig/node12": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
- "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
- "dev": true
- },
- "@tsconfig/node14": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
- "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
- "dev": true
- },
- "@tsconfig/node16": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
- "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
- "dev": true
- },
- "@types/babel__core": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
- "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==",
- "dev": true,
- "requires": {
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
- },
- "@types/babel__generator": {
- "version": "7.6.4",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
- "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.0.0"
- }
- },
- "@types/babel__template": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
- "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
- "dev": true,
- "requires": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "@types/babel__traverse": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz",
- "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.3.0"
- }
- },
- "@types/graceful-fs": {
- "version": "4.1.6",
- "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
- "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==",
- "dev": true,
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/istanbul-lib-coverage": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
- "dev": true
- },
- "@types/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "*"
- }
- },
- "@types/istanbul-reports": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "@types/jest": {
- "version": "29.5.1",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz",
- "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==",
- "dev": true,
- "requires": {
- "expect": "^29.0.0",
- "pretty-format": "^29.0.0"
- }
- },
- "@types/js-yaml": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
- "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
- "dev": true
- },
- "@types/node": {
- "version": "20.9.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
- "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
- "dev": true,
- "requires": {
- "undici-types": "~5.26.4"
- }
- },
- "@types/prettier": {
- "version": "2.7.2",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
- "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==",
- "dev": true
- },
- "@types/q": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz",
- "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==",
- "dev": true
- },
- "@types/stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
- "dev": true
- },
- "@types/yargs": {
- "version": "17.0.17",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz",
- "integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "@types/yargs-parser": {
- "version": "21.0.0",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
- "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
- "dev": true
- },
- "acorn": {
- "version": "8.8.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
- "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
- "dev": true
- },
- "acorn-walk": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
- "dev": true
- },
- "adm-zip": {
- "version": "0.5.10",
- "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
- "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ=="
- },
- "agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "requires": {
- "debug": "4"
- }
- },
- "ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "dev": true,
- "requires": {
- "type-fest": "^0.21.3"
- }
- },
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "arg": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
- "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
- "dev": true
- },
- "argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
- },
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "axios": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
- "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
- "requires": {
- "follow-redirects": "^1.15.0",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
- },
- "dependencies": {
- "form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- }
- }
- }
- },
- "azure-pipelines-task-lib": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-4.7.0.tgz",
- "integrity": "sha512-5MctDC1Bt7eFi9tQTXlikuWRDc2MenCNruMsEwcKuXqBj1ZY+fA/D+E1DbE0Qi2u8kl1p6szT0we8k6RHSOe/w==",
- "requires": {
- "adm-zip": "^0.5.10",
- "deasync": "^0.1.28",
- "minimatch": "3.0.5",
- "nodejs-file-downloader": "^4.11.1",
- "q": "^1.5.1",
- "semver": "^5.1.0",
- "shelljs": "^0.8.5",
- "uuid": "^3.0.1"
- }
- },
- "babel-jest": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz",
- "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==",
- "dev": true,
- "requires": {
- "@jest/transform": "^29.5.0",
- "@types/babel__core": "^7.1.14",
- "babel-plugin-istanbul": "^6.1.1",
- "babel-preset-jest": "^29.5.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "slash": "^3.0.0"
- }
- },
- "babel-plugin-istanbul": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
- "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-instrument": "^5.0.4",
- "test-exclude": "^6.0.0"
- }
- },
- "babel-plugin-jest-hoist": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz",
- "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.3.3",
- "@babel/types": "^7.3.3",
- "@types/babel__core": "^7.1.14",
- "@types/babel__traverse": "^7.0.6"
- }
- },
- "babel-preset-current-node-syntax": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
- "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
- "dev": true,
- "requires": {
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-bigint": "^7.8.3",
- "@babel/plugin-syntax-class-properties": "^7.8.3",
- "@babel/plugin-syntax-import-meta": "^7.8.3",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-top-level-await": "^7.8.3"
- }
- },
- "babel-preset-jest": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz",
- "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==",
- "dev": true,
- "requires": {
- "babel-plugin-jest-hoist": "^29.5.0",
- "babel-preset-current-node-syntax": "^1.0.0"
- }
- },
- "balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
- },
- "bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
- "requires": {
- "file-uri-to-path": "1.0.0"
- }
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "browserslist": {
- "version": "4.21.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
- "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001400",
- "electron-to-chromium": "^1.4.251",
- "node-releases": "^2.0.6",
- "update-browserslist-db": "^1.0.9"
- }
- },
- "bs-logger": {
- "version": "0.2.6",
- "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
- "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
- "dev": true,
- "requires": {
- "fast-json-stable-stringify": "2.x"
- }
- },
- "bser": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
- "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
- "dev": true,
- "requires": {
- "node-int64": "^0.4.0"
- }
- },
- "buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true
- },
- "callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true
- },
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dev": true
- },
- "caniuse-lite": {
- "version": "1.0.30001439",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz",
- "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==",
- "dev": true
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "char-regex": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
- "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
- "dev": true
- },
- "ci-info": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
- "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
- "dev": true
- },
- "cjs-module-lexer": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
- "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
- "dev": true
- },
- "cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- }
- },
- "co": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
- "dev": true
- },
- "collect-v8-coverage": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
- "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
- "dev": true
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "requires": {
- "delayed-stream": "~1.0.0"
- }
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
- },
- "convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true
- },
- "create-require": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
- "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
- "dev": true
- },
- "cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "deasync": {
- "version": "0.1.28",
- "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.28.tgz",
- "integrity": "sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==",
- "requires": {
- "bindings": "^1.5.0",
- "node-addon-api": "^1.7.1"
- }
- },
- "debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "requires": {
- "ms": "2.1.2"
- }
- },
- "dedent": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
- "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
- "dev": true
- },
- "deepmerge": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
- "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==",
- "dev": true
- },
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
- },
- "detect-newline": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
- "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
- "dev": true
- },
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
- },
- "diff-sequences": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
- "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
- "dev": true
- },
- "electron-to-chromium": {
- "version": "1.4.284",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
- "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
- "dev": true
- },
- "emittery": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
- "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dev": true,
- "requires": {
- "is-arrayish": "^0.2.1"
- }
- },
- "escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "dev": true
- },
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true
- },
- "execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "dev": true,
- "requires": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- }
- },
- "exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
- "dev": true
- },
- "expect": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz",
- "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==",
- "dev": true,
- "requires": {
- "@jest/expect-utils": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "jest-matcher-utils": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0"
- }
- },
- "fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
- },
- "fb-watchman": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
- "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
- "dev": true,
- "requires": {
- "bser": "2.1.1"
- }
- },
- "file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "follow-redirects": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
- },
- "fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "optional": true
- },
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
- },
- "gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "get-package-type": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
- "dev": true
- },
- "get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "dev": true
- },
- "glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "dependencies": {
- "minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- }
- }
- },
- "globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true
- },
- "graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "dev": true
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
- },
- "https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "requires": {
- "agent-base": "6",
- "debug": "4"
- }
- },
- "human-signals": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "dev": true
- },
- "import-local": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
- "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
- "dev": true,
- "requires": {
- "pkg-dir": "^4.2.0",
- "resolve-cwd": "^3.0.0"
- }
- },
- "imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "interpret": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
- "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="
- },
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true
- },
- "is-core-module": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
- "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "is-generator-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
- "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
- "dev": true
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
- },
- "istanbul-lib-coverage": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
- "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
- "dev": true
- },
- "istanbul-lib-instrument": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
- "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
- "dev": true,
- "requires": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^3.0.0",
- "supports-color": "^7.1.0"
- }
- },
- "istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
- "requires": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- }
- },
- "istanbul-reports": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
- "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
- "dev": true,
- "requires": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- }
- },
- "jest": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz",
- "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==",
- "dev": true,
- "requires": {
- "@jest/core": "^29.5.0",
- "@jest/types": "^29.5.0",
- "import-local": "^3.0.2",
- "jest-cli": "^29.5.0"
- }
- },
- "jest-changed-files": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz",
- "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==",
- "dev": true,
- "requires": {
- "execa": "^5.0.0",
- "p-limit": "^3.1.0"
- }
- },
- "jest-circus": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz",
- "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==",
- "dev": true,
- "requires": {
- "@jest/environment": "^29.5.0",
- "@jest/expect": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "co": "^4.6.0",
- "dedent": "^0.7.0",
- "is-generator-fn": "^2.0.0",
- "jest-each": "^29.5.0",
- "jest-matcher-utils": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-runtime": "^29.5.0",
- "jest-snapshot": "^29.5.0",
- "jest-util": "^29.5.0",
- "p-limit": "^3.1.0",
- "pretty-format": "^29.5.0",
- "pure-rand": "^6.0.0",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-cli": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz",
- "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==",
- "dev": true,
- "requires": {
- "@jest/core": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/types": "^29.5.0",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "import-local": "^3.0.2",
- "jest-config": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
- "prompts": "^2.0.1",
- "yargs": "^17.3.1"
- }
- },
- "jest-config": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz",
- "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.11.6",
- "@jest/test-sequencer": "^29.5.0",
- "@jest/types": "^29.5.0",
- "babel-jest": "^29.5.0",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "deepmerge": "^4.2.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-circus": "^29.5.0",
- "jest-environment-node": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "jest-regex-util": "^29.4.3",
- "jest-resolve": "^29.5.0",
- "jest-runner": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
- "micromatch": "^4.0.4",
- "parse-json": "^5.2.0",
- "pretty-format": "^29.5.0",
- "slash": "^3.0.0",
- "strip-json-comments": "^3.1.1"
- }
- },
- "jest-diff": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
- "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^29.4.3",
- "jest-get-type": "^29.4.3",
- "pretty-format": "^29.5.0"
- }
- },
- "jest-docblock": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz",
- "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==",
- "dev": true,
- "requires": {
- "detect-newline": "^3.0.0"
- }
- },
- "jest-each": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz",
- "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.5.0",
- "chalk": "^4.0.0",
- "jest-get-type": "^29.4.3",
- "jest-util": "^29.5.0",
- "pretty-format": "^29.5.0"
- }
- },
- "jest-environment-node": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz",
- "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==",
- "dev": true,
- "requires": {
- "@jest/environment": "^29.5.0",
- "@jest/fake-timers": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "jest-mock": "^29.5.0",
- "jest-util": "^29.5.0"
- }
- },
- "jest-get-type": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
- "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
- "dev": true
- },
- "jest-haste-map": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz",
- "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.5.0",
- "@types/graceful-fs": "^4.1.3",
- "@types/node": "*",
- "anymatch": "^3.0.3",
- "fb-watchman": "^2.0.0",
- "fsevents": "^2.3.2",
- "graceful-fs": "^4.2.9",
- "jest-regex-util": "^29.4.3",
- "jest-util": "^29.5.0",
- "jest-worker": "^29.5.0",
- "micromatch": "^4.0.4",
- "walker": "^1.0.8"
- }
- },
- "jest-leak-detector": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz",
- "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==",
- "dev": true,
- "requires": {
- "jest-get-type": "^29.4.3",
- "pretty-format": "^29.5.0"
- }
- },
- "jest-matcher-utils": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz",
- "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "jest-diff": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "pretty-format": "^29.5.0"
- }
- },
- "jest-message-util": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz",
- "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^29.5.0",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^29.5.0",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-mock": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz",
- "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "jest-util": "^29.5.0"
- }
- },
- "jest-pnp-resolver": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
- "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
- "dev": true,
- "requires": {}
- },
- "jest-regex-util": {
- "version": "29.4.3",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz",
- "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==",
- "dev": true
- },
- "jest-resolve": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz",
- "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
- "jest-pnp-resolver": "^1.2.2",
- "jest-util": "^29.5.0",
- "jest-validate": "^29.5.0",
- "resolve": "^1.20.0",
- "resolve.exports": "^2.0.0",
- "slash": "^3.0.0"
- }
- },
- "jest-resolve-dependencies": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz",
- "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==",
- "dev": true,
- "requires": {
- "jest-regex-util": "^29.4.3",
- "jest-snapshot": "^29.5.0"
- }
- },
- "jest-runner": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz",
- "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==",
- "dev": true,
- "requires": {
- "@jest/console": "^29.5.0",
- "@jest/environment": "^29.5.0",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "emittery": "^0.13.1",
- "graceful-fs": "^4.2.9",
- "jest-docblock": "^29.4.3",
- "jest-environment-node": "^29.5.0",
- "jest-haste-map": "^29.5.0",
- "jest-leak-detector": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-resolve": "^29.5.0",
- "jest-runtime": "^29.5.0",
- "jest-util": "^29.5.0",
- "jest-watcher": "^29.5.0",
- "jest-worker": "^29.5.0",
- "p-limit": "^3.1.0",
- "source-map-support": "0.5.13"
- }
- },
- "jest-runtime": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz",
- "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==",
- "dev": true,
- "requires": {
- "@jest/environment": "^29.5.0",
- "@jest/fake-timers": "^29.5.0",
- "@jest/globals": "^29.5.0",
- "@jest/source-map": "^29.4.3",
- "@jest/test-result": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "cjs-module-lexer": "^1.0.0",
- "collect-v8-coverage": "^1.0.0",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-mock": "^29.5.0",
- "jest-regex-util": "^29.4.3",
- "jest-resolve": "^29.5.0",
- "jest-snapshot": "^29.5.0",
- "jest-util": "^29.5.0",
- "slash": "^3.0.0",
- "strip-bom": "^4.0.0"
- }
- },
- "jest-snapshot": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz",
- "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.11.6",
- "@babel/generator": "^7.7.2",
- "@babel/plugin-syntax-jsx": "^7.7.2",
- "@babel/plugin-syntax-typescript": "^7.7.2",
- "@babel/traverse": "^7.7.2",
- "@babel/types": "^7.3.3",
- "@jest/expect-utils": "^29.5.0",
- "@jest/transform": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/babel__traverse": "^7.0.6",
- "@types/prettier": "^2.1.5",
- "babel-preset-current-node-syntax": "^1.0.0",
- "chalk": "^4.0.0",
- "expect": "^29.5.0",
- "graceful-fs": "^4.2.9",
- "jest-diff": "^29.5.0",
- "jest-get-type": "^29.4.3",
- "jest-matcher-utils": "^29.5.0",
- "jest-message-util": "^29.5.0",
- "jest-util": "^29.5.0",
- "natural-compare": "^1.4.0",
- "pretty-format": "^29.5.0",
- "semver": "^7.3.5"
- },
- "dependencies": {
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "jest-util": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz",
- "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- },
- "jest-validate": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz",
- "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.5.0",
- "camelcase": "^6.2.0",
- "chalk": "^4.0.0",
- "jest-get-type": "^29.4.3",
- "leven": "^3.1.0",
- "pretty-format": "^29.5.0"
- },
- "dependencies": {
- "camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true
- }
- }
- },
- "jest-watcher": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz",
- "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==",
- "dev": true,
- "requires": {
- "@jest/test-result": "^29.5.0",
- "@jest/types": "^29.5.0",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "emittery": "^0.13.1",
- "jest-util": "^29.5.0",
- "string-length": "^4.0.1"
- }
- },
- "jest-worker": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz",
- "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==",
- "dev": true,
- "requires": {
- "@types/node": "*",
- "jest-util": "^29.5.0",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "dependencies": {
- "supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "requires": {
- "argparse": "^2.0.1"
- }
- },
- "jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
- "dev": true
- },
- "json-parse-even-better-errors": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
- "dev": true
- },
- "json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "dev": true
- },
- "kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
- "dev": true
- },
- "leven": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
- "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
- "dev": true
- },
- "lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "lodash.memoize": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
- "dev": true
- },
- "lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "make-error": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "dev": true
- },
- "makeerror": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
- "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
- "dev": true,
- "requires": {
- "tmpl": "1.0.5"
- }
- },
- "merge-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
- "dev": true
- },
- "micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
- "dev": true,
- "requires": {
- "braces": "^3.0.2",
- "picomatch": "^2.3.1"
- }
- },
- "mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
- },
- "mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "requires": {
- "mime-db": "1.52.0"
- }
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- },
- "minimatch": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz",
- "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==",
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
- },
- "node-addon-api": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
- "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="
- },
- "node-int64": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
- "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
- "dev": true
- },
- "node-releases": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
- "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==",
- "dev": true
- },
- "nodejs-file-downloader": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/nodejs-file-downloader/-/nodejs-file-downloader-4.12.1.tgz",
- "integrity": "sha512-LpfCTNhh805AlLnJnzt1PuEj+RmbrccbAQZ6hBRw2e6QPVR0Qntuo6qqyvPHG5s77/0w0IEKgRAD4nbSnr/X4w==",
- "requires": {
- "follow-redirects": "^1.15.1",
- "https-proxy-agent": "^5.0.0",
- "mime-types": "^2.1.27",
- "sanitize-filename": "^1.6.3"
- }
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
- "requires": {
- "path-key": "^3.0.0"
- }
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "requires": {
- "wrappy": "1"
- }
- },
- "onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "requires": {
- "yocto-queue": "^0.1.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- },
- "dependencies": {
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- }
- }
- },
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true
- },
- "parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
- },
- "path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
- },
- "picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true
- },
- "pirates": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
- "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
- "dev": true
- },
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
- "requires": {
- "find-up": "^4.0.0"
- }
- },
- "pretty-format": {
- "version": "29.5.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
- "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.4.3",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
- }
- }
- },
- "prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "dev": true,
- "requires": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- }
- },
- "proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
- "pure-rand": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.0.tgz",
- "integrity": "sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==",
- "dev": true
- },
- "q": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
- "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="
- },
- "react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- },
- "rechoir": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
- "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
- "requires": {
- "resolve": "^1.1.6"
- }
- },
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true
- },
- "resolve": {
- "version": "1.22.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
- "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
- "requires": {
- "is-core-module": "^2.9.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- }
- },
- "resolve-cwd": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
- "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
- "dev": true,
- "requires": {
- "resolve-from": "^5.0.0"
- }
- },
- "resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true
- },
- "resolve.exports": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz",
- "integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==",
- "dev": true
- },
- "sanitize-filename": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
- "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
- "requires": {
- "truncate-utf8-bytes": "^1.0.0"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
- },
- "shelljs": {
- "version": "0.8.5",
- "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
- "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
- "requires": {
- "glob": "^7.0.0",
- "interpret": "^1.0.0",
- "rechoir": "^0.6.2"
- }
- },
- "signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true
- },
- "sisteransi": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
- "dev": true
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- },
- "source-map-support": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
- "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "dev": true
- },
- "stack-utils": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
- "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
- "dev": true,
- "requires": {
- "escape-string-regexp": "^2.0.0"
- }
- },
- "string-length": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
- "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
- "dev": true,
- "requires": {
- "char-regex": "^1.0.2",
- "strip-ansi": "^6.0.0"
- }
- },
- "string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- }
- },
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- },
- "strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true
- },
- "strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true
- },
- "strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
- },
- "test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
- "dev": true,
- "requires": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- }
- },
- "tmpl": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
- "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
- "dev": true
- },
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "dev": true
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "truncate-utf8-bytes": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
- "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
- "requires": {
- "utf8-byte-length": "^1.0.1"
- }
- },
- "ts-jest": {
- "version": "29.1.1",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
- "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==",
- "dev": true,
- "requires": {
- "bs-logger": "0.x",
- "fast-json-stable-stringify": "2.x",
- "jest-util": "^29.0.0",
- "json5": "^2.2.3",
- "lodash.memoize": "4.x",
- "make-error": "1.x",
- "semver": "^7.5.3",
- "yargs-parser": "^21.0.1"
- },
- "dependencies": {
- "semver": {
- "version": "7.5.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
- "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "ts-node": {
- "version": "10.9.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
- "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
- "dev": true,
- "requires": {
- "@cspotcode/source-map-support": "^0.8.0",
- "@tsconfig/node10": "^1.0.7",
- "@tsconfig/node12": "^1.0.7",
- "@tsconfig/node14": "^1.0.0",
- "@tsconfig/node16": "^1.0.2",
- "acorn": "^8.4.1",
- "acorn-walk": "^8.1.1",
- "arg": "^4.1.0",
- "create-require": "^1.1.0",
- "diff": "^4.0.1",
- "make-error": "^1.1.1",
- "v8-compile-cache-lib": "^3.0.1",
- "yn": "3.1.1"
- }
- },
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true
- },
- "type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
- "dev": true
- },
- "typescript": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
- "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
- "dev": true
- },
- "undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true
- },
- "update-browserslist-db": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
- "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
- "dev": true,
- "requires": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
- }
- },
- "utf8-byte-length": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
- "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA=="
- },
- "uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
- },
- "v8-compile-cache-lib": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
- "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
- "dev": true
- },
- "v8-to-istanbul": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
- "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
- "dev": true,
- "requires": {
- "@jridgewell/trace-mapping": "^0.3.12",
- "@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^1.6.0"
- },
- "dependencies": {
- "convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "dev": true
- }
- }
- },
- "walker": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
- "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
- "dev": true,
- "requires": {
- "makeerror": "1.0.12"
- }
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
- },
- "write-file-atomic": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
- "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
- "dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "signal-exit": "^3.0.7"
- }
- },
- "y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true
- },
- "yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
- "yargs": {
- "version": "17.7.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
- "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
- "dev": true,
- "requires": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- }
- },
- "yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true
- },
- "yn": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
- "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "dev": true
- },
- "yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true
- }
}
}
diff --git a/extension/package.json b/extension/package.json
index 041657b1..8f9dbad3 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -25,18 +25,19 @@
},
"homepage": "https://github.com/tinglesoftware/dependabot-azure-devops#readme",
"dependencies": {
- "axios": "^1.6.0",
- "azure-pipelines-task-lib": "^4.7.0",
- "js-yaml": "^4.1.0"
+ "axios": "1.7.3",
+ "azure-pipelines-task-lib": "4.15.0",
+ "js-yaml": "4.1.0"
},
"devDependencies": {
- "@types/jest": "^29.5.1",
- "@types/js-yaml": "^4.0.3",
- "@types/node": "^20.9.0",
- "@types/q": "^1.5.5",
- "jest": "^29.5.0",
- "ts-jest": "^29.1.1",
- "typescript": "^5.2.2",
- "ts-node": "^10.9.1"
- }
+ "@types/jest": "29.5.12",
+ "@types/js-yaml": "4.0.9",
+ "@types/node": "22.1.0",
+ "@types/q": "1.5.8",
+ "jest": "29.7.0",
+ "ts-jest": "29.2.4",
+ "typescript": "5.5.4",
+ "ts-node": "10.9.2"
+ },
+ "packageManager": "npm@10.8.1"
}
diff --git a/extension/task/IDependabotConfig.ts b/extension/task/IDependabotConfig.ts
index d4573617..3951b9a8 100644
--- a/extension/task/IDependabotConfig.ts
+++ b/extension/task/IDependabotConfig.ts
@@ -26,8 +26,19 @@ export interface IDependabotUpdate {
packageEcosystem: string;
/**
* Location of package manifests.
+ * https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#directory
* */
directory: string;
+ /**
+ * Locations of package manifests.
+ * https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#directories
+ * */
+ directories?: string[];
+ /**
+ * Dependency group rules
+ * https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups
+ * */
+ groups?: string;
/**
* Customize which updates are allowed.
*/
@@ -43,11 +54,14 @@ export interface IDependabotUpdate {
/**
* Reviewers.
*/
- reviewers?: string;
+ reviewers?: string[];
/**
* Assignees.
*/
- assignees?: string;
+ assignees?: string[];
+ /**
+ * Commit Message.
+ */
commitMessage?: string;
/**
* The milestone to associate pull requests with.
diff --git a/extension/task/index.ts b/extension/task/index.ts
index c08af2ba..7a1e9b3a 100644
--- a/extension/task/index.ts
+++ b/extension/task/index.ts
@@ -3,20 +3,10 @@ import { ToolRunner } from "azure-pipelines-task-lib/toolrunner"
import { IDependabotConfig, IDependabotRegistry, IDependabotUpdate } from "./IDependabotConfig";
import getSharedVariables from "./utils/getSharedVariables";
import { parseConfigFile } from "./utils/parseConfigFile";
+import { resolveAzureDevOpsIdentities } from "./utils/resolveAzureDevOpsIdentities";
async function run() {
try {
- let useConfigFile: boolean = tl.getBoolInput("useConfigFile", true);
- if (!useConfigFile) {
- throw new Error(
- `
- Using explicit inputs is no longer supported.
- Migrate to using a config file at .github/dependabot.yml.
- See https://github.com/tinglesoftware/dependabot-azure-devops/tree/main/extension#usage for more information.
- `
- );
- }
-
// Checking if docker is installed
tl.debug("Checking for docker install ...");
tl.which("docker", true);
@@ -41,6 +31,7 @@ async function run() {
// For each update run docker container
for (const update of updates) {
// Prepare the docker task
+ // tl.which throws an error if the tool is not found
let dockerRunner: ToolRunner = tl.tool(tl.which("docker", true));
dockerRunner.arg(["run"]); // run command
dockerRunner.arg(["--rm"]); // remove after execution
@@ -57,10 +48,13 @@ async function run() {
dockerRunner.arg(["-e", `DEPENDABOT_PACKAGE_MANAGER=${update.packageEcosystem}`]);
dockerRunner.arg(["-e", `DEPENDABOT_OPEN_PULL_REQUESTS_LIMIT=${update.openPullRequestsLimit}`]); // always has a value
- // Set the directory
+ // Set the directory or directories
if (update.directory) {
dockerRunner.arg(["-e", `DEPENDABOT_DIRECTORY=${update.directory}`]);
}
+ if (update.directories && update.directories.length > 0) {
+ dockerRunner.arg(["-e", `DEPENDABOT_DIRECTORIES=${JSON.stringify(update.directories)}`]);
+ }
// Set the target branch
if (update.targetBranch) {
@@ -110,6 +104,12 @@ async function run() {
dockerRunner.arg(["-e", `DEPENDABOT_IGNORE_CONDITIONS=${ignore}`]);
}
+ // Set the dependency groups
+ let groups = update.groups;
+ if (groups) {
+ dockerRunner.arg(["-e", `DEPENDABOT_DEPENDENCY_GROUPS=${groups}`]);
+ }
+
// Set the commit message options
let commitMessage = update.commitMessage;
if (commitMessage) {
@@ -128,12 +128,14 @@ async function run() {
// Set the reviewers
if (update.reviewers) {
- dockerRunner.arg(["-e", `DEPENDABOT_REVIEWERS=${update.reviewers}`]);
+ const reviewers = await resolveAzureDevOpsIdentities(variables.organizationUrl, update.reviewers)
+ dockerRunner.arg(["-e", `DEPENDABOT_REVIEWERS=${JSON.stringify(reviewers.map(identity => identity.id))}`]);
}
// Set the assignees
if (update.assignees) {
- dockerRunner.arg(["-e", `DEPENDABOT_ASSIGNEES=${update.assignees}`]);
+ const assignees = await resolveAzureDevOpsIdentities(variables.organizationUrl, update.assignees)
+ dockerRunner.arg(["-e", `DEPENDABOT_ASSIGNEES=${JSON.stringify(assignees.map(identity => identity.id))}`]);
}
// Set the updater options, if provided
@@ -161,6 +163,11 @@ async function run() {
dockerRunner.arg(["-e", 'DEPENDABOT_SKIP_PULL_REQUESTS=true']);
}
+ // Set skip pull requests if true
+ if (variables.commentPullRequests === true) {
+ dockerRunner.arg(["-e", 'DEPENDABOT_COMMENT_PULL_REQUESTS=true']);
+ }
+
// Set abandon Unwanted pull requests if true
if (variables.abandonUnwantedPullRequests === true) {
dockerRunner.arg(["-e", 'DEPENDABOT_CLOSE_PULL_REQUESTS=true']);
@@ -227,6 +234,11 @@ async function run() {
}
}
+ // Set debug
+ if (variables.debug === true) {
+ dockerRunner.arg(["-e", 'DEPENDABOT_DEBUG=true']);
+ }
+
// Add in extra environment variables
variables.extraEnvironmentVariables.forEach(extraEnvVar => {
dockerRunner.arg(["-e", extraEnvVar]);
@@ -244,7 +256,7 @@ async function run() {
dockerRunner.arg(dockerImage);
// set the script to be run
- dockerRunner.arg('update_script');
+ dockerRunner.arg(variables.command);
// Now execute using docker
await dockerRunner.exec();
diff --git a/extension/task/task.json b/extension/task/task.json
index 3492f76a..211cf035 100644
--- a/extension/task/task.json
+++ b/extension/task/task.json
@@ -4,15 +4,17 @@
"name": "dependabot",
"friendlyName": "Dependabot",
"description": "Automatically update dependencies and vulnerabilities in your code",
- "helpMarkDown": "For help please visit https://github.com/tinglesoftware/dependabot-azure-devops",
+ "helpMarkDown": "For help please visit https://github.com/tinglesoftware/dependabot-azure-devops/issues",
+ "helpUrl": "https://github.com/tinglesoftware/dependabot-azure-devops/issues",
+ "releaseNotes": "https://github.com/tinglesoftware/dependabot-azure-devops/releases",
"category": "Utility",
"visibility": ["Build", "Release"],
"runsOn": ["Agent", "DeploymentGroup"],
"author": "Tingle Software",
- "demands": ["docker"],
+ "demands": [],
"version": {
"Major": 1,
- "Minor": 5,
+ "Minor": 6,
"Patch": 0
},
"instanceNameFormat": "Dependabot",
@@ -20,12 +22,17 @@
"groups": [
{
"name": "security_updates",
- "displayName": "Security advisories, vulnerabilities, and updates.",
+ "displayName": "Security advisories and vulnerabilities",
"isExpanded": false
},
{
- "name": "approval_completion",
- "displayName": "Auto Approval and Auto Completion or PRs",
+ "name": "pull_requests",
+ "displayName": "Pull request options",
+ "isExpanded": false
+ },
+ {
+ "name": "devops",
+ "displayName": "Azure DevOps authentication",
"isExpanded": false
},
{
@@ -41,46 +48,57 @@
],
"inputs": [
{
- "name": "useConfigFile",
+ "name": "useUpdateScriptvNext",
"type": "boolean",
- "label": "Use Dependabot YAML file",
- "defaultValue": "true",
+ "groupName": "advanced",
+ "label": "Use latest update script (vNext) (Experimental)",
+ "defaultValue": "false",
"required": false,
- "helpMarkDown": "Determines if the task will pick config values specified from the yaml file located at `.github/dependabot.yml` or `.github/dependabot.yaml`"
+ "helpMarkDown": "Determines if the task will use the newest 'vNext' update script instead of the default update script. This Defaults to `false`. See the [vNext update script documentation](https://github.com/tinglesoftware/dependabot-azure-devops/pull/1186) for more information."
},
{
"name": "failOnException",
"type": "boolean",
- "label": "Determines if the execution should fail when an exception occurs. Defaults to `true`",
+ "groupName": "advanced",
+ "label": "Fail task when an update exception occurs.",
"defaultValue": true,
"required": false,
- "helpMarkDown": "When set to true, a failure in updating a single dependency will cause the container execution to fail thereby causing the task to fail. This is important when you want a single failure to prevent trying to update other dependencies."
+ "helpMarkDown": "When set to `true`, a failure in updating a single dependency will cause the container execution to fail thereby causing the task to fail. This is important when you want a single failure to prevent trying to update other dependencies."
},
+
{
"name": "skipPullRequests",
"type": "boolean",
- "groupName": "advanced",
- "label": "Whether to skip creation and updating of pull requests.",
+ "groupName": "pull_requests",
+ "label": "Skip creation and updating of pull requests.",
"defaultValue": false,
"required": false,
"helpMarkDown": "When set to `true` the logic to update the dependencies is executed but the actual Pull Requests are not created/updated. Defaults to `false`."
},
+ {
+ "name": "commentPullRequests",
+ "type": "boolean",
+ "groupName": "pull_requests",
+ "label": "Comment on abandoned pull requests with close reason.",
+ "defaultValue": false,
+ "required": false,
+ "helpMarkDown": "When set to `true` a comment will be added to abandoned pull requests explanating why it was closed. Defaults to `false`."
+ },
{
"name": "abandonUnwantedPullRequests",
"type": "boolean",
- "groupName": "advanced",
- "label": "Whether to abandon unwanted pull requests.",
+ "groupName": "pull_requests",
+ "label": "Abandon unwanted pull requests.",
"defaultValue": false,
"required": false,
"helpMarkDown": "When set to `true` pull requests that are no longer needed are closed at the tail end of the execution. Defaults to `false`."
},
-
{
"name": "setAutoComplete",
"type": "boolean",
- "groupName": "approval_completion",
- "label": "Determines if the pull requests that dependabot creates should have auto complete set.",
+ "groupName": "pull_requests",
+ "label": "Auto-complete pull requests when all policies pass",
"defaultValue": false,
"required": false,
"helpMarkDown": "When set to `true`, pull requests that pass all policies will be merged automatically. Defaults to `false`."
@@ -88,7 +106,7 @@
{
"name": "mergeStrategy",
"type": "pickList",
- "groupName": "approval_completion",
+ "groupName": "pull_requests",
"label": "Merge Strategy",
"defaultValue": "squash",
"required": true,
@@ -104,17 +122,18 @@
{
"name": "autoCompleteIgnoreConfigIds",
"type": "string",
- "groupName": "approval_completion",
+ "groupName": "pull_requests",
"label": "Semicolon delimited list of any policy configuration IDs which auto-complete should not wait for.",
"defaultValue": "",
"required": false,
- "helpMarkDown": "A semicolon (`;`) delimited list of any policy configuration IDs which auto-complete should not wait for. Only applies to optional policies (isBlocking == false). Auto-complete always waits for required policies (isBlocking == true)."
+ "helpMarkDown": "A semicolon (`;`) delimited list of any policy configuration IDs which auto-complete should not wait for. Only applies to optional policies (isBlocking == false). Auto-complete always waits for required policies (isBlocking == true).",
+ "visibleRule": "setAutoComplete=true"
},
{
"name": "autoApprove",
"type": "boolean",
- "groupName": "approval_completion",
- "label": "Determines if the pull requests that dependabot creates should be automatically approved.",
+ "groupName": "pull_requests",
+ "label": "Auto-approve pull requests",
"defaultValue": false,
"required": false,
"helpMarkDown": "When set to `true`, pull requests will automatically be approved by the specified user. Defaults to `false`."
@@ -122,7 +141,7 @@
{
"name": "autoApproveUserToken",
"type": "string",
- "groupName": "approval_completion",
+ "groupName": "pull_requests",
"label": "A personal access token of the user that should approve the PR.",
"defaultValue": "",
"required": false,
@@ -161,7 +180,7 @@
{
"name": "azureDevOpsServiceConnection",
"type": "connectedService:Externaltfs",
- "groupName": "advanced",
+ "groupName": "devops",
"label": "Azure DevOps Service Connection to use.",
"required": false,
"helpMarkDown": "Specify a service connection to use, if you want to use a different service principal than the default to create your PRs."
@@ -169,7 +188,7 @@
{
"name": "azureDevOpsAccessToken",
"type": "string",
- "groupName": "advanced",
+ "groupName": "devops",
"label": "Azure DevOps Personal Access Token.",
"required": false,
"helpMarkDown": "The Personal Access Token for accessing Azure DevOps repositories. Supply a value here to avoid using permissions for the Build Service either because you cannot change its permissions or because you prefer that the Pull Requests be done by a different user. Use this in place of `azureDevOpsServiceConnection` such as when it is not possible to create a service connection."
@@ -195,9 +214,9 @@
"name": "updaterOptions",
"type": "string",
"groupName": "advanced",
- "label": "Comma separated list of updater options.",
+ "label": "Comma separated list of Dependabot experiments (updater options).",
"required": false,
- "helpMarkDown": "Set a list of updater options in CSV format. Available options depend on the ecosystem. Example: `goprivate=true,kubernetes_updates=true`."
+ "helpMarkDown": "Set a list of Dependabot experiments (updater options) in CSV format. Available options depend on the ecosystem. Example: `goprivate=true,kubernetes_updates=true`."
},
{
"name": "excludeRequirementsToUnlock",
diff --git a/extension/task/utils/getSharedVariables.ts b/extension/task/utils/getSharedVariables.ts
index 5dab8d86..996c95ed 100644
--- a/extension/task/utils/getSharedVariables.ts
+++ b/extension/task/utils/getSharedVariables.ts
@@ -51,21 +51,32 @@ export interface ISharedVariables {
excludeRequirementsToUnlock: string;
updaterOptions: string;
+ /** Determines if verbose log messages are logged */
+ debug: boolean;
+
/** List of update identifiers to run */
targetUpdateIds: number[];
securityAdvisoriesFile: string | undefined;
+
/** Determines whether to skip creating/updating pull requests */
skipPullRequests: boolean;
+ /** Determines whether to comment on pull requests which an explanation of the reason for closing */
+ commentPullRequests: boolean;
/** Determines whether to abandon unwanted pull requests */
abandonUnwantedPullRequests: boolean;
+
/** List of extra environment variables */
extraEnvironmentVariables: string[];
+
/** Flag used to forward the host ssh socket */
forwardHostSshSocket: boolean;
/** Tag of the docker image to be pulled */
dockerImageTag: string;
+
+ /** Dependabot command to run */
+ command: string;
}
/**
@@ -74,7 +85,6 @@ export interface ISharedVariables {
* @returns shared variables
*/
export default function getSharedVariables(): ISharedVariables {
- // Prepare shared variables
let organizationUrl = tl.getVariable("System.TeamFoundationCollectionUri");
//convert url string into a valid JS URL object
let formattedOrganizationUrl = new URL(organizationUrl);
@@ -119,6 +129,8 @@ export default function getSharedVariables(): ISharedVariables {
tl.getInput("excludeRequirementsToUnlock") || "";
let updaterOptions = tl.getInput("updaterOptions");
+ let debug: boolean = tl.getVariable("System.Debug")?.localeCompare("true") === 0;
+
// Get the target identifiers
let targetUpdateIds = tl
.getDelimitedInput("targetUpdateIds", ";", false)
@@ -129,12 +141,15 @@ export default function getSharedVariables(): ISharedVariables {
"securityAdvisoriesFile"
);
let skipPullRequests: boolean = tl.getBoolInput("skipPullRequests", false);
+ let commentPullRequests: boolean = tl.getBoolInput("commentPullRequests", false);
let abandonUnwantedPullRequests: boolean = tl.getBoolInput("abandonUnwantedPullRequests", true);
+
let extraEnvironmentVariables = tl.getDelimitedInput(
"extraEnvironmentVariables",
";",
false
);
+
let forwardHostSshSocket: boolean = tl.getBoolInput(
"forwardHostSshSocket",
false
@@ -143,9 +158,12 @@ export default function getSharedVariables(): ISharedVariables {
// Prepare variables for the docker image to use
let dockerImageTag: string = getDockerImageTag();
+ let command: string = tl.getBoolInput("useUpdateScriptvNext", false)
+ ? "update_script_vnext"
+ : "update_script";
+
return {
organizationUrl: formattedOrganizationUrl,
-
protocol,
hostname,
port,
@@ -169,14 +187,22 @@ export default function getSharedVariables(): ISharedVariables {
failOnException,
excludeRequirementsToUnlock,
updaterOptions,
+
+ debug,
targetUpdateIds,
securityAdvisoriesFile,
+
skipPullRequests,
+ commentPullRequests,
abandonUnwantedPullRequests,
+
extraEnvironmentVariables,
+
forwardHostSshSocket,
dockerImageTag,
+
+ command
};
}
diff --git a/extension/task/utils/parseConfigFile.ts b/extension/task/utils/parseConfigFile.ts
index 032e3e65..33bb814d 100644
--- a/extension/task/utils/parseConfigFile.ts
+++ b/extension/task/utils/parseConfigFile.ts
@@ -15,7 +15,7 @@ import axios from "axios";
/**
* Parse the dependabot config YAML file to specify update configuration
*
- * The file should be located at '/.github/dependabot.yml' or '/.github/dependabot.yaml'
+ * The file should be located at '/.azuredevops/dependabot.yml' or '/.github/dependabot.yml'
*
* To view YAML file format, visit
* https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#allow
@@ -25,6 +25,9 @@ import axios from "axios";
*/
async function parseConfigFile(variables: ISharedVariables): Promise {
const possibleFilePaths = [
+ "/.azuredevops/dependabot.yml",
+ "/.azuredevops/dependabot.yaml",
+
"/.github/dependabot.yaml",
"/.github/dependabot.yml",
];
@@ -162,6 +165,7 @@ function parseUpdates(config: any): IDependabotUpdate[] {
var dependabotUpdate: IDependabotUpdate = {
packageEcosystem: update["package-ecosystem"],
directory: update["directory"],
+ directories: update["directories"] || [],
openPullRequestsLimit: update["open-pull-requests-limit"],
registries: update["registries"] || [],
@@ -186,14 +190,17 @@ function parseUpdates(config: any): IDependabotUpdate[] {
ignore: update["ignore"] ? JSON.stringify(update["ignore"]) : undefined,
labels: update["labels"] ? JSON.stringify(update["labels"]) : undefined,
reviewers: update["reviewers"]
- ? JSON.stringify(update["reviewers"])
+ ? update["reviewers"]
: undefined,
assignees: update["assignees"]
- ? JSON.stringify(update["assignees"])
+ ? update["assignees"]
: undefined,
commitMessage: update["commit-message"]
? JSON.stringify(update["commit-message"])
: undefined,
+ groups: update["groups"]
+ ? JSON.stringify(update["groups"])
+ : undefined,
};
if (!dependabotUpdate.packageEcosystem) {
@@ -210,9 +217,9 @@ function parseUpdates(config: any): IDependabotUpdate[] {
dependabotUpdate.openPullRequestsLimit = 5;
}
- if (!dependabotUpdate.directory) {
+ if (!dependabotUpdate.directory && dependabotUpdate.directories.length === 0) {
throw new Error(
- "The value 'directory' in dependency update config is missing"
+ "The values 'directory' and 'directories' in dependency update config is missing, you must specify at least one"
);
}
@@ -278,7 +285,7 @@ function parseRegistries(config: any): Record {
}
// parse username, password, key, and token while replacing tokens where necessary
- parsed.username = registryConfig["username"];
+ parsed.username = convertPlaceholder(registryConfig["username"]);
parsed.password = convertPlaceholder(registryConfig["password"]);
parsed.key = convertPlaceholder(registryConfig["key"]);
parsed.token = convertPlaceholder(registryConfig["token"]);
diff --git a/extension/task/utils/resolveAzureDevOpsIdentities.ts b/extension/task/utils/resolveAzureDevOpsIdentities.ts
new file mode 100644
index 00000000..58ff56c0
--- /dev/null
+++ b/extension/task/utils/resolveAzureDevOpsIdentities.ts
@@ -0,0 +1,167 @@
+import * as tl from "azure-pipelines-task-lib/task";
+import axios from "axios";
+import extractOrganization from "./extractOrganization";
+
+export interface IIdentity {
+ /**
+ * The identity id to use for PR reviewer or assignee Id.
+ */
+ id: string,
+ /**
+ * Human readable Username.
+ */
+ displayName?: string,
+ /**
+ * The provided input to use for searching an identity.
+ */
+ input: string,
+}
+
+/**
+ * Resolves the given input email addresses to an array of IIdentity information.
+ * It also handles non email input, which is assumed to be already an identity id
+ * to pass as reviewer id to an PR.
+ *
+ * @param organizationUrl
+ * @param inputs
+ * @returns
+ */
+export async function resolveAzureDevOpsIdentities(organizationUrl: URL, inputs: string[]): Promise {
+ const result: IIdentity[] = [];
+
+ tl.debug(`Attempting to fetch configuration file via REST API ...`);
+ for (const input of inputs) {
+ if (input.indexOf("@") > 0 ) {
+ // input is email to look-up
+ const identityInfo = await querySubject(organizationUrl, input);
+ if (identityInfo) {
+ result.push(identityInfo);
+ }
+ } else {
+ // input is already identity id
+ result.push({id: input, input: input});
+ }
+ }
+ return result;
+}
+
+/**
+ * Returns whether the extension is run in a hosted environment (as opposed to an on-premise environment).
+ * In Azure DevOps terms, hosted environment is also known as "Azure DevOps Services" and on-premise environment is known as
+ * "Team Foundation Server" or "Azure DevOps Server".
+ */
+export function isHostedAzureDevOps(uri: URL): boolean {
+ const hostname = uri.hostname.toLowerCase();
+ return hostname === 'dev.azure.com' || hostname.endsWith('.visualstudio.com');
+}
+
+function decodeBase64(input: string):string {
+ return Buffer.from(input, 'base64').toString('utf8');
+}
+
+function encodeBase64(input: string):string {
+ return Buffer.from(input, 'utf8').toString('base64');
+}
+
+function isSuccessStatusCode(statusCode?: number) : boolean {
+ return (statusCode >= 200) && (statusCode <= 299);
+}
+
+async function querySubject(organizationUrl: URL, email: string): Promise {
+
+ if (isHostedAzureDevOps(organizationUrl)) {
+ const organization: string = extractOrganization(organizationUrl.toString());
+ return await querySubjectHosted(organization, email);
+ } else {
+ return await querySubjectOnPrem(organizationUrl, email);
+ }
+}
+
+/**
+ * Make the HTTP Request for an OnPrem Azure DevOps Server to resolve an email to an IIdentity
+ * @param organizationUrl
+ * @param email
+ * @returns
+ */
+async function querySubjectOnPrem(organizationUrl: URL, email: string): Promise {
+ const url = `${organizationUrl}_apis/identities?searchFilter=MailAddress&queryMembership=None&filterValue=${email}`;
+ tl.debug(`GET ${url}`);
+ try {
+ const response = await axios.get(url, {
+ headers: {
+ Authorization: `Basic ${encodeBase64("PAT:" + tl.getVariable("System.AccessToken"))}`,
+ Accept: "application/json;api-version=5.0",
+ },
+ });
+
+ if (isSuccessStatusCode(response.status)) {
+ return {
+ id: response.data.value[0]?.id,
+ displayName: response.data.value[0]?.providerDisplayName,
+ input: email}
+ }
+ } catch (error) {
+ const responseStatusCode = error?.response?.status;
+ tl.debug(`HTTP Response Status: ${responseStatusCode}`)
+ if (responseStatusCode > 400 && responseStatusCode < 500) {
+ tl.debug(`Access token is ${tl.getVariable("System.AccessToken")?.length > 0 ? "not" : ""} null or empty.`);
+ throw new Error(
+ `The access token provided is empty or does not have permissions to access '${url}'`
+ );
+ } else {
+ throw error;
+ }
+ }
+}
+
+/**
+ * * Make the HTTP Request for a hosted Azure DevOps Service, to resolve an email to an IIdentity
+ * @param organization
+ * @param email
+ * @returns
+ */
+async function querySubjectHosted(organization: string, email: string): Promise {
+ // make HTTP request
+ const url = `https://vssps.dev.azure.com/${organization}/_apis/graph/subjectquery`;
+ tl.debug(`GET ${url}`);
+ try {
+ const response = await axios.post(url, {
+ headers: {
+ Authorization: `Basic ${encodeBase64("PAT:" + tl.getVariable("System.AccessToken"))}`,
+ Accept: "application/json;api-version=6.0-preview.1",
+ "Content-Type": "application/json",
+ },
+ data: {
+ "query": email,
+ "subjectKind": [ "User" ]
+ }
+ });
+
+ tl.debug(`Got Http Response: ${response.status}`);
+
+ if(!isSuccessStatusCode(response.status) || response.data.value.length === 0) {
+ throw new Error(
+ 'Failed to resolve given email in organization'
+ );
+ }
+
+ const descriptor: string = response.data.value[0]?.descriptor || "";
+ const id = decodeBase64(descriptor.substring(descriptor.indexOf(".") + 1))
+ return {
+ id: id,
+ displayName: response.data.value[0]?.displayName,
+ input: email
+ }
+ } catch (error) {
+ const responseStatusCode = error?.response?.status;
+ tl.debug(`HTTP Response Status: ${responseStatusCode}`)
+ if (responseStatusCode > 400 && responseStatusCode < 500) {
+ tl.debug(`Access token is ${tl.getVariable("System.AccessToken")?.length > 0 ? "not" : ""} null or empty.`);
+ throw new Error(
+ `The access token provided is empty or does not have permissions to access '${url}'`
+ );
+ } else {
+ throw error;
+ }
+ }
+}
diff --git a/extension/tests/utils/dependabot.yml b/extension/tests/utils/dependabot.yml
index 9376eeb7..35277f63 100644
--- a/extension/tests/utils/dependabot.yml
+++ b/extension/tests/utils/dependabot.yml
@@ -24,6 +24,17 @@ updates:
update-types: ['version-update:semver-major']
- dependency-name: '@types/react-dom'
update-types: ['version-update:semver-major']
+ - package-ecosystem: 'nuget'
+ directories:
+ - '/src/client'
+ - '/src/server'
+ groups:
+ microsoft:
+ patterns:
+ - "microsoft*"
+ update-types:
+ - "minor"
+ - "patch"
registries:
reg1:
type: nuget-feed
diff --git a/extension/tests/utils/parseConfigFile.test.ts b/extension/tests/utils/parseConfigFile.test.ts
index 64e7448e..4e039cc4 100644
--- a/extension/tests/utils/parseConfigFile.test.ts
+++ b/extension/tests/utils/parseConfigFile.test.ts
@@ -8,11 +8,12 @@ describe("Parse configuration file", () => {
it("Parsing works as expected", () => {
let config: any = load(fs.readFileSync('tests/utils/dependabot.yml', "utf-8"));
let updates = parseUpdates(config);
- expect(updates.length).toBe(2);
+ expect(updates.length).toBe(3);
// first
const first = updates[0];
expect(first.directory).toBe('/');
+ expect(first.directories).toEqual([]);
expect(first.packageEcosystem).toBe('docker');
expect(first.insecureExternalCodeExecution).toBe(undefined);
expect(first.registries).toEqual([]);
@@ -20,9 +21,17 @@ describe("Parse configuration file", () => {
// second
const second = updates[1];
expect(second.directory).toBe('/client');
+ expect(second.directories).toEqual([]);
expect(second.packageEcosystem).toBe('npm');
expect(second.insecureExternalCodeExecution).toBe('deny');
expect(second.registries).toEqual(['reg1', 'reg2']);
+
+ // third
+ const third = updates[2];
+ expect(third.directory).toBe(undefined);
+ expect(third.directories).toEqual(['/src/client', '/src/server']);
+ expect(third.packageEcosystem).toBe('nuget');
+ expect(third.groups).toBe('{\"microsoft\":{\"patterns\":[\"microsoft*\"],\"update-types\":[\"minor\",\"patch\"]}}');
});
});
diff --git a/extension/tests/utils/resolveAzureDevOpsIdentities.test.ts b/extension/tests/utils/resolveAzureDevOpsIdentities.test.ts
new file mode 100644
index 00000000..9b39605b
--- /dev/null
+++ b/extension/tests/utils/resolveAzureDevOpsIdentities.test.ts
@@ -0,0 +1,93 @@
+import { isHostedAzureDevOps, resolveAzureDevOpsIdentities } from "../../task/utils/resolveAzureDevOpsIdentities";
+import { describe } from "node:test";
+import axios from "axios";
+
+describe("isHostedAzureDevOps", () => {
+ it("Old visualstudio url is hosted.", () => {
+ const url = new URL("https://example.visualstudio.com/abc")
+ const result = isHostedAzureDevOps(url);
+
+ expect(result).toBeTruthy();
+ });
+ it("Dev Azure url is hosted.", () => {
+ const url = new URL("https://dev.azure.com/example")
+ const result = isHostedAzureDevOps(url);
+
+ expect(result).toBeTruthy();
+ });
+ it("private url is not hosted.", () => {
+ const url = new URL("https://tfs.example.com/tfs/Collection")
+ const result = isHostedAzureDevOps(url);
+
+ expect(result).toBeFalsy();
+ });
+});
+
+
+jest.mock('axios');
+const mockedAxios = axios as jest.Mocked;
+
+const aliceOnPrem = {
+ id: "any id",
+ email: "alice@example.com",
+ providerDisplayName: "Alice"
+}
+
+const aliceHostedId = "any Id"
+const aliceHosted = {
+ descriptor: "aad." + Buffer.from(aliceHostedId, 'utf8').toString('base64'),
+ email: "alice@example.com",
+ providerDisplayName: "Alice"
+}
+
+describe("resolveAzureDevOpsIdentities", () => {
+ it("No email input, is directly returned.", async () => {
+ const url = new URL("https://example.visualstudio.com/abc")
+
+ const input = ["be9321e2-f404-4ffa-8d6b-44efddb04865"];
+ const results = await resolveAzureDevOpsIdentities(url, input);
+
+ const outputs = results.map(identity => identity.id);
+ expect(outputs).toHaveLength(1);
+ expect(outputs).toContain(input[0]);
+ });
+ it("successfully resolve id for azure devops server", async () => {
+ const url = new URL("https://example.onprem.com/abc")
+
+ // Provide the data object to be returned
+ mockedAxios.get.mockResolvedValue({
+ data: {
+ count: 1,
+ value: [aliceOnPrem],
+ },
+ status: 200,
+ });
+
+ const input = [aliceOnPrem.email];
+ const results = await resolveAzureDevOpsIdentities(url, input);
+
+ const outputs = results.map(identity => identity.id);
+ expect(outputs).toHaveLength(1);
+ expect(outputs).toContain(aliceOnPrem.id);
+ });
+ it("successfully resolve id for hosted azure devops", async () => {
+ const url = new URL("https://dev.azure.com/exampleorganization")
+
+
+ // Provide the data object to be returned
+ mockedAxios.post.mockResolvedValue({
+ data: {
+ count: 1,
+ value: [aliceHosted],
+ },
+ status: 200,
+ });
+
+ const input = [aliceHosted.email];
+ const results = await resolveAzureDevOpsIdentities(url, input);
+
+ const outputs = results.map(identity => identity.id);
+ expect(outputs).toHaveLength(1);
+ expect(outputs).toContain(aliceHostedId);
+ });
+});
\ No newline at end of file
diff --git a/extension/tsconfig.json b/extension/tsconfig.json
index c40e0685..92ec01c1 100644
--- a/extension/tsconfig.json
+++ b/extension/tsconfig.json
@@ -4,7 +4,7 @@
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
- "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
+ "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
diff --git a/server/Tingle.Dependabot.Tests/PeriodicTasks/MissedTriggerCheckerTaskTests.cs b/server/Tingle.Dependabot.Tests/PeriodicTasks/MissedTriggerCheckerTaskTests.cs
index e9d152ae..46b27353 100644
--- a/server/Tingle.Dependabot.Tests/PeriodicTasks/MissedTriggerCheckerTaskTests.cs
+++ b/server/Tingle.Dependabot.Tests/PeriodicTasks/MissedTriggerCheckerTaskTests.cs
@@ -14,19 +14,12 @@
namespace Tingle.Dependabot.Tests.PeriodicTasks;
-public class MissedTriggerCheckerTaskTests
+public class MissedTriggerCheckerTaskTests(ITestOutputHelper outputHelper)
{
private const string ProjectId = "prj_1234567890";
private const string RepositoryId = "repo_1234567890";
private const int UpdateId1 = 1;
- private readonly ITestOutputHelper outputHelper;
-
- public MissedTriggerCheckerTaskTests(ITestOutputHelper outputHelper)
- {
- this.outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper));
- }
-
[Fact]
public async Task CheckAsync_MissedScheduleIsDetected()
{
diff --git a/server/Tingle.Dependabot.Tests/PeriodicTasks/SynchronizationTaskTests.cs b/server/Tingle.Dependabot.Tests/PeriodicTasks/SynchronizationTaskTests.cs
index 105f57c5..80142328 100644
--- a/server/Tingle.Dependabot.Tests/PeriodicTasks/SynchronizationTaskTests.cs
+++ b/server/Tingle.Dependabot.Tests/PeriodicTasks/SynchronizationTaskTests.cs
@@ -13,17 +13,10 @@
namespace Tingle.Dependabot.Tests.PeriodicTasks;
-public class SynchronizationTaskTests
+public class SynchronizationTaskTests(ITestOutputHelper outputHelper)
{
private const string ProjectId = "prj_1234567890";
- private readonly ITestOutputHelper outputHelper;
-
- public SynchronizationTaskTests(ITestOutputHelper outputHelper)
- {
- this.outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper));
- }
-
[Fact]
public async Task SynchronizationInnerAsync_Works()
{
diff --git a/server/Tingle.Dependabot.Tests/PeriodicTasks/UpdateJobsCleanerTaskTests.cs b/server/Tingle.Dependabot.Tests/PeriodicTasks/UpdateJobsCleanerTaskTests.cs
index dee57b93..522832b4 100644
--- a/server/Tingle.Dependabot.Tests/PeriodicTasks/UpdateJobsCleanerTaskTests.cs
+++ b/server/Tingle.Dependabot.Tests/PeriodicTasks/UpdateJobsCleanerTaskTests.cs
@@ -14,18 +14,11 @@
namespace Tingle.Dependabot.Tests.PeriodicTasks;
-public class UpdateJobsCleanerTaskTests
+public class UpdateJobsCleanerTaskTests(ITestOutputHelper outputHelper)
{
private const string ProjectId = "prj_1234567890";
private const string RepositoryId = "repo_1234567890";
- private readonly ITestOutputHelper outputHelper;
-
- public UpdateJobsCleanerTaskTests(ITestOutputHelper outputHelper)
- {
- this.outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper));
- }
-
[Fact]
public async Task CleanupAsync_ResolvesJobs()
{
diff --git a/server/Tingle.Dependabot.Tests/Tingle.Dependabot.Tests.csproj b/server/Tingle.Dependabot.Tests/Tingle.Dependabot.Tests.csproj
index 4243f59c..b6bba131 100644
--- a/server/Tingle.Dependabot.Tests/Tingle.Dependabot.Tests.csproj
+++ b/server/Tingle.Dependabot.Tests/Tingle.Dependabot.Tests.csproj
@@ -10,13 +10,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/server/Tingle.Dependabot.Tests/WebhooksControllerIntegrationTests.cs b/server/Tingle.Dependabot.Tests/WebhooksControllerIntegrationTests.cs
index 36ef8483..000a0935 100644
--- a/server/Tingle.Dependabot.Tests/WebhooksControllerIntegrationTests.cs
+++ b/server/Tingle.Dependabot.Tests/WebhooksControllerIntegrationTests.cs
@@ -17,17 +17,10 @@
namespace Tingle.Dependabot.Tests;
-public class WebhooksControllerIntegrationTests
+public class WebhooksControllerIntegrationTests(ITestOutputHelper outputHelper)
{
private const string ProjectId = "prj_1234567890";
- private readonly ITestOutputHelper outputHelper;
-
- public WebhooksControllerIntegrationTests(ITestOutputHelper outputHelper)
- {
- this.outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper));
- }
-
[Fact]
public async Task Returns_Unauthorized()
{
@@ -211,14 +204,12 @@ private async Task TestAsync(Func execute
services.AddAuthentication()
.AddBasic(AuthConstants.SchemeNameServiceHooks, options => options.Realm = "Dependabot");
- services.AddAuthorization(options =>
- {
- options.AddPolicy(AuthConstants.PolicyNameServiceHooks, policy =>
- {
- policy.AddAuthenticationSchemes(AuthConstants.SchemeNameServiceHooks)
- .RequireAuthenticatedUser();
- });
- });
+ services.AddAuthorizationBuilder()
+ .AddPolicy(AuthConstants.PolicyNameServiceHooks, policy =>
+ {
+ policy.AddAuthenticationSchemes(AuthConstants.SchemeNameServiceHooks)
+ .RequireAuthenticatedUser();
+ });
services.AddEventBus(builder => builder.AddInMemoryTransport().AddInMemoryTestHarness());
})
diff --git a/server/Tingle.Dependabot.Tests/Workflow/UpdateRunnerTests.cs b/server/Tingle.Dependabot.Tests/Workflow/UpdateRunnerTests.cs
index 802cff81..97b377ac 100644
--- a/server/Tingle.Dependabot.Tests/Workflow/UpdateRunnerTests.cs
+++ b/server/Tingle.Dependabot.Tests/Workflow/UpdateRunnerTests.cs
@@ -1,7 +1,6 @@
īģŋusing Tingle.Dependabot.Models.Dependabot;
using Tingle.Dependabot.Workflow;
using Xunit;
-using Xunit.Abstractions;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@@ -9,13 +8,6 @@ namespace Tingle.Dependabot.Tests.Workflow;
public class UpdateRunnerTests
{
- private readonly ITestOutputHelper outputHelper;
-
- public UpdateRunnerTests(ITestOutputHelper outputHelper)
- {
- this.outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper));
- }
-
[Fact]
public void MakeCredentialsMetadata_Works()
{
@@ -319,6 +311,7 @@ public void ConvertEcosystemToPackageManager_Works(string ecosystem, string expe
{ "gradle", "gradle" },
{ "maven", "maven" },
{ "swift", "swift" },
+ { "devcontainers", "devcontainers" },
{ "terraform", "terraform" },
{ "docker", "docker" },
};
diff --git a/server/Tingle.Dependabot.Tests/Workflow/WorkflowOptionsTests.cs b/server/Tingle.Dependabot.Tests/Workflow/WorkflowOptionsTests.cs
new file mode 100644
index 00000000..d8b7f651
--- /dev/null
+++ b/server/Tingle.Dependabot.Tests/Workflow/WorkflowOptionsTests.cs
@@ -0,0 +1,32 @@
+īģŋusing Tingle.Dependabot.Workflow;
+using Xunit;
+
+namespace Tingle.Dependabot.Tests.Workflow;
+
+public class WorkflowOptionsTests
+{
+ [Fact]
+ public void GetUpdaterImageTag_Works()
+ {
+ var project1 = new Dependabot.Models.Management.Project { UpdaterImageTag = "1.25" };
+ var project2 = new Dependabot.Models.Management.Project { };
+ var options = new WorkflowOptions
+ {
+ UpdaterImageTag = "1.26",
+ UpdaterImageTags = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ ["nuget"] = "1.24",
+ },
+ };
+
+ // ecosystem override
+ Assert.Equal("1.24", options.GetUpdaterImageTag("nuget", project1));
+ Assert.Equal("1.24", options.GetUpdaterImageTag("NUGET", project1));
+
+ // project override
+ Assert.Equal("1.25", options.GetUpdaterImageTag("npm", project1));
+
+ // no overrides
+ Assert.Equal("1.26", options.GetUpdaterImageTag("npm", project2));
+ }
+}
diff --git a/server/Tingle.Dependabot/AppSetup.cs b/server/Tingle.Dependabot/AppSetup.cs
index 111bc371..b8b842e7 100644
--- a/server/Tingle.Dependabot/AppSetup.cs
+++ b/server/Tingle.Dependabot/AppSetup.cs
@@ -3,6 +3,7 @@
using System.Text.Json;
using Tingle.Dependabot.Models;
using Tingle.Dependabot.Workflow;
+using Tingle.Extensions.Primitives;
namespace Tingle.Dependabot;
@@ -57,7 +58,7 @@ public static async Task SetupAsync(WebApplication app, CancellationToken cancel
{
project = new Models.Management.Project
{
- Id = $"prj_{KSUID.Ksuid.Generate()}",
+ Id = $"prj_{Ksuid.Generate()}",
Created = DateTimeOffset.UtcNow,
Password = GeneratePassword(32),
Url = setup.Url.ToString(),
diff --git a/server/Tingle.Dependabot/ApplicationInsights/DependabotTelemetryInitializer.cs b/server/Tingle.Dependabot/ApplicationInsights/DependabotTelemetryInitializer.cs
index 1512131d..ddadc734 100644
--- a/server/Tingle.Dependabot/ApplicationInsights/DependabotTelemetryInitializer.cs
+++ b/server/Tingle.Dependabot/ApplicationInsights/DependabotTelemetryInitializer.cs
@@ -4,17 +4,10 @@
namespace Tingle.Dependabot.ApplicationInsights;
-internal class DependabotTelemetryInitializer : ITelemetryInitializer
+internal class DependabotTelemetryInitializer(IHttpContextAccessor httpContextAccessor) : ITelemetryInitializer
{
private const string KeyProjectId = "ProjectId";
- private readonly IHttpContextAccessor httpContextAccessor;
-
- public DependabotTelemetryInitializer(IHttpContextAccessor httpContextAccessor)
- {
- this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
- }
-
public void Initialize(ITelemetry telemetry)
{
var context = httpContextAccessor.HttpContext;
diff --git a/server/Tingle.Dependabot/ApplicationInsights/InsightsFilteringProcessor.cs b/server/Tingle.Dependabot/ApplicationInsights/InsightsFilteringProcessor.cs
index 49802789..2449f723 100644
--- a/server/Tingle.Dependabot/ApplicationInsights/InsightsFilteringProcessor.cs
+++ b/server/Tingle.Dependabot/ApplicationInsights/InsightsFilteringProcessor.cs
@@ -7,7 +7,7 @@ namespace Tingle.Dependabot.ApplicationInsights;
///
/// Implementation of that filters out unneeded telemetry.
///
-internal class InsightsFilteringProcessor : ITelemetryProcessor
+internal class InsightsFilteringProcessor(ITelemetryProcessor next) : ITelemetryProcessor
{
private static readonly string[] excludedRequestNames =
[
@@ -15,13 +15,6 @@ internal class InsightsFilteringProcessor : ITelemetryProcessor
"ServiceBusProcessor.ProcessMessage",
];
- private readonly ITelemetryProcessor next;
-
- public InsightsFilteringProcessor(ITelemetryProcessor next)
- {
- this.next = next;
- }
-
///
public void Process(ITelemetry item)
{
diff --git a/server/Tingle.Dependabot/ApplicationInsights/InsightsShutdownFlushService.cs b/server/Tingle.Dependabot/ApplicationInsights/InsightsShutdownFlushService.cs
index 6b1de3be..492802cc 100644
--- a/server/Tingle.Dependabot/ApplicationInsights/InsightsShutdownFlushService.cs
+++ b/server/Tingle.Dependabot/ApplicationInsights/InsightsShutdownFlushService.cs
@@ -3,15 +3,8 @@
namespace Tingle.Dependabot.ApplicationInsights;
// from https://medium.com/@asimmon/prevent-net-application-insights-telemetry-loss-d82a06c3673f
-internal class InsightsShutdownFlushService : IHostedService
+internal class InsightsShutdownFlushService(TelemetryClient telemetryClient) : IHostedService
{
- private readonly TelemetryClient telemetryClient;
-
- public InsightsShutdownFlushService(TelemetryClient telemetryClient)
- {
- this.telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
- }
-
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public async Task StopAsync(CancellationToken cancellationToken)
@@ -20,7 +13,7 @@ public async Task StopAsync(CancellationToken cancellationToken)
// Using "CancellationToken.None" ensures that the application doesn't stop until the telemetry data is flushed.
//
// If you want to use the "cancellationToken" argument, make sure to configure "HostOptions.ShutdownTimeout" with a sufficiently large duration,
- // and silence the eventual "OperationCanceledException" exception. Otherwise, you will still be at risk of loosing telemetry data.
+ // and silence the eventual "OperationCanceledException" exception. Otherwise, you will still be at risk of losing telemetry data.
var successfullyFlushed = await telemetryClient.FlushAsync(CancellationToken.None);
if (!successfullyFlushed)
{
diff --git a/server/Tingle.Dependabot/BasicUserValidationService.cs b/server/Tingle.Dependabot/BasicUserValidationService.cs
index 81ace82a..8e12a258 100644
--- a/server/Tingle.Dependabot/BasicUserValidationService.cs
+++ b/server/Tingle.Dependabot/BasicUserValidationService.cs
@@ -4,15 +4,8 @@
namespace Tingle.Dependabot;
-internal class BasicUserValidationService : IBasicUserValidationService
+internal class BasicUserValidationService(MainDbContext dbContext) : IBasicUserValidationService
{
- private readonly MainDbContext dbContext;
-
- public BasicUserValidationService(MainDbContext dbContext)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- }
-
public async Task IsValidAsync(string username, string password)
{
var project = await dbContext.Projects.SingleOrDefaultAsync(p => p.Id == username);
diff --git a/server/Tingle.Dependabot/Consumers/ProcessSynchronizationConsumer.cs b/server/Tingle.Dependabot/Consumers/ProcessSynchronizationConsumer.cs
index 158f7ede..962a0862 100644
--- a/server/Tingle.Dependabot/Consumers/ProcessSynchronizationConsumer.cs
+++ b/server/Tingle.Dependabot/Consumers/ProcessSynchronizationConsumer.cs
@@ -6,19 +6,8 @@
namespace Tingle.Dependabot.Consumers;
-internal class ProcessSynchronizationConsumer : IEventConsumer
+internal class ProcessSynchronizationConsumer(MainDbContext dbContext, Synchronizer synchronizer, ILogger logger) : IEventConsumer
{
- private readonly MainDbContext dbContext;
- private readonly Synchronizer synchronizer;
- private readonly ILogger logger;
-
- public ProcessSynchronizationConsumer(MainDbContext dbContext, Synchronizer synchronizer, ILogger logger)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.synchronizer = synchronizer ?? throw new ArgumentNullException(nameof(synchronizer));
- this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
public async Task ConsumeAsync(EventContext context, CancellationToken cancellationToken = default)
{
var evt = context.Event;
diff --git a/server/Tingle.Dependabot/Consumers/RepositoryEventsConsumer.cs b/server/Tingle.Dependabot/Consumers/RepositoryEventsConsumer.cs
index d5caf706..8692ae3f 100644
--- a/server/Tingle.Dependabot/Consumers/RepositoryEventsConsumer.cs
+++ b/server/Tingle.Dependabot/Consumers/RepositoryEventsConsumer.cs
@@ -6,17 +6,8 @@
namespace Tingle.Dependabot.Consumers;
-internal class RepositoryEventsConsumer : IEventConsumer, IEventConsumer, IEventConsumer
+internal class RepositoryEventsConsumer(MainDbContext dbContext, UpdateScheduler scheduler) : IEventConsumer, IEventConsumer, IEventConsumer
{
- private readonly MainDbContext dbContext;
- private readonly UpdateScheduler scheduler;
-
- public RepositoryEventsConsumer(MainDbContext dbContext, UpdateScheduler scheduler)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
- }
-
public async Task ConsumeAsync(EventContext context, CancellationToken cancellationToken)
{
var evt = context.Event;
@@ -43,7 +34,6 @@ public async Task ConsumeAsync(EventContext context, Can
// remove from scheduler
var repositoryId = evt.RepositoryId ?? throw new InvalidOperationException($"'{nameof(evt.RepositoryId)}' cannot be null");
- var repository = await dbContext.Repositories.SingleAsync(r => r.Id == repositoryId, cancellationToken);
await scheduler.RemoveAsync(repositoryId, cancellationToken);
}
}
diff --git a/server/Tingle.Dependabot/Consumers/TriggerUpdateJobsEventConsumer.cs b/server/Tingle.Dependabot/Consumers/TriggerUpdateJobsEventConsumer.cs
index b62e85b5..71cb34da 100644
--- a/server/Tingle.Dependabot/Consumers/TriggerUpdateJobsEventConsumer.cs
+++ b/server/Tingle.Dependabot/Consumers/TriggerUpdateJobsEventConsumer.cs
@@ -4,22 +4,12 @@
using Tingle.Dependabot.Models.Management;
using Tingle.Dependabot.Workflow;
using Tingle.EventBus;
+using Tingle.Extensions.Primitives;
namespace Tingle.Dependabot.Consumers;
-internal class TriggerUpdateJobsEventConsumer : IEventConsumer
+internal class TriggerUpdateJobsEventConsumer(MainDbContext dbContext, UpdateRunner updateRunner, ILogger logger) : IEventConsumer
{
- private readonly MainDbContext dbContext;
- private readonly UpdateRunner updateRunner;
- private readonly ILogger logger;
-
- public TriggerUpdateJobsEventConsumer(MainDbContext dbContext, UpdateRunner updateRunner, ILogger logger)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.updateRunner = updateRunner ?? throw new ArgumentNullException(nameof(updateRunner));
- this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
public async Task ConsumeAsync(EventContext context, CancellationToken cancellationToken)
{
var evt = context.Event;
@@ -85,7 +75,7 @@ public async Task ConsumeAsync(EventContext context, Can
{
// we use this to create azure resources which have name restrictions
// alphanumeric, starts with a letter, does not contain "--", up to 32 characters
- Id = $"job-{FlakeId.Id.Create()}", // flake is 19 chars, total is 23 chars
+ Id = $"job-{SequenceNumber.Generate()}", // sequence number is 19 chars, total is 23 chars
Created = DateTimeOffset.UtcNow,
Status = UpdateJobStatus.Scheduled,
diff --git a/server/Tingle.Dependabot/Consumers/UpdateJobEventsConsumer.cs b/server/Tingle.Dependabot/Consumers/UpdateJobEventsConsumer.cs
index 67897dc2..ff4ca65a 100644
--- a/server/Tingle.Dependabot/Consumers/UpdateJobEventsConsumer.cs
+++ b/server/Tingle.Dependabot/Consumers/UpdateJobEventsConsumer.cs
@@ -7,19 +7,8 @@
namespace Tingle.Dependabot.Consumers;
-internal class UpdateJobEventsConsumer : IEventConsumer, IEventConsumer
+internal class UpdateJobEventsConsumer(MainDbContext dbContext, UpdateRunner updateRunner, ILogger logger) : IEventConsumer, IEventConsumer
{
- private readonly MainDbContext dbContext;
- private readonly UpdateRunner updateRunner;
- private readonly ILogger logger;
-
- public UpdateJobEventsConsumer(MainDbContext dbContext, UpdateRunner updateRunner, ILogger logger)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.updateRunner = updateRunner ?? throw new ArgumentNullException(nameof(updateRunner));
- this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
public async Task ConsumeAsync(EventContext context, CancellationToken cancellationToken)
{
var evt = context.Event;
diff --git a/server/Tingle.Dependabot/Controllers/ManagementController.cs b/server/Tingle.Dependabot/Controllers/ManagementController.cs
index b25ffb31..bf3013ee 100644
--- a/server/Tingle.Dependabot/Controllers/ManagementController.cs
+++ b/server/Tingle.Dependabot/Controllers/ManagementController.cs
@@ -13,19 +13,8 @@ namespace Tingle.Dependabot.Controllers;
[ApiController]
[Route("/mgnt")]
[Authorize(AuthConstants.PolicyNameManagement)]
-public class ManagementController : ControllerBase // TODO: unit test this
+public class ManagementController(MainDbContext dbContext, IEventPublisher publisher, AzureDevOpsProvider adoProvider) : ControllerBase // TODO: unit test this
{
- private readonly MainDbContext dbContext;
- private readonly IEventPublisher publisher;
- private readonly AzureDevOpsProvider adoProvider;
-
- public ManagementController(MainDbContext dbContext, IEventPublisher publisher, AzureDevOpsProvider adoProvider)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
- this.adoProvider = adoProvider ?? throw new ArgumentNullException(nameof(adoProvider));
- }
-
[HttpPost("sync")]
public async Task SyncAsync([FromBody] SynchronizationRequest model)
{
diff --git a/server/Tingle.Dependabot/Controllers/UpdateJobsController.cs b/server/Tingle.Dependabot/Controllers/UpdateJobsController.cs
index 185fe030..0f74e49b 100644
--- a/server/Tingle.Dependabot/Controllers/UpdateJobsController.cs
+++ b/server/Tingle.Dependabot/Controllers/UpdateJobsController.cs
@@ -15,19 +15,8 @@ namespace Tingle.Dependabot.Controllers;
[ApiController]
[Route("/update_jobs")]
[Authorize(AuthConstants.PolicyNameUpdater)]
-public class UpdateJobsController : ControllerBase // TODO: unit and integration test this
+public class UpdateJobsController(MainDbContext dbContext, IEventPublisher publisher, ILogger logger) : ControllerBase // TODO: unit and integration test this
{
- private readonly MainDbContext dbContext;
- private readonly IEventPublisher publisher;
- private readonly ILogger logger;
-
- public UpdateJobsController(MainDbContext dbContext, IEventPublisher publisher, ILogger logger)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
- this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
// TODO: implement logic for *pull_request endpoints
[HttpPost("{id}/create_pull_request")]
@@ -37,7 +26,7 @@ public async Task CreatePullRequestAsync([FromRoute, Required] st
var repository = await dbContext.Repositories.SingleAsync(r => r.Id == job.RepositoryId);
var project = await dbContext.Projects.SingleAsync(p => p.Id == job.ProjectId);
- logger.LogInformation("Received request to create a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model));
+ logger.LogInformation("Received request to create a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id.Replace(Environment.NewLine, ""), JsonSerializer.Serialize(model));
return Ok();
}
@@ -48,7 +37,7 @@ public async Task UpdatePullRequestAsync([FromRoute, Required] st
var repository = await dbContext.Repositories.SingleAsync(r => r.Id == job.RepositoryId);
var project = await dbContext.Projects.SingleAsync(p => p.Id == job.ProjectId);
- logger.LogInformation("Received request to update a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model));
+ logger.LogInformation("Received request to update a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id.Replace(Environment.NewLine, ""), JsonSerializer.Serialize(model));
return Ok();
}
@@ -59,7 +48,7 @@ public async Task ClosePullRequestAsync([FromRoute, Required] str
var repository = await dbContext.Repositories.SingleAsync(r => r.Id == job.RepositoryId);
var project = await dbContext.Projects.SingleAsync(p => p.Id == job.ProjectId);
- logger.LogInformation("Received request to close a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model));
+ logger.LogInformation("Received request to close a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id.Replace(Environment.NewLine, ""), JsonSerializer.Serialize(model));
return Ok();
}
@@ -128,7 +117,7 @@ public async Task UpdateDependencyListAsync([FromRoute, Required]
public async Task RecordEcosystemVersionsAsync([FromRoute, Required] string id, [FromBody] JsonNode model)
{
var job = await dbContext.UpdateJobs.SingleAsync(j => j.Id == id);
- logger.LogInformation("Received request to record ecosystem version from job {JobId} but we did nothing.\r\n{ModelJson}", id, model.ToJsonString());
+ logger.LogInformation("Received request to record ecosystem version from job {JobId} but we did nothing.\r\n{ModelJson}", id.Replace(Environment.NewLine, ""), model.ToJsonString());
return Ok();
}
@@ -136,7 +125,7 @@ public async Task RecordEcosystemVersionsAsync([FromRoute, Requir
public async Task IncrementMetricAsync([FromRoute, Required] string id, [FromBody] JsonNode model)
{
var job = await dbContext.UpdateJobs.SingleAsync(j => j.Id == id);
- logger.LogInformation("Received metrics from job {JobId} but we did nothing with them.\r\n{ModelJson}", id, model.ToJsonString());
+ logger.LogInformation("Received metrics from job {JobId} but we did nothing with them.\r\n{ModelJson}", id.Replace(Environment.NewLine, ""), model.ToJsonString());
return Ok();
}
}
diff --git a/server/Tingle.Dependabot/Controllers/WebhooksController.cs b/server/Tingle.Dependabot/Controllers/WebhooksController.cs
index 5ceda174..2f049a2c 100644
--- a/server/Tingle.Dependabot/Controllers/WebhooksController.cs
+++ b/server/Tingle.Dependabot/Controllers/WebhooksController.cs
@@ -12,24 +12,13 @@ namespace Tingle.Dependabot.Controllers;
[ApiController]
[Route("/webhooks")]
[Authorize(AuthConstants.PolicyNameServiceHooks)]
-public class WebhooksController : ControllerBase // TODO: unit test this
+public class WebhooksController(MainDbContext dbContext, IEventPublisher publisher, ILogger logger) : ControllerBase // TODO: unit test this
{
- private readonly MainDbContext dbContext;
- private readonly IEventPublisher publisher;
- private readonly ILogger logger;
-
- public WebhooksController(MainDbContext dbContext, IEventPublisher publisher, ILogger logger)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
- this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
[HttpPost("azure")]
public async Task PostAsync([FromBody] AzureDevOpsEvent model)
{
var type = model.EventType;
- logger.WebhooksReceivedEvent(type, model.NotificationId, model.SubscriptionId);
+ logger.WebhooksReceivedEvent(type, model.NotificationId, model.SubscriptionId?.Replace(Environment.NewLine, ""));
if (type is AzureDevOpsEventType.GitPush)
{
diff --git a/server/Tingle.Dependabot/Extensions/ILoggerExtensions.cs b/server/Tingle.Dependabot/Extensions/ILoggerExtensions.cs
index b66bf53f..5eea294b 100644
--- a/server/Tingle.Dependabot/Extensions/ILoggerExtensions.cs
+++ b/server/Tingle.Dependabot/Extensions/ILoggerExtensions.cs
@@ -66,7 +66,7 @@ internal static partial class ILoggerExtensions
[LoggerMessage(400, LogLevel.Debug, "Creating/Updating schedules for repository '{RepositoryId}' in project '{ProjectId}'")]
public static partial void SchedulesUpdating(this ILogger logger, string? repositoryId, string? projectId);
- [LoggerMessage(401, LogLevel.Error, "Timer call back does not have correct argument. Exepected '{ExpectedType}' but got '{ActualType}'")]
+ [LoggerMessage(401, LogLevel.Error, "Timer call back does not have correct argument. Expected '{ExpectedType}' but got '{ActualType}'")]
public static partial void SchedulesTimerInvalidCallbackArgument(this ILogger logger, string? expectedType, string? actualType);
[LoggerMessage(402, LogLevel.Warning, "Schedule was missed for {RepositoryId}({UpdateId}) in project '{ProjectId}'. Triggering now ...")]
diff --git a/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.Designer.cs b/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.Designer.cs
index e78fd415..128496b5 100644
--- a/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.Designer.cs
+++ b/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.Designer.cs
@@ -20,7 +20,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "7.0.11")
+ .HasAnnotation("ProductVersion", "8.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@@ -249,6 +249,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.Property("Trigger")
.HasColumnType("int");
+ b.Property("UpdaterImage")
+ .HasColumnType("nvarchar(max)");
+
b.HasKey("Id");
b.HasIndex("AuthKey")
diff --git a/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.cs b/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.cs
index cd109376..4c618ea1 100644
--- a/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.cs
+++ b/server/Tingle.Dependabot/Migrations/20230824083425_InitialCreate.cs
@@ -73,6 +73,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
Directory = table.Column(type: "nvarchar(450)", nullable: false),
Resources_Cpu = table.Column(type: "float", nullable: false),
Resources_Memory = table.Column(type: "float", nullable: false),
+ UpdaterImage = table.Column(type: "nvarchar(max)", nullable: true),
AuthKey = table.Column(type: "nvarchar(450)", nullable: false),
Start = table.Column(type: "datetimeoffset", nullable: true),
End = table.Column(type: "datetimeoffset", nullable: true),
diff --git a/server/Tingle.Dependabot/Migrations/MainDbContextModelSnapshot.cs b/server/Tingle.Dependabot/Migrations/MainDbContextModelSnapshot.cs
index 78de30ac..69056547 100644
--- a/server/Tingle.Dependabot/Migrations/MainDbContextModelSnapshot.cs
+++ b/server/Tingle.Dependabot/Migrations/MainDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "7.0.11")
+ .HasAnnotation("ProductVersion", "8.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@@ -246,6 +246,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Trigger")
.HasColumnType("int");
+ b.Property("UpdaterImage")
+ .HasColumnType("nvarchar(max)");
+
b.HasKey("Id");
b.HasIndex("AuthKey")
diff --git a/server/Tingle.Dependabot/Models/Management/UpdateJob.cs b/server/Tingle.Dependabot/Models/Management/UpdateJob.cs
index 4971eecb..01736d80 100644
--- a/server/Tingle.Dependabot/Models/Management/UpdateJob.cs
+++ b/server/Tingle.Dependabot/Models/Management/UpdateJob.cs
@@ -57,6 +57,9 @@ public class UpdateJob
[Required]
public UpdateJobResources? Resources { get; set; }
+ /// Image used for the updater.
+ public string? UpdaterImage { get; set; }
+
///
/// Authorization key for the job.
/// Used by the updater to make API calls.
diff --git a/server/Tingle.Dependabot/Models/Management/UpdateJobResources.cs b/server/Tingle.Dependabot/Models/Management/UpdateJobResources.cs
index 0a651a78..00347326 100644
--- a/server/Tingle.Dependabot/Models/Management/UpdateJobResources.cs
+++ b/server/Tingle.Dependabot/Models/Management/UpdateJobResources.cs
@@ -5,6 +5,9 @@ namespace Tingle.Dependabot.Models.Management;
public class UpdateJobResources
{
+ // the minimum is 0.25vCPU and 0.5GB but we need more because a lot is happening in the container
+ private static readonly UpdateJobResources Default = new(cpu: 0.5, memory: 1);
+
public UpdateJobResources() { } // required for deserialization
public UpdateJobResources(double cpu, double memory)
@@ -33,14 +36,16 @@ public static UpdateJobResources FromEcosystem(string ecosystem)
{
return ecosystem switch
{
- //"nuget" => new(cpu: 0.25, memory: 0.2),
- //"gitsubmodule" => new(cpu: 0.1, memory: 0.2),
- //"terraform" => new(cpu: 0.25, memory: 1),
- //"npm" => new(cpu: 0.25, memory: 1),
- _ => new UpdateJobResources(cpu: 0.25, memory: 0.5), // the minimum
+ "npm" => Default * 2,
+ "yarn" => Default * 2,
+ "pnpm" => Default * 2,
+ _ => Default,
};
}
+ public static UpdateJobResources operator *(UpdateJobResources resources, double factor) => new(resources.Cpu * factor, resources.Memory * factor);
+ public static UpdateJobResources operator /(UpdateJobResources resources, double factor) => new(resources.Cpu / factor, resources.Memory / factor);
+
public static implicit operator AppContainerResources(UpdateJobResources resources)
{
return new() { Cpu = resources.Cpu, Memory = $"{resources.Memory}Gi", };
diff --git a/server/Tingle.Dependabot/PeriodicTasks/MissedTriggerCheckerTask.cs b/server/Tingle.Dependabot/PeriodicTasks/MissedTriggerCheckerTask.cs
index 4216f3ec..3244557a 100644
--- a/server/Tingle.Dependabot/PeriodicTasks/MissedTriggerCheckerTask.cs
+++ b/server/Tingle.Dependabot/PeriodicTasks/MissedTriggerCheckerTask.cs
@@ -10,10 +10,6 @@ namespace Tingle.Dependabot.PeriodicTasks;
internal class MissedTriggerCheckerTask(MainDbContext dbContext, IEventPublisher publisher, ILogger logger) : IPeriodicTask
{
- private readonly MainDbContext dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- private readonly IEventPublisher publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
- private readonly ILogger logger = logger ?? throw new ArgumentNullException(nameof(logger));
-
public async Task ExecuteAsync(PeriodicTaskExecutionContext context, CancellationToken cancellationToken)
{
await CheckAsync(DateTimeOffset.UtcNow, cancellationToken);
diff --git a/server/Tingle.Dependabot/PeriodicTasks/SynchronizationTask.cs b/server/Tingle.Dependabot/PeriodicTasks/SynchronizationTask.cs
index fa2cfaba..25d6e99e 100644
--- a/server/Tingle.Dependabot/PeriodicTasks/SynchronizationTask.cs
+++ b/server/Tingle.Dependabot/PeriodicTasks/SynchronizationTask.cs
@@ -8,9 +8,6 @@ namespace Tingle.Dependabot.Workflow;
internal class SynchronizationTask(MainDbContext dbContext, IEventPublisher publisher) : IPeriodicTask
{
- private readonly MainDbContext dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- private readonly IEventPublisher publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
-
public async Task ExecuteAsync(PeriodicTaskExecutionContext context, CancellationToken cancellationToken)
{
await SyncAsync(cancellationToken);
diff --git a/server/Tingle.Dependabot/PeriodicTasks/UpdateJobsCleanerTask.cs b/server/Tingle.Dependabot/PeriodicTasks/UpdateJobsCleanerTask.cs
index 1199ad69..3be5a2cd 100644
--- a/server/Tingle.Dependabot/PeriodicTasks/UpdateJobsCleanerTask.cs
+++ b/server/Tingle.Dependabot/PeriodicTasks/UpdateJobsCleanerTask.cs
@@ -7,19 +7,8 @@
namespace Tingle.Dependabot.PeriodicTasks;
-internal class UpdateJobsCleanerTask : IPeriodicTask
+internal class UpdateJobsCleanerTask(MainDbContext dbContext, IEventPublisher publisher, ILogger logger) : IPeriodicTask
{
- private readonly MainDbContext dbContext;
- private readonly IEventPublisher publisher;
- private readonly ILogger logger;
-
- public UpdateJobsCleanerTask(MainDbContext dbContext, IEventPublisher publisher, ILogger logger)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
- this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
public async Task ExecuteAsync(PeriodicTaskExecutionContext context, CancellationToken cancellationToken)
{
await CleanupAsync(cancellationToken);
diff --git a/server/Tingle.Dependabot/Tingle.Dependabot.csproj b/server/Tingle.Dependabot/Tingle.Dependabot.csproj
index 5f62aace..bb3f4730 100644
--- a/server/Tingle.Dependabot/Tingle.Dependabot.csproj
+++ b/server/Tingle.Dependabot/Tingle.Dependabot.csproj
@@ -4,7 +4,7 @@
true
true
$(NoWarn);1591;CA1819;CA1031
- $(GITVERSION_NUGETVERSION)
+ $(GITVERSION_FULLSEMVER)
e58d698d-4791-43fc-8b76-ce1f01cbd092
Linux
..\..
@@ -15,38 +15,37 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
diff --git a/server/Tingle.Dependabot/Workflow/AzureDevOpsProvider.cs b/server/Tingle.Dependabot/Workflow/AzureDevOpsProvider.cs
index c4d71c49..823fc835 100644
--- a/server/Tingle.Dependabot/Workflow/AzureDevOpsProvider.cs
+++ b/server/Tingle.Dependabot/Workflow/AzureDevOpsProvider.cs
@@ -6,13 +6,12 @@
namespace Tingle.Dependabot.Workflow;
-public class AzureDevOpsProvider
+public class AzureDevOpsProvider(HttpClient httpClient, IOptions optionsAccessor)
{
// Possible/allowed paths for the configuration files in a repository.
private static readonly IReadOnlyList ConfigurationFilePaths = new[] {
- // TODO: restore checks in .azuredevops folder once either the code can check that folder or we are passing ignore conditions via update_jobs API
- //".azuredevops/dependabot.yml",
- //".azuredevops/dependabot.yaml",
+ ".azuredevops/dependabot.yml",
+ ".azuredevops/dependabot.yaml",
".github/dependabot.yml",
".github/dependabot.yaml",
@@ -26,14 +25,7 @@ private static readonly (string, string)[] SubscriptionEventTypes =
("ms.vss-code.git-pullrequest-comment-event", "2.0"),
];
- private readonly HttpClient httpClient;
- private readonly WorkflowOptions options;
-
- public AzureDevOpsProvider(HttpClient httpClient, IOptions optionsAccessor)
- {
- this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
- options = optionsAccessor?.Value ?? throw new ArgumentNullException(nameof(optionsAccessor));
- }
+ private readonly WorkflowOptions options = optionsAccessor?.Value ?? throw new ArgumentNullException(nameof(optionsAccessor));
public async Task> CreateOrUpdateSubscriptionsAsync(Project project, CancellationToken cancellationToken = default)
{
@@ -70,7 +62,7 @@ public async Task> CreateOrUpdateSubscriptionsAsync(Project project
Host = url.Hostname,
Port = url.Port ?? -1,
Path = $"{url.OrganizationName}/_apis/hooks/subscriptionsquery",
- Query = "?api-version=7.0",
+ Query = "?api-version=7.1",
}.Uri;
var request = new HttpRequestMessage(HttpMethod.Post, uri) { Content = JsonContent.Create(query), };
var subscriptions = (await SendAsync(project.Token!, request, cancellationToken)).Results;
@@ -140,7 +132,7 @@ public async Task GetProjectAsync(Project project, CancellationToke
Host = url.Hostname,
Port = url.Port ?? -1,
Path = $"{url.OrganizationName}/_apis/projects/{url.ProjectIdOrName}",
- Query = "?api-version=7.0",
+ Query = "?api-version=7.1",
}.Uri;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
return await SendAsync(project.Token!, request, cancellationToken);
@@ -155,7 +147,7 @@ public async Task> GetRepositoriesAsync(Project project, Ca
Host = url.Hostname,
Port = url.Port ?? -1,
Path = $"{url.OrganizationName}/{url.ProjectIdOrName}/_apis/git/repositories",
- Query = "?api-version=7.0",
+ Query = "?api-version=7.1",
}.Uri;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
var data = await SendAsync>(project.Token!, request, cancellationToken);
@@ -171,7 +163,7 @@ public async Task GetRepositoryAsync(Project project, string rep
Host = url.Hostname,
Port = url.Port ?? -1,
Path = $"{url.OrganizationName}/{url.ProjectIdOrName}/_apis/git/repositories/{repositoryIdOrName}",
- Query = "?api-version=7.0",
+ Query = "?api-version=7.1",
}.Uri;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
return await SendAsync(project.Token!, request, cancellationToken);
@@ -192,7 +184,7 @@ public async Task GetRepositoryAsync(Project project, string rep
Host = url.Hostname,
Port = url.Port ?? -1,
Path = $"{url.OrganizationName}/{url.ProjectIdOrName}/_apis/git/repositories/{repositoryIdOrName}/items",
- Query = $"?path={path}&includeContent=true&latestProcessedChange=true&api-version=7.0"
+ Query = $"?path={path}&includeContent=true&latestProcessedChange=true&api-version=7.1"
}.Uri;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
var item = await SendAsync(project.Token!, request, cancellationToken);
diff --git a/server/Tingle.Dependabot/Workflow/Synchronizer.cs b/server/Tingle.Dependabot/Workflow/Synchronizer.cs
index 818233ca..13460b32 100644
--- a/server/Tingle.Dependabot/Workflow/Synchronizer.cs
+++ b/server/Tingle.Dependabot/Workflow/Synchronizer.cs
@@ -5,31 +5,17 @@
using Tingle.Dependabot.Models.Dependabot;
using Tingle.Dependabot.Models.Management;
using Tingle.EventBus;
+using Tingle.Extensions.Primitives;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace Tingle.Dependabot.Workflow;
-internal class Synchronizer
+internal class Synchronizer(MainDbContext dbContext, AzureDevOpsProvider adoProvider, IEventPublisher publisher, ILogger logger)
{
- private readonly MainDbContext dbContext;
- private readonly AzureDevOpsProvider adoProvider;
- private readonly IEventPublisher publisher;
- private readonly ILogger logger;
-
- private readonly IDeserializer yamlDeserializer;
-
- public Synchronizer(MainDbContext dbContext, AzureDevOpsProvider adoProvider, IEventPublisher publisher, ILogger logger)
- {
- this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
- this.adoProvider = adoProvider ?? throw new ArgumentNullException(nameof(adoProvider));
- this.publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
- this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
-
- yamlDeserializer = new DeserializerBuilder().WithNamingConvention(HyphenatedNamingConvention.Instance)
- .IgnoreUnmatchedProperties()
- .Build();
- }
+ private readonly IDeserializer yamlDeserializer = new DeserializerBuilder().WithNamingConvention(HyphenatedNamingConvention.Instance)
+ .IgnoreUnmatchedProperties()
+ .Build();
public async Task SynchronizeAsync(Project project, bool trigger, CancellationToken cancellationToken = default)
{
@@ -196,7 +182,7 @@ internal async Task SynchronizeAsync(Project project,
{
repository = new Repository
{
- Id = $"repo_{KSUID.Ksuid.Generate()}",
+ Id = $"repo_{Ksuid.Generate()}",
Created = DateTimeOffset.UtcNow,
ProjectId = project.Id,
ProviderId = providerInfo.Id,
diff --git a/server/Tingle.Dependabot/Workflow/UpdateRunner.cs b/server/Tingle.Dependabot/Workflow/UpdateRunner.cs
index 79983d15..47bba628 100644
--- a/server/Tingle.Dependabot/Workflow/UpdateRunner.cs
+++ b/server/Tingle.Dependabot/Workflow/UpdateRunner.cs
@@ -70,10 +70,12 @@ public async Task CreateAsync(Project project, Repository repository, Repository
// prepare the container
var volumeName = "working-dir";
+ var ecosystem = job.PackageEcosystem!;
+ var updaterImageTag = options.GetUpdaterImageTag(ecosystem, project);
var container = new ContainerAppContainer
{
Name = UpdaterContainerName,
- Image = $"ghcr.io/tinglesoftware/dependabot-updater-{job.PackageEcosystem}:{project.UpdaterImageTag ?? options.UpdaterImageTag}",
+ Image = $"ghcr.io/tinglesoftware/dependabot-updater-{ecosystem}:{updaterImageTag}",
Resources = job.Resources!,
Args = { useV2 ? "update_files" : "update_script", },
VolumeMounts = { new ContainerAppVolumeMount { VolumeName = volumeName, MountPath = options.WorkingDirectory, }, },
@@ -82,10 +84,11 @@ public async Task CreateAsync(Project project, Repository repository, Repository
foreach (var (key, value) in env) container.Env.Add(new ContainerAppEnvironmentVariable { Name = key, Value = value, });
// prepare the ContainerApp job
+ var timeoutSec = Convert.ToInt32(TimeSpan.FromHours(1).TotalSeconds);
var data = new ContainerAppJobData((project.Location ?? options.Location)!)
{
EnvironmentId = options.AppEnvironmentId,
- Configuration = new ContainerAppJobConfiguration(ContainerAppJobTriggerType.Manual, 1)
+ Configuration = new ContainerAppJobConfiguration(ContainerAppJobTriggerType.Manual, replicaTimeout: timeoutSec)
{
ManualTriggerConfig = new JobConfigurationManualTriggerConfig
{
@@ -93,7 +96,6 @@ public async Task CreateAsync(Project project, Repository repository, Repository
ReplicaCompletionCount = 1,
},
ReplicaRetryLimit = 1,
- ReplicaTimeout = Convert.ToInt32(TimeSpan.FromHours(1).TotalSeconds),
},
Template = new ContainerAppJobTemplate
{
@@ -113,7 +115,7 @@ public async Task CreateAsync(Project project, Repository repository, Repository
Tags =
{
["purpose"] = "dependabot",
- ["ecosystem"] = job.PackageEcosystem,
+ ["ecosystem"] = ecosystem,
["repository"] = job.RepositorySlug,
["directory"] = job.Directory,
["machine-name"] = Environment.MachineName,
@@ -137,6 +139,7 @@ public async Task CreateAsync(Project project, Repository repository, Repository
_ = await operation.Value.StartAsync(Azure.WaitUntil.Completed, cancellationToken: cancellationToken);
logger.StartedContainerAppJob(job.Id);
job.Status = UpdateJobStatus.Running;
+ job.UpdaterImage = container.Image;
}
public async Task DeleteAsync(UpdateJob job, CancellationToken cancellationToken = default)
diff --git a/server/Tingle.Dependabot/Workflow/WorkflowOptions.cs b/server/Tingle.Dependabot/Workflow/WorkflowOptions.cs
index 24634c70..c29ed6e3 100644
--- a/server/Tingle.Dependabot/Workflow/WorkflowOptions.cs
+++ b/server/Tingle.Dependabot/Workflow/WorkflowOptions.cs
@@ -30,6 +30,14 @@ public class WorkflowOptions
/// 1.20
public string? UpdaterImageTag { get; set; }
+ ///
+ /// Versions of the updater docker container images to use per ecosystem.
+ /// Keeping this value fixed in code is important so that the code that depends on it always works.
+ /// If no value is provided for an ecosystem, the default version is used.
+ ///
+ /// 1.20
+ public Dictionary UpdaterImageTags { get; set; } = new(StringComparer.OrdinalIgnoreCase);
+
///
/// Root working directory where file are written during job scheduling and execution.
/// This directory is the root for all jobs.
@@ -54,4 +62,13 @@ public class WorkflowOptions
/// Location/region where to create new update jobs.
/// westeurope
public string? Location { get; set; } // using Azure.Core.Location does not work when binding from IConfiguration
+
+ public string GetUpdaterImageTag(string ecosystem, Models.Management.Project project)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(ecosystem);
+
+ if (UpdaterImageTags.TryGetValue(ecosystem, out var tag)) return tag;
+
+ return project.UpdaterImageTag ?? UpdaterImageTag!;
+ }
}
diff --git a/server/main.bicep b/server/main.bicep
index 137f5dd8..8409aa60 100644
--- a/server/main.bicep
+++ b/server/main.bicep
@@ -6,7 +6,7 @@ param location string = resourceGroup().location
@description('Name of the resources. Make sure it is unique e.g. dependabotcontoso to avoid conflicts or failures')
param name string = 'dependabot'
-@description('JSON array string fo projects to setup. E.g. [{"url":"https://dev.azure.com/tingle/dependabot","token":"dummy","AutoComplete":true}]')
+@description('JSON array string for projects to setup. E.g. [{"url":"https://dev.azure.com/tingle/dependabot","token":"dummy","AutoComplete":true}]')
param projectSetups string = '[]'
@description('Access token for authenticating requests to GitHub.')
@@ -14,13 +14,9 @@ param githubToken string = ''
@minLength(1)
@description('Tag of the docker images.')
-param imageTag string = '#{GITVERSION_NUGETVERSIONV2}#'
+param imageTag string = '#{IMAGE_TAG}#'
-var fileShares = [
- { name: 'certs' }
- { name: 'distributed-locks', writeable: true }
- { name: 'working-dir', writeable: true }
-]
+var fileShares = ['certs', 'distributed-locks', 'working-dir']
// dependabot is not available as of 2023-Sep-25 so we change just for the public deployment
var storageAccountName = replace(replace((name == 'dependabot' ? 'dependabotstore' : name), '-', ''), '_', '') // remove underscores and hyphens
@@ -107,6 +103,10 @@ resource appConfiguration 'Microsoft.AppConfiguration/configurationStores@2023-0
'${managedIdentity.id}': {/*ttk bug*/ }
}
}
+
+ // override the default updater image tag for nuget jobs
+ // TODO: remove this here and on Azure once the authentication issues are resolved (https://github.com/tinglesoftware/dependabot-azure-devops/issues/921)
+ resource nugetVersion 'keyValues' = { name: 'Workflow:UpdaterImageTags:nuget$Production', properties: { value: '1.24' } }
}
/* Storage Account */
@@ -129,13 +129,13 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: 'default'
resource shares 'shares' = [for fs in fileShares: {
- name: fs.name
+ name: fs
properties: {
- accessTier: contains(fs, 'accessTier') ? fs.accessTier : 'TransactionOptimized'
+ accessTier: 'TransactionOptimized'
// container apps does not support NFS
// https://github.com/microsoft/azure-container-apps/issues/717
// enabledProtocols: contains(fs, 'enabledProtocols') ? fs.enabledProtocols : 'SMB'
- shareQuota: contains(fs, 'shareQuota') ? fs.shareQuota : 1
+ shareQuota: 1
}
}]
}
@@ -215,13 +215,13 @@ resource appEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
}
resource storages 'storages' = [for fs in fileShares: {
- name: fs.name
+ name: fs
properties: {
azureFile: {
accountName: storageAccount.name
accountKey: storageAccount.listKeys().keys[0].value
- shareName: fs.name
- accessMode: contains(fs, 'writeable') && bool(fs.writeable) ? 'ReadWrite' : 'ReadOnly'
+ shareName: fs
+ accessMode: 'ReadWrite'
}
}
}]
@@ -317,6 +317,13 @@ resource app 'Microsoft.App/containerApps@2023-05-01' = {
memory: '0.5Gi'
}
probes: [
+ {
+ type: 'Startup'
+ httpGet: { port: 8080, path: '/liveness' }
+ initialDelaySeconds: 10
+ timeoutSeconds: 100
+ failureThreshold: 10
+ }
{ type: 'Liveness', httpGet: { port: 8080, path: '/liveness' } }
{
type: 'Readiness'
diff --git a/server/main.json b/server/main.json
index c75dee46..3babaf67 100644
--- a/server/main.json
+++ b/server/main.json
@@ -22,7 +22,7 @@
"type": "string",
"defaultValue": "[[]",
"metadata": {
- "description": "JSON array string fo projects to setup. E.g. [{\"url\":\"https://dev.azure.com/tingle/dependabot\",\"token\":\"dummy\",\"AutoComplete\":true}]"
+ "description": "JSON array string for projects to setup. E.g. [{\"url\":\"https://dev.azure.com/tingle/dependabot\",\"token\":\"dummy\",\"AutoComplete\":true}]"
}
},
"githubToken": {
@@ -34,7 +34,7 @@
},
"imageTag": {
"type": "string",
- "defaultValue": "#{GITVERSION_NUGETVERSIONV2}#",
+ "defaultValue": "#{IMAGE_TAG}#",
"minLength": 1,
"metadata": {
"description": "Tag of the docker images."
@@ -66,17 +66,9 @@
}
],
"fileShares": [
- {
- "name": "certs"
- },
- {
- "name": "distributed-locks",
- "writeable": true
- },
- {
- "name": "working-dir",
- "writeable": true
- }
+ "certs",
+ "distributed-locks",
+ "working-dir"
],
"storageAccountName": "[replace(replace(if(equals(parameters('name'), 'dependabot'), 'dependabotstore', parameters('name')), '-', ''), '_', '')]",
"sqlServerAdministratorLogin": "[uniqueString(resourceGroup().id)]",
@@ -151,6 +143,17 @@
"[resourceId('Microsoft.KeyVault/vaults', parameters('name'))]"
]
},
+ {
+ "type": "Microsoft.AppConfiguration/configurationStores/keyValues",
+ "apiVersion": "2023-03-01",
+ "name": "[format('{0}/{1}', parameters('name'), 'Workflow:UpdaterImageTags:nuget$Production')]",
+ "properties": {
+ "value": "1.24"
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name'))]"
+ ]
+ },
{
"copy": {
"name": "shares",
@@ -158,10 +161,10 @@
},
"type": "Microsoft.Storage/storageAccounts/fileServices/shares",
"apiVersion": "2022-09-01",
- "name": "[format('{0}/{1}/{2}', variables('storageAccountName'), 'default', variables('fileShares')[copyIndex()].name)]",
+ "name": "[format('{0}/{1}/{2}', variables('storageAccountName'), 'default', variables('fileShares')[copyIndex()])]",
"properties": {
- "accessTier": "[if(contains(variables('fileShares')[copyIndex()], 'accessTier'), variables('fileShares')[copyIndex()].accessTier, 'TransactionOptimized')]",
- "shareQuota": "[if(contains(variables('fileShares')[copyIndex()], 'shareQuota'), variables('fileShares')[copyIndex()].shareQuota, 1)]"
+ "accessTier": "TransactionOptimized",
+ "shareQuota": 1
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
@@ -174,13 +177,13 @@
},
"type": "Microsoft.App/managedEnvironments/storages",
"apiVersion": "2023-05-01",
- "name": "[format('{0}/{1}', parameters('name'), variables('fileShares')[copyIndex()].name)]",
+ "name": "[format('{0}/{1}', parameters('name'), variables('fileShares')[copyIndex()])]",
"properties": {
"azureFile": {
"accountName": "[variables('storageAccountName')]",
"accountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-09-01').keys[0].value]",
- "shareName": "[variables('fileShares')[copyIndex()].name]",
- "accessMode": "[if(and(contains(variables('fileShares')[copyIndex()], 'writeable'), bool(variables('fileShares')[copyIndex()].writeable)), 'ReadWrite', 'ReadOnly')]"
+ "shareName": "[variables('fileShares')[copyIndex()]]",
+ "accessMode": "ReadWrite"
}
},
"dependsOn": [
@@ -542,6 +545,16 @@
"memory": "0.5Gi"
},
"probes": [
+ {
+ "type": "Startup",
+ "httpGet": {
+ "port": 8080,
+ "path": "/liveness"
+ },
+ "initialDelaySeconds": 10,
+ "timeoutSeconds": 100,
+ "failureThreshold": 10
+ },
{
"type": "Liveness",
"httpGet": {
diff --git a/server/main.parameters.json b/server/main.parameters.json
index 859f0a6b..43cd85ad 100644
--- a/server/main.parameters.json
+++ b/server/main.parameters.json
@@ -6,13 +6,13 @@
"value": "dependabot"
},
"projectSetups": {
- "value": "[{\"url\":\"#{System_TeamFoundationCollectionUri}##{System_TeamProject}#\", \"token\":\"#{DependabotProjectToken}#\",\"AutoComplete\":true}]"
+ "value": "[{\"url\":\"https://dev.azure.com/tingle/Core\", \"token\":\"#{DEPENDABOT_PROJECT_TOKEN}#\",\"AutoComplete\":true}]"
},
"githubToken": {
- "value": "#{GithubToken}#"
+ "value": "#{DEPENDABOT_GITHUB_TOKEN}#"
},
"imageTag": {
- "value": "#{GITVERSION_SHORTSHA}#"
+ "value": "#{DOCKER_IMAGE_TAG}#"
}
}
}
diff --git a/update-files.ps1 b/update-files.ps1
index 7a717f0b..4341ddbe 100644
--- a/update-files.ps1
+++ b/update-files.ps1
@@ -12,18 +12,22 @@ Write-Output "Found dependabot-omnibus version: $version"
# Prepare the list of files to be downloaded
$files = @(
".ruby-version"
- # ".rubocop.yml"
- # "Rakefile"
+ ".rubocop.yml"
+ ".rubocop_todo.yml"
+ "Rakefile"
"updater/.rubocop.yml"
"updater/bin/fetch_files.rb"
"updater/bin/update_files.rb"
- "updater/config/.npmrc"
- "updater/config/.yarnrc"
+ # "updater/config/.npmrc"
+ # "updater/config/.yarnrc"
"updater/lib/dependabot/logger/formats.rb"
- "updater/lib/dependabot/updater/operations/create_group_security_update_pull_request.rb"
+ "updater/lib/dependabot/sentry/exception_sanitizer_processor.rb"
+ "updater/lib/dependabot/sentry/processor.rb"
+ "updater/lib/dependabot/sentry/sentry_context_processor.rb"
+ "updater/lib/dependabot/sorbet/runtime.rb"
"updater/lib/dependabot/updater/operations/create_group_update_pull_request.rb"
"updater/lib/dependabot/updater/operations/create_security_update_pull_request.rb"
"updater/lib/dependabot/updater/operations/group_update_all_versions.rb"
@@ -35,25 +39,29 @@ $files = @(
"updater/lib/dependabot/updater/error_handler.rb"
"updater/lib/dependabot/updater/errors.rb"
"updater/lib/dependabot/updater/group_update_creation.rb"
+ "updater/lib/dependabot/updater/group_update_refreshing.rb"
"updater/lib/dependabot/updater/operations.rb"
"updater/lib/dependabot/updater/security_update_helpers.rb"
"updater/lib/dependabot/api_client.rb"
"updater/lib/dependabot/base_command.rb"
- "updater/lib/dependabot/dependency_change.rb"
"updater/lib/dependabot/dependency_change_builder.rb"
+ "updater/lib/dependabot/dependency_change.rb"
"updater/lib/dependabot/dependency_group_engine.rb"
"updater/lib/dependabot/dependency_snapshot.rb"
"updater/lib/dependabot/environment.rb"
"updater/lib/dependabot/file_fetcher_command.rb"
"updater/lib/dependabot/job.rb"
+ "updater/lib/dependabot/opentelemetry.rb"
"updater/lib/dependabot/sentry.rb"
"updater/lib/dependabot/service.rb"
"updater/lib/dependabot/setup.rb"
"updater/lib/dependabot/update_files_command.rb"
"updater/lib/dependabot/updater.rb"
- # "updater/spec/dependabot/updater/operations/group_update_all_versions_spec.rb"
+ "updater/spec/dependabot/sentry/exception_sanitizer_processor_spec.rb"
+ "updater/spec/dependabot/sentry/sentry_context_processor_spec.rb"
# "updater/spec/dependabot/updater/operations/refresh_group_update_pull_request_spec.rb"
+ "updater/spec/dependabot/updater/dependency_group_change_batch_spec.rb"
"updater/spec/dependabot/updater/error_handler_spec.rb"
"updater/spec/dependabot/updater/operations_spec.rb"
"updater/spec/dependabot/api_client_spec.rb"
@@ -62,59 +70,89 @@ $files = @(
"updater/spec/dependabot/dependency_group_engine_spec.rb"
# "updater/spec/dependabot/dependency_snapshot_spec.rb"
"updater/spec/dependabot/environment_spec.rb"
- # "updater/spec/dependabot/file_fetcher_command_spec.rb"
- # "updater/spec/dependabot/integration_spec.rb"
+ "updater/spec/dependabot/file_fetcher_command_spec.rb"
"updater/spec/dependabot/job_spec.rb"
- "updater/spec/dependabot/sentry_spec.rb"
"updater/spec/dependabot/service_spec.rb"
- # "updater/spec/dependabot/update_files_command_spec.rb"
+ "updater/spec/dependabot/update_files_command_spec.rb"
# "updater/spec/dependabot/updater_spec.rb"
+ "updater/spec/fixtures/handle_error.json"
"updater/spec/fixtures/rubygems-index"
"updater/spec/fixtures/rubygems-info-a"
- "updater/spec/fixtures/rubygems-versions-a.json"
"updater/spec/fixtures/rubygems-info-b"
+ "updater/spec/fixtures/rubygems-versions-a.json"
"updater/spec/fixtures/rubygems-versions-b.json"
"updater/spec/fixtures/bundler/original/Gemfile"
"updater/spec/fixtures/bundler/original/Gemfile.lock"
+ "updater/spec/fixtures/bundler/original/sub_dep"
+ "updater/spec/fixtures/bundler/original/sub_dep.lock"
"updater/spec/fixtures/bundler/updated/Gemfile"
"updater/spec/fixtures/bundler/updated/Gemfile.lock"
+ "updater/spec/fixtures/bundler2/original/Gemfile"
+ "updater/spec/fixtures/bundler2/original/Gemfile.lock"
+ "updater/spec/fixtures/bundler2/updated/Gemfile"
+ "updater/spec/fixtures/bundler2/updated/Gemfile.lock"
"updater/spec/fixtures/bundler_gemspec/original/Gemfile"
"updater/spec/fixtures/bundler_gemspec/original/Gemfile.lock"
"updater/spec/fixtures/bundler_gemspec/original/library.gemspec"
- "updater/spec/fixtures/bundler_git/original/Gemfile"
- "updater/spec/fixtures/bundler_git/original/Gemfile.lock"
- "updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile"
- "updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile.lock"
+ "updater/spec/fixtures/bundler_gemspec/updated/Gemfile"
+ "updater/spec/fixtures/bundler_gemspec/updated/Gemfile.lock"
+ "updater/spec/fixtures/bundler_gemspec/updated/library.gemspec"
+ "updater/spec/fixtures/bundler_grouped/original/Gemfile"
+ "updater/spec/fixtures/bundler_grouped/original/Gemfile.lock"
"updater/spec/fixtures/bundler_vendored/original/Gemfile"
"updater/spec/fixtures/bundler_vendored/original/Gemfile.lock"
- "updater/spec/fixtures/docker/original/Dockerfile.bundler"
- "updater/spec/fixtures/docker/original/Dockerfile.cargo"
+ "updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-a-2.0.0.gem"
+ "updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-b-1.1.0.gem"
+ "updater/spec/fixtures/bundler_vendored/original/vendor/cache/ruby-dummy-git-dependency-20151f9b67c8/.bundlecache"
+ "updater/spec/fixtures/bundler_vendored/original/vendor/cache/ruby-dummy-git-dependency-20151f9b67c8/dummy-git-dependency.gemspec"
+ "updater/spec/fixtures/bundler_vendored/updated/Gemfile"
+ "updater/spec/fixtures/bundler_vendored/updated/Gemfile.lock"
+ "updater/spec/fixtures/bundler_vendored/updated/.bundle/config"
+ "updater/spec/fixtures/bundler_vendored/updated/vendor/cache/ruby-dummy-git-dependency-c0e25c2eb332/.bundlecache"
+ "updater/spec/fixtures/bundler_vendored/updated/vendor/cache/ruby-dummy-git-dependency-c0e25c2eb332/dummy-git-dependency.gemspec"
+ "updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-a-2.0.0.gem"
+ "updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-b-1.2.0.gem"
+ "updater/spec/fixtures/composer/original/composer.json"
+ "updater/spec/fixtures/composer/original/composer.lock"
+ "updater/spec/fixtures/composer/updated/composer.json"
+ "updater/spec/fixtures/composer/updated/composer.lock"
+ "updater/spec/fixtures/dummy/original/a.dummy"
+ "updater/spec/fixtures/dummy/original/b.dummy"
+ "updater/spec/fixtures/file_fetcher_output/output.json"
+ "updater/spec/fixtures/file_fetcher_output/vendoring_output.json"
"updater/spec/fixtures/jobs/job_with_credentials.json"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_by_dependency_type.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_empty_group.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_overlapping_groups.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_existing_pr.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_ungrouped.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_vendoring.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping_with_global_ignores.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_peer_manifests.yaml"
+ "updater/spec/fixtures/jobs/job_with_dummy.json"
+ "updater/spec/fixtures/jobs/job_with_vendor_dependencies.json"
+ "updater/spec/fixtures/jobs/job_without_credentials.json"
+ "updater/spec/fixtures/job_definitions/bundler/security_updates/group_update_multi_dir.yaml"
"updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh.yaml"
"updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_dependencies_changed.yaml"
"updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_empty_group.yaml"
"updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_missing_group.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_versions_changed.yaml"
"updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_similar_pr.yaml"
- "updater/spec/fixtures/job_definitions/bundler/version_updates/update_all_simple.yaml"
- "updater/spec/fixtures/job_definitions/docker/version_updates/group_update_peer_manifests.yaml"
+ "updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_versions_changed.yaml"
+ "updater/spec/fixtures/job_definitions/dummy/version_updates/group_update_peer_manifests.yaml"
"updater/spec/fixtures/job_definitions/README.md"
+ "updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/when_connectivity_is_broken/logs_connectivity_failed_and_does_not_raise_an_error.yml"
+ "updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/logs_connectivity_is_successful_and_does_not_raise_an_error.yml"
+ "updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/does_not_clone_the_repo.yml"
+ "updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/fetches_the_files_and_writes_the_fetched_files_to_output_json.yml"
+ "updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_contains_a_git_dependency/creates_individual_PRs_since_git_dependencies_cannot_be_grouped_as_semver.yml"
+ "updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_a_gemspec/creates_a_DependencyChange_for_just_the_modified_files_without_reporting_errors.yml"
+ "updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_vendored_dependencies/creates_a_pull_request_that_includes_changes_to_the_vendored_files.yml"
+ "updater/spec/support/dummy_package_manager/dummy.rb"
+ "updater/spec/support/dummy_package_manager/file_fetcher.rb"
+ "updater/spec/support/dummy_package_manager/file_parser.rb"
+ "updater/spec/support/dummy_package_manager/file_updater.rb"
+ "updater/spec/support/dummy_package_manager/requirement.rb"
+ "updater/spec/support/dummy_package_manager/update_checker.rb"
+ "updater/spec/support/dummy_package_manager/version.rb"
"updater/spec/support/dependency_file_helpers.rb"
"updater/spec/support/dummy_pkg_helpers.rb"
- # "updater/spec/spec_helper.rb"
+ "updater/spec/spec_helper.rb"
)
# Download each file listed
@@ -123,11 +161,11 @@ foreach ($name in $files) {
$sourceUrl = "$baseUrl/v$version/$($name)"
$destinationPath = Join-Path -Path '.' -ChildPath "$name"
- # Write-Host "`Downloading $name ..."
+ Write-Host "`Downloading $name ..."
+
# [System.IO.Directory]::CreateDirectory("$(Split-Path -Path "$destinationPath")") | Out-Null
# Invoke-WebRequest -Uri $sourceUrl -OutFile $destinationPath
- echo "Downloading $($name) ..."
mkdir -p "$(dirname "$destinationPath")"
curl -sL "$sourceUrl" -o "$destinationPath"
}
diff --git a/updater/.gitignore b/updater/.gitignore
index 334d24eb..4c463fc3 100644
--- a/updater/.gitignore
+++ b/updater/.gitignore
@@ -1,3 +1,4 @@
/.bundle/
/spec/examples.txt
/tmp/
+/job/
\ No newline at end of file
diff --git a/updater/Dockerfile b/updater/Dockerfile
index 60f41666..abd5da9f 100644
--- a/updater/Dockerfile
+++ b/updater/Dockerfile
@@ -1,7 +1,12 @@
-# The docker images in https://github.com/dependabot/dependabot-core are no longer versioned like the ruby Gems
-#TODO: find out how to lock the base image version without the ruby Gem version
ARG ECOSYSTEM
-FROM ghcr.io/dependabot/dependabot-updater-$ECOSYSTEM
+ARG BASE_VERSION=latest
+ARG DEPENDABOT_UPDATER_VERSION=unknown
+
+# The Dependabot docker images in https://github.com/dependabot/dependabot-core are no longer versioned like the Ruby Gems; instead they are versioned by the commit SHA of the release tag.
+# In production, the build pipeline automatically calculates BASE_VERSION to match the dependabot-omnibus version set in updater/Gemfile (see .github/workflows/updater.yml).
+# In local/dev, the "latest" tag will be used by default. You can override this by setting BASE_VERSION to the commit SHA of a dependabot-core release tag.
+# e.g. for v0.264.0, use BASE_VERSION="e8d8a1268ea61304e939ba9ab963e249cac5b241"
+FROM ghcr.io/dependabot/dependabot-updater-$ECOSYSTEM:$BASE_VERSION
LABEL org.opencontainers.image.source="https://github.com/tinglesoftware/dependabot-azure-devops"
@@ -21,6 +26,10 @@ RUN bundle config set --local path 'vendor' && \
COPY --chown=dependabot:dependabot LICENSE $DEPENDABOT_HOME
COPY --chown=dependabot:dependabot updater $DEPENDABOT_HOME/dependabot-updater
+# Add ENV
+ENV DEPENDABOT_UPDATER_VERSION=$DEPENDABOT_UPDATER_VERSION
+ENV OTEL_ENABLED=true
+
# ENTRYPOINT IS USED instead of CMD so as to avoid adding
# 'bin/run.sh' before the file name when running the image
ENTRYPOINT ["bin/run.sh"]
diff --git a/updater/Gemfile b/updater/Gemfile
index b4310eb2..68d9db52 100644
--- a/updater/Gemfile
+++ b/updater/Gemfile
@@ -8,19 +8,39 @@ source "https://rubygems.org"
# They are so many, our reference won't be found for it to be updated.
# Hence adding the branch.
+gem "dependabot-omnibus", "~>0.268.0"
# gem "dependabot-omnibus", github: "dependabot/dependabot-core", branch: "main"
# gem "dependabot-omnibus", github: "dependabot/dependabot-core", tag: "v0.232.0"
-gem "dependabot-omnibus", "~>0.237.0"
+# gem "dependabot-omnibus", github: "dependabot/dependabot-core", ref: "ffde6f6"
-gem "http", "~> 5.1"
+gem "http", "~> 5.2"
gem "octokit", "6.1.1"
-gem "sentry-raven", "~> 3.1"
+gem "opentelemetry-exporter-otlp", "~> 0.28"
+gem "opentelemetry-instrumentation-excon", "~> 0.22"
+gem "opentelemetry-instrumentation-faraday", "~> 0.24"
+gem "opentelemetry-instrumentation-http", "~> 0.23"
+gem "opentelemetry-instrumentation-net_http", "~> 0.22"
+gem "opentelemetry-sdk", "~> 1.5"
+gem "sentry-opentelemetry", "~> 5.18"
+gem "sentry-ruby", "~> 5.17"
gem "terminal-table", "~> 3.0.2"
+gem "flamegraph", "~> 0.9.5"
+
group :test do
- gem "rspec"
- gem "rubocop"
- gem "rubocop-performance"
- gem "vcr"
- gem "webmock"
+ gem "debug", "~> 1.9.2"
+ gem "gpgme", "~> 2.0"
+ gem "rake", "~> 13"
+ gem "rspec", "~> 3.12"
+ gem "rspec-its", "~> 1.3"
+ gem "rspec-sorbet", "~> 1.9.2"
+ gem "rubocop", "~> 1.65.1"
+ gem "rubocop-performance", "~> 1.21.0"
+ gem "rubocop-rspec", "~> 2.29.1"
+ gem "rubocop-sorbet", "~> 0.8.1"
+ gem "simplecov", "~> 0.22.0"
+ gem "turbo_tests", "~> 2.2.0"
+ gem "vcr", "~> 6.1"
+ gem "webmock", "~> 3.18"
+ gem "webrick", ">= 1.7"
end
diff --git a/updater/Gemfile.lock b/updater/Gemfile.lock
index ab582ad0..70ffc0af 100644
--- a/updater/Gemfile.lock
+++ b/updater/Gemfile.lock
@@ -1,106 +1,121 @@
GEM
remote: https://rubygems.org/
specs:
- addressable (2.8.5)
- public_suffix (>= 2.0.2, < 6.0)
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
ast (2.4.2)
- aws-eventstream (1.2.0)
- aws-partitions (1.855.0)
- aws-sdk-codecommit (1.60.0)
- aws-sdk-core (~> 3, >= 3.184.0)
- aws-sigv4 (~> 1.1)
- aws-sdk-core (3.187.1)
- aws-eventstream (~> 1, >= 1.0.2)
- aws-partitions (~> 1, >= 1.651.0)
+ aws-eventstream (1.3.0)
+ aws-partitions (1.961.0)
+ aws-sdk-codecommit (1.72.0)
+ aws-sdk-core (~> 3, >= 3.201.0)
aws-sigv4 (~> 1.5)
+ aws-sdk-core (3.201.3)
+ aws-eventstream (~> 1, >= 1.3.0)
+ aws-partitions (~> 1, >= 1.651.0)
+ aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-ecr (1.66.0)
- aws-sdk-core (~> 3, >= 3.184.0)
- aws-sigv4 (~> 1.1)
- aws-sigv4 (1.6.1)
+ aws-sdk-ecr (1.79.0)
+ aws-sdk-core (~> 3, >= 3.201.0)
+ aws-sigv4 (~> 1.5)
+ aws-sigv4 (1.9.1)
aws-eventstream (~> 1, >= 1.0.2)
- base64 (0.1.1)
+ base64 (0.2.0)
+ bigdecimal (3.1.8)
citrus (3.0.2)
commonmarker (0.23.10)
- crack (0.4.5)
+ concurrent-ruby (1.3.3)
+ crack (1.0.0)
+ bigdecimal
rexml
- dependabot-bundler (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-cargo (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-common (0.237.0)
+ csv (3.3.0)
+ debug (1.9.2)
+ irb (~> 1.10)
+ reline (>= 0.3.8)
+ dependabot-bundler (0.268.0)
+ dependabot-common (= 0.268.0)
+ parallel (~> 1.24)
+ dependabot-cargo (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-common (0.268.0)
aws-sdk-codecommit (~> 1.28)
aws-sdk-ecr (~> 1.5)
bundler (>= 1.16, < 3.0.0)
commonmarker (>= 0.20.1, < 0.24.0)
docker_registry2 (~> 1.18.0)
- excon (~> 0.96, < 0.105)
+ excon (~> 0.109)
faraday (= 2.7.11)
faraday-retry (= 2.2.0)
gitlab (= 4.19.0)
+ json (< 2.7)
nokogiri (~> 1.8)
octokit (>= 4.6, < 7.0)
opentelemetry-sdk (~> 1.3)
parser (>= 2.5, < 4.0)
psych (~> 5.0)
- sorbet-runtime (~> 0.5.11026)
- toml-rb (>= 1.1.2, < 3.0)
- dependabot-composer (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-docker (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-elm (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-git_submodules (0.237.0)
- dependabot-common (= 0.237.0)
+ sorbet-runtime (~> 0.5.11178)
+ stackprof (~> 0.2.16)
+ toml-rb (>= 1.1.2, < 4.0)
+ dependabot-composer (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-devcontainers (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-docker (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-elm (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-git_submodules (0.268.0)
+ dependabot-common (= 0.268.0)
parseconfig (~> 1.0, < 1.1.0)
- dependabot-github_actions (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-go_modules (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-gradle (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-maven (= 0.237.0)
- dependabot-hex (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-maven (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-npm_and_yarn (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-nuget (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-omnibus (0.237.0)
- dependabot-bundler (= 0.237.0)
- dependabot-cargo (= 0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-composer (= 0.237.0)
- dependabot-docker (= 0.237.0)
- dependabot-elm (= 0.237.0)
- dependabot-git_submodules (= 0.237.0)
- dependabot-github_actions (= 0.237.0)
- dependabot-go_modules (= 0.237.0)
- dependabot-gradle (= 0.237.0)
- dependabot-hex (= 0.237.0)
- dependabot-maven (= 0.237.0)
- dependabot-npm_and_yarn (= 0.237.0)
- dependabot-nuget (= 0.237.0)
- dependabot-pub (= 0.237.0)
- dependabot-python (= 0.237.0)
- dependabot-swift (= 0.237.0)
- dependabot-terraform (= 0.237.0)
- dependabot-pub (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-python (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-swift (0.237.0)
- dependabot-common (= 0.237.0)
- dependabot-terraform (0.237.0)
- dependabot-common (= 0.237.0)
- diff-lcs (1.5.0)
- docker_registry2 (1.18.0)
+ dependabot-github_actions (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-go_modules (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-gradle (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-maven (= 0.268.0)
+ dependabot-hex (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-maven (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-npm_and_yarn (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-nuget (0.268.0)
+ dependabot-common (= 0.268.0)
+ rubyzip (>= 2.3.2, < 3.0)
+ dependabot-omnibus (0.268.0)
+ dependabot-bundler (= 0.268.0)
+ dependabot-cargo (= 0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-composer (= 0.268.0)
+ dependabot-devcontainers (= 0.268.0)
+ dependabot-docker (= 0.268.0)
+ dependabot-elm (= 0.268.0)
+ dependabot-git_submodules (= 0.268.0)
+ dependabot-github_actions (= 0.268.0)
+ dependabot-go_modules (= 0.268.0)
+ dependabot-gradle (= 0.268.0)
+ dependabot-hex (= 0.268.0)
+ dependabot-maven (= 0.268.0)
+ dependabot-npm_and_yarn (= 0.268.0)
+ dependabot-nuget (= 0.268.0)
+ dependabot-pub (= 0.268.0)
+ dependabot-python (= 0.268.0)
+ dependabot-swift (= 0.268.0)
+ dependabot-terraform (= 0.268.0)
+ dependabot-pub (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-python (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-swift (0.268.0)
+ dependabot-common (= 0.268.0)
+ dependabot-terraform (0.268.0)
+ dependabot-common (= 0.268.0)
+ diff-lcs (1.5.1)
+ docile (1.4.1)
+ docker_registry2 (1.18.2)
rest-client (>= 1.8.0)
- domain_name (0.6.20231109)
- excon (0.104.0)
+ domain_name (0.6.20240107)
+ excon (0.111.0)
faraday (2.7.11)
base64
faraday-net_http (>= 2.0, < 3.1)
@@ -108,143 +123,288 @@ GEM
faraday-net_http (3.0.2)
faraday-retry (2.2.0)
faraday (~> 2.0)
- ffi (1.15.5)
- ffi-compiler (1.0.1)
- ffi (>= 1.0.0)
+ ffi (1.17.0)
+ ffi (1.17.0-aarch64-linux-gnu)
+ ffi (1.17.0-aarch64-linux-musl)
+ ffi (1.17.0-arm-linux-gnu)
+ ffi (1.17.0-arm-linux-musl)
+ ffi (1.17.0-arm64-darwin)
+ ffi (1.17.0-x86-linux-gnu)
+ ffi (1.17.0-x86-linux-musl)
+ ffi (1.17.0-x86_64-darwin)
+ ffi (1.17.0-x86_64-linux-gnu)
+ ffi (1.17.0-x86_64-linux-musl)
+ ffi-compiler (1.3.2)
+ ffi (>= 1.15.5)
rake
+ flamegraph (0.9.5)
gitlab (4.19.0)
httparty (~> 0.20)
terminal-table (>= 1.5.1)
- hashdiff (1.0.1)
- http (5.1.1)
+ google-protobuf (4.27.3)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.27.3-aarch64-linux)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.27.3-arm64-darwin)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.27.3-x86-linux)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.27.3-x86_64-darwin)
+ bigdecimal
+ rake (>= 13)
+ google-protobuf (4.27.3-x86_64-linux)
+ bigdecimal
+ rake (>= 13)
+ googleapis-common-protos-types (1.15.0)
+ google-protobuf (>= 3.18, < 5.a)
+ gpgme (2.0.24)
+ mini_portile2 (~> 2.7)
+ hashdiff (1.1.1)
+ http (5.2.0)
addressable (~> 2.8)
+ base64 (~> 0.1)
http-cookie (~> 1.0)
http-form_data (~> 2.2)
- llhttp-ffi (~> 0.4.0)
+ llhttp-ffi (~> 0.5.0)
http-accept (1.7.0)
- http-cookie (1.0.5)
+ http-cookie (1.0.6)
domain_name (~> 0.5)
http-form_data (2.3.0)
- httparty (0.21.0)
+ httparty (0.22.0)
+ csv
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
+ io-console (0.7.2)
+ irb (1.14.0)
+ rdoc (>= 4.0.0)
+ reline (>= 0.4.2)
jmespath (1.6.2)
json (2.6.3)
language_server-protocol (3.17.0.3)
- llhttp-ffi (0.4.0)
+ llhttp-ffi (0.5.0)
ffi-compiler (~> 1.0)
rake (~> 13.0)
- mime-types (3.5.1)
+ mime-types (3.5.2)
mime-types-data (~> 3.2015)
- mime-types-data (3.2023.1003)
+ mime-types-data (3.2024.0702)
mini_mime (1.1.5)
- multi_xml (0.6.0)
+ mini_portile2 (2.8.7)
+ multi_xml (0.7.1)
+ bigdecimal (~> 3.1)
netrc (0.11.0)
- nokogiri (1.15.5-aarch64-linux)
+ nokogiri (1.16.7)
+ mini_portile2 (~> 2.8.2)
+ racc (~> 1.4)
+ nokogiri (1.16.7-aarch64-linux)
+ racc (~> 1.4)
+ nokogiri (1.16.7-arm-linux)
+ racc (~> 1.4)
+ nokogiri (1.16.7-arm64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.16.7-x86-linux)
racc (~> 1.4)
- nokogiri (1.15.5-arm64-darwin)
+ nokogiri (1.16.7-x86_64-darwin)
racc (~> 1.4)
- nokogiri (1.15.5-x86_64-linux)
+ nokogiri (1.16.7-x86_64-linux)
racc (~> 1.4)
octokit (6.1.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
- opentelemetry-api (1.2.3)
- opentelemetry-common (0.20.0)
+ opentelemetry-api (1.3.0)
+ opentelemetry-common (0.21.0)
+ opentelemetry-api (~> 1.0)
+ opentelemetry-exporter-otlp (0.28.1)
+ google-protobuf (>= 3.18)
+ googleapis-common-protos-types (~> 1.3)
+ opentelemetry-api (~> 1.1)
+ opentelemetry-common (~> 0.20)
+ opentelemetry-sdk (~> 1.2)
+ opentelemetry-semantic_conventions
+ opentelemetry-instrumentation-base (0.22.5)
+ opentelemetry-api (~> 1.0)
+ opentelemetry-common (~> 0.21)
+ opentelemetry-registry (~> 0.1)
+ opentelemetry-instrumentation-excon (0.22.4)
+ opentelemetry-api (~> 1.0)
+ opentelemetry-instrumentation-base (~> 0.22.1)
+ opentelemetry-instrumentation-faraday (0.24.6)
+ opentelemetry-api (~> 1.0)
+ opentelemetry-instrumentation-base (~> 0.22.1)
+ opentelemetry-instrumentation-http (0.23.4)
+ opentelemetry-api (~> 1.0)
+ opentelemetry-instrumentation-base (~> 0.22.1)
+ opentelemetry-instrumentation-net_http (0.22.7)
opentelemetry-api (~> 1.0)
- opentelemetry-registry (0.3.0)
+ opentelemetry-instrumentation-base (~> 0.22.1)
+ opentelemetry-registry (0.3.1)
opentelemetry-api (~> 1.1)
- opentelemetry-sdk (1.3.1)
+ opentelemetry-sdk (1.5.0)
opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20)
opentelemetry-registry (~> 0.2)
opentelemetry-semantic_conventions
- opentelemetry-semantic_conventions (1.10.0)
+ opentelemetry-semantic_conventions (1.10.1)
opentelemetry-api (~> 1.0)
- parallel (1.23.0)
+ parallel (1.25.1)
+ parallel_tests (4.7.1)
+ parallel
parseconfig (1.0.8)
- parser (3.2.2.4)
+ parser (3.3.4.0)
ast (~> 2.4.1)
racc
- psych (5.1.1.1)
+ psych (5.1.2)
stringio
- public_suffix (5.0.4)
- racc (1.7.3)
+ public_suffix (6.0.1)
+ racc (1.8.1)
rainbow (3.1.1)
- rake (13.0.6)
- regexp_parser (2.8.2)
+ rake (13.2.1)
+ rdoc (6.7.0)
+ psych (>= 4.0.0)
+ regexp_parser (2.9.2)
+ reline (0.5.9)
+ io-console (~> 0.5)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
- rexml (3.2.6)
- rspec (3.12.0)
- rspec-core (~> 3.12.0)
- rspec-expectations (~> 3.12.0)
- rspec-mocks (~> 3.12.0)
- rspec-core (3.12.1)
- rspec-support (~> 3.12.0)
- rspec-expectations (3.12.2)
+ rexml (3.3.4)
+ strscan
+ rspec (3.13.0)
+ rspec-core (~> 3.13.0)
+ rspec-expectations (~> 3.13.0)
+ rspec-mocks (~> 3.13.0)
+ rspec-core (3.13.0)
+ rspec-support (~> 3.13.0)
+ rspec-expectations (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-mocks (3.12.4)
+ rspec-support (~> 3.13.0)
+ rspec-its (1.3.0)
+ rspec-core (>= 3.0.0)
+ rspec-expectations (>= 3.0.0)
+ rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.12.0)
- rspec-support (3.12.0)
- rubocop (1.57.1)
- base64 (~> 0.1.1)
+ rspec-support (~> 3.13.0)
+ rspec-sorbet (1.9.2)
+ sorbet-runtime
+ rspec-support (3.13.1)
+ rubocop (1.65.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
- parser (>= 3.2.2.4)
+ parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
- regexp_parser (>= 1.8, < 3.0)
+ regexp_parser (>= 2.4, < 3.0)
rexml (>= 3.2.5, < 4.0)
- rubocop-ast (>= 1.28.1, < 2.0)
+ rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
- rubocop-ast (1.29.0)
- parser (>= 3.2.1.0)
- rubocop-performance (1.19.0)
- rubocop (>= 1.7.0, < 2.0)
- rubocop-ast (>= 0.4.0)
+ rubocop-ast (1.31.3)
+ parser (>= 3.3.1.0)
+ rubocop-capybara (2.21.0)
+ rubocop (~> 1.41)
+ rubocop-factory_bot (2.26.1)
+ rubocop (~> 1.61)
+ rubocop-performance (1.21.1)
+ rubocop (>= 1.48.1, < 2.0)
+ rubocop-ast (>= 1.31.1, < 2.0)
+ rubocop-rspec (2.29.2)
+ rubocop (~> 1.40)
+ rubocop-capybara (~> 2.17)
+ rubocop-factory_bot (~> 2.22)
+ rubocop-rspec_rails (~> 2.28)
+ rubocop-rspec_rails (2.29.1)
+ rubocop (~> 1.61)
+ rubocop-sorbet (0.8.5)
+ rubocop (>= 1)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
+ rubyzip (2.3.2)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
- sentry-raven (3.1.2)
- faraday (>= 1.0)
- sorbet-runtime (0.5.11141)
- stringio (3.0.9)
+ sentry-opentelemetry (5.18.2)
+ opentelemetry-sdk (~> 1.0)
+ sentry-ruby (~> 5.18.2)
+ sentry-ruby (5.18.2)
+ bigdecimal
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ simplecov (0.22.0)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
+ simplecov-html (0.12.3)
+ simplecov_json_formatter (0.1.4)
+ sorbet-runtime (0.5.11506)
+ stackprof (0.2.26)
+ stringio (3.1.1)
+ strscan (3.1.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
- toml-rb (2.2.0)
+ toml-rb (3.0.1)
citrus (~> 3.0, > 3.0)
+ racc (~> 1.7)
+ turbo_tests (2.2.4)
+ parallel_tests (>= 3.3.0, < 5)
+ rspec (>= 3.10)
unicode-display_width (2.5.0)
vcr (6.2.0)
- webmock (3.19.1)
+ webmock (3.23.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
+ webrick (1.8.1)
PLATFORMS
aarch64-linux
- arm64-darwin-22
+ aarch64-linux-gnu
+ aarch64-linux-musl
+ arm-linux
+ arm-linux-gnu
+ arm-linux-musl
+ arm64-darwin
+ ruby
+ x86-linux
+ x86-linux-gnu
+ x86-linux-musl
+ x86_64-darwin
x86_64-linux
+ x86_64-linux-gnu
+ x86_64-linux-musl
DEPENDENCIES
- dependabot-omnibus (~> 0.237.0)
- http (~> 5.1)
+ debug (~> 1.9.2)
+ dependabot-omnibus (~> 0.268.0)
+ flamegraph (~> 0.9.5)
+ gpgme (~> 2.0)
+ http (~> 5.2)
octokit (= 6.1.1)
- rspec
- rubocop
- rubocop-performance
- sentry-raven (~> 3.1)
+ opentelemetry-exporter-otlp (~> 0.28)
+ opentelemetry-instrumentation-excon (~> 0.22)
+ opentelemetry-instrumentation-faraday (~> 0.24)
+ opentelemetry-instrumentation-http (~> 0.23)
+ opentelemetry-instrumentation-net_http (~> 0.22)
+ opentelemetry-sdk (~> 1.5)
+ rake (~> 13)
+ rspec (~> 3.12)
+ rspec-its (~> 1.3)
+ rspec-sorbet (~> 1.9.2)
+ rubocop (~> 1.65.1)
+ rubocop-performance (~> 1.21.0)
+ rubocop-rspec (~> 2.29.1)
+ rubocop-sorbet (~> 0.8.1)
+ sentry-opentelemetry (~> 5.18)
+ sentry-ruby (~> 5.17)
+ simplecov (~> 0.22.0)
terminal-table (~> 3.0.2)
- vcr
- webmock
+ turbo_tests (~> 2.2.0)
+ vcr (~> 6.1)
+ webmock (~> 3.18)
+ webrick (>= 1.7)
BUNDLED WITH
- 2.3.26
+ 2.5.14
diff --git a/updater/bin/azure_helpers.rb b/updater/bin/azure_helpers.rb
deleted file mode 100644
index 35070745..00000000
--- a/updater/bin/azure_helpers.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-# frozen_string_literal: true
-
-require "json"
-require "dependabot/shared_helpers"
-require "excon"
-
-module Dependabot
- module Clients
- class Azure
- def pull_requests_active(user_id, default_branch)
- # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests?view=azure-devops-rest-6.0&tabs=HTTP
- response = get(source.api_endpoint +
- source.organization + "/" + source.project +
- "/_apis/git/repositories/" + source.unscoped_repo +
- "/pullrequests?api-version=6.0&searchCriteria.status=active" \
- "&searchCriteria.creatorId=#{user_id}" \
- "&searchCriteria.targetRefName=refs/heads/#{default_branch}")
-
- JSON.parse(response.body).fetch("value")
- end
-
- def pull_request_abandon(pull_request_id)
- content = {
- status: "abandoned"
- }
-
- patch(source.api_endpoint +
- source.organization + "/" + source.project +
- "/_apis/git/repositories/" + source.unscoped_repo +
- "/pullrequests/#{pull_request_id}?api-version=6.0", content.to_json)
- end
-
- def branch_delete(name)
- branch_name = name.gsub("refs/heads/", "")
- branch_object_id = branch(branch_name)["objectId"]
-
- # https://developercommunity.visualstudio.com/t/delete-tags-or-branches-using-rest-apis/698220
- # https://github.com/MicrosoftDocs/azure-devops-docs/issues/2648
- update_ref(branch_name, branch_object_id, "0000000000000000000000000000000000000000")
- end
-
- def pull_request_commits(pull_request_id)
- response = get(source.api_endpoint +
- source.organization + "/" + source.project +
- "/_apis/git/repositories/" + source.unscoped_repo +
- "/pullrequests/#{pull_request_id}/commits?api-version=6.0")
-
- JSON.parse(response.body).fetch("value")
- end
-
- def get_user_id(token = nil)
- # https://learn.microsoft.com/en-us/javascript/api/azure-devops-extension-api/connectiondata
- # https://stackoverflow.com/a/53227325
- response = if token
- get_with_token(source.api_endpoint + source.organization + "/_apis/connectionData", token)
- else
- get(source.api_endpoint + source.organization + "/_apis/connectionData")
- end
- JSON.parse(response.body).fetch("authenticatedUser")["id"]
- end
-
- def pull_request_approve(pull_request_id, reviewer_token)
- user_id = get_user_id(reviewer_token)
-
- # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-reviewers/create-pull-request-reviewers?view=azure-devops-rest-6.0
- content = {
- # 10 - approved 5 - approved with suggestions 0 - no vote -5 - waiting for author -10 - rejected
- vote: 10
- }
-
- put_with_token(source.api_endpoint + source.organization + "/" + source.project +
- "/_apis/git/repositories/" + source.unscoped_repo +
- "/pullrequests/#{pull_request_id}/reviewers/#{user_id}" \
- "?api-version=6.0", content.to_json, reviewer_token)
- end
-
- def get_with_token(url, token)
- response = Excon.get(
- url,
- user: credentials&.fetch("username", nil),
- password: token,
- idempotent: true,
- **SharedHelpers.excon_defaults(
- headers: auth_header
- )
- )
-
- raise Unauthorized if response.status == 401
- raise Forbidden if response.status == 403
- raise NotFound if response.status == 404
-
- response
- end
-
- def put_with_token(url, json, token)
- response = Excon.put(
- url,
- body: json,
- user: credentials&.fetch("username", nil),
- password: token,
- idempotent: true,
- **SharedHelpers.excon_defaults(
- headers: auth_header.merge(
- {
- "Content-Type" => "application/json"
- }
- )
- )
- )
- raise Unauthorized if response.status == 401
- raise Forbidden if response.status == 403
- raise NotFound if response.status == 404
-
- response
- end
- end
- end
-end
diff --git a/updater/bin/fetch_files.rb b/updater/bin/fetch_files.rb
index 5479eeec..2c867836 100644
--- a/updater/bin/fetch_files.rb
+++ b/updater/bin/fetch_files.rb
@@ -1,11 +1,15 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
-$LOAD_PATH.unshift(__dir__ + "/../lib")
+require "sorbet-runtime"
+
+$LOAD_PATH.unshift(T.must(__dir__) + "/../lib")
$stdout.sync = true
-require "raven"
+require "dependabot/api_client"
+require "dependabot/environment"
+require "dependabot/service"
require "dependabot/setup"
require "dependabot/file_fetcher_command"
require "debug" if ENV["DEBUG"]
@@ -15,12 +19,21 @@ class UpdaterKilledError < StandardError; end
trap("TERM") do
puts "Received SIGTERM"
error = UpdaterKilledError.new("Updater process killed with SIGTERM")
- tags = { update_job_id: ENV.fetch("DEPENDABOT_JOB_ID", nil) }
- Raven.capture_exception(error, tags: tags)
+ tags = { "gh.dependabot_api.update_job.id": ENV.fetch("DEPENDABOT_JOB_ID", nil) }
+
+ api_client =
+ Dependabot::ApiClient.new(
+ Dependabot::Environment.api_url,
+ Dependabot::Environment.job_id,
+ Dependabot::Environment.job_token
+ )
+ Dependabot::Service.new(client: api_client).capture_exception(error: error, tags: tags)
exit
end
begin
+ RubyVM::YJIT.enable if Dependabot::Environment.job_id.to_i.even?
+
Dependabot::FileFetcherCommand.new.run
rescue Dependabot::RunFailure
exit 1
diff --git a/updater/bin/run.sh b/updater/bin/run.sh
index ffcad007..400ffde0 100755
--- a/updater/bin/run.sh
+++ b/updater/bin/run.sh
@@ -3,7 +3,7 @@ set -e
command="$1"
if [ -z "$command" ]; then
- echo "usage: run [update_script|fetch_files|update_files]"
+ echo "usage: run [fetch_files|update_files|update_script|update_script_vnext]"
exit 1
fi
@@ -13,4 +13,13 @@ export HEX_CACERTS_PATH=/etc/ssl/certs/ca-certificates.crt
# Tell python to use the system-wide CA bundle
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
+# This is a WORKAROUND for fixing various quirks that might exist within the container environment that we don't have much control over.
+# see: https://github.com/ruby/resolv/issues/23 (Ruby)
+# https://github.com/tinglesoftware/dependabot-azure-devops/pull/369 (Ruby)
+# https://github.com/tinglesoftware/dependabot-azure-devops/pull/834 (Ruby)
+# https://github.com/tinglesoftware/dependabot-azure-devops/issues/921#issuecomment-2162273558 (NuGet)
+if [ -n "$WORKAROUND_CMD" ]; then
+ eval "$WORKAROUND_CMD"
+fi
+
bundle exec ruby "bin/${command}.rb"
diff --git a/updater/bin/update_files.rb b/updater/bin/update_files.rb
index e47e8b9a..e2a7fe1d 100644
--- a/updater/bin/update_files.rb
+++ b/updater/bin/update_files.rb
@@ -1,27 +1,50 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
$LOAD_PATH.unshift(__dir__ + "/../lib")
$stdout.sync = true
-require "raven"
+require "dependabot/api_client"
+require "dependabot/environment"
+require "dependabot/service"
require "dependabot/setup"
require "dependabot/update_files_command"
require "debug" if ENV["DEBUG"]
+flamegraph = ENV.fetch("FLAMEGRAPH", nil)
+if flamegraph
+ require "stackprof"
+ require "flamegraph"
+end
+
class UpdaterKilledError < StandardError; end
trap("TERM") do
puts "Received SIGTERM"
error = UpdaterKilledError.new("Updater process killed with SIGTERM")
- tags = { update_job_id: ENV.fetch("DEPENDABOT_JOB_ID", nil) }
- Raven.capture_exception(error, tags: tags)
+ tags = { "gh.dependabot_api.update_job.id": ENV.fetch("DEPENDABOT_JOB_ID", nil) }
+
+ api_client =
+ Dependabot::ApiClient.new(
+ Dependabot::Environment.api_url,
+ Dependabot::Environment.job_id,
+ Dependabot::Environment.job_token
+ )
+ Dependabot::Service.new(client: api_client).capture_exception(error: error, tags: tags)
exit
end
begin
- Dependabot::UpdateFilesCommand.new.run
+ RubyVM::YJIT.enable if Dependabot::Environment.job_id.to_i.even?
+
+ if flamegraph
+ Flamegraph.generate("/tmp/dependabot-flamegraph.html") do
+ Dependabot::UpdateFilesCommand.new.run
+ end
+ else
+ Dependabot::UpdateFilesCommand.new.run
+ end
rescue Dependabot::RunFailure
exit 1
end
diff --git a/updater/bin/update_script.rb b/updater/bin/update_script.rb
index 424de0e4..096dbc50 100644
--- a/updater/bin/update_script.rb
+++ b/updater/bin/update_script.rb
@@ -1,22 +1,30 @@
+# typed: true
# frozen_string_literal: true
# rubocop:disable Style/GlobalVars
+$LOAD_PATH.unshift(__dir__ + "/../lib")
+
require "json"
require "logger"
require "dependabot/logger"
+require "dependabot/shared_helpers"
+
+# require "git"
Dependabot.logger = Logger.new($stdout)
# ensure logs are output immediately. Useful when running in certain hosts like ContainerGroups
$stdout.sync = true
+require "dependabot/credential"
require "dependabot/file_fetchers"
require "dependabot/file_parsers"
require "dependabot/update_checkers"
require "dependabot/file_updaters"
require "dependabot/pull_request_creator"
require "dependabot/pull_request_updater"
+require "dependabot/requirements_update_strategy"
require "dependabot/simple_instrumentor"
require "dependabot/bundler"
@@ -35,10 +43,16 @@
require "dependabot/python"
require "dependabot/pub"
require "dependabot/swift"
+require "dependabot/devcontainers"
require "dependabot/terraform"
-require_relative "azure_helpers"
-require_relative "vulnerabilities"
+require "tinglesoftware/dependabot/clients/azure"
+require "tinglesoftware/dependabot/vulnerabilities"
+
+# Fixes for NuGet feed auth issues
+# TODO: Remove this once https://github.com/dependabot/dependabot-core/pull/8927 is resolved or auth works natively.
+require "tinglesoftware/azure/artifacts_credential_provider"
+require "tinglesoftware/dependabot/overrides/nuget/nuget_config_credential_helpers"
# These options try to follow the dry-run.rb script.
# https://github.com/dependabot/dependabot-core/blob/main/bin/dry-run.rb
@@ -61,7 +75,7 @@
reviewers: nil, # nil instead of empty array to avoid API rejection
assignees: nil, # nil instead of empty array to avoid API rejection
branch_name_separator: ENV["DEPENDABOT_BRANCH_NAME_SEPARATOR"] || "/", # Separator used for created branches.
- milestone: ENV["DEPENDABOT_MILESTONE"] || nil, # Get the work item to attach
+ milestone: ENV["DEPENDABOT_MILESTONE"].to_i || nil, # Get the work item to attach
vendor_dependencies: ENV["DEPENDABOT_VENDOR"] == "true",
repo_contents_path: ENV["DEPENDABOT_REPO_CONTENTS_PATH"] || nil,
updater_options: {},
@@ -76,7 +90,7 @@
# See description of requirements here:
# https://github.com/dependabot/dependabot-core/issues/600#issuecomment-407808103
# https://github.com/wemake-services/kira-dependencies/pull/210
- excluded_requirements: ENV["DEPENDABOT_EXCLUDE_REQUIREMENTS_TO_UNLOCK"]&.split(" ")&.map(&:to_sym) || [],
+ excluded_requirements: ENV["DEPENDABOT_EXCLUDE_REQUIREMENTS_TO_UNLOCK"]&.split&.map(&:to_sym) || [],
# Details on the location of the repository
azure_organization: ENV.fetch("AZURE_ORGANIZATION", nil),
@@ -95,7 +109,10 @@
# Automatic Approval
auto_approve_pr: ENV["AZURE_AUTO_APPROVE_PR"] == "true",
- auto_approve_user_token: ENV["AZURE_AUTO_APPROVE_USER_TOKEN"] || ENV.fetch("AZURE_ACCESS_TOKEN", nil)
+ auto_approve_user_token: ENV["AZURE_AUTO_APPROVE_USER_TOKEN"] || ENV.fetch("AZURE_ACCESS_TOKEN", nil),
+
+ # Details on SSL checks
+ excon_ssl_verify_peer: ENV["EXCON_SSL_VERIFY_PEER"] == "true"
}
# Name of the package manager you'd like to do the update for. Options are:
@@ -138,30 +155,33 @@
# Add GitHub Access Token (PAT) to avoid rate limiting, #
# Setup extra credentials #
########################################################
-$options[:credentials] << {
+$options[:credentials] << Dependabot::Credential.new({
"type" => "git_source",
"host" => $options[:azure_hostname],
- "username" => ENV["AZURE_ACCESS_USERNAME"] || "x-access-token",
- "password" => ENV.fetch("AZURE_ACCESS_TOKEN", nil)
-}
+ "token" => ENV.fetch("AZURE_ACCESS_TOKEN", "test")
+})
$vulnerabilities_fetcher = nil
unless ENV["GITHUB_ACCESS_TOKEN"].to_s.strip.empty?
puts "GitHub access token has been provided."
github_token = ENV.fetch("GITHUB_ACCESS_TOKEN", nil) # A GitHub access token with read access to public repos
- $options[:credentials] << {
+ $options[:credentials] << Dependabot::Credential.new({
"type" => "git_source",
"host" => "github.com",
"username" => "x-access-token",
"password" => github_token
- }
+ })
$vulnerabilities_fetcher =
- Dependabot::Vulnerabilities::Fetcher.new($package_manager, github_token)
+ TingleSoftware::Dependabot::Vulnerabilities::Fetcher.new($package_manager, github_token)
end
# DEPENDABOT_EXTRA_CREDENTIALS, for example:
# "[{\"type\":\"npm_registry\",\"registry\":\"registry.npmjs.org\",\"token\":\"123\"}]"
unless ENV["DEPENDABOT_EXTRA_CREDENTIALS"].to_s.strip.empty?
- $options[:credentials] += JSON.parse(ENV.fetch("DEPENDABOT_EXTRA_CREDENTIALS", nil))
+ $options[:credentials].concat(
+ JSON.parse(ENV.fetch("DEPENDABOT_EXTRA_CREDENTIALS", nil)).map do |cred|
+ Dependabot::Credential.new(cred)
+ end
+ )
end
##########################################
@@ -171,32 +191,35 @@
unless ENV["DEPENDABOT_VERSIONING_STRATEGY"].to_s.strip.empty?
# [Hash]
VERSIONING_STRATEGIES = {
- "auto" => :auto,
- "lockfile-only" => :lockfile_only,
- "widen" => :widen_ranges,
- "increase" => :bump_versions,
- "increase-if-necessary" => :bump_versions_if_necessary
+ "lockfile-only" => Dependabot::RequirementsUpdateStrategy::LockfileOnly,
+ "widen" => Dependabot::RequirementsUpdateStrategy::WidenRanges,
+ "increase" => Dependabot::RequirementsUpdateStrategy::BumpVersions,
+ "increase-if-necessary" => Dependabot::RequirementsUpdateStrategy::BumpVersionsIfNecessary
}.freeze
- strategy_raw = ENV["DEPENDABOT_VERSIONING_STRATEGY"] || "auto"
- $options[:requirements_update_strategy] = VERSIONING_STRATEGIES.fetch(strategy_raw)
+ strategy_raw = ENV.fetch("DEPENDABOT_VERSIONING_STRATEGY", nil)
+ $options[:requirements_update_strategy] = case strategy_raw
+ when nil then nil
+ when "auto" then nil
+ else VERSIONING_STRATEGIES.fetch(strategy_raw)
+ end
# For npm_and_yarn & composer, we must correct the strategy to one allowed
# https://github.com/dependabot/dependabot-core/blob/5ec858331d11253a30aa15fab25ae22fbdecdee0/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker/requirements_updater.rb#L18-L19
# https://github.com/dependabot/dependabot-core/blob/5926b243b2875ad0d8c0a52c09210c4f5f274c5e/composer/lib/dependabot/composer/update_checker/requirements_updater.rb#L23-L24
if $package_manager == "npm_and_yarn" || $package_manager == "composer"
strategy = $options[:requirements_update_strategy]
- $options[:requirements_update_strategy] = :bump_versions if strategy == :auto || strategy == :lockfile_only
+ if strategy.nil? || strategy == Dependabot::RequirementsUpdateStrategy::LockfileOnly
+ $options[:requirements_update_strategy] = Dependabot::RequirementsUpdateStrategy::BumpVersions
+ end
end
# For pub, we also correct the strategy
# https://github.com/dependabot/dependabot-core/blob/ca9f236591ba49fa6e2a8d5f06e538614033a628/pub/lib/dependabot/pub/update_checker.rb#L110
if $package_manager == "pub"
strategy = $options[:requirements_update_strategy]
- $options[:requirements_update_strategy] = case strategy
- when :auto then nil
- when :lockfile_only then "bump_versions"
- else strategy.to_s
- end
+ if strategy == Dependabot::RequirementsUpdateStrategy::LockfileOnly
+ $options[:requirements_update_strategy] = Dependabot::RequirementsUpdateStrategy::BumpVersions
+ end
end
end
@@ -213,17 +236,21 @@
# [Hash] handlers for type allow rules
TYPE_HANDLERS = {
"all" => proc { true },
- "direct" => proc { |dep| dep.top_level? },
+ "direct" => proc(&:top_level?),
"indirect" => proc { |dep| !dep.top_level? },
- "production" => proc { |dep| dep.production? },
+ "production" => proc(&:production?),
"development" => proc { |dep| !dep.production? },
"security" => proc { |_, checker| checker.vulnerable? }
}.freeze
def allow_conditions_for(dep)
# Find where the name matches then get the type e.g. production, direct, etc
- found = $options[:allow_conditions].find { |al| dep.name.match?(al["dependency-name"]) }
- found ? found["dependency-type"] : "all" # when not specified, allow all types
+ condition = $options[:allow_conditions].find do |al|
+ Dependabot::Config::UpdateConfig.wildcard_match?(al["dependency-name"] || "*", dep.name)
+ end
+ return nil unless condition
+
+ condition["dependency-type"] || "all" # when not specified, allow all types
end
#################################################################
@@ -448,16 +475,17 @@ def show_diff(original_file, updated_file)
raise StandardError, "Security updates are enabled but a GitHub token is not supplied! Cannot proceed"
end
-Excon.defaults[:ssl_verify_peer] = false
+Excon.defaults[:ssl_verify_peer] = $options[:excon_ssl_verify_peer]
####################################################
# Setup the hostname, protocol and port to be used #
####################################################
-$options[:azure_port] = ""
-# ENV["AZURE_PORT"] || ($options[:azure_protocol] == "http" ? "80" : "443")
-$api_endpoint = "#{$options[:azure_protocol]}://#{$options[:azure_hostname]}/"
-# $api_endpoint = "#{$options[:azure_protocol]}://#{$options[:azure_hostname]}:#{$options[:azure_port]}/"
+$options[:azure_port] = ENV["AZURE_PORT"] || ($options[:azure_protocol] == "http" ? "80" : "443")
+$api_endpoint = "#{$options[:azure_protocol]}://#{$options[:azure_hostname]}:#{$options[:azure_port]}/"
+$hostname = "#{$options[:azure_hostname]}:#{$options[:azure_port]}"
+
unless $options[:azure_virtual_directory].empty?
$api_endpoint = $api_endpoint + "#{$options[:azure_virtual_directory]}/"
+ $hostname = $hostname + "/#{$options[:azure_virtual_directory]}"
end
# Full name of the repo targeted.
$repo_name = "#{$options[:azure_organization]}/#{$options[:azure_project]}/_git/#{$options[:azure_repository]}"
@@ -465,10 +493,11 @@ def show_diff(original_file, updated_file)
puts "Pull Requests shall be linked to milestone (work item) #{$options[:milestone]}" if $options[:milestone]
puts "Pull Requests shall be labeled #{$options[:custom_labels]}" if $options[:custom_labels]
puts "Working in #{$repo_name}, '#{$options[:branch] || 'default'}' branch under '#{$options[:directory]}' directory"
+puts "hostname '#{$hostname}'"
$source = Dependabot::Source.new(
provider: $options[:provider],
- hostname: $options[:azure_hostname],
+ hostname: $hostname,
api_endpoint: $api_endpoint,
repo: $repo_name,
directory: $options[:directory],
@@ -488,38 +517,26 @@ def show_diff(original_file, updated_file)
##############################
# Fetch the dependency files #
##############################
-puts "Fetching #{$package_manager} dependency files for #{$repo_name}"
-fetcher = Dependabot::FileFetchers.for_package_manager($package_manager).new(
+clone = true
+$options[:repo_contents_path] ||= File.expand_path(File.join("tmp", $repo_name.split("/"))) if clone
+fetcher_args = {
source: $source,
credentials: $options[:credentials],
repo_contents_path: $options[:repo_contents_path],
options: $options[:updater_options]
-)
+}
+fetcher = Dependabot::FileFetchers.for_package_manager($package_manager).new(**fetcher_args)
+if clone
+ fetcher.clone_repo_contents
+else
+ puts "Fetching #{$package_manager} dependency files ..."
+end
files = fetcher.files
commit = fetcher.commit
-# clone = $options[:vendor_dependencies] || Dependabot::Utils.always_clone_for_package_manager?($package_manager)
-# $options[:repo_contents_path] ||= File.expand_path(File.join("tmp", $repo_name.split("/"))) if clone
-# fetcher_args = {
-# source: $source,
-# credentials: $options[:credentials],
-# repo_contents_path: $options[:repo_contents_path],
-# options: $options[:updater_options]
-# }
-# fetcher = Dependabot::FileFetchers.for_package_manager($package_manager).new(**fetcher_args)
-
-# if clone
-# puts "Cloning repository into #{$options[:repo_contents_path]}"
-# fetcher.clone_repo_contents
-# else
-# puts "Fetching #{$package_manager} dependency files ..."
-# end
-# files = fetcher.files
-# commit = fetcher.commit
puts "Found #{files.length} dependency file(s) at commit #{commit}"
files.each { |f| puts " - #{f.path}" }
-
##############################
# Parse the dependency files #
##############################
@@ -536,17 +553,16 @@ def show_diff(original_file, updated_file)
dependencies = parser.parse
puts "Found #{dependencies.count(&:top_level?)} dependencies"
dependencies.select(&:top_level?).each { |d| puts " - #{d.name} (#{d.version})" }
-
################################################
# Get active pull requests for this repository #
################################################
-azure_client = Dependabot::Clients::Azure.for_source(
+azure_client = TingleSoftware::Dependabot::Clients::Azure.for_source(
source: $source,
credentials: $options[:credentials]
)
user_id = azure_client.get_user_id
target_branch_name = $options[:branch] || azure_client.fetch_default_branch($source.repo)
-active_pull_requests = azure_client.pull_requests_active(user_id, target_branch_name)
+active_pull_requests = azure_client.pull_requests_active_for_user_and_targeting_branch(user_id, target_branch_name)
pull_requests_count = 0
@@ -580,10 +596,15 @@ def show_diff(original_file, updated_file)
# For vulnerable dependencies
if checker.vulnerable?
- if checker.lowest_security_fix_version
- puts "#{dep.name} #{dep.version} is vulnerable. Earliest non-vulnerable is " \
- "#{checker.lowest_security_fix_version}"
- else
+ begin
+ lowest_security_fix_version = checker.lowest_resolvable_security_fix_version
+ if lowest_security_fix_version
+ puts "#{dep.name} #{dep.version} is vulnerable. Earliest non-vulnerable is #{lowest_security_fix_version}"
+ else
+ puts "#{dep.name} #{dep.version} is vulnerable. Can't find non-vulnerable version. đ¨"
+ end
+ rescue TypeError => e
+ # Handle the type error, which occurs when the return type is not as expected
puts "#{dep.name} #{dep.version} is vulnerable. Can't find non-vulnerable version. đ¨"
end
end
@@ -790,6 +811,7 @@ def show_diff(original_file, updated_file)
end
pull_request_id = nil
+ created_or_updated = false
if conflict_pull_request_commit && conflict_pull_request_id
##############################################
# Update pull request with conflict resolved #
@@ -808,6 +830,8 @@ def show_diff(original_file, updated_file)
pr_updater.update
pull_request = existing_pull_request
pull_request_id = conflict_pull_request_id
+
+ created_or_updated = true
elsif !existing_pull_request # Only create PR if there is none existing
########################################
# Create a pull request for the update #
@@ -853,6 +877,8 @@ def show_diff(original_file, updated_file)
else
puts "Seems PR is already present."
end
+
+ created_or_updated = true
else
pull_request = existing_pull_request # One already existed
pull_request_id = pull_request["pullRequestId"]
@@ -863,7 +889,7 @@ def show_diff(original_file, updated_file)
next unless pull_request_id
# Auto approve this Pull Request
- if $options[:auto_approve_pr]
+ if $options[:auto_approve_pr] && created_or_updated
puts "Auto Approving PR #{pull_request_id}"
azure_client.pull_request_approve(
@@ -887,7 +913,7 @@ def show_diff(original_file, updated_file)
# - [Changelog](....)
# - [Commits](....)
merge_commit_message = "Merged PR #{pull_request_id}: #{msg.pr_name}\n\n#{msg.commit_message}"
- if $options[:set_auto_complete]
+ if $options[:set_auto_complete] && created_or_updated
auto_complete_user_id = pull_request["createdBy"]["id"]
puts "Setting auto complete on ##{pull_request_id}."
azure_client.autocomplete_pull_request(
@@ -913,7 +939,7 @@ def show_diff(original_file, updated_file)
# look for pull requests that are no longer needed to be abandoned
if $options[:close_unwanted]
puts "Looking for pull requests that are no longer needed."
- active_pull_requests = azure_client.pull_requests_active(user_id, target_branch_name)
+ active_pull_requests = azure_client.pull_requests_active_for_user_and_targeting_branch(user_id, target_branch_name)
active_pull_requests.each do |pr|
pr_id = pr["pullRequestId"]
title = pr["title"]
diff --git a/updater/bin/update_script_vnext.rb b/updater/bin/update_script_vnext.rb
new file mode 100644
index 00000000..36b10c8e
--- /dev/null
+++ b/updater/bin/update_script_vnext.rb
@@ -0,0 +1,29 @@
+# typed: strict
+# frozen_string_literal: true
+
+$LOAD_PATH.unshift(__dir__ + "/../lib")
+
+# Ensure logs are output immediately. Useful when running in certain hosts like ContainerGroups
+$stdout.sync = true
+
+require "tinglesoftware/dependabot/setup"
+require "tinglesoftware/dependabot/job"
+require "tinglesoftware/dependabot/commands/update_all_dependencies_synchronous_command"
+
+ENV["UPDATER_ONE_CONTAINER"] = "true" # The full end-to-end update will happen in a single container
+ENV["UPDATER_DETERMINISTIC"] = "true" # The list of dependencies to update will be consistent across multiple runs
+
+begin
+ TingleSoftware::Dependabot::Commands::UpdateAllDependenciesSynchronousCommand.new(
+ job: TingleSoftware::Dependabot::Job.new(
+ # Override Dependabot updater options (feature flags) required by this job
+ experiments: {
+ # Required for correctly detecting existing PRs when refreshing group dependency updates.
+ # Without this, Dependabot::DependencyGroup.matches_existing_pr? will always return false for group updates.
+ "dependency_has_directory" => true
+ }
+ )
+ ).run
+rescue ::Dependabot::RunFailure
+ exit 1
+end
diff --git a/updater/bin/vulnerabilities.rb b/updater/bin/vulnerabilities.rb
deleted file mode 100644
index 950b773c..00000000
--- a/updater/bin/vulnerabilities.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-require "octokit"
-
-module Dependabot
- module Vulnerabilities
- class Fetcher
- class QueryError < StandardError; end
-
- ECOSYSTEM_LOOKUP = {
- "github_actions" => "ACTIONS",
- "composer" => "COMPOSER",
- "elm" => "ERLANG",
- "go_modules" => "GO",
- "maven" => "MAVEN",
- "npm_and_yarn" => "NPM",
- "nuget" => "NUGET",
- "pip" => "PIP",
- "pub" => "PUB",
- "bundler" => "RUBYGEMS",
- "cargo" => "RUST"
- }.freeze
-
- GRAPHQL_QUERY = <<-GRAPHQL
- query($ecosystem: SecurityAdvisoryEcosystem, $package: String) {
- securityVulnerabilities(first: 100, ecosystem: $ecosystem, package: $package) {
- nodes {
- firstPatchedVersion {
- identifier
- }
- vulnerableVersionRange
- }
- }
- }
- GRAPHQL
-
- def initialize(package_manager, github_token)
- @ecosystem = ECOSYSTEM_LOOKUP.fetch(package_manager, nil)
- @client ||= Octokit::Client.new(access_token: github_token)
- end
-
- def fetch(dependency_name)
- [] unless @ecosystem
-
- variables = { ecosystem: @ecosystem, package: dependency_name }
- response = @client.post "/graphql", { query: GRAPHQL_QUERY, variables: variables }.to_json
- raise(QueryError, response[:errors]&.map(&:message)&.join(", ")) if response[:errors]
-
- vulnerabilities = []
- response.data[:securityVulnerabilities][:nodes].map do |node|
- vulnerable_version_range = node[:vulnerableVersionRange]
- first_patched_version = node.dig :firstPatchedVersion, :identifier
- vulnerabilities << {
- "dependency-name" => dependency_name,
- "affected-versions" => [vulnerable_version_range],
- "patched-versions" => [first_patched_version],
- "unaffected-versions" => []
- }
- end
-
- vulnerabilities
- end
- end
- end
-end
diff --git a/updater/config/.npmrc b/updater/config/.npmrc
deleted file mode 100644
index 601d250c..00000000
--- a/updater/config/.npmrc
+++ /dev/null
@@ -1,20 +0,0 @@
-# TODO: Remove these hacks once we've deprecated npm 6 support as it no longer
-# spwans a child process to npm install git dependencies.
-
-# Only set our custom CA cert for npm because the system ca's + our custom ca
-# causes npm to blow up when installing git dependencies (E2BIG exception). This
-# happens because the ca-file contents are passed as a cli argument to npm
-# install from npm/cli/lib/pack.js as --ca="contents of ca file" - "ca" is
-# populated automatically by npm when setting "--cafile" and passed through in
-# when spawning the cli to install git dependencies.
-cafile=/usr/local/share/ca-certificates/dbot-ca.crt
-# Because npm doesn't pass through all npm config when doing git installs in
-# npm/cli/lib/pack.js we also need to disable audit here to prevent npm from
-# auditing git dependencies, we do this to sped up installs
-audit=false
-# Similarly, dry-run and ignore-scripts are also not passed through when doing
-# git installs in npm/cli/lib/pack.js so we set dry-run and ignore-scripts to
-# prevent any lifecycle hooks for git installs. dry-run disables "prepare" and
-# "prepack" scripts, ignore-scripts disables all other scripts
-dry-run=true
-ignore-scripts=true
diff --git a/updater/config/.yarnrc b/updater/config/.yarnrc
deleted file mode 100644
index 65545853..00000000
--- a/updater/config/.yarnrc
+++ /dev/null
@@ -1,6 +0,0 @@
-# TODO: Remove these hacks once we've deprecated npm 6 support as it no longer
-# spwans a child process to npm install git dependencies.
-# yarn lockfile v1
-
-# Tell yarn to use the system-wide CA bundle overriding the .npmrc cafile
-cafile "/etc/ssl/certs/ca-certificates.crt"
diff --git a/updater/lib/dependabot/api_client.rb b/updater/lib/dependabot/api_client.rb
index 878a23c2..65be0c1d 100644
--- a/updater/lib/dependabot/api_client.rb
+++ b/updater/lib/dependabot/api_client.rb
@@ -1,8 +1,10 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
require "http"
require "dependabot/job"
+require "dependabot/opentelemetry"
+require "sorbet-runtime"
# Provides a client to access the internal Dependabot Service's API
#
@@ -17,6 +19,9 @@ module Dependabot
class ApiError < StandardError; end
class ApiClient
+ extend T::Sig
+
+ sig { params(base_url: String, job_id: T.any(String, Integer), job_token: String).void }
def initialize(base_url, job_id, job_token)
@base_url = base_url
@job_id = job_id
@@ -24,164 +29,236 @@ def initialize(base_url, job_id, job_token)
end
# TODO: Make `base_commit_sha` part of Dependabot::DependencyChange
+ sig { params(dependency_change: Dependabot::DependencyChange, base_commit_sha: String).void }
def create_pull_request(dependency_change, base_commit_sha)
- api_url = "#{base_url}/update_jobs/#{job_id}/create_pull_request"
- data = create_pull_request_data(dependency_change, base_commit_sha)
- response = http_client.post(api_url, json: { data: data })
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ ::Dependabot::OpenTelemetry.tracer.in_span("create_pull_request", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::BASE_COMMIT_SHA, base_commit_sha)
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::DEPENDENCY_NAMES, dependency_change.humanized)
+
+ api_url = "#{base_url}/update_jobs/#{job_id}/create_pull_request"
+ data = create_pull_request_data(dependency_change, base_commit_sha)
+ response = http_client.post(api_url, json: { data: data })
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
# TODO: Make `base_commit_sha` part of Dependabot::DependencyChange
# TODO: Determine if we should regenerate the PR message within core for updates
+ sig { params(dependency_change: Dependabot::DependencyChange, base_commit_sha: String).void }
def update_pull_request(dependency_change, base_commit_sha)
- api_url = "#{base_url}/update_jobs/#{job_id}/update_pull_request"
- body = {
- data: {
- "dependency-names": dependency_change.updated_dependencies.map(&:name),
- "updated-dependency-files": dependency_change.updated_dependency_files_hash,
- "base-commit-sha": base_commit_sha
+ ::Dependabot::OpenTelemetry.tracer.in_span("update_pull_request", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::BASE_COMMIT_SHA, base_commit_sha)
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::DEPENDENCY_NAMES, dependency_change.humanized)
+
+ api_url = "#{base_url}/update_jobs/#{job_id}/update_pull_request"
+ body = {
+ data: {
+ "dependency-names": dependency_change.updated_dependencies.map(&:name),
+ "updated-dependency-files": dependency_change.updated_dependency_files_hash,
+ "base-commit-sha": base_commit_sha
+ }
}
- }
- response = http_client.post(api_url, json: body)
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ response = http_client.post(api_url, json: body)
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
- def close_pull_request(dependency_name, reason)
- api_url = "#{base_url}/update_jobs/#{job_id}/close_pull_request"
- body = { data: { "dependency-names": dependency_name, reason: reason } }
- response = http_client.post(api_url, json: body)
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ sig { params(dependency_names: T.any(String, T::Array[String]), reason: T.any(String, Symbol)).void }
+ def close_pull_request(dependency_names, reason)
+ ::Dependabot::OpenTelemetry.tracer.in_span("close_pull_request", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::PR_CLOSE_REASON, reason.to_s)
+
+ api_url = "#{base_url}/update_jobs/#{job_id}/close_pull_request"
+ body = { data: { "dependency-names": dependency_names, reason: reason } }
+ response = http_client.post(api_url, json: body)
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
+ sig { params(error_type: T.any(String, Symbol), error_details: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def record_update_job_error(error_type:, error_details:)
- api_url = "#{base_url}/update_jobs/#{job_id}/record_update_job_error"
- body = {
- data: {
- "error-type": error_type,
- "error-details": error_details
+ ::Dependabot::OpenTelemetry.tracer.in_span("record_update_job_error", kind: :internal) do |_span|
+ ::Dependabot::OpenTelemetry.record_update_job_error(job_id: job_id, error_type: error_type,
+ error_details: error_details)
+ api_url = "#{base_url}/update_jobs/#{job_id}/record_update_job_error"
+ body = {
+ data: {
+ "error-type": error_type,
+ "error-details": error_details
+ }
}
- }
- response = http_client.post(api_url, json: body)
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ response = http_client.post(api_url, json: body)
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
- def record_update_job_unknown_error(error_type: "unknown_error", error_details:)
- api_url = "#{base_url}/update_jobs/#{job_id}/record_update_job_unknown_error"
- body = {
- data: {
- "error-type": error_type,
- "error-details": error_details
+ sig { params(error_type: T.any(Symbol, String), error_details: T.nilable(T::Hash[T.untyped, T.untyped])).void }
+ def record_update_job_unknown_error(error_type:, error_details:)
+ error_type = "unknown_error" if error_type.nil?
+ ::Dependabot::OpenTelemetry.tracer.in_span("record_update_job_unknown_error", kind: :internal) do |_span|
+ ::Dependabot::OpenTelemetry.record_update_job_error(job_id: job_id, error_type: error_type,
+ error_details: error_details)
+
+ api_url = "#{base_url}/update_jobs/#{job_id}/record_update_job_unknown_error"
+ body = {
+ data: {
+ "error-type": error_type,
+ "error-details": error_details
+ }
}
- }
- response = http_client.post(api_url, json: body)
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ response = http_client.post(api_url, json: body)
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
+ sig { params(base_commit_sha: String).void }
def mark_job_as_processed(base_commit_sha)
- api_url = "#{base_url}/update_jobs/#{job_id}/mark_as_processed"
- body = { data: { "base-commit-sha": base_commit_sha } }
- response = http_client.patch(api_url, json: body)
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ ::Dependabot::OpenTelemetry.tracer.in_span("mark_job_as_processed", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::BASE_COMMIT_SHA, base_commit_sha)
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+
+ api_url = "#{base_url}/update_jobs/#{job_id}/mark_as_processed"
+ body = { data: { "base-commit-sha": base_commit_sha } }
+ response = http_client.patch(api_url, json: body)
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
+ sig { params(dependencies: T::Array[T::Hash[Symbol, T.untyped]], dependency_files: T::Array[String]).void }
def update_dependency_list(dependencies, dependency_files)
- api_url = "#{base_url}/update_jobs/#{job_id}/update_dependency_list"
- body = {
- data: {
- dependencies: dependencies,
- dependency_files: dependency_files
+ ::Dependabot::OpenTelemetry.tracer.in_span("update_dependency_list", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+
+ api_url = "#{base_url}/update_jobs/#{job_id}/update_dependency_list"
+ body = {
+ data: {
+ dependencies: dependencies,
+ dependency_files: dependency_files
+ }
}
- }
- response = http_client.post(api_url, json: body)
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ response = http_client.post(api_url, json: body)
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
+ sig { params(ecosystem_versions: T::Hash[Symbol, T.untyped]).void }
def record_ecosystem_versions(ecosystem_versions)
- api_url = "#{base_url}/update_jobs/#{job_id}/record_ecosystem_versions"
- body = {
- data: { ecosystem_versions: ecosystem_versions }
- }
- response = http_client.post(api_url, json: body)
- raise ApiError, response.body if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- retry_count ||= 0
- retry_count += 1
- raise if retry_count > 3
-
- sleep(rand(3.0..10.0)) && retry
+ ::Dependabot::OpenTelemetry.tracer.in_span("record_ecosystem_versions", kind: :internal) do |_span|
+ api_url = "#{base_url}/update_jobs/#{job_id}/record_ecosystem_versions"
+ body = {
+ data: { ecosystem_versions: ecosystem_versions }
+ }
+ response = http_client.post(api_url, json: body)
+ raise ApiError, response.body if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ retry_count ||= 0
+ retry_count += 1
+ raise if retry_count > 3
+
+ sleep(rand(3.0..10.0))
+ retry
+ end
end
+ sig { params(metric: String, tags: T::Hash[String, String]).void }
def increment_metric(metric, tags:)
- api_url = "#{base_url}/update_jobs/#{job_id}/increment_metric"
- body = {
- data: {
- metric: metric,
- tags: tags
+ ::Dependabot::OpenTelemetry.tracer.in_span("increment_metric", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID.to_s, job_id.to_s)
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::METRIC.to_s, metric)
+ tags.each do |key, value|
+ span.set_attribute(key.to_s, value.to_s)
+ end
+
+ api_url = "#{base_url}/update_jobs/#{job_id}/increment_metric"
+ body = {
+ data: {
+ metric: metric,
+ tags: tags
+ }
}
- }
- response = http_client.post(api_url, json: body)
- # We treat metrics as fire-and-forget, so just warn if they fail.
- Dependabot.logger.debug("Unable to report metric '#{metric}'.") if response.code >= 400
- rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
- Dependabot.logger.debug("Unable to report metric '#{metric}'.")
+ response = http_client.post(api_url, json: body)
+ # We treat metrics as fire-and-forget, so just warn if they fail.
+ Dependabot.logger.debug("Unable to report metric '#{metric}'.") if response.code >= 400
+ rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError
+ Dependabot.logger.debug("Unable to report metric '#{metric}'.")
+ end
end
private
- attr_reader :base_url, :job_id, :job_token
+ sig { returns(String) }
+ attr_reader :base_url
+
+ sig { returns(T.any(String, Integer)) }
+ attr_reader :job_id
+ sig { returns(String) }
+ attr_reader :job_token
+
+ sig { returns(T.untyped) }
def http_client
- client = HTTP.auth(job_token)
- proxy = ENV["HTTPS_PROXY"] ? URI(ENV["HTTPS_PROXY"]) : URI(base_url).find_proxy
+ client = HTTP::Client.new.auth(job_token)
+ proxy = ENV["HTTPS_PROXY"] ? URI(T.must(ENV["HTTPS_PROXY"])) : URI(base_url).find_proxy
unless proxy.nil?
- args = [proxy.host, proxy.port, proxy.user, proxy.password].compact
+ args = T.unsafe([proxy.host, proxy.port, proxy.user, proxy.password].compact)
client = client.via(*args)
end
client
end
+ sig { params(dependency_change: Dependabot::DependencyChange).returns(T::Hash[String, T.untyped]) }
def dependency_group_hash(dependency_change)
return {} unless dependency_change.grouped_update?
@@ -191,6 +268,10 @@ def dependency_group_hash(dependency_change)
{ "dependency-group": dependency_change.dependency_group.to_h }.compact
end
+ sig do
+ params(dependency_change: Dependabot::DependencyChange,
+ base_commit_sha: String).returns(T::Hash[String, T.untyped])
+ end
def create_pull_request_data(dependency_change, base_commit_sha)
data = {
dependencies: dependency_change.updated_dependencies.map do |dep|
@@ -198,7 +279,8 @@ def create_pull_request_data(dependency_change, base_commit_sha)
name: dep.name,
"previous-version": dep.previous_version,
requirements: dep.requirements,
- "previous-requirements": dep.previous_requirements
+ "previous-requirements": dep.previous_requirements,
+ directory: dep.directory
}.merge({
version: dep.version,
removed: dep.removed? ? true : nil
@@ -208,8 +290,6 @@ def create_pull_request_data(dependency_change, base_commit_sha)
"base-commit-sha": base_commit_sha
}.merge(dependency_group_hash(dependency_change))
- return data unless dependency_change.pr_message
-
data["commit-message"] = dependency_change.pr_message.commit_message
data["pr-title"] = dependency_change.pr_message.pr_name
data["pr-body"] = dependency_change.pr_message.pr_message
diff --git a/updater/lib/dependabot/base_command.rb b/updater/lib/dependabot/base_command.rb
index 7ad8063d..df6c20ff 100644
--- a/updater/lib/dependabot/base_command.rb
+++ b/updater/lib/dependabot/base_command.rb
@@ -1,27 +1,11 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
-require "raven"
require "dependabot/api_client"
+require "dependabot/errors"
require "dependabot/service"
require "dependabot/logger"
require "dependabot/logger/formats"
-require "dependabot/python"
-require "dependabot/terraform"
-require "dependabot/elm"
-require "dependabot/docker"
-require "dependabot/git_submodules"
-require "dependabot/github_actions"
-require "dependabot/composer"
-require "dependabot/nuget"
-require "dependabot/gradle"
-require "dependabot/maven"
-require "dependabot/hex"
-require "dependabot/cargo"
-require "dependabot/go_modules"
-require "dependabot/npm_and_yarn"
-require "dependabot/bundler"
-require "dependabot/pub"
require "dependabot/environment"
module Dependabot
@@ -56,6 +40,8 @@ def run
handle_exception(e)
service.mark_job_as_processed(base_commit_sha)
ensure
+ # Ensure that we shut down the open telemetry exporter.
+ ::Dependabot::OpenTelemetry.shutdown
Dependabot.logger.formatter = Dependabot::Logger::BasicFormatter.new
Dependabot.logger.info(service.summary) unless service.noop?
raise Dependabot::RunFailure if Dependabot::Environment.github_actions? && service.failure?
@@ -72,13 +58,14 @@ def handle_exception(err)
def handle_unknown_error(err)
error_details = {
- "error-class" => err.class.to_s,
- "error-message" => err.message,
- "error-backtrace" => err.backtrace.join("\n"),
- "package-manager" => job.package_manager,
- "job-id" => job.id,
- "job-dependencies" => job.dependencies,
- "job-dependency_group" => job.dependency_groups
+ ErrorAttributes::CLASS => err.class.to_s,
+ ErrorAttributes::MESSAGE => err.message,
+ ErrorAttributes::BACKTRACE => err.backtrace.join("\n"),
+ ErrorAttributes::FINGERPRINT => err.respond_to?(:sentry_context) ? err.sentry_context[:fingerprint] : nil,
+ ErrorAttributes::PACKAGE_MANAGER => job.package_manager,
+ ErrorAttributes::JOB_ID => job.id,
+ ErrorAttributes::DEPENDENCIES => job.dependencies,
+ ErrorAttributes::DEPENDENCY_GROUPS => job.dependency_groups
}.compact
service.record_update_job_unknown_error(error_type: "updater_error", error_details: error_details)
service.increment_metric("updater.update_job_unknown_error", tags: {
diff --git a/updater/lib/dependabot/dependency_change.rb b/updater/lib/dependabot/dependency_change.rb
index e9ecea3d..161cbb50 100644
--- a/updater/lib/dependabot/dependency_change.rb
+++ b/updater/lib/dependabot/dependency_change.rb
@@ -1,6 +1,10 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
+require "sorbet-runtime"
+require "dependabot/errors"
+require "dependabot/pull_request_creator/message_builder"
+
# This class describes a change to the project's Dependencies which has been
# determined by a Dependabot operation.
#
@@ -12,19 +16,58 @@
# by adapters to create a Pull Request, apply the changes on disk, etc.
module Dependabot
class DependencyChange
- attr_reader :job, :updated_dependencies, :updated_dependency_files, :dependency_group
+ extend T::Sig
+
+ class InvalidUpdatedDependencies < Dependabot::DependabotError
+ extend T::Sig
+
+ sig { params(deps_no_previous_version: T::Array[String], deps_no_change: T::Array[String]).void }
+ def initialize(deps_no_previous_version:, deps_no_change:)
+ msg = ""
+ if deps_no_previous_version.any?
+ msg += "Previous version was not provided for: '#{deps_no_previous_version.join(', ')}' "
+ end
+ msg += "No requirements change for: '#{deps_no_change.join(', ')}'" if deps_no_change.any?
+
+ super(msg)
+ end
+ end
+
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
+
+ sig { returns(T::Array[Dependabot::Dependency]) }
+ attr_reader :updated_dependencies
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
+ attr_reader :updated_dependency_files
+
+ sig { returns(T.nilable(Dependabot::DependencyGroup)) }
+ attr_reader :dependency_group
+
+ sig do
+ params(
+ job: Dependabot::Job,
+ updated_dependencies: T::Array[Dependabot::Dependency],
+ updated_dependency_files: T::Array[Dependabot::DependencyFile],
+ dependency_group: T.nilable(Dependabot::DependencyGroup)
+ ).void
+ end
def initialize(job:, updated_dependencies:, updated_dependency_files:, dependency_group: nil)
@job = job
@updated_dependencies = updated_dependencies
@updated_dependency_files = updated_dependency_files
@dependency_group = dependency_group
+
+ @pr_message = T.let(nil, T.nilable(Dependabot::PullRequestCreator::Message))
+ ensure_dependencies_have_directories
end
+ sig { returns(Dependabot::PullRequestCreator::Message) }
def pr_message
- return @pr_message if defined?(@pr_message)
+ return @pr_message unless @pr_message.nil?
- case job.source&.provider
+ case job.source.provider
when "github"
pr_message_max_length = Dependabot::PullRequestCreator::Github::PR_DESCRIPTION_MAX_LENGTH
when "azure"
@@ -38,7 +81,7 @@ def pr_message
pr_message_max_length = Dependabot::PullRequestCreator::Github::PR_DESCRIPTION_MAX_LENGTH
end
- @pr_message = Dependabot::PullRequestCreator::MessageBuilder.new(
+ message = Dependabot::PullRequestCreator::MessageBuilder.new(
source: job.source,
dependencies: updated_dependencies,
files: updated_dependency_files,
@@ -49,18 +92,23 @@ def pr_message
pr_message_encoding: pr_message_encoding,
ignore_conditions: job.ignore_conditions
).message
+
+ @pr_message = message
end
+ sig { returns(String) }
def humanized
updated_dependencies.map do |dependency|
"#{dependency.name} ( from #{dependency.humanized_previous_version} to #{dependency.humanized_version} )"
end.join(", ")
end
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
def updated_dependency_files_hash
updated_dependency_files.map(&:to_h)
end
+ sig { returns(T::Boolean) }
def grouped_update?
!!dependency_group
end
@@ -72,43 +120,95 @@ def grouped_update?
# rather than supersede it as the new changes don't necessarily follow
# from the previous ones; dependencies could have been removed from the
# project, or pinned by other changes.
+ sig { returns(T::Boolean) }
def should_replace_existing_pr?
return false unless job.updating_a_pull_request?
# NOTE: Gradle, Maven and Nuget dependency names can be case-insensitive
# and the dependency name injected from a security advisory often doesn't
# match what users have specified in their manifest.
- updated_dependencies.map { |x| x.name.downcase } != job.dependencies.map(&:downcase)
+ updated_dependencies.map { |x| x.name.downcase }.uniq.sort != T.must(job.dependencies).map(&:downcase).uniq.sort
end
- def matches_existing_pr?
- !!existing_pull_request
+ sig { params(dependency_changes: T::Array[DependencyChange]).void }
+ def merge_changes!(dependency_changes)
+ dependency_changes.each do |dependency_change|
+ updated_dependencies.concat(dependency_change.updated_dependencies)
+ updated_dependency_files.concat(dependency_change.updated_dependency_files)
+ end
+ updated_dependencies.compact!
+ updated_dependency_files.compact!
end
- private
+ sig { returns(T::Boolean) }
+ def all_have_previous_version?
+ return true if updated_dependencies.all?(&:requirements_changed?)
+ return true if updated_dependencies.all?(&:previous_version)
- def existing_pull_request
+ false
+ end
+
+ sig { void }
+ def check_dependencies_have_previous_version
+ return if all_have_previous_version?
+
+ deps_no_previous_version = updated_dependencies.reject(&:previous_version)
+ deps_no_change = updated_dependencies.reject(&:requirements_changed?)
+ raise InvalidUpdatedDependencies.new(
+ deps_no_previous_version: deps_no_previous_version.map(&:name),
+ deps_no_change: deps_no_change.map(&:name)
+ )
+ end
+
+ sig { returns(T::Boolean) }
+ def matches_existing_pr?
if grouped_update?
# We only want PRs for the same group that have the same versions
- job.existing_group_pull_requests.find do |pr|
- pr["dependency-group-name"] == dependency_group.name &&
- Set.new(pr["dependencies"]) == updated_dependencies_set
+ job.existing_group_pull_requests.any? do |pr|
+ directories_in_use = pr["dependencies"].all? { |dep| dep["directory"] }
+
+ pr["dependency-group-name"] == dependency_group&.name &&
+ Set.new(pr["dependencies"]) == updated_dependencies_set(should_consider_directory: directories_in_use)
end
else
- job.existing_pull_requests.find { |pr| Set.new(pr) == updated_dependencies_set }
+ job.existing_pull_requests.any? do |pr|
+ directories_in_use = pr.all? { |dep| dep["directory"] }
+
+ Set.new(pr) == updated_dependencies_set(should_consider_directory: directories_in_use)
+ end
end
end
- def updated_dependencies_set
+ private
+
+ # Older PRs will not have a directory key, in that case do not consider directory in the comparison. This will
+ # allow rebases to continue working for those, but for multi-directory configs we do compare with the directory.
+ sig { params(should_consider_directory: T::Boolean).returns(T::Set[T::Hash[String, T.any(String, T::Boolean)]]) }
+ def updated_dependencies_set(should_consider_directory:)
Set.new(
updated_dependencies.map do |dep|
{
"dependency-name" => dep.name,
"dependency-version" => dep.version,
+ "directory" => should_consider_directory ? dep.directory : nil,
"dependency-removed" => dep.removed? ? true : nil
}.compact
end
)
end
+
+ sig { returns(T::Array[Dependabot::Dependency]) }
+ def ensure_dependencies_have_directories
+ updated_dependencies.each do |dep|
+ dep.directory = directory
+ end
+ end
+
+ sig { returns(String) }
+ def directory
+ return "" if updated_dependency_files.empty?
+
+ T.must(updated_dependency_files.first).directory
+ end
end
end
diff --git a/updater/lib/dependabot/dependency_change_builder.rb b/updater/lib/dependabot/dependency_change_builder.rb
index 34ae43bc..6e09dcf2 100644
--- a/updater/lib/dependabot/dependency_change_builder.rb
+++ b/updater/lib/dependabot/dependency_change_builder.rb
@@ -1,6 +1,7 @@
-# typed: false
+# typed: strong
# frozen_string_literal: true
+require "sorbet-runtime"
require "dependabot/dependency"
require "dependabot/dependency_change"
require "dependabot/file_updaters"
@@ -22,19 +23,51 @@
# a DependencyGroup
module Dependabot
class DependencyChangeBuilder
- def self.create_from(**kwargs)
- new(**kwargs).run
+ extend T::Sig
+
+ sig do
+ params(
+ job: Dependabot::Job,
+ dependency_files: T::Array[Dependabot::DependencyFile],
+ updated_dependencies: T::Array[Dependabot::Dependency],
+ change_source: T.any(Dependabot::Dependency, Dependabot::DependencyGroup)
+ ).returns(Dependabot::DependencyChange)
+ end
+ def self.create_from(job:, dependency_files:, updated_dependencies:, change_source:)
+ new(
+ job: job,
+ dependency_files: dependency_files,
+ updated_dependencies: updated_dependencies,
+ change_source: change_source
+ ).run
end
+ sig do
+ params(
+ job: Dependabot::Job,
+ dependency_files: T::Array[Dependabot::DependencyFile],
+ updated_dependencies: T::Array[Dependabot::Dependency],
+ change_source: T.any(Dependabot::Dependency, Dependabot::DependencyGroup)
+ ).void
+ end
def initialize(job:, dependency_files:, updated_dependencies:, change_source:)
@job = job
- @dependency_files = dependency_files
+
+ dir = Pathname.new(job.source.directory).cleanpath
+ @dependency_files = T.let(dependency_files.select { |f| Pathname.new(f.directory).cleanpath == dir },
+ T::Array[Dependabot::DependencyFile])
+
+ raise "Missing directory in dependency files: #{dir}" unless @dependency_files.any?
+
@updated_dependencies = updated_dependencies
@change_source = change_source
end
+ sig { returns(Dependabot::DependencyChange) }
def run
updated_files = generate_dependency_files
+ raise DependabotError, "FileUpdater failed" unless updated_files.any?
+
# Remove any unchanged dependencies from the updated list
updated_deps = updated_dependencies.reject do |d|
# Avoid rejecting the source dependency
@@ -45,6 +78,8 @@ def run
d.version == d.previous_version
end
+ updated_deps.each { |d| d.metadata[:directory] = job.source.directory } if job.source.directory
+
Dependabot::DependencyChange.new(
job: job,
updated_dependencies: updated_deps,
@@ -55,23 +90,36 @@ def run
private
- attr_reader :job, :dependency_files, :updated_dependencies, :change_source
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
+
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
+ attr_reader :dependency_files
+
+ sig { returns(T::Array[Dependabot::Dependency]) }
+ attr_reader :updated_dependencies
+
+ sig { returns(T.any(Dependabot::Dependency, Dependabot::DependencyGroup)) }
+ attr_reader :change_source
+ sig { returns(T.nilable(String)) }
def source_dependency_name
return nil unless change_source.is_a? Dependabot::Dependency
- change_source.name
+ T.cast(change_source, Dependabot::Dependency).name
end
+ sig { returns(T.nilable(Dependabot::DependencyGroup)) }
def source_dependency_group
return nil unless change_source.is_a? Dependabot::DependencyGroup
- change_source
+ T.cast(change_source, Dependabot::DependencyGroup)
end
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
def generate_dependency_files
if updated_dependencies.count == 1
- updated_dependency = updated_dependencies.first
+ updated_dependency = T.must(updated_dependencies.first)
Dependabot.logger.info("Updating #{updated_dependency.name} from " \
"#{updated_dependency.previous_version} to " \
"#{updated_dependency.version}")
@@ -87,6 +135,7 @@ def generate_dependency_files
file_updater_for(relevant_dependencies).updated_dependency_files
end
+ sig { params(dependencies: T::Array[Dependabot::Dependency]).returns(Dependabot::FileUpdaters::Base) }
def file_updater_for(dependencies)
Dependabot::FileUpdaters.for_package_manager(job.package_manager).new(
dependencies: dependencies,
diff --git a/updater/lib/dependabot/dependency_group_engine.rb b/updater/lib/dependabot/dependency_group_engine.rb
index ad429d53..da31605c 100644
--- a/updater/lib/dependabot/dependency_group_engine.rb
+++ b/updater/lib/dependabot/dependency_group_engine.rb
@@ -1,6 +1,8 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
+require "sorbet-runtime"
+
require "dependabot/dependency_group"
# This class implements our strategy for keeping track of and matching dependency
@@ -11,32 +13,46 @@
# the groups.
#
# We permit dependencies to be in more than one group and also track those which
-# have zero matches so they may be updated individuall.
+# have zero matches so they may be updated individually.
#
# **Note:** This is currently an experimental feature which is not supported
# in the service or as an integration point.
#
module Dependabot
class DependencyGroupEngine
+ extend T::Sig
+
class ConfigurationError < StandardError; end
+ sig { params(job: Dependabot::Job).returns(Dependabot::DependencyGroupEngine) }
def self.from_job_config(job:)
groups = job.dependency_groups.map do |group|
- Dependabot::DependencyGroup.new(name: group["name"], rules: group["rules"])
+ Dependabot::DependencyGroup.new(name: group["name"], rules: group["rules"], applies_to: group["applies-to"])
end
+ # Filter out version updates when doing security updates and visa versa
+ groups = if job.security_updates_only?
+ groups.select { |group| group.applies_to == "security-updates" }
+ else
+ groups.select { |group| group.applies_to == "version-updates" }
+ end
+
new(dependency_groups: groups)
end
- attr_reader :dependency_groups, :groups_calculated, :ungrouped_dependencies
+ sig { returns(T::Array[Dependabot::DependencyGroup]) }
+ attr_reader :dependency_groups
+ sig { returns(T::Array[Dependabot::Dependency]) }
+ attr_reader :ungrouped_dependencies
+
+ sig { params(name: String).returns(T.nilable(Dependabot::DependencyGroup)) }
def find_group(name:)
dependency_groups.find { |group| group.name == name }
end
+ sig { params(dependencies: T::Array[Dependabot::Dependency]).void }
def assign_to_groups!(dependencies:)
- raise ConfigurationError, "dependency groups have already been configured!" if @groups_calculated
-
if dependency_groups.any?
dependencies.each do |dependency|
matched_groups = @dependency_groups.each_with_object([]) do |group, matches|
@@ -50,33 +66,34 @@ def assign_to_groups!(dependencies:)
@ungrouped_dependencies << dependency if matched_groups.empty?
end
else
- @ungrouped_dependencies = dependencies
+ @ungrouped_dependencies += dependencies
end
validate_groups
- @groups_calculated = true
end
private
+ sig { params(dependency_groups: T::Array[Dependabot::DependencyGroup]).void }
def initialize(dependency_groups:)
@dependency_groups = dependency_groups
- @ungrouped_dependencies = []
- @groups_calculated = false
+ @ungrouped_dependencies = T.let([], T::Array[Dependabot::Dependency])
end
+ sig { void }
def validate_groups
empty_groups = dependency_groups.select { |group| group.dependencies.empty? }
warn_misconfigured_groups(empty_groups) if empty_groups.any?
end
+ sig { params(groups: T::Array[Dependabot::DependencyGroup]).void }
def warn_misconfigured_groups(groups)
Dependabot.logger.warn <<~WARN
Please check your configuration as there are groups where no dependencies match:
#{groups.map { |g| "- #{g.name}" }.join("\n")}
This can happen if:
- - the group's 'pattern' rules are mispelled
+ - the group's 'pattern' rules are misspelled
- your configuration's 'allow' rules do not permit any of the dependencies that match the group
- the dependencies that match the group rules have been removed from your project
WARN
diff --git a/updater/lib/dependabot/dependency_snapshot.rb b/updater/lib/dependabot/dependency_snapshot.rb
index 185e589c..9fe29693 100644
--- a/updater/lib/dependabot/dependency_snapshot.rb
+++ b/updater/lib/dependabot/dependency_snapshot.rb
@@ -1,7 +1,9 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
require "base64"
+require "sorbet-runtime"
+
require "dependabot/file_parsers"
# This class describes the dependencies obtained from a project at a specific commit SHA
@@ -12,13 +14,25 @@
# representing the output.
module Dependabot
class DependencySnapshot
+ extend T::Sig
+
+ sig do
+ params(job: Dependabot::Job, job_definition: T::Hash[String, T.untyped]).returns(Dependabot::DependencySnapshot)
+ end
def self.create_from_job_definition(job:, job_definition:)
decoded_dependency_files = job_definition.fetch("base64_dependency_files").map do |a|
file = Dependabot::DependencyFile.new(**a.transform_keys(&:to_sym))
- file.content = Base64.decode64(file.content).force_encoding("utf-8") unless file.binary? && !file.deleted?
+ unless file.binary? && !file.deleted?
+ file.content = Base64.decode64(T.must(file.content)).force_encoding("utf-8")
+ end
file
end
+ if job.source.directories
+ # The job.source.directory may contain globs, so we use the directories from the fetched files
+ job.source.directories = decoded_dependency_files.flat_map(&:directory).uniq
+ end
+
new(
job: job,
base_commit_sha: job_definition.fetch("base_commit_sha"),
@@ -26,23 +40,47 @@ def self.create_from_job_definition(job:, job_definition:)
)
end
- attr_reader :base_commit_sha, :dependency_files, :dependencies, :handled_dependencies
+ sig { returns(String) }
+ attr_reader :base_commit_sha
- def add_handled_dependencies(dependency_names)
- @handled_dependencies += Array(dependency_names)
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
+ def all_dependency_files
+ @dependency_files
+ end
+
+ sig { returns(T::Array[Dependabot::Dependency]) }
+ def all_dependencies
+ @dependencies.values.flatten
+ end
+
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
+ def dependency_files
+ assert_current_directory_set!
+ @dependency_files.select { |f| f.directory == @current_directory }
+ end
+
+ sig { returns(T::Array[Dependabot::Dependency]) }
+ def dependencies
+ assert_current_directory_set!
+ T.must(@dependencies[@current_directory])
end
# Returns the subset of all project dependencies which are permitted
# by the project configuration.
+ sig { returns(T::Array[Dependabot::Dependency]) }
def allowed_dependencies
- @allowed_dependencies ||= dependencies.select { |d| job.allowed_update?(d) }
+ if job.security_updates_only?
+ dependencies.select { |d| T.must(job.dependencies).include?(d.name) }
+ else
+ dependencies.select { |d| job.allowed_update?(d) }
+ end
end
# Returns the subset of all project dependencies which are specifically
# requested to be updated by the job definition.
+ sig { returns(T::Array[Dependabot::Dependency]) }
def job_dependencies
return [] unless job.dependencies&.any?
- return @job_dependencies if defined? @job_dependencies
# Gradle, Maven and Nuget dependency names can be case-insensitive and
# the dependency name in the security advisory often doesn't match what
@@ -52,54 +90,132 @@ def job_dependencies
# private registry but shouldn't cause problems here as job.dependencies
# is set either from an existing PR rebase/recreate or a security
# advisory.
- job_dependency_names = job.dependencies.map(&:downcase)
- @job_dependencies = dependencies.select do |dep|
+ job_dependency_names = T.must(job.dependencies).map(&:downcase)
+ dependencies.select do |dep|
job_dependency_names.include?(dep.name.downcase)
end
end
# Returns just the group that is specifically requested to be updated by
# the job definition
+ sig { returns(T.nilable(Dependabot::DependencyGroup)) }
def job_group
return nil unless job.dependency_group_to_refresh
- return @job_group if defined?(@job_group)
- @job_group = @dependency_group_engine.find_group(name: job.dependency_group_to_refresh)
+ @dependency_group_engine.find_group(name: T.must(job.dependency_group_to_refresh))
+ end
+
+ sig { params(group: Dependabot::DependencyGroup).void }
+ def mark_group_handled(group)
+ directories.each do |directory|
+ @current_directory = directory
+
+ # add the existing dependencies in the group so individual updates don't try to update them
+ add_handled_dependencies(dependencies_in_existing_pr_for_group(group).filter_map { |d| d["dependency-name"] })
+ # also add dependencies that might be in the group, as a rebase would add them;
+ # this avoids individual PR creation that immediately is superseded by a group PR supersede
+ add_handled_dependencies(group.dependencies.map(&:name))
+ end
end
+ sig { params(dependency_names: T.any(String, T::Array[String])).void }
+ def add_handled_dependencies(dependency_names)
+ assert_current_directory_set!
+ set = @handled_dependencies[@current_directory] || Set.new
+ set += Array(dependency_names)
+ @handled_dependencies[@current_directory] = set
+ end
+
+ sig { returns(T::Set[String]) }
+ def handled_dependencies
+ assert_current_directory_set!
+ T.must(@handled_dependencies[@current_directory])
+ end
+
+ sig { params(dir: String).void }
+ def current_directory=(dir)
+ @current_directory = dir
+ @handled_dependencies[dir] = Set.new unless @handled_dependencies.key?(dir)
+ end
+
+ sig { returns(T::Array[Dependabot::DependencyGroup]) }
def groups
@dependency_group_engine.dependency_groups
end
+ sig { returns(T::Array[Dependabot::Dependency]) }
def ungrouped_dependencies
# If no groups are defined, all dependencies are ungrouped by default.
return allowed_dependencies unless groups.any?
# Otherwise return dependencies that haven't been handled during the group update portion.
- allowed_dependencies.reject { |dep| @handled_dependencies.include?(dep.name) }
+ allowed_dependencies.reject { |dep| handled_dependencies.include?(dep.name) }
end
private
- def initialize(job:, base_commit_sha:, dependency_files:)
+ sig do
+ params(job: Dependabot::Job, base_commit_sha: String, dependency_files: T::Array[Dependabot::DependencyFile]).void
+ end
+ def initialize(job:, base_commit_sha:, dependency_files:) # rubocop:disable Metrics/AbcSize
+ @original_directory = T.let(job.source.directory, T.nilable(String))
+
@job = job
@base_commit_sha = base_commit_sha
@dependency_files = dependency_files
- @handled_dependencies = Set.new
+ @handled_dependencies = T.let({}, T::Hash[String, T::Set[String]])
+ @current_directory = T.let("", String)
+
+ @dependencies = T.let({}, T::Hash[String, T::Array[Dependabot::Dependency]])
+ directories.each do |dir|
+ @current_directory = dir
+ @dependencies[dir] = parse_files!
+ end
+
+ @dependency_group_engine = T.let(DependencyGroupEngine.from_job_config(job: job),
+ Dependabot::DependencyGroupEngine)
+ directories.each do |dir|
+ @current_directory = dir
+ @dependency_group_engine.assign_to_groups!(dependencies: allowed_dependencies)
+ end
+
+ # The non-grouped operations depend on there being a job.source.directory, so we want to not burden it with
+ # multi-dir support, yet. The rest of this method maintains multi-dir logic by setting some defaults.
+ if @original_directory.nil? && @dependency_group_engine.dependency_groups.none? && job.security_updates_only?
+ @original_directory = T.must(job.source.directories).first
+ end
- @dependencies = parse_files!
+ job.source.directory = @original_directory
+ # reset to ensure we don't accidentally use it later without setting it
+ @current_directory = ""
+ return unless job.source.directory
- @dependency_group_engine = DependencyGroupEngine.from_job_config(job: job)
- @dependency_group_engine.assign_to_groups!(dependencies: allowed_dependencies)
+ @current_directory = T.must(job.source.directory)
+ @handled_dependencies[@current_directory] = Set.new
end
+ # Helper simplifies some of the logic, no need to check for one or the other!
+ sig { returns(T::Array[String]) }
+ def directories
+ if @original_directory
+ [@original_directory]
+ else
+ T.must(job.source.directories)
+ end
+ end
+
+ sig { returns(Dependabot::Job) }
attr_reader :job
+ sig { returns(T::Array[Dependabot::Dependency]) }
def parse_files!
dependency_file_parser.parse
end
+ sig { returns(Dependabot::FileParsers::Base) }
def dependency_file_parser
+ assert_current_directory_set!
+ job.source.directory = @current_directory
Dependabot::FileParsers.for_package_manager(job.package_manager).new(
dependency_files: dependency_files,
repo_contents_path: job.repo_contents_path,
@@ -109,5 +225,22 @@ def dependency_file_parser
options: job.experiments
)
end
+
+ sig { params(group: Dependabot::DependencyGroup).returns(T::Array[T::Hash[String, String]]) }
+ def dependencies_in_existing_pr_for_group(group)
+ job.existing_group_pull_requests.find do |pr|
+ pr["dependency-group-name"] == group.name
+ end&.fetch("dependencies", []) || []
+ end
+
+ sig { void }
+ def assert_current_directory_set!
+ if @current_directory == "" && directories.count == 1
+ @current_directory = T.must(directories.first)
+ return
+ end
+
+ raise DependabotError, "Assertion failed: Current directory not set" if @current_directory == ""
+ end
end
end
diff --git a/updater/lib/dependabot/environment.rb b/updater/lib/dependabot/environment.rb
index 1055a91f..230423dd 100644
--- a/updater/lib/dependabot/environment.rb
+++ b/updater/lib/dependabot/environment.rb
@@ -1,64 +1,97 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
+require "sorbet-runtime"
+
module Dependabot
module Environment
+ extend T::Sig
+ extend T::Generic
+
+ sig { returns(String) }
def self.job_id
- @job_id ||= environment_variable("DEPENDABOT_JOB_ID")
+ @job_id ||= T.let(environment_variable("DEPENDABOT_JOB_ID"), T.nilable(String))
end
+ sig { returns(String) }
def self.job_token
- @job_token ||= environment_variable("DEPENDABOT_JOB_TOKEN")
+ @job_token ||= T.let(environment_variable("DEPENDABOT_JOB_TOKEN"), T.nilable(String))
end
+ sig { returns(T::Boolean) }
def self.debug_enabled?
- @debug_enabled ||= job_debug_enabled? || environment_debug_enabled?
+ @debug_enabled ||= T.let(job_debug_enabled? || environment_debug_enabled?, T.nilable(T::Boolean))
end
+ sig { returns(Symbol) }
def self.log_level
debug_enabled? ? :debug : :info
end
+ sig { returns(String) }
def self.api_url
- @api_url ||= environment_variable("DEPENDABOT_API_URL", "http://localhost:3001")
+ @api_url ||= T.let(environment_variable("DEPENDABOT_API_URL", "http://localhost:3001"), T.nilable(String))
end
+ sig { returns(String) }
def self.job_path
- @job_path ||= environment_variable("DEPENDABOT_JOB_PATH")
+ @job_path ||= T.let(environment_variable("DEPENDABOT_JOB_PATH"), T.nilable(String))
end
+ sig { returns(String) }
def self.output_path
- @output_path ||= environment_variable("DEPENDABOT_OUTPUT_PATH")
+ @output_path ||= T.let(environment_variable("DEPENDABOT_OUTPUT_PATH"), T.nilable(String))
end
+ sig { returns(T.nilable(String)) }
def self.repo_contents_path
- @repo_contents_path ||= environment_variable("DEPENDABOT_REPO_CONTENTS_PATH", nil)
+ @repo_contents_path ||= T.let(environment_variable("DEPENDABOT_REPO_CONTENTS_PATH", nil), T.nilable(String))
end
+ sig { returns(T::Boolean) }
def self.github_actions?
- @github_actions ||= environment_variable("GITHUB_ACTIONS", false)
+ b = T.cast(environment_variable("GITHUB_ACTIONS", false), T::Boolean)
+ @github_actions ||= T.let(b, T.nilable(T::Boolean))
end
+ sig { returns(T::Boolean) }
def self.deterministic_updates?
- @deterministic_updates ||= environment_variable("UPDATER_DETERMINISTIC", false)
+ b = T.cast(environment_variable("UPDATER_DETERMINISTIC", false), T::Boolean)
+ @deterministic_updates ||= T.let(b, T.nilable(T::Boolean))
end
+ sig { returns(T::Hash[String, T.untyped]) }
def self.job_definition
- @job_definition ||= JSON.parse(File.read(job_path))
+ @job_definition ||= T.let(JSON.parse(File.read(job_path)), T.nilable(T::Hash[String, T.untyped]))
end
+ sig do
+ type_parameters(:T)
+ .params(variable_name: String, default: T.any(Symbol, T.type_parameter(:T)))
+ .returns(T.any(String, T.type_parameter(:T)))
+ end
private_class_method def self.environment_variable(variable_name, default = :_undefined)
- return ENV.fetch(variable_name, default) unless default == :_undefined
-
- ENV.fetch(variable_name) do
- raise ArgumentError, "Missing environment variable #{variable_name}"
+ case default
+ when :_undefined
+ ENV.fetch(variable_name) do
+ raise ArgumentError, "Missing environment variable #{variable_name}"
+ end
+ else
+ val = ENV.fetch(variable_name, T.cast(default, T.type_parameter(:T)))
+ case val
+ when String
+ val = T.must(val.casecmp("true")).zero? if [true, false].include? default
+ end
+ T.cast(val, T.type_parameter(:T))
end
end
+ sig { returns(T::Boolean) }
private_class_method def self.job_debug_enabled?
!!job_definition.dig("job", "debug")
end
+ sig { returns(T::Boolean) }
private_class_method def self.environment_debug_enabled?
!!environment_variable("DEPENDABOT_DEBUG", false)
end
diff --git a/updater/lib/dependabot/file_fetcher_command.rb b/updater/lib/dependabot/file_fetcher_command.rb
index 3569602a..927db8f3 100644
--- a/updater/lib/dependabot/file_fetcher_command.rb
+++ b/updater/lib/dependabot/file_fetcher_command.rb
@@ -1,8 +1,10 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
require "base64"
require "dependabot/base_command"
+require "dependabot/errors"
+require "dependabot/opentelemetry"
require "dependabot/updater"
require "octokit"
@@ -13,42 +15,48 @@ class FileFetcherCommand < BaseCommand
# NotImplementedError if it is referenced
attr_reader :base_commit_sha
- def perform_job
+ def perform_job # rubocop:disable Metrics/PerceivedComplexity,Metrics/AbcSize
@base_commit_sha = nil
- begin
- connectivity_check if ENV["ENABLE_CONNECTIVITY_CHECK"] == "1"
- clone_repo_contents
- @base_commit_sha = file_fetcher.commit
- raise "base commit SHA not found" unless @base_commit_sha
-
- # We don't set this flag in GHES because there's no point in recording versions since we can't access that data.
- if Experiments.enabled?(:record_ecosystem_versions)
- ecosystem_versions = file_fetcher.ecosystem_versions
- api_client.record_ecosystem_versions(ecosystem_versions) unless ecosystem_versions.nil?
- end
+ Dependabot.logger.info("Job definition: #{File.read(Environment.job_path)}") if Environment.job_path
+ ::Dependabot::OpenTelemetry.tracer.in_span("file_fetcher", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+
+ begin
+ connectivity_check if ENV["ENABLE_CONNECTIVITY_CHECK"] == "1"
+ clone_repo_contents
+ @base_commit_sha = file_fetcher.commit
+ raise "base commit SHA not found" unless @base_commit_sha
- dependency_files
- rescue StandardError => e
- @base_commit_sha ||= "unknown"
- if Octokit::RATE_LIMITED_ERRORS.include?(e.class)
- remaining = rate_limit_error_remaining(e)
- Dependabot.logger.error("Repository is rate limited, attempting to retry in " \
- "#{remaining}s")
- else
- Dependabot.logger.error("Error during file fetching; aborting")
+ # In the older versions of GHES (> 3.11.0) job.source.directories will be nil as source.directories was
+ # introduced after 3.11.0 release. So, this also supports backward compatibility for older versions of GHES.
+ if job.source.directories
+ dependency_files_for_multi_directories
+ else
+ dependency_files
+ end
+ rescue StandardError => e
+ @base_commit_sha ||= "unknown"
+ if Octokit::RATE_LIMITED_ERRORS.include?(e.class)
+ remaining = rate_limit_error_remaining(e)
+ Dependabot.logger.error("Repository is rate limited, attempting to retry in " \
+ "#{remaining}s")
+ else
+ Dependabot.logger.error("Error during file fetching; aborting: #{e.message}")
+ end
+ handle_file_fetcher_error(e)
+ service.mark_job_as_processed(@base_commit_sha)
+ return nil
end
- handle_file_fetcher_error(e)
- service.mark_job_as_processed(@base_commit_sha)
- return
- end
- File.write(Environment.output_path, JSON.dump(
- base64_dependency_files: base64_dependency_files.map(&:to_h),
- base_commit_sha: @base_commit_sha
- ))
+ Dependabot.logger.info("Base commit SHA: #{@base_commit_sha}")
+ File.write(Environment.output_path, JSON.dump(
+ base64_dependency_files: base64_dependency_files.map(&:to_h),
+ base_commit_sha: @base_commit_sha
+ ))
- save_job_details
+ save_job_details
+ end
end
private
@@ -64,12 +72,99 @@ def save_job_details
))
end
+ # A method that abstracts the file fetcher creation logic and applies the same settings across all instances
+ def create_file_fetcher(directory: nil)
+ # Use the provided directory or fallback to job.source.directory if directory is nil.
+ directory_to_use = directory || job.source.directory
+
+ args = {
+ source: job.source.clone.tap { |s| s.directory = directory_to_use },
+ credentials: Environment.job_definition.fetch("credentials", []),
+ options: job.experiments
+ }
+ args[:repo_contents_path] = Environment.repo_contents_path if job.clone? || already_cloned?
+ Dependabot::FileFetchers.for_package_manager(job.package_manager).new(**args)
+ end
+
+ # The main file fetcher method that now calls the create_file_fetcher method
+ # and ensures it uses the same repo_contents_path setting as others.
+ def file_fetcher
+ @file_fetcher ||= create_file_fetcher
+ end
+
+ # This method is responsible for creating or retrieving a file fetcher
+ # from a cache (@file_fetchers) for the given directory.
+ def file_fetcher_for_directory(directory)
+ @file_fetchers ||= {}
+ @file_fetchers[directory] ||= create_file_fetcher(directory: directory)
+ end
+
+ # rubocop:disable Metrics/PerceivedComplexity
+ def dependency_files_for_multi_directories
+ return @dependency_files_for_multi_directories if defined?(@dependency_files_for_multi_directories)
+
+ has_glob = T.let(false, T::Boolean)
+ directories = Dir.chdir(job.repo_contents_path) do
+ job.source.directories.map do |dir|
+ next dir unless glob?(dir)
+
+ has_glob = true
+ dir = dir.delete_prefix("/")
+ Dir.glob(dir, File::FNM_DOTMATCH).select { |d| File.directory?(d) }.map { |d| "/#{d}" }
+ end.flatten
+ end.uniq
+
+ @dependency_files_for_multi_directories = directories.flat_map do |dir|
+ ff = with_retries { file_fetcher_for_directory(dir) }
+
+ begin
+ files = ff.files
+ rescue Dependabot::DependencyFileNotFound
+ # skip directories that don't contain manifests if globbing is used
+ next if has_glob
+
+ raise
+ end
+
+ post_ecosystem_versions(ff) if should_record_ecosystem_versions?
+ files
+ end.compact
+
+ if @dependency_files_for_multi_directories.empty?
+ raise Dependabot::DependencyFileNotFound, job.source.directories.join(", ")
+ end
+
+ @dependency_files_for_multi_directories
+ end
+ # rubocop:enable Metrics/PerceivedComplexity
+
def dependency_files
- file_fetcher.files
- rescue Octokit::BadGateway
- @file_fetcher_retries ||= 0
- @file_fetcher_retries += 1
- @file_fetcher_retries <= 2 ? retry : raise
+ return @dependency_files if defined?(@dependency_files)
+
+ @dependency_files = with_retries { file_fetcher.files }
+ post_ecosystem_versions(file_fetcher) if should_record_ecosystem_versions?
+ @dependency_files
+ end
+
+ def should_record_ecosystem_versions?
+ # We don't set this flag in GHES because there's no point in recording versions since we can't access that data.
+ Experiments.enabled?(:record_ecosystem_versions)
+ end
+
+ def post_ecosystem_versions(file_fetcher)
+ ecosystem_versions = file_fetcher.ecosystem_versions
+ api_client.record_ecosystem_versions(ecosystem_versions) unless ecosystem_versions.nil?
+ end
+
+ def with_retries(max_retries: 2)
+ retries ||= 0
+ begin
+ yield
+ rescue Octokit::BadGateway
+ retries += 1
+ retry if retries <= max_retries
+ raise
+ end
end
def clone_repo_contents
@@ -79,7 +174,8 @@ def clone_repo_contents
end
def base64_dependency_files
- dependency_files.map do |file|
+ files = job.source.directories ? dependency_files_for_multi_directories : dependency_files
+ files.map do |file|
base64_file = file.dup
base64_file.content = Base64.encode64(file.content) unless file.binary?
base64_file
@@ -94,21 +190,6 @@ def job
)
end
- def file_fetcher
- return @file_fetcher if defined? @file_fetcher
-
- args = {
- source: job.source,
- credentials: Environment.job_definition.fetch("credentials", []),
- options: job.experiments
- }
- # This bypasses the `job.repo_contents_path` presenter to ensure we fetch
- # from the file system if the repository contents are mounted even if
- # cloning is disabled.
- args[:repo_contents_path] = Environment.repo_contents_path if job.clone? || already_cloned?
- @file_fetcher ||= Dependabot::FileFetchers.for_package_manager(job.package_manager).new(**args)
- end
-
def already_cloned?
return false unless Environment.repo_contents_path
@@ -116,94 +197,39 @@ def already_cloned?
@already_cloned ||= File.directory?(File.join(Environment.repo_contents_path, ".git"))
end
- # rubocop:disable Metrics/MethodLength
def handle_file_fetcher_error(error)
- error_details =
- case error
- when Dependabot::ToolVersionNotSupported
- {
- "error-type": "tool_version_not_supported",
- "error-detail": {
- "tool-name": error.tool_name,
- "detected-version": error.detected_version,
- "supported-versions": error.supported_versions
- }
- }
- when Dependabot::BranchNotFound
- {
- "error-type": "branch_not_found",
- "error-detail": { "branch-name": error.branch_name }
- }
- when Dependabot::RepoNotFound
- # This happens if the repo gets removed after a job gets kicked off.
- # This also happens when a configured personal access token is not authz'd to fetch files from the job repo.
- {
- "error-type": "job_repo_not_found",
- "error-detail": {}
- }
- when Dependabot::DependencyFileNotParseable
- {
- "error-type": "dependency_file_not_parseable",
- "error-detail": {
- message: error.message,
- "file-path": error.file_path
- }
- }
- when Dependabot::DependencyFileNotFound
- {
- "error-type": "dependency_file_not_found",
- "error-detail": { "file-path": error.file_path }
- }
- when Dependabot::OutOfDisk
- {
- "error-type": "out_of_disk",
- "error-detail": {}
- }
- when Dependabot::PathDependenciesNotReachable
- {
- "error-type": "path_dependencies_not_reachable",
- "error-detail": { dependencies: error.dependencies }
- }
- when Octokit::Unauthorized
- { "error-type": "octokit_unauthorized" }
- when Octokit::ServerError
- # If we get a 500 from GitHub there's very little we can do about it,
- # and responsibility for fixing it is on them, not us. As a result we
- # quietly log these as errors
- { "error-type": "server_error" }
- when *Octokit::RATE_LIMITED_ERRORS
- # If we get a rate-limited error we let dependabot-api handle the
- # retry by re-enqueing the update job after the reset
- {
- "error-type": "octokit_rate_limited",
- "error-detail": {
- "rate-limit-reset": error.response_headers["X-RateLimit-Reset"]
- }
- }
- else
- log_error(error)
-
- unknown_error_details = {
- "error-class" => error.class.to_s,
- "error-message" => error.message,
- "error-backtrace" => error.backtrace.join("\n"),
- "package-manager" => job.package_manager,
- "job-id" => job.id,
- "job-dependencies" => job.dependencies,
- "job-dependency_group" => job.dependency_groups
- }.compact
-
- service.capture_exception(error: error, job: job)
- {
- "error-type": "file_fetcher_error",
- "error-detail": unknown_error_details
- }
- end
+ error_details = Dependabot.fetcher_error_details(error)
+
+ if error_details.nil?
+ log_error(error)
+
+ unknown_error_details = {
+ ErrorAttributes::CLASS => error.class.to_s,
+ ErrorAttributes::MESSAGE => error.message,
+ ErrorAttributes::BACKTRACE => error.backtrace.join("\n"),
+ ErrorAttributes::FINGERPRINT => error.respond_to?(:sentry_context) ? error.sentry_context[:fingerprint] : nil,
+ ErrorAttributes::PACKAGE_MANAGER => job.package_manager,
+ ErrorAttributes::JOB_ID => job.id,
+ ErrorAttributes::DEPENDENCIES => job.dependencies,
+ ErrorAttributes::DEPENDENCY_GROUPS => job.dependency_groups
+ }.compact
+
+ error_details = {
+ "error-type": "file_fetcher_error",
+ "error-detail": unknown_error_details
+ }
+ end
- record_error(error_details) if error_details
+ service.record_update_job_error(
+ error_type: error_details.fetch(:"error-type"),
+ error_details: error_details[:"error-detail"]
+ )
+
+ return unless error_details.fetch(:"error-type") == "file_fetcher_error"
+
+ service.capture_exception(error: error, job: job)
end
- # rubocop:enable Metrics/MethodLength
def rate_limit_error_remaining(error)
# Time at which the current rate limit window resets in UTC epoch secs.
expires_at = error.response_headers["X-RateLimit-Reset"].to_i
@@ -254,5 +280,10 @@ def github_connectivity_client(job)
}
})
end
+
+ def glob?(directory)
+ # We could tighten this up, but it's probably close enough.
+ directory.include?("*") || directory.include?("?") || (directory.include?("[") && directory.include?("]"))
+ end
end
end
diff --git a/updater/lib/dependabot/job.rb b/updater/lib/dependabot/job.rb
index ba0b66cb..99fdc2d5 100644
--- a/updater/lib/dependabot/job.rb
+++ b/updater/lib/dependabot/job.rb
@@ -1,12 +1,16 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
+require "sorbet-runtime"
+require "wildcard_matcher"
+
require "dependabot/config/ignore_condition"
require "dependabot/config/update_config"
+require "dependabot/credential"
require "dependabot/dependency_group_engine"
require "dependabot/experiments"
+require "dependabot/requirements_update_strategy"
require "dependabot/source"
-require "wildcard_matcher"
# Describes a single Dependabot workload within the GitHub-integrated Service
#
@@ -16,12 +20,14 @@
#
# See: https://github.com/dependabot/cli#job-description-file
#
-# This class should evenually be promoted to common/lib and augmented to
+# This class should eventually be promoted to common/lib and augmented to
# validate job description files.
module Dependabot
class Job
- TOP_LEVEL_DEPENDENCY_TYPES = %w(direct production development).freeze
- PERMITTED_KEYS = %i(
+ extend T::Sig
+
+ TOP_LEVEL_DEPENDENCY_TYPES = T.let(%w(direct production development).freeze, T::Array[String])
+ PERMITTED_KEYS = T.let(%i(
allowed_updates
commit_message_options
dependencies
@@ -43,121 +49,184 @@ class Job
dependency_groups
dependency_group_to_refresh
repo_private
- ).freeze
-
- attr_reader :allowed_updates,
- :credentials,
- :dependencies,
- :existing_pull_requests,
- :existing_group_pull_requests,
- :id,
- :ignore_conditions,
- :package_manager,
- :requirements_update_strategy,
- :security_advisories,
- :security_updates_only,
- :source,
- :token,
- :vendor_dependencies,
- :dependency_groups,
- :dependency_group_to_refresh
+ ).freeze, T::Array[Symbol])
+
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
+ attr_reader :allowed_updates
+
+ sig { returns(T::Array[Dependabot::Credential]) }
+ attr_reader :credentials
+
+ sig { returns(T.nilable(T::Array[String])) }
+ attr_reader :dependencies
+
+ sig { returns(T::Array[T::Array[T::Hash[String, String]]]) }
+ attr_reader :existing_pull_requests
+
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
+ attr_reader :existing_group_pull_requests
+
+ sig { returns(String) }
+ attr_reader :id
+
+ sig { returns(T::Array[T.untyped]) }
+ attr_reader :ignore_conditions
+ sig { returns(String) }
+ attr_reader :package_manager
+
+ sig { returns(T.nilable(Dependabot::RequirementsUpdateStrategy)) }
+ attr_reader :requirements_update_strategy
+
+ sig { returns(T::Array[T.untyped]) }
+ attr_reader :security_advisories
+
+ sig { returns(T::Boolean) }
+ attr_reader :security_updates_only
+
+ sig { returns(Dependabot::Source) }
+ attr_reader :source
+
+ sig { returns(T.nilable(String)) }
+ attr_reader :token
+
+ sig { returns(T::Boolean) }
+ attr_reader :vendor_dependencies
+
+ sig { returns(T::Array[T.untyped]) }
+ attr_reader :dependency_groups
+
+ sig { returns(T.nilable(String)) }
+ attr_reader :dependency_group_to_refresh
+
+ sig do
+ params(job_id: String, job_definition: T::Hash[String, T.untyped],
+ repo_contents_path: T.nilable(String)).returns(Job)
+ end
def self.new_fetch_job(job_id:, job_definition:, repo_contents_path: nil)
- attrs = standardise_keys(job_definition["job"]).slice(*PERMITTED_KEYS)
+ attrs = standardise_keys(job_definition["job"]).select { |k, _| PERMITTED_KEYS.include?(k) }
new(attrs.merge(id: job_id, repo_contents_path: repo_contents_path))
end
+ sig do
+ params(job_id: String, job_definition: T::Hash[String, T.untyped],
+ repo_contents_path: T.nilable(String)).returns(Job)
+ end
def self.new_update_job(job_id:, job_definition:, repo_contents_path: nil)
job_hash = standardise_keys(job_definition["job"])
- attrs = job_hash.slice(*PERMITTED_KEYS)
+ attrs = job_hash.select { |k, _| PERMITTED_KEYS.include?(k) }
attrs[:credentials] = job_hash[:credentials_metadata] || []
new(attrs.merge(id: job_id, repo_contents_path: repo_contents_path))
end
+ sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped]) }
def self.standardise_keys(hash)
hash.transform_keys { |key| key.tr("-", "_").to_sym }
end
# NOTE: "attributes" are fetched and injected at run time from
# dependabot-api using the UpdateJobPrivateSerializer
- def initialize(attributes)
- @id = attributes.fetch(:id)
- @allowed_updates = attributes.fetch(:allowed_updates)
- @commit_message_options = attributes.fetch(:commit_message_options, {})
- @credentials = attributes.fetch(:credentials, [])
- @dependencies = attributes.fetch(:dependencies)
- @existing_pull_requests = attributes.fetch(:existing_pull_requests)
+ sig { params(attributes: T.untyped).void }
+ def initialize(attributes) # rubocop:disable Metrics/AbcSize
+ @id = T.let(attributes.fetch(:id), String)
+ @allowed_updates = T.let(attributes.fetch(:allowed_updates), T::Array[T.untyped])
+ @commit_message_options = T.let(attributes.fetch(:commit_message_options, {}),
+ T.nilable(T::Hash[T.untyped, T.untyped]))
+ @credentials = T.let(attributes.fetch(:credentials, []).map do |data|
+ Dependabot::Credential.new(data)
+ end,
+ T::Array[Dependabot::Credential])
+ @dependencies = T.let(attributes.fetch(:dependencies), T.nilable(T::Array[T.untyped]))
+ @existing_pull_requests = T.let(attributes.fetch(:existing_pull_requests),
+ T::Array[T::Array[T::Hash[String, String]]])
# TODO: Make this hash required
#
# We will need to do a pass updating the CLI and smoke tests before this is possible,
# so let's consider it optional for now. If we get a nil value, let's force it to be
# an array.
- @existing_group_pull_requests = attributes.fetch(:existing_group_pull_requests, []) || []
- @experiments = attributes.fetch(:experiments, {})
- @ignore_conditions = attributes.fetch(:ignore_conditions)
- @package_manager = attributes.fetch(:package_manager)
- @reject_external_code = attributes.fetch(:reject_external_code, false)
- @repo_contents_path = attributes.fetch(:repo_contents_path, nil)
-
- @requirements_update_strategy = build_update_strategy(
- **attributes.slice(:requirements_update_strategy, :lockfile_only)
- )
-
- @security_advisories = attributes.fetch(:security_advisories)
- @security_updates_only = attributes.fetch(:security_updates_only)
- @source = build_source(attributes.fetch(:source))
- @token = attributes.fetch(:token, nil)
- @update_subdependencies = attributes.fetch(:update_subdependencies)
- @updating_a_pull_request = attributes.fetch(:updating_a_pull_request)
- @vendor_dependencies = attributes.fetch(:vendor_dependencies, false)
+ @existing_group_pull_requests = T.let(attributes.fetch(:existing_group_pull_requests, []) || [],
+ T::Array[T::Hash[String, T.untyped]])
+ @experiments = T.let(attributes.fetch(:experiments, {}),
+ T.nilable(T::Hash[T.untyped, T.untyped]))
+ @ignore_conditions = T.let(attributes.fetch(:ignore_conditions), T::Array[T.untyped])
+ @package_manager = T.let(attributes.fetch(:package_manager), String)
+ @reject_external_code = T.let(attributes.fetch(:reject_external_code, false), T::Boolean)
+ @repo_contents_path = T.let(attributes.fetch(:repo_contents_path, nil), T.nilable(String))
+
+ @requirements_update_strategy = T.let(build_update_strategy(
+ **attributes.slice(:requirements_update_strategy, :lockfile_only)
+ ), T.nilable(Dependabot::RequirementsUpdateStrategy))
+
+ @security_advisories = T.let(attributes.fetch(:security_advisories), T::Array[T.untyped])
+ @security_updates_only = T.let(attributes.fetch(:security_updates_only), T::Boolean)
+ @source = T.let(build_source(attributes.fetch(:source)), Dependabot::Source)
+ @token = T.let(attributes.fetch(:token, nil), T.nilable(String))
+ @update_subdependencies = T.let(attributes.fetch(:update_subdependencies), T::Boolean)
+ @updating_a_pull_request = T.let(attributes.fetch(:updating_a_pull_request), T::Boolean)
+ @vendor_dependencies = T.let(attributes.fetch(:vendor_dependencies, false), T::Boolean)
# TODO: Make this hash required
#
# We will need to do a pass updating the CLI and smoke tests before this is possible,
# so let's consider it optional for now. If we get a nil value, let's force it to be
# an array.
- @dependency_groups = attributes.fetch(:dependency_groups, []) || []
- @dependency_group_to_refresh = attributes.fetch(:dependency_group_to_refresh, nil)
- @repo_private = attributes.fetch(:repo_private, nil)
+ @dependency_groups = T.let(attributes.fetch(:dependency_groups, []) || [], T::Array[T.untyped])
+ @dependency_group_to_refresh = T.let(attributes.fetch(:dependency_group_to_refresh, nil), T.nilable(String))
+ @repo_private = T.let(attributes.fetch(:repo_private, nil), T.nilable(T::Boolean))
+
+ @update_config = T.let(calculate_update_config, Dependabot::Config::UpdateConfig)
register_experiments
+ validate_job
end
+ sig { returns(T::Boolean) }
def clone?
- vendor_dependencies? ||
- Dependabot::Utils.always_clone_for_package_manager?(@package_manager)
+ true
end
# Some Core components test for a non-nil repo_contents_path as an implicit
# signal they should use cloning behaviour, so we present it as nil unless
# cloning is enabled to avoid unexpected behaviour.
+ sig { returns(T.nilable(String)) }
def repo_contents_path
return nil unless clone?
@repo_contents_path
end
+ sig { returns(T.nilable(T::Boolean)) }
def repo_private?
@repo_private
end
+ sig { returns(T.nilable(String)) }
+ def repo_owner
+ source.organization
+ end
+
+ sig { returns(T::Boolean) }
def updating_a_pull_request?
@updating_a_pull_request
end
+ sig { returns(T::Boolean) }
def update_subdependencies?
@update_subdependencies
end
+ sig { returns(T::Boolean) }
def security_updates_only?
@security_updates_only
end
+ sig { returns(T::Boolean) }
def vendor_dependencies?
@vendor_dependencies
end
+ sig { returns(T::Boolean) }
def reject_external_code?
@reject_external_code
end
@@ -172,6 +241,7 @@ def reject_external_code?
#
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
+ sig { params(dependency: Dependency).returns(T::Boolean) }
def allowed_update?(dependency)
# Ignoring all versions is another way to say no updates allowed
if completely_ignored?(dependency)
@@ -209,6 +279,7 @@ def allowed_update?(dependency)
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
+ sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
def vulnerable?(dependency)
security_advisories = security_advisories_for(dependency)
return false if security_advisories.none?
@@ -228,26 +299,31 @@ def vulnerable?(dependency)
security_advisories.any? { |a| all_versions.any? { |v| a.vulnerable?(v) } }
end
+ sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
def security_fix?(dependency)
security_advisories_for(dependency).any? { |a| a.fixed_by?(dependency) }
end
+ sig { returns(T.nilable(T.proc.params(arg0: String).returns(String))) }
def name_normaliser
Dependabot::Dependency.name_normaliser_for_package_manager(package_manager)
end
+ sig { returns(T::Hash[Symbol, T.untyped]) }
def experiments
return {} unless @experiments
self.class.standardise_keys(@experiments)
end
+ sig { returns(T::Hash[Symbol, T.untyped]) }
def commit_message_options
return {} unless @commit_message_options
self.class.standardise_keys(@commit_message_options).compact
end
+ sig { params(dependency: Dependabot::Dependency).returns(T::Array[Dependabot::SecurityAdvisory]) }
def security_advisories_for(dependency)
relevant_advisories =
security_advisories
@@ -267,6 +343,7 @@ def security_advisories_for(dependency)
end
end
+ sig { params(dependency: Dependabot::Dependency).returns(T::Array[String]) }
def ignore_conditions_for(dependency)
update_config.ignored_versions_for(
dependency,
@@ -284,6 +361,7 @@ def ignore_conditions_for(dependency)
# that it does not have a 'source' attribute which we currently
# use to distinguish rules from the config file from those that
# were created via "@dependabot ignore version" commands
+ sig { params(dependency: Dependabot::Dependency).void }
def log_ignore_conditions_for(dependency)
conditions = ignore_conditions.select { |ic| name_match?(ic["dependency-name"], dependency.name) }
return if conditions.empty?
@@ -304,51 +382,102 @@ def log_ignore_conditions_for(dependency)
private
+ sig { returns(Dependabot::Config::UpdateConfig) }
+ attr_reader :update_config
+
+ sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
def completely_ignored?(dependency)
ignore_conditions_for(dependency).any?(Dependabot::Config::IgnoreCondition::ALL_VERSIONS)
end
+ sig { void }
def register_experiments
- experiments.each do |name, value|
+ experiments.entries.each do |name, value|
Dependabot::Experiments.register(name, value)
end
end
+ sig { void }
+ def validate_job
+ raise "Either directory or directories must be provided" unless source.directory.nil? ^ source.directories.nil?
+ end
+
+ sig { params(name1: String, name2: String).returns(T::Boolean) }
def name_match?(name1, name2)
WildcardMatcher.match?(
- name_normaliser.call(name1),
- name_normaliser.call(name2)
+ T.must(name_normaliser).call(name1),
+ T.must(name_normaliser).call(name2)
)
end
+ sig do
+ params(
+ requirements_update_strategy: T.nilable(String),
+ lockfile_only: T::Boolean
+ )
+ .returns(T.nilable(Dependabot::RequirementsUpdateStrategy))
+ end
def build_update_strategy(requirements_update_strategy:, lockfile_only:)
- return requirements_update_strategy unless requirements_update_strategy.nil?
+ unless requirements_update_strategy.nil?
+ return RequirementsUpdateStrategy.deserialize(requirements_update_strategy)
+ end
- lockfile_only ? "lockfile_only" : nil
+ lockfile_only ? RequirementsUpdateStrategy::LockfileOnly : nil
end
+ sig { params(source_details: T::Hash[String, T.untyped]).returns(Dependabot::Source) }
def build_source(source_details)
+ # Immediately normalize the source directory, ensure it starts with a "/"
+ directory, directories = clean_directories(source_details)
+
Dependabot::Source.new(
- **source_details.transform_keys { |k| k.tr("-", "_").to_sym }
+ provider: T.let(source_details["provider"], String),
+ repo: T.let(source_details["repo"], String),
+ directory: directory,
+ directories: directories,
+ branch: T.let(source_details["branch"], T.nilable(String)),
+ commit: T.let(source_details["commit"], T.nilable(String)),
+ hostname: T.let(source_details["hostname"], T.nilable(String)),
+ api_endpoint: T.let(source_details["api-endpoint"], T.nilable(String))
)
end
+ sig { params(source_details: T::Hash[String, T.untyped]).returns([T.nilable(String), T.nilable(T::Array[String])]) }
+ def clean_directories(source_details)
+ directory = T.let(source_details["directory"], T.nilable(String))
+ unless directory.nil?
+ directory = Pathname.new(directory).cleanpath.to_s
+ directory = "/#{directory}" unless directory.start_with?("/")
+ end
+ directories = T.let(source_details["directories"], T.nilable(T::Array[String]))
+ unless directories.nil?
+ directories = directories.map do |dir|
+ dir = Pathname.new(dir).cleanpath.to_s
+ dir = "/#{dir}" unless dir.start_with?("/")
+ dir
+ end
+ end
+ [directory, directories]
+ end
+
# Provides a Dependabot::Config::UpdateConfig objected hydrated with
# relevant information obtained from the job definition.
#
# At present we only use this for ignore rules.
- def update_config
- return @update_config if defined? @update_config
-
- @update_config ||= Dependabot::Config::UpdateConfig.new(
- ignore_conditions: ignore_conditions.map do |ic|
- Dependabot::Config::IgnoreCondition.new(
- dependency_name: ic["dependency-name"],
- versions: [ic["version-requirement"]].compact,
- update_types: ic["update-types"]
- )
- end
+ sig { returns(Dependabot::Config::UpdateConfig) }
+ def calculate_update_config
+ update_config_ignore_conditions = ignore_conditions.map do |ic|
+ Dependabot::Config::IgnoreCondition.new(
+ dependency_name: T.let(ic["dependency-name"], String),
+ versions: T.let([ic["version-requirement"]].compact, T::Array[String]),
+ update_types: T.let(ic["update-types"], T.nilable(T::Array[String]))
+ )
+ end
+
+ update_config = Dependabot::Config::UpdateConfig.new(
+ ignore_conditions: T.let(update_config_ignore_conditions, T::Array[Dependabot::Config::IgnoreCondition])
)
+ T.let(update_config, Dependabot::Config::UpdateConfig)
end
end
end
diff --git a/updater/lib/dependabot/logger/formats.rb b/updater/lib/dependabot/logger/formats.rb
index 96a99050..cb8288bf 100644
--- a/updater/lib/dependabot/logger/formats.rb
+++ b/updater/lib/dependabot/logger/formats.rb
@@ -1,4 +1,4 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
require "logger"
diff --git a/updater/lib/dependabot/opentelemetry.rb b/updater/lib/dependabot/opentelemetry.rb
new file mode 100644
index 00000000..3d89b654
--- /dev/null
+++ b/updater/lib/dependabot/opentelemetry.rb
@@ -0,0 +1,109 @@
+# typed: strict
+# frozen_string_literal: true
+
+require "sorbet-runtime"
+require "opentelemetry/sdk"
+
+module Dependabot
+ module OpenTelemetry
+ extend T::Sig
+
+ module Attributes
+ JOB_ID = "dependabot.job.id"
+ ERROR_TYPE = "dependabot.job.error_type"
+ ERROR_DETAILS = "dependabot.job.error_details"
+ METRIC = "dependabot.metric"
+ BASE_COMMIT_SHA = "dependabot.base_commit_sha"
+ DEPENDENCY_NAMES = "dependabot.dependency_names"
+ PR_CLOSE_REASON = "dependabot.pr_close_reason"
+ end
+
+ sig { returns(T::Boolean) }
+ def self.should_configure?
+ ENV["OTEL_ENABLED"] == "true"
+ end
+
+ sig { void }
+ def self.configure
+ return unless should_configure?
+
+ puts "OpenTelemetry is enabled, configuring..."
+
+ require "opentelemetry/exporter/otlp"
+
+ # OpenTelemetry instrumentation expects the related gem to be loaded.
+ # While most are already loaded by this point in initialization, some are not.
+ # We explicitly load them here to ensure that the instrumentation is enabled.
+ require "excon"
+ require "opentelemetry/instrumentation/excon"
+ require "faraday"
+ require "opentelemetry/instrumentation/faraday"
+ require "http"
+ require "opentelemetry/instrumentation/http"
+ require "net/http"
+ require "opentelemetry/instrumentation/net/http"
+
+ ::OpenTelemetry::SDK.configure do |config|
+ config.service_name = "dependabot"
+ config.use "OpenTelemetry::Instrumentation::Excon"
+ config.use "OpenTelemetry::Instrumentation::Faraday"
+ config.use "OpenTelemetry::Instrumentation::HTTP"
+ config.use "OpenTelemetry::Instrumentation::Net::HTTP"
+ end
+
+ tracer
+ end
+
+ sig { returns(::OpenTelemetry::Trace::Tracer) }
+ def self.tracer
+ ::OpenTelemetry.tracer_provider.tracer("dependabot", Dependabot::VERSION)
+ end
+
+ sig { void }
+ def self.shutdown
+ return unless should_configure?
+
+ ::OpenTelemetry.tracer_provider.force_flush
+ ::OpenTelemetry.tracer_provider.shutdown
+ end
+
+ sig do
+ params(
+ job_id: T.any(String, Integer),
+ error_type: T.any(String, Symbol),
+ error_details: T.nilable(T::Hash[T.untyped, T.untyped])
+ ).void
+ end
+ def self.record_update_job_error(job_id:, error_type:, error_details:)
+ current_span = ::OpenTelemetry::Trace.current_span
+
+ attributes = {
+ Attributes::JOB_ID => job_id,
+ Attributes::ERROR_TYPE => error_type
+ }
+
+ error_details&.each do |key, value|
+ attributes.store("#{Attributes::ERROR_DETAILS}.#{key}", value)
+ end
+
+ current_span.add_event(error_type, attributes: attributes)
+ end
+
+ sig do
+ params(
+ error: StandardError,
+ job: T.untyped,
+ tags: T::Hash[String, T.untyped]
+ ).void
+ end
+ def self.record_exception(error:, job: nil, tags: {})
+ current_span = ::OpenTelemetry::Trace.current_span
+
+ current_span.set_attribute(Attributes::JOB_ID, job.id.to_s) if job
+ current_span.add_attributes(tags) if tags.any?
+
+ current_span.status = ::OpenTelemetry::Trace::Status.error(error.message)
+ current_span.record_exception(error)
+ end
+ end
+end
diff --git a/updater/lib/dependabot/sentry.rb b/updater/lib/dependabot/sentry.rb
index 0943c2f9..b4f2b66a 100644
--- a/updater/lib/dependabot/sentry.rb
+++ b/updater/lib/dependabot/sentry.rb
@@ -1,30 +1,21 @@
-# typed: false
+# typed: strong
# frozen_string_literal: true
-require "raven"
+require "sorbet-runtime"
+require "dependabot/sentry/exception_sanitizer_processor"
+require "dependabot/sentry/sentry_context_processor"
-# ExceptionSanitizer filters potential secrets/PII from exception payloads
-class ExceptionSanitizer < Raven::Processor
- REPO = %r{[\w.\-]+/([\w.\-]+)}
- PATTERNS = {
- auth_token: /(?:authorization|bearer):? (\w+)/i,
- repo: %r{api\.github\.com/repos/#{REPO}|github\.com/#{REPO}}
- }.freeze
+module Dependabot
+ module Sentry
+ extend T::Sig
- def process(data)
- return data unless data[:exception] && data[:exception][:values]
-
- data[:exception][:values] = data[:exception][:values].map do |e|
- PATTERNS.each do |key, regex|
- next unless (matches = e[:value].scan(regex))
-
- matches.flatten.compact.each do |match|
- e[:value] = e[:value].gsub(match, "[FILTERED_#{key.to_s.upcase}]")
- end
+ # The default processor chain.
+ # This chain is applied in the order of the array.
+ sig { params(event: ::Sentry::Event, hint: T::Hash[Symbol, T.untyped]).returns(::Sentry::Event) }
+ def self.process_chain(event, hint)
+ [ExceptionSanitizer, SentryContext].each(&:new).reduce(event) do |acc, processor|
+ processor.new.process(acc, hint)
end
- e
end
-
- data
end
end
diff --git a/updater/lib/dependabot/sentry/exception_sanitizer_processor.rb b/updater/lib/dependabot/sentry/exception_sanitizer_processor.rb
new file mode 100644
index 00000000..4bae7463
--- /dev/null
+++ b/updater/lib/dependabot/sentry/exception_sanitizer_processor.rb
@@ -0,0 +1,43 @@
+# typed: strong
+# frozen_string_literal: true
+
+require "sentry-ruby"
+require "sorbet-runtime"
+
+require "dependabot/sentry/processor"
+
+# ExceptionSanitizer filters potential secrets/PII from exception payloads
+class ExceptionSanitizer < ::Dependabot::Sentry::Processor
+ extend T::Sig
+
+ REPO = %r{[\w.\-]+/([\w.\-]+)}
+ PATTERNS = T.let(
+ {
+ auth_token: /(?:authorization|bearer):? (\w+)/i,
+ repo: %r{https://api\.github\.com/repos/#{REPO}|https://github\.com/#{REPO}|git@github\.com:#{REPO}}
+ }.freeze,
+ T::Hash[Symbol, Regexp]
+ )
+
+ sig do
+ override
+ .params(
+ event: ::Sentry::Event,
+ _hint: T::Hash[Symbol, T.untyped]
+ )
+ .returns(::Sentry::Event)
+ end
+ def process(event, _hint)
+ return event unless event.is_a?(::Sentry::ErrorEvent)
+
+ event.exception.values.each do |e|
+ PATTERNS.each do |key, regex|
+ e.value = e.value.gsub(regex) do |match|
+ match.sub(/#{T.must(Regexp.last_match).captures.compact.first}\z/, "[FILTERED_#{key.to_s.upcase}]")
+ end
+ end
+ end
+
+ event
+ end
+end
diff --git a/updater/lib/dependabot/sentry/processor.rb b/updater/lib/dependabot/sentry/processor.rb
new file mode 100644
index 00000000..57103090
--- /dev/null
+++ b/updater/lib/dependabot/sentry/processor.rb
@@ -0,0 +1,26 @@
+# typed: strong
+# frozen_string_literal: true
+
+require "sorbet-runtime"
+
+module Dependabot
+ module Sentry
+ class Processor
+ extend T::Sig
+ extend T::Helpers
+
+ abstract!
+
+ # Process an event before it is sent to Sentry
+ sig do
+ abstract
+ .params(
+ event: ::Sentry::Event,
+ hint: T::Hash[Symbol, T.untyped]
+ )
+ .returns(::Sentry::Event)
+ end
+ def process(event, hint); end
+ end
+ end
+end
diff --git a/updater/lib/dependabot/sentry/sentry_context_processor.rb b/updater/lib/dependabot/sentry/sentry_context_processor.rb
new file mode 100644
index 00000000..4420c46d
--- /dev/null
+++ b/updater/lib/dependabot/sentry/sentry_context_processor.rb
@@ -0,0 +1,26 @@
+# typed: strict
+# frozen_string_literal: true
+
+require "sentry-ruby"
+require "sorbet-runtime"
+
+require "dependabot/sentry/processor"
+
+class SentryContext < ::Dependabot::Sentry::Processor
+ sig do
+ override
+ .params(
+ event: ::Sentry::Event,
+ hint: T::Hash[Symbol, T.untyped]
+ )
+ .returns(::Sentry::Event)
+ end
+ def process(event, hint)
+ if (exception = hint[:exception]) && exception.respond_to?(:sentry_context)
+ exception.sentry_context&.each do |key, value|
+ event.send(:"#{key}=", value)
+ end
+ end
+ event
+ end
+end
diff --git a/updater/lib/dependabot/service.rb b/updater/lib/dependabot/service.rb
index dc835289..5e69e3d2 100644
--- a/updater/lib/dependabot/service.rb
+++ b/updater/lib/dependabot/service.rb
@@ -1,9 +1,14 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
-require "raven"
+require "sentry-ruby"
+require "sorbet-runtime"
require "terminal-table"
+
require "dependabot/api_client"
+require "dependabot/errors"
+require "dependabot/opentelemetry"
+require "dependabot/experiments"
# This class provides an output adapter for the Dependabot Service which manages
# communication with the private API as well as consolidated error handling.
@@ -13,85 +18,128 @@
#
module Dependabot
class Service
+ extend T::Sig
extend Forwardable
- attr_reader :pull_requests, :errors
+ sig { returns(T::Array[T.untyped]) }
+ attr_reader :pull_requests
+
+ sig { returns(T::Array[T::Array[T.untyped]]) }
+ attr_reader :errors
+
+ sig { params(client: Dependabot::ApiClient).void }
def initialize(client:)
@client = client
- @pull_requests = []
- @errors = []
+ @pull_requests = T.let([], T::Array[T.untyped])
+ @errors = T.let([], T::Array[T.untyped])
+ @threads = T.let([], T::Array[T.untyped])
end
def_delegators :client,
:mark_job_as_processed,
- :update_dependency_list,
:record_ecosystem_versions,
:increment_metric
+ sig { void }
+ def wait_for_calls_to_finish
+ return unless Experiments.enabled?("threaded_metadata")
+
+ @threads.each(&:join)
+ end
+
+ sig { params(dependency_change: Dependabot::DependencyChange, base_commit_sha: String).void }
def create_pull_request(dependency_change, base_commit_sha)
- client.create_pull_request(dependency_change, base_commit_sha)
- @pull_requests << [dependency_change.humanized, :created]
+ dependency_change.check_dependencies_have_previous_version if Experiments.enabled?("dependency_change_validation")
+
+ if Experiments.enabled?("threaded_metadata")
+ @threads << Thread.new { client.create_pull_request(dependency_change, base_commit_sha) }
+ else
+ client.create_pull_request(dependency_change, base_commit_sha)
+ end
+ pull_requests << [dependency_change.humanized, :created]
end
+ sig { params(dependency_change: Dependabot::DependencyChange, base_commit_sha: String).void }
def update_pull_request(dependency_change, base_commit_sha)
client.update_pull_request(dependency_change, base_commit_sha)
- @pull_requests << [dependency_change.humanized, :updated]
+ pull_requests << [dependency_change.humanized, :updated]
end
+ sig { params(dependencies: T.any(String, T::Array[String]), reason: T.any(String, Symbol)).void }
def close_pull_request(dependencies, reason)
client.close_pull_request(dependencies, reason)
humanized_deps = dependencies.is_a?(String) ? dependencies : dependencies.join(",")
- @pull_requests << [humanized_deps, "closed: #{reason}"]
+ pull_requests << [humanized_deps, "closed: #{reason}"]
end
+ sig do
+ params(error_type: T.any(String, Symbol), error_details: T.nilable(T::Hash[T.untyped, T.untyped]),
+ dependency: T.nilable(Dependabot::Dependency)).void
+ end
def record_update_job_error(error_type:, error_details:, dependency: nil)
- @errors << [error_type.to_s, dependency]
+ errors << [error_type.to_s, dependency]
client.record_update_job_error(error_type: error_type, error_details: error_details)
end
+ sig { params(error_type: T.any(String, Symbol), error_details: T.nilable(T::Hash[T.untyped, T.untyped])).void }
def record_update_job_unknown_error(error_type:, error_details:)
client.record_update_job_unknown_error(error_type: error_type, error_details: error_details)
end
+ sig { params(dependency_snapshot: Dependabot::DependencySnapshot).void }
def update_dependency_list(dependency_snapshot:)
- dependency_payload = dependency_snapshot.dependencies.map do |dep|
+ dependency_payload = dependency_snapshot.all_dependencies.map do |dep|
{
name: dep.name,
version: dep.version,
requirements: dep.requirements
}
end
- dependency_file_paths = dependency_snapshot.dependency_files.reject(&:support_file).map(&:path)
+ dependency_file_paths = dependency_snapshot.all_dependency_files.reject(&:support_file).map(&:path)
client.update_dependency_list(dependency_payload, dependency_file_paths)
end
- # This method wraps the Raven client as the Application error tracker
+ # This method wraps the Sentry client as the Application error tracker
# the service uses to notice errors.
#
# This should be called as an alternative/in addition to record_update_job_error
# for cases where an error could indicate a problem with the service.
- def capture_exception(error:, job: nil, dependency: nil, dependency_group: nil, tags: {}, extra: {})
- Raven.capture_exception(
- error,
- {
- tags: tags.merge({
- update_job_id: job&.id,
- package_manager: job&.package_manager,
- repo_private: job&.repo_private?
- }.compact),
- extra: extra.merge({
- dependency_name: dependency&.name,
- dependency_group: dependency_group&.name
- }.compact)
- }
- )
- end
-
+ sig do
+ params(
+ error: StandardError,
+ job: T.untyped,
+ dependency: T.nilable(Dependabot::Dependency),
+ dependency_group: T.nilable(Dependabot::DependencyGroup),
+ tags: T::Hash[String, T.untyped]
+ ).void
+ end
+ def capture_exception(error:, job: nil, dependency: nil, dependency_group: nil, tags: {})
+ ::Dependabot::OpenTelemetry.record_exception(error: error, job: job, tags: tags)
+
+ # some GHES versions do not support reporting errors to the service
+ return unless Experiments.enabled?(:record_update_job_unknown_error)
+
+ error_details = {
+ ErrorAttributes::CLASS => error.class.to_s,
+ ErrorAttributes::MESSAGE => error.message,
+ ErrorAttributes::BACKTRACE => error.backtrace&.join("\n"),
+ ErrorAttributes::FINGERPRINT => error.respond_to?(:sentry_context) ? T.unsafe(error).sentry_context[:fingerprint] : nil, # rubocop:disable Layout/LineLength
+ ErrorAttributes::PACKAGE_MANAGER => job&.package_manager,
+ ErrorAttributes::JOB_ID => job&.id,
+ ErrorAttributes::DEPENDENCIES => dependency&.name || job&.dependencies,
+ ErrorAttributes::DEPENDENCY_GROUPS => dependency_group&.name || job&.dependency_groups,
+ ErrorAttributes::SECURITY_UPDATE => job&.security_updates_only?
+ }.compact
+ record_update_job_unknown_error(error_type: "unknown_error", error_details: error_details)
+ end
+
+ sig { returns(T::Boolean) }
def noop?
pull_requests.empty? && errors.empty?
end
+ sig { returns(T::Boolean) }
def failure?
errors.any?
end
@@ -106,6 +154,7 @@ def failure?
# | closed:dependency-removed | package-c |
# +----------------------------+-----------------------------------+
#
+ sig { returns(T.nilable(String)) }
def summary
return if noop?
@@ -120,17 +169,20 @@ def summary
private
+ sig { returns(Dependabot::ApiClient) }
attr_reader :client
+ sig { returns(T.nilable(Terminal::Table)) }
def pull_request_summary
return unless pull_requests.any?
- Terminal::Table.new do |t|
+ T.unsafe(Terminal::Table).new do |t|
t.title = "Changes to Dependabot Pull Requests"
t.rows = pull_requests.map { |deps, action| [action, truncate(deps)] }
end
end
+ sig { returns(T.nilable(String)) }
def error_summary
return unless errors.any?
@@ -144,11 +196,12 @@ def error_summary
# +--------------------+
# | job_repo_not_found |
# +--------------------+
+ sig { returns(T.nilable(Terminal::Table)) }
def job_error_type_summary
job_error_types = errors.filter_map { |error_type, dependency| [error_type] if dependency.nil? }
return if job_error_types.none?
- Terminal::Table.new do |t|
+ T.unsafe(Terminal::Table).new do |t|
t.title = "Errors"
t.rows = job_error_types
end
@@ -161,18 +214,20 @@ def job_error_type_summary
# +---------------------+---------------+
# | best_dependency_yay | unknown_error |
# +---------------------+---------------+
+ sig { returns(T.nilable(Terminal::Table)) }
def dependency_error_summary
dependency_errors = errors.filter_map do |error_type, dependency|
[dependency.name, error_type] unless dependency.nil?
end
return if dependency_errors.none?
- Terminal::Table.new do |t|
+ T.unsafe(Terminal::Table).new do |t|
t.title = "Dependencies failed to update"
t.rows = dependency_errors
end
end
+ sig { params(string: String, max: Integer).returns(String) }
def truncate(string, max: 120)
snip = max - 3
string.length > max ? "#{string[0...snip]}..." : string
diff --git a/updater/lib/dependabot/setup.rb b/updater/lib/dependabot/setup.rb
index 4887c990..8ef255eb 100644
--- a/updater/lib/dependabot/setup.rb
+++ b/updater/lib/dependabot/setup.rb
@@ -1,17 +1,23 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
+require "sentry-ruby"
+require "sorbet-runtime"
+
+require "dependabot/environment"
require "dependabot/logger"
require "dependabot/logger/formats"
-require "dependabot/environment"
+require "dependabot/opentelemetry"
+require "dependabot/sentry"
+require "dependabot/sorbet/runtime"
Dependabot.logger = Logger.new($stdout).tap do |logger|
logger.level = Dependabot::Environment.log_level
logger.formatter = Dependabot::Logger::BasicFormatter.new
end
-require "dependabot/sentry"
-Raven.configure do |config|
+Sentry.init do |config|
+ config.release = ENV.fetch("DEPENDABOT_UPDATER_VERSION")
config.logger = Dependabot.logger
config.project_root = File.expand_path("../../..", __dir__)
@@ -36,15 +42,20 @@
npm_and_yarn|
bundler|
pub|
- swift
+ silent|
+ swift|
+ devcontainers
)}x
- config.processors += [ExceptionSanitizer]
+ config.before_send = ->(event, hint) { Dependabot::Sentry.process_chain(event, hint) }
+ config.propagate_traces = false
+ config.instrumenter = ::Dependabot::OpenTelemetry.should_configure? ? :otel : :sentry
end
-# We configure `Dependabot::Utils.register_always_clone` for some ecosystems. In
-# order for that configuration to take effect, we need to make sure that these
-# registration commands have been executed.
+Dependabot::OpenTelemetry.configure
+Dependabot::Sorbet::Runtime.silently_report_errors!
+
+# Ecosystems
require "dependabot/python"
require "dependabot/terraform"
require "dependabot/elm"
@@ -61,4 +72,6 @@
require "dependabot/npm_and_yarn"
require "dependabot/bundler"
require "dependabot/pub"
+require "dependabot/silent"
require "dependabot/swift"
+require "dependabot/devcontainers"
diff --git a/updater/lib/dependabot/sorbet/runtime.rb b/updater/lib/dependabot/sorbet/runtime.rb
new file mode 100644
index 00000000..b5bc3798
--- /dev/null
+++ b/updater/lib/dependabot/sorbet/runtime.rb
@@ -0,0 +1,33 @@
+# typed: strict
+# frozen_string_literal: true
+
+require "sorbet-runtime"
+
+require "dependabot/api_client"
+require "dependabot/service"
+
+module Dependabot
+ module Sorbet
+ module Runtime
+ class InformationalError < StandardError; end
+ extend T::Sig
+
+ sig { void }
+ def self.silently_report_errors!
+ T::Configuration.call_validation_error_handler = lambda do |_sig, opts|
+ error = InformationalError.new(opts[:pretty_message])
+ error.set_backtrace(caller.dup)
+
+ api_client =
+ Dependabot::ApiClient.new(
+ Environment.api_url,
+ Environment.job_id,
+ Environment.job_token
+ )
+
+ Dependabot::Service.new(client: api_client).capture_exception(error: error)
+ end
+ end
+ end
+ end
+end
diff --git a/updater/lib/dependabot/update_files_command.rb b/updater/lib/dependabot/update_files_command.rb
index 3fa96399..b3cb91d7 100644
--- a/updater/lib/dependabot/update_files_command.rb
+++ b/updater/lib/dependabot/update_files_command.rb
@@ -1,9 +1,11 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
require "base64"
require "dependabot/base_command"
require "dependabot/dependency_snapshot"
+require "dependabot/errors"
+require "dependabot/opentelemetry"
require "dependabot/updater"
module Dependabot
@@ -13,35 +15,42 @@ def perform_job
# encoded files and commit information in the environment, so let's retrieve
# them, decode and parse them into an object that knows the current state
# of the project's dependencies.
- begin
- dependency_snapshot = Dependabot::DependencySnapshot.create_from_job_definition(
+ ::Dependabot::OpenTelemetry.tracer.in_span("update_files", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+
+ begin
+ dependency_snapshot = Dependabot::DependencySnapshot.create_from_job_definition(
+ job: job,
+ job_definition: Environment.job_definition
+ )
+ rescue StandardError => e
+ handle_parser_error(e)
+ # If dependency file parsing has failed, there's nothing more we can do,
+ # so let's mark the job as processed and stop.
+ return service.mark_job_as_processed(Environment.job_definition["base_commit_sha"])
+ end
+
+ # Update the service's metadata about this project
+ service.update_dependency_list(dependency_snapshot: dependency_snapshot)
+
+ # TODO: Pull fatal error handling handling up into this class
+ #
+ # As above, we can remove the responsibility for handling fatal/job halting
+ # errors from Dependabot::Updater entirely.
+ Dependabot::Updater.new(
+ service: service,
job: job,
- job_definition: Environment.job_definition
- )
- rescue StandardError => e
- handle_parser_error(e)
- # If dependency file parsing has failed, there's nothing more we can do,
- # so let's mark the job as processed and stop.
- return service.mark_job_as_processed(Environment.job_definition["base_commit_sha"])
- end
+ dependency_snapshot: dependency_snapshot
+ ).run
+
+ # Wait for all PRs to be created
+ service.wait_for_calls_to_finish
- # Update the service's metadata about this project
- service.update_dependency_list(dependency_snapshot: dependency_snapshot)
-
- # TODO: Pull fatal error handling handling up into this class
- #
- # As above, we can remove the responsibility for handling fatal/job halting
- # errors from Dependabot::Updater entirely.
- Dependabot::Updater.new(
- service: service,
- job: job,
- dependency_snapshot: dependency_snapshot
- ).run
-
- # Finally, mark the job as processed. The Dependabot::Updater may have
- # reported errors to the service, but we always consider the job as
- # successfully processed unless it actually raises.
- service.mark_job_as_processed(dependency_snapshot.base_commit_sha)
+ # Finally, mark the job as processed. The Dependabot::Updater may have
+ # reported errors to the service, but we always consider the job as
+ # successfully processed unless it actually raises.
+ service.mark_job_as_processed(dependency_snapshot.base_commit_sha)
+ end
end
private
@@ -58,99 +67,42 @@ def base_commit_sha
Environment.job_definition["base_commit_sha"]
end
- # rubocop:disable Metrics/AbcSize
- # rubocop:disable Metrics/CyclomaticComplexity
- # rubocop:disable Metrics/MethodLength
+ # rubocop:disable Metrics/AbcSize, Layout/LineLength
def handle_parser_error(error)
# This happens if the repo gets removed after a job gets kicked off.
# The service will handle the removal without any prompt from the updater,
# so no need to add an error to the errors array
return if error.is_a? Dependabot::RepoNotFound
- error_details =
- case error
- when Dependabot::DependencyFileNotEvaluatable
- {
- "error-type": "dependency_file_not_evaluatable",
- "error-detail": { message: error.message }
- }
- when Dependabot::DependencyFileNotResolvable
- {
- "error-type": "dependency_file_not_resolvable",
- "error-detail": { message: error.message }
- }
- when Dependabot::BranchNotFound
- {
- "error-type": "branch_not_found",
- "error-detail": { "branch-name": error.branch_name }
- }
- when Dependabot::DependencyFileNotParseable
- {
- "error-type": "dependency_file_not_parseable",
- "error-detail": {
- message: error.message,
- "file-path": error.file_path
- }
- }
- when Dependabot::DependencyFileNotFound
- {
- "error-type": "dependency_file_not_found",
- "error-detail": { "file-path": error.file_path }
- }
- when Dependabot::PathDependenciesNotReachable
- {
- "error-type": "path_dependencies_not_reachable",
- "error-detail": { dependencies: error.dependencies }
- }
- when Dependabot::PrivateSourceAuthenticationFailure
- {
- "error-type": "private_source_authentication_failure",
- "error-detail": { source: error.source }
- }
- when Dependabot::GitDependenciesNotReachable
- {
- "error-type": "git_dependencies_not_reachable",
- "error-detail": { "dependency-urls": error.dependency_urls }
- }
- when Dependabot::NotImplemented
+ error_details = Dependabot.parser_error_details(error)
+
+ error_details ||=
+ # Check if the error is a known "run halting" state we should handle
+ if (error_type = Updater::ErrorHandler::RUN_HALTING_ERRORS[error.class])
+ { "error-type": error_type }
+ else
+ # If it isn't, then log all the details and let the application error
+ # tracker know about it
+ Dependabot.logger.error error.message
+ error.backtrace.each { |line| Dependabot.logger.error line }
+ unknown_error_details = {
+ ErrorAttributes::CLASS => error.class.to_s,
+ ErrorAttributes::MESSAGE => error.message,
+ ErrorAttributes::BACKTRACE => error.backtrace.join("\n"),
+ ErrorAttributes::FINGERPRINT => error.respond_to?(:sentry_context) ? error.sentry_context[:fingerprint] : nil,
+ ErrorAttributes::PACKAGE_MANAGER => job.package_manager,
+ ErrorAttributes::JOB_ID => job.id,
+ ErrorAttributes::DEPENDENCIES => job.dependencies,
+ ErrorAttributes::DEPENDENCY_GROUPS => job.dependency_groups
+ }.compact
+
+ service.capture_exception(error: error, job: job)
+
+ # Set an unknown error type as update_files_error to be added to the job
{
- "error-type": "not_implemented",
- "error-detail": {
- message: error.message
- }
+ "error-type": "update_files_error",
+ "error-detail": unknown_error_details
}
- when Octokit::ServerError
- # If we get a 500 from GitHub there's very little we can do about it,
- # and responsibility for fixing it is on them, not us. As a result we
- # quietly log these as errors
- { "error-type": "server_error" }
- else
- # Check if the error is a known "run halting" state we should handle
- if (error_type = Updater::ErrorHandler::RUN_HALTING_ERRORS[error.class])
- { "error-type": error_type }
- else
- # If it isn't, then log all the details and let the application error
- # tracker know about it
- Dependabot.logger.error error.message
- error.backtrace.each { |line| Dependabot.logger.error line }
- unknown_error_details = {
- "error-class" => error.class.to_s,
- "error-message" => error.message,
- "error-backtrace" => error.backtrace.join("\n"),
- "package-manager" => job.package_manager,
- "job-id" => job.id,
- "job-dependencies" => job.dependencies,
- "job-dependency_group" => job.dependency_groups
- }.compact
-
- service.capture_exception(error: error, job: job)
-
- # Set an unknown error type as update_files_error to be added to the job
- {
- "error-type": "update_files_error",
- "error-detail": unknown_error_details
- }
- end
end
service.record_update_job_error(
@@ -166,8 +118,6 @@ def handle_parser_error(error)
error_details: error_details[:"error-detail"]
)
end
- # rubocop:enable Metrics/AbcSize
- # rubocop:enable Metrics/CyclomaticComplexity
- # rubocop:enable Metrics/MethodLength
+ # rubocop:enable Metrics/AbcSize, Layout/LineLength
end
end
diff --git a/updater/lib/dependabot/updater.rb b/updater/lib/dependabot/updater.rb
index e98ffc01..7c5b421a 100644
--- a/updater/lib/dependabot/updater.rb
+++ b/updater/lib/dependabot/updater.rb
@@ -1,4 +1,4 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
# Dependabot components
@@ -11,25 +11,6 @@
require "dependabot/security_advisory"
require "dependabot/update_checkers"
-# Ecosystems
-require "dependabot/python"
-require "dependabot/terraform"
-require "dependabot/elm"
-require "dependabot/docker"
-require "dependabot/git_submodules"
-require "dependabot/github_actions"
-require "dependabot/composer"
-require "dependabot/nuget"
-require "dependabot/gradle"
-require "dependabot/maven"
-require "dependabot/hex"
-require "dependabot/cargo"
-require "dependabot/go_modules"
-require "dependabot/npm_and_yarn"
-require "dependabot/bundler"
-require "dependabot/pub"
-require "dependabot/swift"
-
# Updater components
require "dependabot/updater/error_handler"
require "dependabot/updater/operations"
@@ -82,6 +63,9 @@ def run
private
- attr_reader :service, :job, :dependency_snapshot, :error_handler
+ attr_reader :service
+ attr_reader :job
+ attr_reader :dependency_snapshot
+ attr_reader :error_handler
end
end
diff --git a/updater/lib/dependabot/updater/dependency_group_change_batch.rb b/updater/lib/dependabot/updater/dependency_group_change_batch.rb
index 26691ed3..96f542b7 100644
--- a/updater/lib/dependabot/updater/dependency_group_change_batch.rb
+++ b/updater/lib/dependabot/updater/dependency_group_change_batch.rb
@@ -1,6 +1,8 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
+require "pathname"
+
# This class is responsible for aggregating individual DependencyChange objects
# by tracking changes to individual files and the overall dependency list.
module Dependabot
@@ -22,10 +24,17 @@ def initialize(initial_dependency_files:)
end
# Returns an array of DependencyFile objects for the current state
- def current_dependency_files
- @dependency_file_batch.map do |_path, data|
- data[:file]
+ def current_dependency_files(job)
+ directory = Pathname.new(job.source.directory).cleanpath.to_s
+
+ files = @dependency_file_batch.filter_map do |_path, data|
+ data[:file] if Pathname.new(data[:file].directory).cleanpath.to_s == directory
end
+ # This should be prevented in the FileFetcher, but possible due to directory cleaning
+ # that all files are filtered out.
+ raise "No files found for directory #{directory}" if files.empty?
+
+ files
end
# Returns an array of DependencyFile objects for dependency files that have changed at least once merged with
@@ -46,6 +55,11 @@ def merge(dependency_change)
debug_current_file_state
end
+ # add an updated dependency without changing any files, useful for incidental updates
+ def add_updated_dependency(dependency)
+ merge_dependency_changes([dependency])
+ end
+
private
# We should retain a list of all dependencies that we change, in future we may need to account for the folder
diff --git a/updater/lib/dependabot/updater/error_handler.rb b/updater/lib/dependabot/updater/error_handler.rb
index 6593a1f0..645b5f95 100644
--- a/updater/lib/dependabot/updater/error_handler.rb
+++ b/updater/lib/dependabot/updater/error_handler.rb
@@ -1,7 +1,9 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
+require "dependabot/errors"
require "dependabot/updater/errors"
+require "octokit"
# This class is responsible for determining how to present a Dependabot::Error
# to the Service and Logger.
@@ -116,7 +118,8 @@ def log_job_error(error:, error_type:, error_detail: nil)
private
- attr_reader :service, :job
+ attr_reader :service
+ attr_reader :job
# This method accepts an error class and returns an appropriate `error_details` hash
# to be reported to the backend service.
@@ -124,86 +127,20 @@ def log_job_error(error:, error_type:, error_detail: nil)
# For some specific errors, it also passes additional information to the
# exception service to aid in debugging, the optional arguments provide
# context to pass through in these cases.
- def error_details_for(error, dependency: nil, dependency_group: nil) # rubocop:disable Metrics/MethodLength
+ def error_details_for(error, dependency: nil, dependency_group: nil)
+ error_details = Dependabot.updater_error_details(error)
+ return error_details if error_details
+
case error
- when Dependabot::DependencyFileNotResolvable
- {
- "error-type": "dependency_file_not_resolvable",
- "error-detail": { message: error.message }
- }
- when Dependabot::DependencyFileNotEvaluatable
- {
- "error-type": "dependency_file_not_evaluatable",
- "error-detail": { message: error.message }
- }
- when Dependabot::GitDependenciesNotReachable
- {
- "error-type": "git_dependencies_not_reachable",
- "error-detail": { "dependency-urls": error.dependency_urls }
- }
- when Dependabot::GitDependencyReferenceNotFound
- {
- "error-type": "git_dependency_reference_not_found",
- "error-detail": { dependency: error.dependency }
- }
- when Dependabot::PrivateSourceAuthenticationFailure
- {
- "error-type": "private_source_authentication_failure",
- "error-detail": { source: error.source }
- }
- when Dependabot::PrivateSourceTimedOut
- {
- "error-type": "private_source_timed_out",
- "error-detail": { source: error.source }
- }
- when Dependabot::PrivateSourceCertificateFailure
- {
- "error-type": "private_source_certificate_failure",
- "error-detail": { source: error.source }
- }
- when Dependabot::MissingEnvironmentVariable
- {
- "error-type": "missing_environment_variable",
- "error-detail": {
- "environment-variable": error.environment_variable
- }
- }
- when Dependabot::GoModulePathMismatch
- {
- "error-type": "go_module_path_mismatch",
- "error-detail": {
- "declared-path": error.declared_path,
- "discovered-path": error.discovered_path,
- "go-mod": error.go_mod
- }
- }
- when Dependabot::NotImplemented
- {
- "error-type": "not_implemented",
- "error-detail": {
- message: error.message
- }
- }
when Dependabot::SharedHelpers::HelperSubprocessFailed
# If a helper subprocess has failed the error may include sensitive
# info such as file contents or paths. This information is already
# in the job logs, so we send a breadcrumb to Sentry to retrieve those
# instead.
- msg = "Subprocess #{error.raven_context[:fingerprint]} failed to run. Check the job logs for error messages"
- sanitized_error = SubprocessFailed.new(msg, raven_context: error.raven_context)
+ msg = "Subprocess #{error.sentry_context[:fingerprint]} failed to run. Check the job logs for error messages"
+ sanitized_error = SubprocessFailed.new(msg, sentry_context: error.sentry_context)
sanitized_error.set_backtrace(error.backtrace)
service.capture_exception(error: sanitized_error, job: job)
-
- { "error-type": "unknown_error" }
- when *Octokit::RATE_LIMITED_ERRORS
- # If we get a rate-limited error we let dependabot-api handle the
- # retry by re-enqueing the update job after the reset
- {
- "error-type": "octokit_rate_limited",
- "error-detail": {
- "rate-limit-reset": error.response_headers["X-RateLimit-Reset"]
- }
- }
else
service.capture_exception(
error: error,
@@ -211,19 +148,21 @@ def error_details_for(error, dependency: nil, dependency_group: nil) # rubocop:d
dependency: dependency,
dependency_group: dependency_group
)
- { "error-type": "unknown_error" }
end
+
+ { "error-type": "unknown_error" }
end
def log_unknown_error_with_backtrace(error)
error_details = {
- "error-class" => error.class.to_s,
- "error-message" => error.message,
- "error-backtrace" => error.backtrace.join("\n"),
- "package-manager" => job.package_manager,
- "job-id" => job.id,
- "job-dependencies" => job.dependencies,
- "job-dependency_group" => job.dependency_groups
+ ErrorAttributes::CLASS => error.class.to_s,
+ ErrorAttributes::MESSAGE => error.message,
+ ErrorAttributes::BACKTRACE => error.backtrace.join("\n"),
+ ErrorAttributes::FINGERPRINT => error.respond_to?(:sentry_context) ? error.sentry_context[:fingerprint] : nil,
+ ErrorAttributes::PACKAGE_MANAGER => job.package_manager,
+ ErrorAttributes::JOB_ID => job.id,
+ ErrorAttributes::DEPENDENCIES => job.dependencies,
+ ErrorAttributes::DEPENDENCY_GROUPS => job.dependency_groups
}.compact
service.increment_metric("updater.update_job_unknown_error", tags: {
diff --git a/updater/lib/dependabot/updater/errors.rb b/updater/lib/dependabot/updater/errors.rb
index 09353e2b..75e71208 100644
--- a/updater/lib/dependabot/updater/errors.rb
+++ b/updater/lib/dependabot/updater/errors.rb
@@ -1,15 +1,15 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
module Dependabot
class Updater
class SubprocessFailed < StandardError
- attr_reader :raven_context
+ attr_reader :sentry_context
- def initialize(message, raven_context:)
+ def initialize(message, sentry_context:)
super(message)
- @raven_context = raven_context
+ @sentry_context = sentry_context
end
end
end
diff --git a/updater/lib/dependabot/updater/group_update_creation.rb b/updater/lib/dependabot/updater/group_update_creation.rb
index c7b951f2..50d5ddca 100644
--- a/updater/lib/dependabot/updater/group_update_creation.rb
+++ b/updater/lib/dependabot/updater/group_update_creation.rb
@@ -1,6 +1,8 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
+require "sorbet-runtime"
+
require "dependabot/dependency_change_builder"
require "dependabot/updater/dependency_group_change_batch"
require "dependabot/workspace"
@@ -15,18 +17,45 @@
#
module Dependabot
class Updater
+ extend T::Sig
+
module GroupUpdateCreation
+ extend T::Sig
+ extend T::Helpers
+
+ abstract!
+
+ sig { returns(Dependabot::DependencySnapshot) }
+ attr_reader :dependency_snapshot
+
+ sig { returns(Dependabot::Updater::ErrorHandler) }
+ attr_reader :error_handler
+
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
+
+ sig { returns(Dependabot::DependencyGroup) }
+ attr_reader :group
+
# Returns a Dependabot::DependencyChange object that encapsulates the
# outcome of attempting to update every dependency iteratively which
# can be used for PR creation.
- def compile_all_dependency_changes_for(group) # rubocop:disable Metrics/AbcSize
+ # rubocop:disable Metrics/AbcSize
+ # rubocop:disable Metrics/MethodLength
+ # rubocop:disable Metrics/PerceivedComplexity
+ sig { params(group: Dependabot::DependencyGroup).returns(T.nilable(Dependabot::DependencyChange)) }
+ def compile_all_dependency_changes_for(group)
prepare_workspace
group_changes = Dependabot::Updater::DependencyGroupChangeBatch.new(
initial_dependency_files: dependency_snapshot.dependency_files
)
+ original_dependencies = dependency_snapshot.dependencies
+ Dependabot.logger.info("Updating the #{job.source.directory} directory.")
group.dependencies.each do |dependency|
+ # We still want to update a dependency if it's been updated in another manifest files,
+ # but we should skip it if it's been updated in _the same_ manifest file
if dependency_snapshot.handled_dependencies.include?(dependency.name)
Dependabot.logger.info(
"Skipping #{dependency.name} as it has already been handled by a previous group"
@@ -34,8 +63,8 @@ def compile_all_dependency_changes_for(group) # rubocop:disable Metrics/AbcSize
next
end
- # Get the current state of the dependency files for use in this iteration
- dependency_files = group_changes.current_dependency_files
+ # Get the current state of the dependency files for use in this iteration, filter by directory
+ dependency_files = group_changes.current_dependency_files(job)
# Reparse the current files
reparsed_dependencies = dependency_file_parser(dependency_files).parse
@@ -45,15 +74,22 @@ def compile_all_dependency_changes_for(group) # rubocop:disable Metrics/AbcSize
# dependency update
next if dependency.nil?
- updated_dependencies = compile_updates_for(dependency, dependency_files, group)
+ # If the dependency version changed, then we can deduce that the dependency was updated already.
+ original_dependency = original_dependencies.find { |d| d.name == dependency.name }
+ updated_dependency = deduce_updated_dependency(dependency, original_dependency)
+ unless updated_dependency.nil?
+ group_changes.add_updated_dependency(updated_dependency)
+ next
+ end
+ updated_dependencies = compile_updates_for(dependency, dependency_files, group)
next unless updated_dependencies.any?
lead_dependency = updated_dependencies.find do |dep|
- dep.name.casecmp(dependency.name).zero?
+ dep.name.casecmp(dependency.name)&.zero?
end
- dependency_change = create_change_for(lead_dependency, updated_dependencies, dependency_files, group)
+ dependency_change = create_change_for(T.must(lead_dependency), updated_dependencies, dependency_files, group)
# Move on to the next dependency using the existing files if we
# could not create a change for any reason
@@ -66,16 +102,38 @@ def compile_all_dependency_changes_for(group) # rubocop:disable Metrics/AbcSize
# Create a single Dependabot::DependencyChange that aggregates everything we've updated
# into a single object we can pass to PR creation.
- Dependabot::DependencyChange.new(
+ dependency_change = Dependabot::DependencyChange.new(
job: job,
updated_dependencies: group_changes.updated_dependencies,
updated_dependency_files: group_changes.updated_dependency_files,
dependency_group: group
)
+
+ if Experiments.enabled?("dependency_change_validation") && !dependency_change.all_have_previous_version?
+ log_missing_previous_version(dependency_change)
+ return nil
+ end
+
+ dependency_change
ensure
cleanup_workspace
end
+ # rubocop:enable Metrics/PerceivedComplexity
+ # rubocop:enable Metrics/AbcSize
+ # rubocop:enable Metrics/MethodLength
+ def log_missing_previous_version(dependency_change)
+ deps_no_previous_version = dependency_change.updated_dependencies.reject(&:previous_version).map(&:name)
+ deps_no_change = dependency_change.updated_dependencies.reject(&:requirements_changed?).map(&:name)
+ msg = "Skipping change to group #{group.name} in directory #{job.source.directory}: "
+ if deps_no_previous_version.any?
+ msg += "Previous version was not provided for: '#{deps_no_previous_version.join(', ')}' "
+ end
+ msg += "No requirements change for: '#{deps_no_change.join(', ')}'" if deps_no_change.any?
+ Dependabot.logger.info(msg)
+ end
+
+ sig { params(dependency_files: T::Array[Dependabot::DependencyFile]).returns(Dependabot::FileParsers::Base) }
def dependency_file_parser(dependency_files)
Dependabot::FileParsers.for_package_manager(job.package_manager).new(
dependency_files: dependency_files,
@@ -91,6 +149,15 @@ def dependency_file_parser(dependency_files)
# list of dependencies to be updated
#
# This method **must** return false in the event of an error
+ sig do
+ params(
+ lead_dependency: Dependabot::Dependency,
+ updated_dependencies: T::Array[Dependabot::Dependency],
+ dependency_files: T::Array[Dependabot::DependencyFile],
+ dependency_group: Dependabot::DependencyGroup
+ )
+ .returns(T.any(Dependabot::DependencyChange, FalseClass))
+ end
def create_change_for(lead_dependency, updated_dependencies, dependency_files, dependency_group)
Dependabot::DependencyChangeBuilder.create_from(
job: job,
@@ -121,6 +188,14 @@ def create_change_for(lead_dependency, updated_dependencies, dependency_files, d
#
# This method **must** must return an Array when it errors
#
+ sig do
+ params(
+ dependency: Dependabot::Dependency,
+ dependency_files: T::Array[Dependabot::DependencyFile],
+ group: Dependabot::DependencyGroup
+ )
+ .returns(T::Array[Dependabot::Dependency])
+ end
def compile_updates_for(dependency, dependency_files, group) # rubocop:disable Metrics/MethodLength
checker = update_checker_for(
dependency,
@@ -157,7 +232,7 @@ def compile_updates_for(dependency, dependency_files, group) # rubocop:disable M
requirements_to_unlock: requirements_to_unlock
)
rescue Dependabot::InconsistentRegistryResponse => e
- dependency_snapshot.add_handled_dependencies(dependency)
+ dependency_snapshot.add_handled_dependencies(dependency.name)
error_handler.log_dependency_error(
dependency: dependency,
error: e,
@@ -173,16 +248,27 @@ def compile_updates_for(dependency, dependency_files, group) # rubocop:disable M
[] # return an empty set
end
+ sig { params(dependency: Dependabot::Dependency).void }
def log_up_to_date(dependency)
Dependabot.logger.info(
"No update needed for #{dependency.name} #{dependency.version}"
)
end
+ sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
def raise_on_ignored?(dependency)
job.ignore_conditions_for(dependency).any?
end
+ sig do
+ params(
+ dependency: Dependabot::Dependency,
+ dependency_files: T::Array[Dependabot::DependencyFile],
+ dependency_group: Dependabot::DependencyGroup,
+ raise_on_ignored: T::Boolean
+ )
+ .returns(Dependabot::UpdateCheckers::Base)
+ end
def update_checker_for(dependency, dependency_files, dependency_group, raise_on_ignored:)
Dependabot::UpdateCheckers.for_package_manager(job.package_manager).new(
dependency: dependency,
@@ -198,6 +284,7 @@ def update_checker_for(dependency, dependency_files, dependency_group, raise_on_
)
end
+ sig { params(dependency: Dependabot::Dependency).void }
def log_checking_for_update(dependency)
Dependabot.logger.info(
"Checking if #{dependency.name} #{dependency.version} needs updating"
@@ -205,8 +292,13 @@ def log_checking_for_update(dependency)
job.log_ignore_conditions_for(dependency)
end
+ sig { params(dependency: Dependabot::Dependency, checker: Dependabot::UpdateCheckers::Base).returns(T::Boolean) }
def all_versions_ignored?(dependency, checker)
- Dependabot.logger.info("Latest version is #{checker.latest_version}")
+ if job.security_updates_only?
+ Dependabot.logger.info("Lowest security fix version is #{checker.lowest_security_fix_version}")
+ else
+ Dependabot.logger.info("Latest version is #{checker.latest_version}")
+ end
false
rescue Dependabot::AllVersionsIgnored
Dependabot.logger.info("All updates for #{dependency.name} were ignored")
@@ -217,6 +309,15 @@ def all_versions_ignored?(dependency, checker)
# then it should not be in the group, but be an individual PR, or in another group that fits it.
# SemVer Grouping rules have to be applied after we have a checker, because we need to know the latest version.
# Other rules are applied earlier in the process.
+ # rubocop:disable Metrics/AbcSize
+ sig do
+ params(
+ group: Dependabot::DependencyGroup,
+ dependency: Dependabot::Dependency,
+ checker: Dependabot::UpdateCheckers::Base
+ )
+ .returns(T::Boolean)
+ end
def semver_rules_allow_grouping?(group, dependency, checker)
# There are no group rules defined, so this dependency can be included in the group.
return true unless group.rules["update-types"]
@@ -232,14 +333,19 @@ def semver_rules_allow_grouping?(group, dependency, checker)
# Not every version class implements .major, .minor, .patch so we calculate it here from the segments
latest = semver_segments(latest_version)
current = semver_segments(version)
- return group.rules["update-types"].include?("major") if latest[:major] > current[:major]
- return group.rules["update-types"].include?("minor") if latest[:minor] > current[:minor]
- return group.rules["update-types"].include?("patch") if latest[:patch] > current[:patch]
+ # Ensure that semver components are of the same type and can be compared with each other.
+ return false unless %i(major minor patch).all? { |k| current[k].instance_of?(latest[k].class) }
+
+ return T.must(group.rules["update-types"]).include?("major") if T.must(latest[:major]) > T.must(current[:major])
+ return T.must(group.rules["update-types"]).include?("minor") if T.must(latest[:minor]) > T.must(current[:minor])
+ return T.must(group.rules["update-types"]).include?("patch") if T.must(latest[:patch]) > T.must(current[:patch])
# some ecosystems don't do semver exactly, so anything lower gets individual for now
false
end
+ # rubocop:enable Metrics/AbcSize
+ sig { params(version: Gem::Version).returns(T::Hash[Symbol, Integer]) }
def semver_segments(version)
{
major: version.segments[0] || 0,
@@ -248,6 +354,7 @@ def semver_segments(version)
}
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).returns(Symbol) }
def requirements_to_unlock(checker)
if !checker.requirements_unlocked_or_can_be?
if checker.can_update?(requirements_to_unlock: :none) then :none
@@ -261,16 +368,18 @@ def requirements_to_unlock(checker)
end
end
+ sig { params(requirements_to_unlock: Symbol, checker: Dependabot::UpdateCheckers::Base).void }
def log_requirements_for_update(requirements_to_unlock, checker)
Dependabot.logger.info("Requirements to unlock #{requirements_to_unlock}")
return unless checker.respond_to?(:requirements_update_strategy)
Dependabot.logger.info(
- "Requirements update strategy #{checker.requirements_update_strategy}"
+ "Requirements update strategy #{checker.requirements_update_strategy&.serialize}"
)
end
+ sig { params(group: Dependabot::DependencyGroup).void }
def warn_group_is_empty(group)
Dependabot.logger.warn(
"Skipping update group for '#{group.name}' as it does not match any allowed dependencies."
@@ -285,35 +394,64 @@ def warn_group_is_empty(group)
DEBUG
end
+ sig { void }
def prepare_workspace
return unless job.clone? && job.repo_contents_path
Dependabot::Workspace.setup(
- repo_contents_path: job.repo_contents_path,
+ repo_contents_path: T.must(job.repo_contents_path),
directory: Pathname.new(job.source.directory || "/").cleanpath
)
end
+ sig do
+ params(dependency: Dependabot::Dependency)
+ .returns(T.nilable(T::Array[Dependabot::Workspace::ChangeAttempt]))
+ end
def store_changes(dependency)
return unless job.clone? && job.repo_contents_path
Dependabot::Workspace.store_change(memo: "Updating #{dependency.name}")
end
+ sig { void }
def cleanup_workspace
return unless job.clone? && job.repo_contents_path
Dependabot::Workspace.cleanup!
end
+ sig { params(group: Dependabot::DependencyGroup).returns(T::Boolean) }
def pr_exists_for_dependency_group?(group)
- job.existing_group_pull_requests&.any? { |pr| pr["dependency-group-name"] == group.name }
+ job.existing_group_pull_requests.any? { |pr| pr["dependency-group-name"] == group.name }
+ end
+
+ sig do
+ params(
+ dependency: T.nilable(Dependabot::Dependency),
+ original_dependency: T.nilable(Dependabot::Dependency)
+ )
+ .returns(T.nilable(Dependabot::Dependency))
end
+ def deduce_updated_dependency(dependency, original_dependency)
+ return nil if dependency.nil? || original_dependency.nil?
+ return nil if original_dependency.version == dependency.version
+
+ Dependabot.logger.info(
+ "Skipping #{dependency.name} as it has already been updated to #{dependency.version}"
+ )
+ dependency_snapshot.handled_dependencies << dependency.name
+
+ dependency_params = {
+ name: dependency.name,
+ version: dependency.version,
+ previous_version: original_dependency.version,
+ requirements: dependency.requirements,
+ previous_requirements: original_dependency.requirements,
+ package_manager: dependency.package_manager
+ }
- def dependencies_in_existing_pr_for_group(group)
- job.existing_group_pull_requests.find do |pr|
- pr["dependency-group-name"] == group.name
- end.fetch("dependencies", [])
+ Dependabot::Dependency.new(**dependency_params)
end
end
end
diff --git a/updater/lib/dependabot/updater/group_update_refreshing.rb b/updater/lib/dependabot/updater/group_update_refreshing.rb
new file mode 100644
index 00000000..74b18269
--- /dev/null
+++ b/updater/lib/dependabot/updater/group_update_refreshing.rb
@@ -0,0 +1,82 @@
+# typed: true
+# frozen_string_literal: true
+
+require "sorbet-runtime"
+
+# This module contains the methods required to refresh (upsert or recreate)
+# existing grouped pull requests.
+#
+# When included in an Operation it expects the following to be available:
+# - job: the current Dependabot::Job object
+# - dependency_snapshot: the Dependabot::DependencySnapshot of the current state
+# - error_handler: a Dependabot::UpdaterErrorHandler to report any problems to
+#
+module Dependabot
+ class Updater
+ module GroupUpdateRefreshing
+ extend T::Sig
+ extend T::Helpers
+
+ abstract!
+
+ sig { returns(Dependabot::Service) }
+ attr_reader :service
+
+ sig { returns(Dependabot::Updater::ErrorHandler) }
+ attr_reader :error_handler
+
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
+
+ sig { returns(Dependabot::DependencySnapshot) }
+ attr_reader :dependency_snapshot
+
+ sig { params(dependency_change: Dependabot::DependencyChange, group: Dependabot::DependencyGroup).void }
+ def upsert_pull_request_with_error_handling(dependency_change, group)
+ if dependency_change.updated_dependencies.any?
+ upsert_pull_request(dependency_change, group)
+ else
+ Dependabot.logger.info("No updated dependencies, closing existing Pull Request")
+ close_pull_request(reason: :update_no_longer_possible, group: group)
+ end
+ rescue StandardError => e
+ error_handler.handle_job_error(error: e, dependency_group: dependency_snapshot.job_group)
+ end
+
+ # Having created the dependency_change, we need to determine the right strategy to apply it to the project:
+ # - Replace existing PR if the dependencies involved have changed
+ # - Update the existing PR if the dependencies and the target versions remain the same
+ # - Supersede the existing PR if the dependencies are the same but the target versions have changed
+ sig { params(dependency_change: Dependabot::DependencyChange, group: Dependabot::DependencyGroup).void }
+ def upsert_pull_request(dependency_change, group)
+ if dependency_change.should_replace_existing_pr?
+ Dependabot.logger.info("Dependencies have changed, closing existing Pull Request")
+ close_pull_request(reason: :dependencies_changed, group: group)
+ Dependabot.logger.info("Creating a new pull request for '#{group.name}'")
+ service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
+ elsif dependency_change.matches_existing_pr?
+ Dependabot.logger.info("Updating pull request for '#{group.name}'")
+ service.update_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
+ else
+ # If the changes do not match an existing PR, then we should open a new pull request and leave it to
+ # the backend to close the existing pull request with a comment that it has been superseded.
+ Dependabot.logger.info("Target versions have changed, existing Pull Request should be superseded")
+ Dependabot.logger.info("Creating a new pull request for '#{group.name}'")
+ service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
+ end
+ end
+
+ sig { params(reason: Symbol, group: Dependabot::DependencyGroup).void }
+ def close_pull_request(reason:, group:)
+ reason_string = reason.to_s.tr("_", " ")
+ Dependabot.logger.info(
+ "Telling backend to close pull request for the " \
+ "#{group.name} group " \
+ "(#{job.dependencies&.join(', ')}) - #{reason_string}"
+ )
+
+ service.close_pull_request(T.must(job.dependencies), reason)
+ end
+ end
+ end
+end
diff --git a/updater/lib/dependabot/updater/operations.rb b/updater/lib/dependabot/updater/operations.rb
index 22482942..b1b59187 100644
--- a/updater/lib/dependabot/updater/operations.rb
+++ b/updater/lib/dependabot/updater/operations.rb
@@ -1,7 +1,6 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
-require "dependabot/updater/operations/create_group_security_update_pull_request"
require "dependabot/updater/operations/create_security_update_pull_request"
require "dependabot/updater/operations/group_update_all_versions"
require "dependabot/updater/operations/refresh_group_update_pull_request"
@@ -31,12 +30,11 @@ module Operations
# that does, so these Operations should be ordered so that those with most
# specific preconditions go before those with more permissive checks.
OPERATIONS = [
- CreateGroupSecurityUpdatePullRequest,
+ GroupUpdateAllVersions,
+ RefreshGroupUpdatePullRequest,
CreateSecurityUpdatePullRequest,
RefreshSecurityUpdatePullRequest,
- RefreshGroupUpdatePullRequest,
RefreshVersionUpdatePullRequest,
- GroupUpdateAllVersions,
UpdateAllVersions
].freeze
diff --git a/updater/lib/dependabot/updater/operations/create_group_security_update_pull_request.rb b/updater/lib/dependabot/updater/operations/create_group_security_update_pull_request.rb
deleted file mode 100644
index 433e6697..00000000
--- a/updater/lib/dependabot/updater/operations/create_group_security_update_pull_request.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-# typed: false
-# frozen_string_literal: true
-
-require "dependabot/updater/security_update_helpers"
-require "dependabot/updater/group_update_creation"
-
-# This class implements our strategy for updating multiple, insecure dependencies
-# to a secure version. We attempt to make the smallest version update possible,
-# i.e. semver patch-level increase is preferred over minor-level increase.
-module Dependabot
- class Updater
- module Operations
- class CreateGroupSecurityUpdatePullRequest
- include SecurityUpdateHelpers
- include GroupUpdateCreation
-
- def self.applies_to?(job:)
- return false if job.updating_a_pull_request?
- # If we haven't been given data for the vulnerable dependency,
- # this strategy cannot act.
- return false unless job.dependencies&.any?
-
- return false unless job.security_updates_only?
-
- true if job.dependencies.count > 1
- end
-
- def self.tag_name
- :create_security_pr
- end
-
- def initialize(service:, job:, dependency_snapshot:, error_handler:)
- @service = service
- @job = job
- @dependency_snapshot = dependency_snapshot
- @error_handler = error_handler
- # TODO: Collect @created_pull_requests on the Job object?
- @created_pull_requests = []
- end
-
- def perform
- Dependabot.logger.info("Starting security update job for #{job.source.repo}")
-
- target_dependencies = dependency_snapshot.job_dependencies
-
- if target_dependencies.empty?
- record_security_update_dependency_not_found
- else
- # make a temporary fake group to use the existing logic
- group = Dependabot::DependencyGroup.new(
- name: "#{job.package_manager} at #{job.source.directory || '/'} security update",
- rules: {
- "patterns" => "*" # The grouping is more dictated by the dependencies passed in.
- }
- )
- target_dependencies.each do |dep|
- group.dependencies << dep
- end
-
- dependency_change = compile_all_dependency_changes_for(group)
-
- if dependency_change.updated_dependencies.any?
- Dependabot.logger.info("Creating a pull request for '#{group.name}'")
- begin
- service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
- rescue StandardError => e
- error_handler.handle_job_error(error: e, dependency_group: group)
- end
- else
- Dependabot.logger.info("Nothing to update for Dependency Group: '#{group.name}'")
- end
-
- dependency_change
- end
- end
-
- private
-
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler,
- :created_pull_requests
- end
- end
- end
-end
diff --git a/updater/lib/dependabot/updater/operations/create_group_update_pull_request.rb b/updater/lib/dependabot/updater/operations/create_group_update_pull_request.rb
index 6d5641be..91c047ba 100644
--- a/updater/lib/dependabot/updater/operations/create_group_update_pull_request.rb
+++ b/updater/lib/dependabot/updater/operations/create_group_update_pull_request.rb
@@ -1,11 +1,12 @@
-# typed: false
+# typed: strong
# frozen_string_literal: true
require "dependabot/updater/group_update_creation"
+require "sorbet-runtime"
# This class implements our strategy for creating a single Pull Request which
# updates all outdated Dependencies within a specific project folder that match
-# a specificed Dependency Group.
+# a specified Dependency Group.
#
# This will always post a new Pull Request to Dependabot API and does not check
# to see if any exists for the group or any of the dependencies involved.
@@ -14,20 +15,32 @@ module Dependabot
class Updater
module Operations
class CreateGroupUpdatePullRequest
+ extend T::Sig
include GroupUpdateCreation
# We do not invoke this class directly for any jobs, so let's return false in the event this
# check is called.
- def self.applies_to?(*)
+ sig { params(_job: Dependabot::Job).returns(T::Boolean) }
+ def self.applies_to?(_job:)
false
end
+ sig { returns(Symbol) }
def self.tag_name
:create_version_group_pr
end
# Since this class is not invoked generically based on the job definition, this class accepts a `group` argument
# which is expected to be a prepopulated DependencyGroup object.
+ sig do
+ params(
+ service: Dependabot::Service,
+ job: Dependabot::Job,
+ dependency_snapshot: Dependabot::DependencySnapshot,
+ error_handler: ErrorHandler,
+ group: Dependabot::DependencyGroup
+ ).void
+ end
def initialize(service:, job:, dependency_snapshot:, error_handler:, group:)
@service = service
@job = job
@@ -36,17 +49,19 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:, group:)
@group = group
end
+ sig { returns(T.nilable(Dependabot::DependencyChange)) }
def perform
- return warn_group_is_empty(group) if group.dependencies.empty?
+ if group.dependencies.empty?
+ warn_group_is_empty(group)
+ return nil
+ end
Dependabot.logger.info("Starting update group for '#{group.name}'")
- dependency_change = compile_all_dependency_changes_for(group)
-
- if dependency_change.updated_dependencies.any?
+ if dependency_change&.updated_dependencies&.any?
Dependabot.logger.info("Creating a pull request for '#{group.name}'")
begin
- service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
+ service.create_pull_request(T.must(dependency_change), dependency_snapshot.base_commit_sha)
rescue StandardError => e
error_handler.handle_job_error(error: e, dependency_group: group)
end
@@ -59,11 +74,40 @@ def perform
private
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler,
- :group
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
+
+ sig { returns(Dependabot::Service) }
+ attr_reader :service
+
+ sig { returns(Dependabot::DependencySnapshot) }
+ attr_reader :dependency_snapshot
+
+ sig { returns(Dependabot::Updater::ErrorHandler) }
+ attr_reader :error_handler
+
+ sig { returns(Dependabot::DependencyGroup) }
+ attr_reader :group
+
+ sig { returns(T.nilable(Dependabot::DependencyChange)) }
+ def dependency_change
+ return @dependency_change if defined?(@dependency_change)
+
+ if job.source.directories.nil?
+ @dependency_change = compile_all_dependency_changes_for(group)
+ else
+ dependency_changes = T.must(job.source.directories).filter_map do |directory|
+ job.source.directory = directory
+ dependency_snapshot.current_directory = directory
+ compile_all_dependency_changes_for(group)
+ end
+
+ # merge the changes together into one
+ dependency_change = T.let(T.must(dependency_changes.first), Dependabot::DependencyChange)
+ dependency_change.merge_changes!(T.must(dependency_changes[1..-1])) if dependency_changes.count > 1
+ @dependency_change = T.let(dependency_change, T.nilable(Dependabot::DependencyChange))
+ end
+ end
end
end
end
diff --git a/updater/lib/dependabot/updater/operations/create_security_update_pull_request.rb b/updater/lib/dependabot/updater/operations/create_security_update_pull_request.rb
index d505c3a1..941f469b 100644
--- a/updater/lib/dependabot/updater/operations/create_security_update_pull_request.rb
+++ b/updater/lib/dependabot/updater/operations/create_security_update_pull_request.rb
@@ -1,4 +1,4 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
require "dependabot/updater/security_update_helpers"
@@ -10,8 +10,10 @@ module Dependabot
class Updater
module Operations
class CreateSecurityUpdatePullRequest
+ extend T::Sig
include SecurityUpdateHelpers
+ sig { params(job: Job).returns(T::Boolean) }
def self.applies_to?(job:)
return false if job.updating_a_pull_request?
# If we haven't been given data for the vulnerable dependency,
@@ -21,17 +23,26 @@ def self.applies_to?(job:)
job.security_updates_only?
end
+ sig { returns(Symbol) }
def self.tag_name
:create_security_pr
end
+ sig do
+ params(
+ service: Dependabot::Service,
+ job: Dependabot::Job,
+ dependency_snapshot: Dependabot::DependencySnapshot,
+ error_handler: Dependabot::Updater::ErrorHandler
+ ).void
+ end
def initialize(service:, job:, dependency_snapshot:, error_handler:)
@service = service
@job = job
@dependency_snapshot = dependency_snapshot
@error_handler = error_handler
# TODO: Collect @created_pull_requests on the Job object?
- @created_pull_requests = []
+ @created_pull_requests = T.let([], T::Array[T::Array[T::Hash[String, String]]])
end
# TODO: We currently tolerate multiple dependencies for this operation
@@ -40,6 +51,7 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:)
# Changing this contract now without some safety catches introduces
# risk, so we'll maintain the interface as-is for now, but this is
# something we should make much more intentional in future.
+ sig { void }
def perform
Dependabot.logger.info("Starting security update job for #{job.source.repo}")
@@ -54,12 +66,18 @@ def perform
private
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler,
- :created_pull_requests
-
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
+ sig { returns(Dependabot::Service) }
+ attr_reader :service
+ sig { returns(Dependabot::DependencySnapshot) }
+ attr_reader :dependency_snapshot
+ sig { returns(Dependabot::Updater::ErrorHandler) }
+ attr_reader :error_handler
+ sig { returns(T::Array[T::Array[T::Hash[String, String]]]) }
+ attr_reader :created_pull_requests
+
+ sig { params(dependency: Dependabot::Dependency).void }
def check_and_create_pr_with_error_handling(dependency)
check_and_create_pull_request(dependency)
rescue Dependabot::InconsistentRegistryResponse => e
@@ -76,7 +94,9 @@ def check_and_create_pr_with_error_handling(dependency)
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
+ sig { params(dependency: Dependabot::Dependency).void }
def check_and_create_pull_request(dependency)
+ dependency = vulnerable_version(dependency) if dependency.metadata[:all_versions]
checker = update_checker_for(dependency)
log_checking_for_update(dependency)
@@ -87,7 +107,7 @@ def check_and_create_pull_request(dependency)
# The current dependency isn't vulnerable if the version is correct and
# can be matched against the advisories affected versions
if checker.version_class.correct?(checker.dependency.version)
- return record_security_update_not_needed_error(checker)
+ return record_security_update_not_needed_error(checker.dependency)
end
return record_dependency_file_not_supported_error(checker)
@@ -120,7 +140,7 @@ def check_and_create_pull_request(dependency)
# Prevent updates that don't end up fixing any security advisories,
# blocking any updates where dependabot-core updates to a vulnerable
- # version. This happens for npm/yarn subdendencies where Dependabot has no
+ # version. This happens for npm/yarn sub-dependencies where Dependabot has no
# control over the target version. Related issue:
# https://github.com/github/dependabot-api/issues/905
return record_security_update_not_possible_error(checker) if updated_deps.none? { |d| job.security_fix?(d) }
@@ -151,6 +171,7 @@ def check_and_create_pull_request(dependency)
updated_dependencies: updated_deps,
change_source: checker.dependency
)
+
create_pull_request(dependency_change)
rescue Dependabot::AllVersionsIgnored
Dependabot.logger.info("All updates for #{dependency.name} were ignored")
@@ -160,7 +181,22 @@ def check_and_create_pull_request(dependency)
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/PerceivedComplexity
+ sig { params(dependency: Dependabot::Dependency).returns(Dependabot::Dependency) }
+ def vulnerable_version(dependency)
+ return dependency if dependency.metadata[:all_versions].count == 1
+ vulnerable_dependency = dependency.metadata[:all_versions].find do |dep|
+ checker = update_checker_for(dep)
+ checker.version_class.correct?(dep.version) && checker.vulnerable?
+ end
+
+ # this will lead to a security update not found error
+ return dependency unless vulnerable_dependency
+
+ vulnerable_dependency
+ end
+
+ sig { params(dependency: Dependabot::Dependency).returns(Dependabot::UpdateCheckers::Base) }
def update_checker_for(dependency)
Dependabot::UpdateCheckers.for_package_manager(job.package_manager).new(
dependency: dependency,
@@ -175,6 +211,7 @@ def update_checker_for(dependency)
)
end
+ sig { params(dependency: Dependabot::Dependency).void }
def log_checking_for_update(dependency)
Dependabot.logger.info(
"Checking if #{dependency.name} #{dependency.version} needs updating"
@@ -182,22 +219,25 @@ def log_checking_for_update(dependency)
job.log_ignore_conditions_for(dependency)
end
+ sig { params(dependency: Dependabot::Dependency).void }
def log_up_to_date(dependency)
Dependabot.logger.info(
"No update needed for #{dependency.name} #{dependency.version}"
)
end
+ sig { params(requirements_to_unlock: Symbol, checker: Dependabot::UpdateCheckers::Base).void }
def log_requirements_for_update(requirements_to_unlock, checker)
Dependabot.logger.info("Requirements to unlock #{requirements_to_unlock}")
return unless checker.respond_to?(:requirements_update_strategy)
Dependabot.logger.info(
- "Requirements update strategy #{checker.requirements_update_strategy}"
+ "Requirements update strategy #{checker.requirements_update_strategy&.serialize}"
)
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).returns(T::Boolean) }
def pr_exists_for_latest_version?(checker)
latest_version = checker.latest_version&.to_s
return false if latest_version.nil?
@@ -205,10 +245,14 @@ def pr_exists_for_latest_version?(checker)
job.existing_pull_requests
.select { |pr| pr.count == 1 }
.map(&:first)
- .select { |pr| pr.fetch("dependency-name") == checker.dependency.name }
- .any? { |pr| pr.fetch("dependency-version", nil) == latest_version }
+ .select { |pr| pr && pr.fetch("dependency-name") == checker.dependency.name }
+ .any? { |pr| pr && pr.fetch("dependency-version", nil) == latest_version }
end
+ sig do
+ params(updated_dependencies: T::Array[Dependabot::Dependency])
+ .returns(T.nilable(T::Array[T::Hash[String, String]]))
+ end
def existing_pull_request(updated_dependencies)
new_pr_set = Set.new(
updated_dependencies.map do |dep|
@@ -224,6 +268,7 @@ def existing_pull_request(updated_dependencies)
created_pull_requests.find { |pr| Set.new(pr) == new_pr_set }
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).returns(Symbol) }
def requirements_to_unlock(checker)
if !checker.requirements_unlocked_or_can_be?
if checker.can_update?(requirements_to_unlock: :none) then :none
@@ -237,6 +282,7 @@ def requirements_to_unlock(checker)
end
end
+ sig { params(dependency_change: Dependabot::DependencyChange).void }
def create_pull_request(dependency_change)
Dependabot.logger.info("Submitting #{dependency_change.updated_dependencies.map(&:name).join(', ')} " \
"pull request for creation")
@@ -244,26 +290,17 @@ def create_pull_request(dependency_change)
service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
created_pull_requests << dependency_change.updated_dependencies.map do |dep|
- {
- "dependency-name" => dep.name,
- "dependency-version" => dep.version,
- "dependency-removed" => dep.removed? ? true : nil
- }.compact
+ create_pull_request_for_dependency(dep)
end
end
- def update_pull_request(dependency_change)
- Dependabot.logger.info("Submitting #{dependency_change.updated_dependencies.map(&:name).join(', ')} " \
- "pull request for update")
-
- service.update_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
- end
-
- def close_pull_request(reason:)
- reason_string = reason.to_s.tr("_", " ")
- Dependabot.logger.info("Telling backend to close pull request for " \
- "#{job.dependencies.join(', ')} - #{reason_string}")
- service.close_pull_request(job.dependencies, reason)
+ sig { params(dependency: Dependabot::Dependency).returns(T::Hash[String, String]) }
+ def create_pull_request_for_dependency(dependency)
+ {
+ "dependency-name" => dependency.name,
+ "dependency-version" => dependency.version,
+ "dependency-removed" => dependency.removed? ? true : nil
+ }.compact
end
end
end
diff --git a/updater/lib/dependabot/updater/operations/group_update_all_versions.rb b/updater/lib/dependabot/updater/operations/group_update_all_versions.rb
index f2600d22..afb90751 100644
--- a/updater/lib/dependabot/updater/operations/group_update_all_versions.rb
+++ b/updater/lib/dependabot/updater/operations/group_update_all_versions.rb
@@ -1,4 +1,4 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
require "dependabot/updater/operations/create_group_update_pull_request"
@@ -18,28 +18,50 @@ module Dependabot
class Updater
module Operations
class GroupUpdateAllVersions
+ extend T::Sig
include GroupUpdateCreation
- def self.applies_to?(job:)
- return false if job.security_updates_only?
+ sig { params(job: Dependabot::Job).returns(T::Boolean) }
+ def self.applies_to?(job:) # rubocop:disable Metrics/PerceivedComplexity
return false if job.updating_a_pull_request?
- return false if job.dependencies&.any?
+ if Dependabot::Experiments.enabled?(:grouped_security_updates_disabled) && job.security_updates_only?
+ return false
+ end
+
+ return true if job.source.directories && T.must(job.source.directories).count > 1
+
+ if job.security_updates_only?
+ return true if job.dependencies && T.must(job.dependencies).count > 1
+ return true if job.dependency_groups.any? { |group| group["applies-to"] == "security-updates" }
+
+ return false
+ end
- job.dependency_groups&.any?
+ job.dependency_groups.any?
end
+ sig { returns(Symbol) }
def self.tag_name
:group_update_all_versions
end
+ sig do
+ params(
+ service: Dependabot::Service,
+ job: Dependabot::Job,
+ dependency_snapshot: Dependabot::DependencySnapshot,
+ error_handler: Dependabot::Updater::ErrorHandler
+ ).void
+ end
def initialize(service:, job:, dependency_snapshot:, error_handler:)
@service = service
@job = job
@dependency_snapshot = dependency_snapshot
@error_handler = error_handler
- @dependencies_handled = Set.new
+ @dependencies_handled = T.let(Set.new, T::Set[String])
end
+ sig { void }
def perform
if dependency_snapshot.groups.any?
run_grouped_dependency_updates
@@ -65,12 +87,20 @@ def perform
private
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
+
+ sig { returns(Dependabot::Service) }
+ attr_reader :service
+
+ sig { returns(Dependabot::DependencySnapshot) }
+ attr_reader :dependency_snapshot
- def run_grouped_dependency_updates # rubocop:disable Metrics/AbcSize
+ sig { returns(Dependabot::Updater::ErrorHandler) }
+ attr_reader :error_handler
+
+ sig { returns(T::Array[Dependabot::DependencyGroup]) }
+ def run_grouped_dependency_updates
Dependabot.logger.info("Starting grouped update job for #{job.source.repo}")
Dependabot.logger.info("Found #{dependency_snapshot.groups.count} group(s).")
@@ -82,10 +112,7 @@ def run_grouped_dependency_updates # rubocop:disable Metrics/AbcSize
Dependabot.logger.info(
"Deferring creation of a new pull request. The existing pull request will update in a separate job."
)
- # add the dependencies in the group so individual updates don't try to update them
- dependency_snapshot.add_handled_dependencies(
- dependencies_in_existing_pr_for_group(group).map { |d| d["dependency-name"] }
- )
+ dependency_snapshot.mark_group_handled(group)
next
end
@@ -93,18 +120,14 @@ def run_grouped_dependency_updates # rubocop:disable Metrics/AbcSize
end
groups_without_pr.each do |group|
- result = run_update_for(group)
- if result
- # Add the actual updated dependencies to the handled list so they don't get updated individually.
- dependency_snapshot.add_handled_dependencies(result.updated_dependencies.map(&:name))
- else
- # The update failed, add the suspected dependencies to the handled list so they don't update individually.
- dependency_snapshot.add_handled_dependencies(group.dependencies.map(&:name))
- end
+ dependency_change = run_grouped_update_for(group)
+ # The update failed, add the suspected dependencies to the handled list so they don't update individually.
+ dependency_snapshot.mark_group_handled(group) if dependency_change.nil?
end
end
- def run_update_for(group)
+ sig { params(group: Dependabot::DependencyGroup).returns(T.nilable(Dependabot::DependencyChange)) }
+ def run_grouped_update_for(group)
Dependabot::Updater::Operations::CreateGroupUpdatePullRequest.new(
service: service,
job: job,
@@ -114,15 +137,31 @@ def run_update_for(group)
).perform
end
+ sig { void }
def run_ungrouped_dependency_updates
- return if dependency_snapshot.ungrouped_dependencies.empty?
-
- Dependabot::Updater::Operations::UpdateAllVersions.new(
- service: service,
- job: job,
- dependency_snapshot: dependency_snapshot,
- error_handler: error_handler
- ).perform
+ if job.source.directories.nil?
+ return if dependency_snapshot.ungrouped_dependencies.empty?
+
+ Dependabot::Updater::Operations::UpdateAllVersions.new(
+ service: service,
+ job: job,
+ dependency_snapshot: dependency_snapshot,
+ error_handler: error_handler
+ ).perform
+ else
+ T.must(job.source.directories).each do |directory|
+ job.source.directory = directory
+ dependency_snapshot.current_directory = directory
+ next if dependency_snapshot.ungrouped_dependencies.empty?
+
+ Dependabot::Updater::Operations::UpdateAllVersions.new(
+ service: service,
+ job: job,
+ dependency_snapshot: dependency_snapshot,
+ error_handler: error_handler
+ ).perform
+ end
+ end
end
end
end
diff --git a/updater/lib/dependabot/updater/operations/refresh_group_update_pull_request.rb b/updater/lib/dependabot/updater/operations/refresh_group_update_pull_request.rb
index 094196fd..4f813232 100644
--- a/updater/lib/dependabot/updater/operations/refresh_group_update_pull_request.rb
+++ b/updater/lib/dependabot/updater/operations/refresh_group_update_pull_request.rb
@@ -1,11 +1,13 @@
-# typed: false
+# typed: strict
# frozen_string_literal: true
require "dependabot/updater/group_update_creation"
+require "dependabot/updater/group_update_refreshing"
+require "sorbet-runtime"
# This class implements our strategy for refreshing a single Pull Request which
# updates all outdated Dependencies within a specific project folder that match
-# a specificed Dependency Group.
+# a specified Dependency Group.
#
# Refreshing a Dependency Group pull request essentially has two outcomes, we
# either update or supersede the existing PR.
@@ -22,23 +24,46 @@ module Dependabot
class Updater
module Operations
class RefreshGroupUpdatePullRequest
+ extend T::Sig
include GroupUpdateCreation
+ include GroupUpdateRefreshing
- def self.applies_to?(job:)
- return false if job.security_updates_only?
+ sig { params(job: Dependabot::Job).returns(T::Boolean) }
+ def self.applies_to?(job:) # rubocop:disable Metrics/PerceivedComplexity
# If we haven't been given metadata about the dependencies present
# in the pull request and the Dependency Group that originally created
# it, this strategy cannot act.
return false unless job.dependencies&.any?
return false unless job.dependency_group_to_refresh
+ if Dependabot::Experiments.enabled?(:grouped_security_updates_disabled) && job.security_updates_only?
+ return false
+ end
+
+ return true if job.source.directories && T.must(job.source.directories).count > 1
+
+ if job.security_updates_only?
+ return true if T.must(job.dependencies).count > 1
+ return true if job.dependency_groups.any? { |group| group["applies-to"] == "security-updates" }
+
+ return false
+ end
job.updating_a_pull_request?
end
+ sig { returns(Symbol) }
def self.tag_name
:update_version_group_pr
end
+ sig do
+ params(
+ service: Dependabot::Service,
+ job: Dependabot::Job,
+ dependency_snapshot: Dependabot::DependencySnapshot,
+ error_handler: Dependabot::Updater::ErrorHandler
+ ).void
+ end
def initialize(service:, job:, dependency_snapshot:, error_handler:)
@service = service
@job = job
@@ -46,6 +71,7 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:)
@error_handler = error_handler
end
+ sig { void }
def perform # rubocop:disable Metrics/AbcSize
# This guards against any jobs being performed where the data is malformed, this should not happen unless
# there was is defect in the service and we emitted a payload where the job and configuration data objects
@@ -62,84 +88,72 @@ def perform # rubocop:disable Metrics/AbcSize
return
end
+ job_group = T.must(dependency_snapshot.job_group)
+
Dependabot.logger.info("Starting PR update job for #{job.source.repo}")
- if dependency_snapshot.job_group.dependencies.empty?
+ if job_group.dependencies.empty?
# If the group is empty that means any Dependencies that did match this group
# have been removed from the project or are no longer allowed by the config.
#
# Let's warn that the group is empty and then signal the PR should be closed
# so users are informed this group is no longer actionable by Dependabot.
- warn_group_is_empty(dependency_snapshot.job_group)
- close_pull_request(reason: :dependency_group_empty)
+ warn_group_is_empty(job_group)
+ close_pull_request(reason: :dependency_group_empty, group: job_group)
else
- Dependabot.logger.info("Updating the '#{dependency_snapshot.job_group.name}' group")
+ Dependabot.logger.info("Updating the '#{job_group.name}' group")
# Preprocess to discover existing group PRs and add their dependencies to the handled list before processing
# the refresh. This prevents multiple PRs from being created for the same dependency during the refresh.
dependency_snapshot.groups.each do |group|
- next unless group.name != dependency_snapshot.job_group.name && pr_exists_for_dependency_group?(group)
+ next unless group.name != job_group.name && pr_exists_for_dependency_group?(group)
- dependency_snapshot.add_handled_dependencies(
- dependencies_in_existing_pr_for_group(group).map { |d| d["dependency-name"] }
- )
+ dependency_snapshot.mark_group_handled(group)
end
- dependency_change = compile_all_dependency_changes_for(dependency_snapshot.job_group)
+ if dependency_change.nil?
+ Dependabot.logger.info("Nothing could update for Dependency Group: '#{job_group.name}'")
+ return
+ end
- upsert_pull_request_with_error_handling(dependency_change)
+ upsert_pull_request_with_error_handling(T.must(dependency_change), job_group)
end
end
private
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler
+ sig { returns(Dependabot::Job) }
+ attr_reader :job
- def upsert_pull_request_with_error_handling(dependency_change)
- if dependency_change.updated_dependencies.any?
- upsert_pull_request(dependency_change)
- else
- Dependabot.logger.info("Dependencies are up to date, closing existing Pull Request")
- close_pull_request(reason: :up_to_date)
- end
- rescue StandardError => e
- error_handler.handle_job_error(error: e, group: dependency_snapshot.job_group)
- end
+ sig { returns(Dependabot::Service) }
+ attr_reader :service
- # Having created the dependency_change, we need to determine the right strategy to apply it to the project:
- # - Replace existing PR if the dependencies involved have changed
- # - Update the existing PR if the dependencies and the target versions remain the same
- # - Supersede the existing PR if the dependencies are the same but the target versions have changed
- def upsert_pull_request(dependency_change)
- if dependency_change.should_replace_existing_pr?
- Dependabot.logger.info("Dependencies have changed, closing existing Pull Request")
- close_pull_request(reason: :dependencies_changed)
- Dependabot.logger.info("Creating a new pull request for '#{dependency_snapshot.job_group.name}'")
- service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
- elsif dependency_change.matches_existing_pr?
- Dependabot.logger.info("Updating pull request for '#{dependency_snapshot.job_group.name}'")
- service.update_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
- else
- # If the changes do not match an existing PR, then we should open a new pull request and leave it to
- # the backend to close the existing pull request with a comment that it has been superseded.
- Dependabot.logger.info("Target versions have changed, existing Pull Request should be superseded")
- Dependabot.logger.info("Creating a new pull request for '#{dependency_snapshot.job_group.name}'")
- service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
- end
- end
+ sig { returns(DependencySnapshot) }
+ attr_reader :dependency_snapshot
- def close_pull_request(reason:)
- reason_string = reason.to_s.tr("_", " ")
- Dependabot.logger.info(
- "Telling backend to close pull request for the " \
- "#{dependency_snapshot.job_group.name} group " \
- "(#{job.dependencies.join(', ')}) - #{reason_string}"
- )
+ sig { returns(Dependabot::Updater::ErrorHandler) }
+ attr_reader :error_handler
- service.close_pull_request(job.dependencies, reason)
+ sig { returns(T.nilable(Dependabot::DependencyChange)) }
+ def dependency_change
+ return @dependency_change if defined?(@dependency_change)
+
+ job_group = T.must(dependency_snapshot.job_group)
+
+ if job.source.directories.nil?
+ @dependency_change = compile_all_dependency_changes_for(job_group)
+ else
+ dependency_changes = T.let(T.must(job.source.directories).filter_map do |directory|
+ job.source.directory = directory
+ dependency_snapshot.current_directory = directory
+ compile_all_dependency_changes_for(job_group)
+ end, T::Array[Dependabot::DependencyChange])
+
+ # merge the changes together into one
+ dependency_change = T.let(T.must(dependency_changes.first), Dependabot::DependencyChange)
+ dependency_change.merge_changes!(T.must(dependency_changes[1..-1])) if dependency_changes.count > 1
+ @dependency_change = T.let(dependency_change, T.nilable(Dependabot::DependencyChange))
+ end
end
end
end
diff --git a/updater/lib/dependabot/updater/operations/refresh_security_update_pull_request.rb b/updater/lib/dependabot/updater/operations/refresh_security_update_pull_request.rb
index ab2a6409..1b1aefa6 100644
--- a/updater/lib/dependabot/updater/operations/refresh_security_update_pull_request.rb
+++ b/updater/lib/dependabot/updater/operations/refresh_security_update_pull_request.rb
@@ -1,4 +1,4 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
# This class implements our strategy for 'refreshing' an existing Pull Request
@@ -45,10 +45,10 @@ def perform
private
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler
+ attr_reader :job
+ attr_reader :service
+ attr_reader :dependency_snapshot
+ attr_reader :error_handler
def dependencies
dependency_snapshot.job_dependencies
@@ -135,7 +135,7 @@ def check_and_update_pull_request(dependencies)
create_pull_request(dependency_change)
end
rescue Dependabot::AllVersionsIgnored
- Dependabot.logger.info("All updates for #{dependency.name} were ignored")
+ Dependabot.logger.info("All updates for #{job.dependencies.first} were ignored")
# Report this error to the backend to create an update job error
raise
@@ -190,11 +190,26 @@ def log_requirements_for_update(requirements_to_unlock, checker)
return unless checker.respond_to?(:requirements_update_strategy)
Dependabot.logger.info(
- "Requirements update strategy #{checker.requirements_update_strategy}"
+ "Requirements update strategy #{checker.requirements_update_strategy&.serialize}"
)
end
def existing_pull_request(updated_dependencies)
+ new_pr_set = Set.new(
+ updated_dependencies.map do |dep|
+ {
+ "dependency-name" => dep.name,
+ "dependency-version" => dep.version,
+ "dependency-removed" => dep.removed? ? true : nil,
+ "directory" => dep.directory
+ }.compact
+ end
+ )
+
+ existing = job.existing_pull_requests.find { |pr| Set.new(pr) == new_pr_set }
+ return existing if existing
+
+ # try the search again without directory
new_pr_set = Set.new(
updated_dependencies.map do |dep|
{
diff --git a/updater/lib/dependabot/updater/operations/refresh_version_update_pull_request.rb b/updater/lib/dependabot/updater/operations/refresh_version_update_pull_request.rb
index 747f73a8..81c1d660 100644
--- a/updater/lib/dependabot/updater/operations/refresh_version_update_pull_request.rb
+++ b/updater/lib/dependabot/updater/operations/refresh_version_update_pull_request.rb
@@ -1,4 +1,4 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
# This class implements our strategy for 'refreshing' an existing Pull Request
@@ -30,6 +30,10 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:)
@job = job
@dependency_snapshot = dependency_snapshot
@error_handler = error_handler
+
+ return unless job.source.directory.nil? && job.source.directories.count == 1
+
+ job.source.directory = job.source.directories.first
end
def perform
@@ -42,11 +46,11 @@ def perform
private
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler,
- :created_pull_requests
+ attr_reader :job
+ attr_reader :service
+ attr_reader :dependency_snapshot
+ attr_reader :error_handler
+ attr_reader :created_pull_requests
def dependencies
dependency_snapshot.job_dependencies
@@ -191,11 +195,26 @@ def log_requirements_for_update(requirements_to_unlock, checker)
return unless checker.respond_to?(:requirements_update_strategy)
Dependabot.logger.info(
- "Requirements update strategy #{checker.requirements_update_strategy}"
+ "Requirements update strategy #{checker.requirements_update_strategy&.serialize}"
)
end
def existing_pull_request(updated_dependencies)
+ new_pr_set = Set.new(
+ updated_dependencies.map do |dep|
+ {
+ "dependency-name" => dep.name,
+ "dependency-version" => dep.version,
+ "dependency-removed" => dep.removed? ? true : nil,
+ "directory" => dep.directory
+ }.compact
+ end
+ )
+
+ existing = job.existing_pull_requests.find { |pr| Set.new(pr) == new_pr_set }
+ return existing if existing
+
+ # try the search again without directory
new_pr_set = Set.new(
updated_dependencies.map do |dep|
{
diff --git a/updater/lib/dependabot/updater/operations/update_all_versions.rb b/updater/lib/dependabot/updater/operations/update_all_versions.rb
index 1adee110..453f5249 100644
--- a/updater/lib/dependabot/updater/operations/update_all_versions.rb
+++ b/updater/lib/dependabot/updater/operations/update_all_versions.rb
@@ -1,4 +1,4 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
# This class implements our strategy for iterating over all of the dependencies
@@ -27,6 +27,10 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:)
@error_handler = error_handler
# TODO: Collect @created_pull_requests on the Job object?
@created_pull_requests = []
+
+ return unless job.source.directory.nil? && job.source.directories.count == 1
+
+ job.source.directory = job.source.directories.first
end
def perform
@@ -37,11 +41,11 @@ def perform
private
- attr_reader :job,
- :service,
- :dependency_snapshot,
- :error_handler,
- :created_pull_requests
+ attr_reader :job
+ attr_reader :service
+ attr_reader :dependency_snapshot
+ attr_reader :error_handler
+ attr_reader :created_pull_requests
def dependencies
if dependency_snapshot.dependencies.any? && dependency_snapshot.allowed_dependencies.none?
@@ -58,6 +62,10 @@ def dependencies
def check_and_create_pr_with_error_handling(dependency)
check_and_create_pull_request(dependency)
+ rescue URI::InvalidURIError => e
+ msg = e.class.to_s + " with message: " + e.message
+ e = Dependabot::DependencyFileNotResolvable.new(msg)
+ error_handler.handle_dependency_error(error: e, dependency: dependency)
rescue Dependabot::InconsistentRegistryResponse => e
error_handler.log_dependency_error(
dependency: dependency,
@@ -66,11 +74,12 @@ def check_and_create_pr_with_error_handling(dependency)
error_detail: e.message
)
rescue StandardError => e
- error_handler.handle_dependency_error(error: e, dependency: dependency)
+ process_dependency_error(e, dependency)
end
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
+ # rubocop:disable Metrics/PerceivedComplexity
def check_and_create_pull_request(dependency)
checker = update_checker_for(dependency, raise_on_ignored: raise_on_ignored?(dependency))
@@ -99,6 +108,10 @@ def check_and_create_pull_request(dependency)
requirements_to_unlock: requirements_to_unlock
)
+ if updated_deps.empty?
+ raise "Dependabot found some dependency requirements to unlock, yet it failed to update any dependencies"
+ end
+
if (existing_pr = existing_pull_request(updated_deps))
deps = existing_pr.map do |dep|
if dep.fetch("dependency-removed", false)
@@ -126,8 +139,14 @@ def check_and_create_pull_request(dependency)
updated_dependencies: updated_deps,
change_source: checker.dependency
)
+
+ if dependency_change.updated_dependency_files.empty?
+ raise "UpdateChecker found viable dependencies to be updated, but FileUpdater failed to update any files"
+ end
+
create_pull_request(dependency_change)
end
+ # rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
@@ -162,6 +181,15 @@ def log_checking_for_update(dependency)
job.log_ignore_conditions_for(dependency)
end
+ def process_dependency_error(error, dependency)
+ if error.class.to_s.include?("RegistryError")
+ ex = Dependabot::DependencyFileNotResolvable.new(error.message)
+ error_handler.handle_dependency_error(error: ex, dependency: dependency)
+ else
+ error_handler.handle_dependency_error(error: error, dependency: dependency)
+ end
+ end
+
def all_versions_ignored?(dependency, checker)
Dependabot.logger.info("Latest version is #{checker.latest_version}")
false
@@ -215,7 +243,7 @@ def log_requirements_for_update(requirements_to_unlock, checker)
return unless checker.respond_to?(:requirements_update_strategy)
Dependabot.logger.info(
- "Requirements update strategy #{checker.requirements_update_strategy}"
+ "Requirements update strategy #{checker.requirements_update_strategy&.serialize}"
)
end
@@ -226,6 +254,7 @@ def peer_dependency_should_update_instead?(dependency_name, updated_deps)
.reject { |dep| dep.name == dependency_name }
.any? do |dep|
next true if existing_pull_request([dep])
+ next false if dep.previous_requirements.nil?
original_peer_dep = ::Dependabot::Dependency.new(
name: dep.name,
diff --git a/updater/lib/dependabot/updater/security_update_helpers.rb b/updater/lib/dependabot/updater/security_update_helpers.rb
index 727719ef..c409a521 100644
--- a/updater/lib/dependabot/updater/security_update_helpers.rb
+++ b/updater/lib/dependabot/updater/security_update_helpers.rb
@@ -1,26 +1,38 @@
-# typed: false
+# typed: true
# frozen_string_literal: true
+require "sorbet-runtime"
+
# This module extracts all helpers required to perform additional update job
# error recording and logging for Security Updates since they are shared
# between a few operations.
module Dependabot
class Updater
module SecurityUpdateHelpers
- def record_security_update_not_needed_error(checker)
+ extend T::Sig
+ extend T::Helpers
+
+ abstract!
+
+ sig { returns(Dependabot::Service) }
+ attr_reader :service
+
+ sig { params(dependency: Dependabot::Dependency).void }
+ def record_security_update_not_needed_error(dependency)
Dependabot.logger.info(
- "no security update needed as #{checker.dependency.name} " \
+ "no security update needed as #{dependency.name} " \
"is no longer vulnerable"
)
service.record_update_job_error(
error_type: "security_update_not_needed",
error_details: {
- "dependency-name": checker.dependency.name
+ "dependency-name": dependency.name
}
)
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).void }
def record_security_update_ignored(checker)
Dependabot.logger.info(
"Dependabot cannot update to the required version as all versions " \
@@ -35,6 +47,7 @@ def record_security_update_ignored(checker)
)
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).void }
def record_dependency_file_not_supported_error(checker)
Dependabot.logger.info(
"Dependabot can't update vulnerable dependencies for projects " \
@@ -50,16 +63,17 @@ def record_dependency_file_not_supported_error(checker)
)
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).void }
def record_security_update_not_possible_error(checker)
latest_allowed_version =
(checker.lowest_resolvable_security_fix_version ||
checker.dependency.version)&.to_s
lowest_non_vulnerable_version =
- checker.lowest_security_fix_version&.to_s
+ checker.lowest_security_fix_version.to_s
conflicting_dependencies = checker.conflicting_dependencies
Dependabot.logger.info(
- security_update_not_possible_message(checker, latest_allowed_version, conflicting_dependencies)
+ security_update_not_possible_message(checker, T.must(latest_allowed_version), conflicting_dependencies)
)
Dependabot.logger.info(
earliest_fixed_version_message(lowest_non_vulnerable_version)
@@ -76,6 +90,7 @@ def record_security_update_not_possible_error(checker)
)
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).void }
def record_security_update_not_found(checker)
Dependabot.logger.info(
"Dependabot can't find a published or compatible non-vulnerable " \
@@ -93,6 +108,7 @@ def record_security_update_not_found(checker)
)
end
+ sig { params(checker: Dependabot::UpdateCheckers::Base).void }
def record_pull_request_exists_for_latest_version(checker)
service.record_update_job_error(
error_type: "pull_request_exists_for_latest_version",
@@ -104,6 +120,7 @@ def record_pull_request_exists_for_latest_version(checker)
)
end
+ sig { params(existing_pull_request: T::Array[T::Hash[String, String]]).void }
def record_pull_request_exists_for_security_update(existing_pull_request)
updated_dependencies = existing_pull_request.map do |dep|
{
@@ -121,6 +138,7 @@ def record_pull_request_exists_for_security_update(existing_pull_request)
)
end
+ sig { void }
def record_security_update_dependency_not_found
service.record_update_job_error(
error_type: "security_update_dependency_not_found",
@@ -128,6 +146,7 @@ def record_security_update_dependency_not_found
)
end
+ sig { params(lowest_non_vulnerable_version: T.nilable(String)).returns(String) }
def earliest_fixed_version_message(lowest_non_vulnerable_version)
if lowest_non_vulnerable_version
"The earliest fixed version is #{lowest_non_vulnerable_version}."
@@ -136,8 +155,15 @@ def earliest_fixed_version_message(lowest_non_vulnerable_version)
end
end
- def security_update_not_possible_message(checker, latest_allowed_version,
- conflicting_dependencies)
+ sig do
+ params(
+ checker: Dependabot::UpdateCheckers::Base,
+ latest_allowed_version: String,
+ conflicting_dependencies: T::Array[T::Hash[String, String]]
+ )
+ .returns(String)
+ end
+ def security_update_not_possible_message(checker, latest_allowed_version, conflicting_dependencies)
if conflicting_dependencies.any?
dep_messages = conflicting_dependencies.map do |dep|
" #{dep['explanation']}"
diff --git a/updater/lib/tinglesoftware/azure/artifacts_credential_provider.rb b/updater/lib/tinglesoftware/azure/artifacts_credential_provider.rb
new file mode 100644
index 00000000..4d53ecbe
--- /dev/null
+++ b/updater/lib/tinglesoftware/azure/artifacts_credential_provider.rb
@@ -0,0 +1,61 @@
+# typed: strict
+# frozen_string_literal: true
+
+require "dependabot/shared_helpers"
+
+#
+# This module auto installs the Azure Artifacts Credential Provider if any NuGet feeds are configured.
+# Without it, package feed authentication is not passed down to the native helper tools (i.e. dotnet).
+# See: https://github.com/tinglesoftware/dependabot-azure-devops/pull/1233 for more info.
+#
+# This module primarily fixes auth in .NET [Core] projects; .NET Framework project auth is handled by nuget.config.
+# See: tinglesoftware/dependabot/overrides/nuget/nuget_config_credential_helpers.rb for more info.
+#
+# This credential provider is required for ALL private NuGet feeds, even if they are not hosted in Azure DevOps.
+# See README.md (Credentials for private registries and feeds) for more details.
+#
+
+# TODO: Remove this once https://github.com/dependabot/dependabot-core/pull/8927 is resolved or auth works natively.
+
+module TingleSoftware
+ module Azure
+ module ArtifactsCredentialProvider
+ def self.install_if_private_nuget_feeds_are_configured
+ return if private_nuget_feeds.empty?
+
+ # Configure NuGet feed authentication
+ ENV.store(
+ "VSS_NUGET_EXTERNAL_FEED_ENDPOINTS",
+ JSON.dump({
+ "endpointCredentials" => private_nuget_feeds.map do |cred|
+ {
+ "endpoint" => cred["url"],
+ # Use username/password auth if provided, otherwise fallback to token auth.
+ # This provides maximum compatibility with Azure DevOps, DevOps Server, and other third-party feeds.
+ # When using DevOps PATs, the token is split into username/password parts; Username is not significant.
+ # e.g. token "PAT:12345" --> { "username": "PAT", "password": "12345" }
+ # ":12345" --> { "username": "", "password": "12345" }
+ # "12345" --> { "username": "12345", "password": "12345" }
+ "username" => cred["username"] || cred["token"]&.split(":")&.first,
+ "password" => cred["password"] || cred["token"]&.split(":")&.last
+ }
+ end
+ })
+ )
+
+ # Install cred provider from https://github.com/microsoft/artifacts-credprovider
+ puts ::Dependabot::SharedHelpers.run_shell_command(
+ %(sh -c "$(curl -fsSL https://aka.ms/install-artifacts-credprovider.sh)"), allow_unsafe_shell_command: true
+ )
+ end
+
+ def self.private_nuget_feeds
+ JSON.parse(ENV.fetch("DEPENDABOT_EXTRA_CREDENTIALS", "[]")).select do |cred|
+ cred["type"] == "nuget_feed" && (cred["username"] || cred["password"] || cred["token"])
+ end
+ end
+ end
+ end
+end
+
+TingleSoftware::Azure::ArtifactsCredentialProvider.install_if_private_nuget_feeds_are_configured
diff --git a/updater/lib/tinglesoftware/dependabot/api_clients/azure_api_client.rb b/updater/lib/tinglesoftware/dependabot/api_clients/azure_api_client.rb
new file mode 100644
index 00000000..3d8748af
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/api_clients/azure_api_client.rb
@@ -0,0 +1,464 @@
+# typed: strict
+# frozen_string_literal: true
+
+require "json"
+require "dependabot/api_client"
+require "dependabot/pull_request_creator"
+require "dependabot/pull_request_updater"
+require "dependabot/credential"
+
+#
+# Azure DevOps implementation of the [internal] Dependabot Service API client.
+#
+# This API is normally reserved for Dependabot internal use and is used to send job data to Dependabots [internal] APIs.
+# Actions like creating/updating/closing pull requests are deferred to this API to be actioned later asynchronously.
+# However, in Azure DevOps, we (normally) run inside a pipeline agent and don't have a remote API to defer actions to,
+# so we instead perform pull request changes here synchronously. This keeps the entire end-to-end update process
+# contained within a single self-contained job.
+#
+module TingleSoftware
+ module Dependabot
+ module ApiClients
+ class AzureApiClient < ::Dependabot::ApiClient
+ extend T::Sig
+ attr_reader :job
+
+ # Custom properties used to store dependabot metadata in Azure DevOps pull requests.
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-properties
+ module PullRequest
+ module Properties
+ PACKAGE_MANAGER = "dependabot.package_manager"
+ BASE_COMMIT_SHA = "dependabot.base_commit_sha"
+ UPDATED_DEPENDENCIES = "dependabot.updated_dependencies"
+ end
+ end
+
+ def initialize(job:, dependency_snapshot_resolver:)
+ @job = job
+ @dependency_snapshot_resolver = dependency_snapshot_resolver
+ end
+
+ sig { params(dependency_change: ::Dependabot::DependencyChange, base_commit_sha: String).void }
+ def create_pull_request(dependency_change, base_commit_sha) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
+ return log_skipped_pull_request("creation", dependency_change) if job.skip_pull_requests
+ return log_open_limit_reached_for_pull_requests if job.open_pull_request_limit_reached?
+
+ ::Dependabot.logger.info(
+ "Creating pull request for '#{dependency_change.pr_message.pr_name}'."
+ )
+
+ # Create the pull request
+ pull_request = ::Dependabot::PullRequestCreator.new(
+ source: job.source,
+ base_commit: base_commit_sha,
+ dependencies: dependency_change.updated_dependencies,
+ dependency_group: dependency_change.dependency_group,
+ files: dependency_change.updated_dependency_files,
+ credentials: job.credentials,
+ pr_message_header: pull_request_header_with_compatibility_scores(dependency_change.updated_dependencies),
+ pr_message_footer: job.pr_message_footer,
+ author_details: {
+ name: job.pr_author_name,
+ email: job.pr_author_email
+ },
+ signature_key: job.pr_signature_key,
+ commit_message_options: job.commit_message_options,
+ custom_labels: job.pr_custom_labels,
+ reviewers: job.pr_reviewers,
+ assignees: job.pr_assignees,
+ milestone: job.pr_milestone,
+ vulnerabilities_fixed: job.vulnerabilities_fixed_for(dependency_change.updated_dependencies),
+ branch_name_separator: job.pr_branch_name_separator,
+ branch_name_prefix: job.pr_branch_name_prefix,
+ label_language: true,
+ automerge_candidate: true,
+ github_redirection_service: ::Dependabot::PullRequestCreator::DEFAULT_GITHUB_REDIRECTION_SERVICE,
+ provider_metadata: {
+ work_item: job.pr_milestone
+ }
+ ).create
+
+ # Parse the response and log the result
+ if pull_request
+ req_status = pull_request&.status
+ if req_status == 201
+ pull_request = JSON[pull_request.body]
+ pull_request_id = pull_request["pullRequestId"]
+ pull_request_title = pull_request["title"]
+ ::Dependabot.logger.info(
+ "Created PR ##{pull_request_id}: #{pull_request_title}"
+ )
+ else
+ content = JSON[pull_request.body]
+ message = content["message"]
+ ::Dependabot.logger.error("Failed! PR already exists or an error has occurred.")
+ # throw exception here because pull_request.create does not throw
+ raise StandardError, "Pull Request creation failed with status #{req_status}. Message: #{message}"
+ end
+ else
+ ::Dependabot.logger.warn("Seems PR is already present.")
+ end
+
+ # Update the pull request property metadata, auto-complete, auto-approve, and refresh active pull requests
+ pull_request_sync_state_data(pull_request, dependency_change, base_commit_sha, true) if pull_request_id
+ end
+
+ sig { params(dependency_change: ::Dependabot::DependencyChange, base_commit_sha: String).void }
+ def update_pull_request(dependency_change, base_commit_sha) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
+ return log_skipped_pull_request("update", dependency_change) if job.skip_pull_requests
+
+ # Find the pull request to update
+ dependency_names = dependency_change.updated_dependencies.map(&:name).join(",")
+ dependency_group_name = dependency_change.dependency_group.name if dependency_change.grouped_update?
+ pull_request = job.existing_pull_request_with_updated_dependencies(
+ pull_request_updated_dependencies_property_data(dependency_change)
+ )
+ pull_request_id = pull_request["pullRequestId"].to_s if pull_request
+ pull_request_title = pull_request["title"].to_s if pull_request
+ unless pull_request_id
+ raise StandardError, "Unable to find pull request for #{dependency_group_name || dependency_names}"
+ end
+
+ # Ignore pull requests that don't have conflicts. If there is no conflict, we don't need to update anything
+ if pull_request["mergeStatus"] != "conflicts"
+ ::Dependabot.logger.info(
+ "Skipping pull request update for PR ##{pull_request_id}: #{pull_request_title}, " \
+ "there are no merge conflicts to resolve (i.e. nothing actually needs updating)."
+ )
+ return
+ end
+
+ # Ignore the pull request if it has been manually edited
+ if job.azure_client.pull_request_commits(pull_request_id).length > 1
+ ::Dependabot.logger.info(
+ "Skipping pull request update for PR ##{pull_request_id}: #{pull_request_title}, " \
+ "it has been manually edited. It is assumed that somebody else is working on it already."
+ )
+ return
+ end
+
+ ::Dependabot.logger.info(
+ "Updating pull request for PR ##{pull_request_id}: #{pull_request_title}, " \
+ "resolving merge conflict(s)."
+ )
+
+ # Update the pull request
+ ::Dependabot::PullRequestUpdater.new(
+ source: job.source,
+ base_commit: base_commit_sha,
+ old_commit: pull_request["lastMergeSourceCommit"]["commitId"],
+ files: dependency_change.updated_dependency_files,
+ credentials: job.credentials,
+ pull_request_number: pull_request_id.to_i,
+ author_details: {
+ name: job.pr_author_name,
+ email: job.pr_author_email
+ },
+ signature_key: job.pr_signature_key
+ ).update
+
+ # Update the pull request property metadata, auto-complete, auto-approve, and refresh active pull requests
+ pull_request_sync_state_data(pull_request, dependency_change, base_commit_sha, false)
+ end
+
+ sig { params(dependency_names: T.any(String, T::Array[String]), reason: T.any(String, Symbol)).void }
+ def close_pull_request(dependency_names, reason)
+ return log_skipped_pull_request("close", nil) if job.skip_pull_requests
+
+ # Find the pull request to close
+ pull_request = job.existing_pull_request_for_dependency_names(dependency_names)
+ pull_request_id = pull_request["pullRequestId"].to_s if pull_request
+ raise StandardError, "Unable to find pull request for #{dependency_names.join(',')}" unless pull_request_id
+
+ # Comment on the PR explaining why it was closed
+ pull_request_add_comment_with_close_reason(pull_request, dependency_names, reason)
+
+ return log_skipped_pull_request("close", nil) unless job.close_pull_requests
+
+ # Delete the source branch
+ # Do this first to avoid hanging branches
+ pull_request_delete_source_branch(pull_request)
+
+ # Close the pull request
+ job.azure_client.pull_request_abandon(pull_request_id)
+ end
+
+ sig { params(action: String, dependency_change: T.nilable(::Dependabot::DependencyChange)).void }
+ def log_skipped_pull_request(action, dependency_change)
+ ::Dependabot.logger.info("Skipping pull request #{action} as it is disabled for this job.")
+ return unless job.debug_enabled?
+
+ ::Dependabot.logger.debug("Staged file changes were:") if dependency_change
+ dependency_snapshot = @dependency_snapshot_resolver.call
+ dependency_change&.updated_dependency_files&.each do |updated_file|
+ log_file_diff(
+ dependency_snapshot.dependency_files.find { |f| f.name == updated_file.name },
+ updated_file
+ )
+ end
+ end
+
+ def log_file_diff(original_file, updated_file)
+ return unless original_file
+ return if original_file.content == updated_file.content
+
+ summary = case updated_file.operation
+ when ::Dependabot::DependencyFile::Operation::CREATE
+ " + Created '#{updated_file.name}' in '#{updated_file.directory}'"
+ when ::Dependabot::DependencyFile::Operation::UPDATE
+ " Âą Updated '#{updated_file.name}' in '#{updated_file.directory}'"
+ when ::Dependabot::DependencyFile::Operation::DELETE
+ " - Deleted '#{updated_file.name}' in '#{updated_file.directory}'"
+ end
+
+ original_tmp_file = Tempfile.new("original")
+ original_tmp_file.write(original_file.content)
+ original_tmp_file.close
+
+ updated_tmp_file = Tempfile.new("updated")
+ updated_tmp_file.write(updated_file.content)
+ updated_tmp_file.close
+
+ diff = `diff -u #{original_tmp_file.path} #{updated_tmp_file.path}`.lines
+ added_lines = diff.count { |line| line.start_with?("+") }
+ removed_lines = diff.count { |line| line.start_with?("-") }
+
+ ::Dependabot.logger.debug(
+ summary + "\n" \
+ "~~~\n" \
+ "#{diff.join}\n" \
+ "~~~\n" \
+ "#{added_lines} insertions (+), #{removed_lines} deletions (-)"
+ )
+ end
+
+ def log_open_limit_reached_for_pull_requests
+ ::Dependabot.logger.info(
+ "Skipping pull request creation as the open pull request limit (#{job.open_pull_requests_limit}) " \
+ "has been reached."
+ )
+ end
+
+ sig { params(error_type: T.any(String, Symbol), error_details: T.nilable(T::Hash[T.untyped, T.untyped])).void }
+ def record_update_job_error(error_type:, error_details:)
+ # No implementation required for Azure DevOps, errors are dumped to output console already
+ end
+
+ sig { params(error_type: T.any(Symbol, String), error_details: T.nilable(T::Hash[T.untyped, T.untyped])).void }
+ def record_update_job_unknown_error(error_type:, error_details:)
+ # No implementation required for Azure DevOps, errors are dumped to output console already
+ end
+
+ sig { params(base_commit_sha: String).void }
+ def mark_job_as_processed(base_commit_sha)
+ # No implementation required for Azure DevOps
+ end
+
+ sig { params(dependencies: T::Array[T::Hash[Symbol, T.untyped]], dependency_files: T::Array[String]).void }
+ def update_dependency_list(dependencies, dependency_files)
+ # No implementation required for Azure DevOps
+ end
+
+ sig { params(ecosystem_versions: T::Hash[Symbol, T.untyped]).void }
+ def record_ecosystem_versions(ecosystem_versions)
+ # No implementation required for Azure DevOps
+ end
+
+ sig { params(metric: String, tags: T::Hash[String, String]).void }
+ def increment_metric(metric, tags:)
+ # No implementation required for Azure DevOps
+ end
+
+ private
+
+ def pull_request_sync_state_data(pull_request, dependency_change, base_commit_sha, is_new_pr)
+ # Update the pull request properties with our dependabot metadata
+ pull_request_replace_property_metadata(pull_request, job.package_manager, dependency_change, base_commit_sha)
+
+ # Apply auto-complete and auto-approve settings
+ pull_request_auto_complete(pull_request) if job.azure_set_auto_complete
+ pull_request_auto_approve(pull_request) if job.azure_set_auto_approve
+
+ # Refresh active pull requests to include the new PR
+ # Required to ensure that the new PR is included in the next action (if any) and duplicates are avoided
+ job.refresh_open_pull_requests if is_new_pr
+ end
+
+ def pull_request_add_comment_with_close_reason(pull_request, dependency_names, reason)
+ return unless job.comment_pull_requests
+
+ # Generate a user-friendly comment based on the reason for closing the PR
+ # The first dependency is the "lead" dependency in a multi-dependency update
+ lead_dep_name = dependency_names.first
+ reason_for_close_comment = {
+ dependencies_changed: "Looks like the dependencies have changed",
+ dependency_group_empty: "Looks like the dependencies in this group are now empty",
+ dependency_removed: "Looks like #{lead_dep_name} is no longer a dependency",
+ up_to_date: "Looks like #{lead_dep_name} is up-to-date now",
+ update_no_longer_possible: "Looks like #{lead_dep_name} can no longer be updated"
+ # ??? => "Looks like these dependencies are updatable in another way, so this is no longer needed"
+ # ??? => "Superseded by ##{new_pull_request_id}"
+ }.freeze.fetch(reason) + ", so this is no longer needed."
+
+ return unless reason_for_close_comment
+
+ # Comment on the PR explaining why it was closed
+ pull_request_id = pull_request["pullRequestId"].to_s
+ job.azure_client.pull_request_thread_with_comments(
+ pull_request_id, "system", [reason_for_close_comment], "fixed"
+ )
+ rescue StandardError => e
+ # This has most likely happened because our access token does not have permission to comment on PRs
+ # Commenting on the PR is not critical to the process, so continue on
+ ::Dependabot.logger.warn(
+ "Failed to comment on PR ##{pull_request_id} with close reason. The error was: #{e.message}"
+ )
+ end
+
+ def pull_request_delete_source_branch(pull_request)
+ pull_request_id = pull_request["pullRequestId"]
+ pull_request_source_ref_name = pull_request["sourceRefName"]
+ job.azure_client.branch_delete(pull_request_source_ref_name)
+ rescue StandardError => e
+ # This has most likely happened because the branch has already been deleted or our access token does
+ # not have permission to manage branches. Deleting the branch is not critical to the process, so continue on
+ ::Dependabot.logger.warn(
+ "Failed to delete source branch for PR ##{pull_request_id}. The error was: #{e.message}"
+ )
+ end
+
+ def pull_request_auto_complete(pull_request)
+ pull_request_id = pull_request["pullRequestId"]
+ pull_request_title = pull_request["title"]
+ pull_request_description = pull_request["description"]
+
+ auto_complete_user_id = pull_request["createdBy"]["id"].to_s
+
+ #
+ # Pull requests that pass all policies will be merged automatically.
+ # Optional policies can be ignored by passing their identifiers
+ #
+ # The merge commit message should contain the PR number and title for tracking.
+ # This is the default behaviour in Azure DevOps
+ # Example:
+ # Merged PR 24093: Bump Tingle.Extensions.Logging.LogAnalytics from 3.4.2-ci0005 to 3.4.2-ci0006
+ #
+ # Bumps [Tingle.Extensions.Logging.LogAnalytics](...) from 3.4.2-ci0005 to 3.4.2-ci0006
+ # - [Release notes](....)
+ # - [Changelog](....)
+ # - [Commits](....)
+ #
+ # There appears to be a DevOps bug when setting "completeOptions" with a "mergeCommitMessage" that is
+ # truncated to 4000 characters. The error message is:
+ # Invalid argument value.
+ # Parameter name: Completion options have exceeded the maximum encoded length (4184/4000)
+ #
+ # Most users seem to agree that the effective limit is about 3500 characters.
+ # https://developercommunity.visualstudio.com/t/raise-the-character-limit-for-pull-request-descrip/365708
+ #
+ # Until this is fixed, we hard cap the max length to 3500 characters
+ #
+ merge_commit_message_max_length = 3500 # ::Dependabot::PullRequestCreator::Azure::PR_DESCRIPTION_MAX_LENGTH
+ merge_commit_message_encoding = ::Dependabot::PullRequestCreator::Azure::PR_DESCRIPTION_ENCODING
+ merge_commit_message = "Merged PR #{pull_request_id}: #{pull_request_title}\n\n#{pull_request_description}"
+ .force_encoding(merge_commit_message_encoding)
+
+ if merge_commit_message.length > merge_commit_message_max_length
+ merge_commit_message = merge_commit_message[0..merge_commit_message_max_length]
+ end
+
+ ::Dependabot.logger.info("Setting auto complete on PR ##{pull_request_id}.")
+ job.azure_client.autocomplete_pull_request(
+ # Adding argument names will fail! Maybe because there is no spec?
+ pull_request_id.to_i,
+ auto_complete_user_id,
+ merge_commit_message,
+ true, # delete_source_branch
+ true, # squash_merge
+ job.azure_merge_strategy,
+ false, # trans_work_items
+ job.azure_auto_complete_ignore_config_ids
+ )
+ rescue StandardError => e
+ # This has most likely happened because merge_commit_message exceeded 4000 characters (see comments above)
+ # Auto-completing the PR is not critical to the process, so continue on
+ ::Dependabot.logger.warn(
+ "Failed to set auto-complete status for PR ##{pull_request_id}. The error was: #{e.message}"
+ )
+ end
+
+ def pull_request_auto_approve(pull_request)
+ pull_request_id = pull_request["pullRequestId"]
+ ::Dependabot.logger.info("Setting auto approval on PR ##{pull_request_id}")
+ job.azure_client.pull_request_approve(
+ # Adding argument names will fail! Maybe because there is no spec?
+ pull_request_id.to_i,
+ job.azure_auto_approve_user_token
+ )
+ rescue StandardError => e
+ # This has most likely happened because the auto-approve user token is invalid
+ # Auto-approving the PR is not critical to the process, so continue on
+ ::Dependabot.logger.warn(
+ "Failed to set auto-approve status for PR ##{pull_request_id}. The error was: #{e.message}"
+ )
+ end
+
+ def pull_request_replace_property_metadata(pull_request, package_manager, dependency_change, base_commit_sha)
+ # Update the pull request property metadata with info about the updated dependencies.
+ # This is used in `job.rb` to calculate "existing_pull_requests" in future jobs.
+ pull_request_id = pull_request["pullRequestId"]
+ ::Dependabot.logger.info("Setting Dependabot metadata properties for PR ##{pull_request_id}")
+ job.azure_client.pull_request_properties_update(
+ pull_request_id.to_s,
+ {
+ PullRequest::Properties::PACKAGE_MANAGER =>
+ package_manager,
+ PullRequest::Properties::BASE_COMMIT_SHA =>
+ base_commit_sha.to_s,
+ PullRequest::Properties::UPDATED_DEPENDENCIES =>
+ pull_request_updated_dependencies_property_data(dependency_change).to_json
+ }
+ )
+ end
+
+ def pull_request_updated_dependencies_property_data(dependency_change)
+ updated_dependencies = dependency_change.updated_dependencies.map do |dep|
+ {
+ "dependency-name" => dep.name,
+ "dependency-version" => dep.version,
+ "directory" => dependency_change.grouped_update? ? dep.directory : nil,
+ "dependency-removed" => dep.removed? ? true : nil
+ }.compact
+ end
+ if dependency_change.grouped_update?
+ {
+ "dependency-group-name" => dependency_change.dependency_group.name,
+ "dependencies" => updated_dependencies.compact
+ }
+ else
+ updated_dependencies
+ end
+ end
+
+ def pull_request_header_with_compatibility_scores(dependencies)
+ return job.pr_message_header unless dependencies.any? && job.pr_compatibility_scores_badge
+
+ # Compatibility score badges are intended for single dependency security updates, not group updates.
+ # https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores
+ # In group updates, the compatibility score is not very useful and can easily exceed the max message length,
+ # so we don't show it.
+ return job.pr_message_header if dependencies.length > 1
+
+ compatibility_score_badges = dependencies.map do |dep|
+ "[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?" \
+ "dependency-name=#{dep.name}&package-manager=#{job.package_manager}&" \
+ "previous-version=#{dep.previous_version}&new-version=#{dep.version})]" \
+ "(https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)"
+ end&.join(" ")
+
+ ((job.pr_message_header || "") + "\n\n" + compatibility_score_badges).strip
+ end
+ end
+ end
+ end
+end
diff --git a/updater/lib/tinglesoftware/dependabot/clients/azure.rb b/updater/lib/tinglesoftware/dependabot/clients/azure.rb
new file mode 100644
index 00000000..cb578844
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/clients/azure.rb
@@ -0,0 +1,203 @@
+# typed: true
+# frozen_string_literal: true
+
+require "json"
+require "dependabot/shared_helpers"
+require "excon"
+
+#
+# Azure DevOps client that provides additional helper methods not available in the dependabot-core client.
+#
+module TingleSoftware
+ module Dependabot
+ module Clients
+ class Azure < ::Dependabot::Clients::Azure
+ API_VERSION = "7.0"
+
+ # https://learn.microsoft.com/en-us/javascript/api/azure-devops-extension-api/connectiondata
+ def get_user_id(token = nil)
+ # https://stackoverflow.com/a/53227325
+ puts "auth_header = #{auth_header}"
+ puts "credentials = #{credentials}"
+ response = if token
+ get_with_token(source.api_endpoint + source.organization + "/_apis/connectionData", token)
+ else
+ get(source.api_endpoint + source.organization + "/_apis/connectionData")
+ end
+ JSON.parse(response.body).fetch("authenticatedUser")["id"]
+ end
+
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests?view=azure-devops-rest-7.1
+ def pull_requests_active_for_user_and_targeting_branch(user_id, default_branch)
+ response = get(
+ azure_devops_api_url(
+ "git/repositories/" + source.unscoped_repo + "/pullrequests",
+ "searchCriteria.status=active",
+ "searchCriteria.creatorId=#{user_id}",
+ "searchCriteria.targetRefName=refs/heads/#{default_branch}"
+ )
+ )
+ JSON.parse(response.body).fetch("value")
+ end
+
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-request?view=azure-devops-rest-7.1
+ def pull_request_abandon(pull_request_id)
+ content = {
+ status: "abandoned"
+ }
+ patch(
+ azure_devops_api_url(
+ "git/repositories/" + source.unscoped_repo + "/pullrequests/#{pull_request_id}"
+ ),
+ content.to_json
+ )
+ end
+
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-commits/get-pull-request-commits?view=azure-devops-rest-7.1
+ def pull_request_commits(pull_request_id)
+ response = get(
+ azure_devops_api_url(
+ "git/repositories/" + source.unscoped_repo + "/pullrequests/#{pull_request_id}/commits"
+ )
+ )
+ JSON.parse(response.body).fetch("value")
+ end
+
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-reviewers/create-pull-request-reviewers?view=azure-devops-rest-7.1
+ def pull_request_approve(pull_request_id, reviewer_token)
+ user_id = get_user_id(reviewer_token)
+ content = {
+ vote: 10, # 10 - approved 5 - approved with suggestions 0 - no vote -5 - waiting for author -10 - rejected
+ isReapprove: true
+ }
+ put_with_token(
+ azure_devops_api_url(
+ "git/repositories/" + source.unscoped_repo + "/pullrequests/#{pull_request_id}/reviewers/#{user_id}"
+ ),
+ content.to_json,
+ reviewer_token
+ )
+ end
+
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-threads/create?view=azure-devops-rest-7.1
+ def pull_request_thread_with_comments(pull_request_id, type, comments, status)
+ content = {
+ comments: comments.map { |c| { commentType: type || "text", content: c } },
+ status: status || "active"
+ }
+ post(
+ azure_devops_api_url(
+ "git/repositories/" + source.unscoped_repo + "/pullrequests/" + pull_request_id + "/threads"
+ ),
+ content.to_json
+ )
+ end
+
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-properties/list?view=azure-devops-rest-7.1
+ def pull_request_properties_list(pull_request_id)
+ response = get(
+ azure_devops_api_url(
+ "git/repositories/" + source.unscoped_repo + "/pullrequests/" + pull_request_id + "/properties"
+ )
+ )
+ JSON.parse(response.body).fetch("value")
+ end
+
+ # https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-properties/update?view=azure-devops-rest-7.1
+ def pull_request_properties_update(pull_request_id, properties)
+ content = properties.map do |key, value|
+ {
+ "op" => "replace", # update if exists; create if not exists
+ "path" => "/#{key}",
+ "value" => value.to_s
+ }
+ end
+ patch_as_json_patch(
+ azure_devops_api_url(
+ "git/repositories/" + source.unscoped_repo + "/pullrequests/" + pull_request_id + "/properties"
+ ),
+ content.to_json
+ )
+ end
+
+ def branch_delete(name)
+ # https://developercommunity.visualstudio.com/t/delete-tags-or-branches-using-rest-apis/698220
+ # https://github.com/MicrosoftDocs/azure-devops-docs/issues/2648
+ branch_name = name.gsub("refs/heads/", "")
+ branch_object_id = branch(branch_name)["objectId"]
+ update_ref(branch_name, branch_object_id, "0000000000000000000000000000000000000000")
+ end
+
+ private
+
+ def azure_devops_api_url(path, *query_string_params)
+ source.api_endpoint + source.organization + "/" +
+ source.project + "/_apis/" + path + "?api-version=" + API_VERSION + "&" + query_string_params&.join("&")
+ end
+
+ def patch_as_json_patch(url, json)
+ response = Excon.patch(
+ url,
+ body: json,
+ user: credentials&.fetch("username", nil),
+ password: credentials&.fetch("password", nil),
+ idempotent: true,
+ **::Dependabot::SharedHelpers.excon_defaults(
+ headers: auth_header.merge(
+ {
+ "Content-Type" => "application/json-patch+json"
+ }
+ )
+ )
+ )
+
+ raise Unauthorized if response&.status == 401
+ raise Forbidden if response&.status == 403
+ raise NotFound if response&.status == 404
+
+ response
+ end
+
+ def get_with_token(url, token)
+ response = Excon.get(
+ url,
+ user: credentials&.fetch("username", nil),
+ password: token,
+ idempotent: true,
+ **::Dependabot::SharedHelpers.excon_defaults(
+ headers: auth_header
+ )
+ )
+
+ raise Unauthorized if response.status == 401
+ raise Forbidden if response.status == 403
+ raise NotFound if response.status == 404
+
+ response
+ end
+
+ def put_with_token(url, json, token)
+ response = Excon.put(
+ url,
+ body: json,
+ user: credentials&.fetch("username", nil),
+ password: token,
+ idempotent: true,
+ **::Dependabot::SharedHelpers.excon_defaults(
+ headers: auth_header.merge(
+ {
+ "Content-Type" => "application/json"
+ }
+ )
+ )
+ )
+ raise Unauthorized if response.status == 401
+ raise Forbidden if response.status == 403
+ raise NotFound if response.status == 404
+
+ response
+ end
+ end
+ end
+ end
+end
diff --git a/updater/lib/tinglesoftware/dependabot/commands/update_all_dependencies_synchronous_command.rb b/updater/lib/tinglesoftware/dependabot/commands/update_all_dependencies_synchronous_command.rb
new file mode 100644
index 00000000..f3295e59
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/commands/update_all_dependencies_synchronous_command.rb
@@ -0,0 +1,257 @@
+# typed: true
+# frozen_string_literal: true
+
+require "base64"
+require "dependabot/base_command"
+require "dependabot/dependency_snapshot"
+require "dependabot/errors"
+require "dependabot/opentelemetry"
+require "dependabot/updater"
+require "octokit"
+
+require "tinglesoftware/dependabot/api_clients/azure_api_client"
+
+#
+# This command is a combination of "FileFetcherCommand" and "UpdateFilesCommand" from the dependabot-core updater.
+# Normally Dependabot splits dependency updates up asynchronously over multiple smaller jobs and actions.
+# e.g. fetch-files, update-dependency-snapshot, update-files, create-pull-request, etc
+#
+# In Azure DevOps, we want to do everything synchronously in a single job/command because we are expected to perform
+# dependency updates in a self-contained environment without delegating or queuing follow-up actions to external APIs.
+#
+# This command will ensure the entire end-to-end update process is done synchronously.
+#
+module TingleSoftware
+ module Dependabot
+ module Commands
+ class UpdateAllDependenciesSynchronousCommand < ::Dependabot::BaseCommand
+ attr_reader :job
+
+ # BaseCommand does not implement this method, so we should expose
+ # the instance variable for error handling to avoid raising a
+ # NotImplementedError if it is referenced
+ attr_reader :base_commit_sha
+
+ def initialize(job:)
+ @job = job
+ @service = ::Dependabot::Service.new(
+ # Use the Azure DevOps API client rather than the (default) Dependabot Service API.
+ # This allows us to perform pull request changes synchronously to within the context of this job.
+ client: TingleSoftware::Dependabot::ApiClients::AzureApiClient.new(
+ job: job,
+ dependency_snapshot_resolver: proc { @dependency_snapshot }
+ )
+ )
+ end
+
+ def perform_job
+ ::Dependabot::OpenTelemetry.tracer.in_span("update_all_dependencies_synchronous", kind: :internal) do |span|
+ span.set_attribute(::Dependabot::OpenTelemetry::Attributes::JOB_ID, job_id.to_s)
+
+ # Clone the repo contents then find all files that could contain dependency references
+ clone_repo_and_snapshot_dependency_files
+ log_what_we_found
+
+ # Update/close any existing pull requests that are out of date or no longer required
+ update_all_existing_pull_requests
+
+ # Create new pull requests any dependency [groups] that still need updating (i.e. not in an open PR already)
+ update_all_dependencies
+ end
+ end
+
+ private
+
+ def log_what_we_found
+ ::Dependabot.logger.info(
+ "Repository scan completed for '#{job.source.url}' at commit '#{@base_commit_sha}'"
+ )
+ log_found_dependency_files
+ log_found_dependencies
+ log_found_dependency_groups
+ log_found_open_pull_requests
+ end
+
+ def log_found_dependency_files
+ ::Dependabot.logger.info(
+ "Found #{dependency_files.count} #{job.package_manager} dependency reference files:"
+ )
+ dependency_files.select.each do |f|
+ ::Dependabot.logger.info(" - #{f.directory}#{File::SEPARATOR}#{f.name}")
+ end
+ end
+
+ def log_found_dependencies
+ ::Dependabot.logger.info(
+ "Found #{dependency_snapshot.dependencies.count(&:top_level?)} top-level dependencies:"
+ )
+ dependency_snapshot.dependencies.select(&:top_level?).each do |d|
+ ::Dependabot.logger.info(" - #{d.name} (#{d.version}) #{job.vulnerable?(d) ? '(VULNERABLE!)' : ''}")
+ end
+ ::Dependabot.logger.info(
+ "Found #{dependency_snapshot.dependencies.count { |d| !d.top_level? }} transitive dependencies:"
+ )
+ dependency_snapshot.dependencies.reject(&:top_level?).each do |d|
+ ::Dependabot.logger.info(" - #{d.name} (#{d.version}) #{job.vulnerable?(d) ? '(VULNERABLE!)' : ''}")
+ end
+ end
+
+ def log_found_dependency_groups
+ ::Dependabot.logger.info(
+ "Found #{dependency_snapshot.groups.count} dependency group(s):"
+ )
+ dependency_snapshot.groups.select.each do |g|
+ ::Dependabot.logger.info(" - #{g.name}")
+ g.dependencies.each { |d| ::Dependabot.logger.info(" - #{d.name} (#{d.version})") }
+ end
+ end
+
+ def log_found_open_pull_requests
+ ::Dependabot.logger.info("Found #{job.open_pull_requests.count} open pull requests(s):")
+ job.open_pull_requests.select.each do |pr|
+ ::Dependabot.logger.info(" - ##{pr['pullRequestId']}: #{pr['title']}")
+ end
+ end
+
+ def update_all_existing_pull_requests # rubocop:disable Metrics/PerceivedComplexity
+ job.open_pull_requests.each do |pr|
+ ::Dependabot.logger.info(
+ "Checking if PR ##{pr['pullRequestId']}: #{pr['title']} needs to be updated"
+ )
+
+ deps = pr["updated_dependencies"]
+ next if deps.nil? # Ignore PRs with no updated dependency info as we can't be sure what they are updating
+
+ dependency_group_name = deps.is_a?(Hash) ? deps.fetch("dependency-group-name", nil) : nil
+ dependency_names = (deps.is_a?(Array) ? deps : deps["dependencies"])&.map { |d| d["dependency-name"] } || []
+
+ # Refocus our job towards updating this single PR, using the CURRENT snapshot of the dependecneis
+ job.for_pull_request_update(
+ dependency_group_name: dependency_group_name,
+ dependency_names: dependency_snapshot.dependencies
+ .select { |d| dependency_names.include?(d.name) }
+ .select { |d| job.allowed_update?(d) }
+ .map(&:name)
+ )
+
+ # Run the update on the PR using a clone our job with the OLD snapshot of the dependencies that existed
+ # at the time the PR created. This is important for Dependabot to be able to determine if the PR is still
+ # relevant or not.
+ run_updates_for(
+ job.clone.for_pull_request_update(
+ dependency_group_name: dependency_group_name,
+ dependency_names: dependency_names
+ )
+ )
+ end
+ end
+
+ def update_all_dependencies
+ ::Dependabot.logger.info("Checking if any dependencies need a new pull request created")
+ run_updates_for(
+ job.for_all_updates(
+ dependency_names: job.security_updates_only? ? dependencies_allowed_to_update.map(&:name) : nil
+ )
+ )
+ end
+
+ def dependencies_allowed_to_update
+ dependency_snapshot.dependencies.select { |d| job.allowed_update?(d) }
+ end
+
+ def run_updates_for(job)
+ ::Dependabot::Updater.new(
+ service: service,
+ job: job,
+ dependency_snapshot: dependency_snapshot
+ ).run
+ end
+
+ def clone_repo_and_snapshot_dependency_files
+ return unless job.clone?
+
+ ::Dependabot.logger.info(
+ "Cloning repository '#{file_fetcher.source.url}' to '#{file_fetcher.repo_contents_path}'"
+ )
+
+ # Clone the repo contents
+ file_fetcher.clone_repo_contents
+ @base_commit_sha = file_fetcher.commit
+ raise "Base commit SHA not found for '#{file_fetcher.source.url}'" unless @base_commit_sha
+
+ # Run dependency discovery
+ dependency_snapshot.all_dependencies
+ end
+
+ def dependency_snapshot
+ @dependency_snapshot ||= create_dependency_snapshot
+ end
+
+ def create_dependency_snapshot
+ ::Dependabot::DependencySnapshot.create_from_job_definition(
+ job: job,
+ job_definition: {
+ "base64_dependency_files" => base64_dependency_files.map(&:to_h),
+ "base_commit_sha" => @base_commit_sha
+ }
+ )
+ end
+
+ def file_fetcher
+ @file_fetcher ||= create_file_fetcher
+ end
+
+ # This method is responsible for creating or retrieving a file fetcher
+ # from a cache (@file_fetchers) for the given directory.
+ def file_fetcher_for_directory(directory)
+ @file_fetchers ||= {}
+ @file_fetchers[directory] ||= create_file_fetcher(directory: directory)
+ end
+
+ # A method that abstracts the file fetcher creation logic and applies the same settings across all instances
+ def create_file_fetcher(directory: nil)
+ # Use the provided directory or fallback to job.source.directory if directory is nil.
+ directory_to_use = directory || job.source.directory
+ args = {
+ source: job.source.clone.tap { |s| s.directory = directory_to_use },
+ credentials: job.credentials,
+ repo_contents_path: job.repo_contents_path,
+ options: job.experiments
+ }
+
+ ::Dependabot::FileFetchers.for_package_manager(job.package_manager).new(**args)
+ end
+
+ def dependency_files
+ @dependency_files ||= (job.source.directories || [job.source.directory]).flat_map do |dir|
+ ::Dependabot.logger.info(
+ "Searching for #{job.package_manager} dependency reference files in '#{dir}', this can take a while..."
+ )
+ ff = with_retries { file_fetcher_for_directory(dir) }
+ files = ff.files
+ files
+ end
+ end
+
+ def base64_dependency_files
+ dependency_files.map do |file|
+ base64_file = file.dup
+ base64_file.content = Base64.encode64(file.content) unless file.binary?
+ base64_file
+ end
+ end
+
+ def with_retries(max_retries: 2)
+ retries ||= 0
+ begin
+ yield
+ rescue Octokit::BadGateway
+ retries += 1
+ retry if retries <= max_retries
+ raise
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/updater/lib/tinglesoftware/dependabot/job.rb b/updater/lib/tinglesoftware/dependabot/job.rb
new file mode 100644
index 00000000..aa765974
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/job.rb
@@ -0,0 +1,529 @@
+# typed: strict
+# frozen_string_literal: true
+
+require "dependabot/job"
+require "tinglesoftware/dependabot/clients/azure"
+require "tinglesoftware/dependabot/vulnerabilities"
+
+#
+# Represents a Dependabot job; A single unit of work that Dependabot can be performed (e.g. "update all dependencies").
+# This class contains all the user configuration needed to perform a job, parsed from environment variables.
+#
+module TingleSoftware
+ module Dependabot
+ class Job < ::Dependabot::Job # rubocop:disable Metrics/ClassLength
+ extend T::Sig
+
+ def initialize(azure_client: nil, experiments: nil)
+ @azure_client = azure_client
+ super(
+ id: _id,
+ allowed_updates: _allowed_updates,
+ commit_message_options: _commit_message_options,
+ credentials: _credentials,
+ dependencies: [],
+ existing_pull_requests: _existing_pull_requests,
+ existing_group_pull_requests: _existing_group_pull_requests,
+ experiments: _experiments.merge(experiments || {}),
+ ignore_conditions: _ignore_conditions,
+ package_manager: _package_manager,
+ reject_external_code: _reject_external_code,
+ repo_contents_path: _repo_contents_path,
+ requirements_update_strategy: _requirements_update_strategy,
+ lockfile_only: _lockfile_only,
+ security_advisories: security_advisories,
+ security_updates_only: security_updates_only,
+ source: _source,
+ token: github_access_token,
+ update_subdependencies: true,
+ updating_a_pull_request: false,
+ vendor_dependencies: _vendor_dependencies,
+ dependency_groups: _dependency_groups,
+ dependency_group_to_refresh: nil
+ )
+ end
+
+ #
+ # Reconfigure the job to update all dependencies.
+ # The job will focus on creating new pull requests for all discovered dependencies.
+ # This is the default configuration when a job is created.
+ #
+ def for_all_updates(dependency_names: nil)
+ @updating_a_pull_request = false
+ @dependencies = dependency_names || []
+ @dependency_group_to_refresh = nil
+ @existing_pull_requests = _existing_pull_requests
+ @existing_group_pull_requests = _existing_group_pull_requests
+ self
+ end
+
+ #
+ # Reconfigure the job to update a single pull request.
+ # The job will focus on updating or closing any existing pull request with the given dependencies or group name.
+ #
+ def for_pull_request_update(dependency_names: nil, dependency_group_name: nil)
+ @updating_a_pull_request = true
+ @dependencies = dependency_names
+ @dependency_group_to_refresh = dependency_group_name
+ @existing_pull_requests = _existing_pull_requests
+ @existing_group_pull_requests = _existing_group_pull_requests
+ self
+ end
+
+ def validate_job
+ super
+ return unless security_updates_only && !token
+
+ raise StandardError,
+ "Security only updates are enabled but a GitHub token is not supplied! Cannot proceed"
+ end
+
+ def vulnerabilities_fetcher
+ return unless token
+
+ @vulnerabilities_fetcher ||= TingleSoftware::Dependabot::Vulnerabilities::Fetcher.new(package_manager,
+ token)
+ end
+
+ def vulnerabilities_fixed_for(updated_dependencies)
+ updated_dependencies.filter_map do |dep|
+ {
+ dep.name => @security_advisories.select { |adv| adv["dependency-name"] == dep.name }
+ .select { |adv| self.class.security_advisory_fixed_by?(dep, adv) }
+ .map { |adv| adv.transform_keys { |key| key.tr("-", "_") } }
+ }
+ end&.reduce(:merge)
+ end
+
+ def self.security_advisory_fixed_by?(dep, adv)
+ ::Dependabot::SecurityAdvisory.new(
+ dependency_name: dep.name,
+ package_manager: dep.package_manager,
+ vulnerable_versions: adv["affected-versions"] || [],
+ safe_versions: (adv["patched-versions"] || []) +
+ (adv["unaffected-versions"] || [])
+ ).fixed_by?(dep)
+ end
+
+ def security_advisories_for(dependency)
+ # If configured, fetch security advisories from GitHub's Security Advisory API
+ fetch_missing_advisories_for(dependency) if vulnerabilities_fetcher
+ super
+ end
+
+ def fetch_missing_advisories_for(dependency)
+ @fetched_advisories_for_deps ||= []
+ return if @fetched_advisories_for_deps.any?(dependency.name)
+
+ # Cache the dependency name to avoid fetching the same advisories multiple times
+ @fetched_advisories_for_deps.push(dependency.name)
+ @security_advisories.push(
+ *vulnerabilities_fetcher.fetch(dependency.name)
+ )
+ end
+
+ def _id
+ ENV.fetch("DEPENDABOT_JOB_ID", Time.now.to_i.to_s)
+ end
+
+ def _allowed_updates
+ conditions = JSON.parse(ENV.fetch("DEPENDABOT_ALLOW_CONDITIONS", "[]")).compact
+ return conditions if conditions.count.nonzero?
+
+ # If no conditions are specified, default to updating all dependencies
+ conditions << {
+ "dependency-type" => "all"
+ }
+ end
+
+ def _commit_message_options
+ JSON.parse(ENV.fetch("DEPENDABOT_COMMIT_MESSAGE_OPTIONS", "{}"))
+ end
+
+ def _credentials
+ creds = [
+ # Access to DevOps source repositories
+ {
+ "type" => "git_source",
+ "host" => azure_hostname,
+ "username" => ENV.fetch("AZURE_ACCESS_USERNAME", nil) || "x-access-token",
+ "password" => ENV.fetch("AZURE_ACCESS_TOKEN", nil)
+ },
+ # Access to other user-specified sources
+ *JSON.parse(ENV.fetch("DEPENDABOT_EXTRA_CREDENTIALS", "[]"))
+ ]
+ if github_access_token
+ # Access to GitHub source repositories and GitHub Advisory API
+ creds << {
+ "type" => "git_source",
+ "host" => "github.com",
+ "username" => "x-access-token",
+ "password" => github_access_token
+ }
+ end
+ creds.compact
+ end
+
+ def _experiments
+ ENV.fetch("DEPENDABOT_UPDATER_OPTIONS", "").split(",").to_h do |o|
+ if o.include?("=") # key/value pair, e.g. goprivate=true
+ o.split("=", 2).map.with_index do |v, i|
+ if i.zero?
+ v.strip.downcase
+ else
+ v.strip
+ end
+ end
+ else # just a key, e.g. "vendor"
+ [o.strip.downcase, true]
+ end
+ end
+ end
+
+ def _ignore_conditions
+ JSON.parse(ENV.fetch("DEPENDABOT_IGNORE_CONDITIONS", "[]")).compact
+ end
+
+ def _package_manager
+ pkg_mgr = ENV.fetch("DEPENDABOT_PACKAGE_MANAGER", "bundler")
+
+ # GitHub native implementation modifies some of the names in the config file
+ # https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem
+ {
+ "github-actions" => "github_actions",
+ "gitsubmodule" => "submodules",
+ "gomod" => "go_modules",
+ "mix" => "hex",
+ "npm" => "npm_and_yarn",
+ # Additional ones
+ "yarn" => "npm_and_yarn",
+ "pipenv" => "pip",
+ "pip-compile" => "pip",
+ "poetry" => "pip"
+ }.freeze.fetch(pkg_mgr, pkg_mgr)
+ end
+
+ def _reject_external_code
+ ENV.fetch("DEPENDABOT_REJECT_EXTERNAL_CODE", nil) == "true"
+ end
+
+ def _repo_contents_path
+ ENV.fetch("DEPENDABOT_REPO_CONTENTS_PATH", nil) ||
+ File.expand_path(File.join("job", _id, "repo", azure_repository_path.split("/")))
+ end
+
+ def _requirements_update_strategy
+ versioning_strategy = ENV.fetch("DEPENDABOT_VERSIONING_STRATEGY", nil)
+ return nil if versioning_strategy.nil? || versioning_strategy.empty? || versioning_strategy == "auto"
+
+ # GitHub native implementation modifies some of the names in the config file
+ # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#versioning-strategy
+ {
+ "increase" => "bump_versions",
+ "increase-if-necessary" => "bump_versions_if_necessary",
+ "lockfile-only" => "lockfile_only",
+ "widen" => "widen_ranges"
+ }.freeze.fetch(versioning_strategy, versioning_strategy)
+ end
+
+ def _lockfile_only
+ ENV.fetch("DEPENDABOT_LOCKFILE_ONLY", nil) == "true"
+ end
+
+ def _vendor_dependencies
+ ENV.fetch("DEPENDABOT_VENDOR", nil) == "true"
+ end
+
+ def _dependency_groups
+ groups = JSON.parse(ENV.fetch("DEPENDABOT_DEPENDENCY_GROUPS", "{}")).map do |k, v|
+ {
+ "name" => k,
+ "rules" => v
+ }
+ end
+ return groups if groups.count.nonzero?
+
+ nil
+ end
+
+ def _source
+ {
+ "provider" => provider,
+ "hostname" => azure_hostname,
+ "api-endpoint" => azure_api_endpoint,
+ "repo" => azure_repository_path,
+ "directory" => (directories.any? ? nil : directory),
+ "directories" => (directories.any? ? directories : nil),
+ "branch" => branch
+ }
+ end
+
+ def directory
+ ENV.fetch("DEPENDABOT_DIRECTORY", "/")
+ end
+
+ def directories
+ JSON.parse(ENV.fetch("DEPENDABOT_DIRECTORIES", nil.to_json))&.compact || []
+ end
+
+ def branch
+ ENV.fetch("DEPENDABOT_TARGET_BRANCH", nil)
+ end
+
+ def github_access_token
+ ENV.fetch("GITHUB_ACCESS_TOKEN", nil)
+ end
+
+ def provider
+ "azure"
+ end
+
+ def azure_client
+ @azure_client ||= TingleSoftware::Dependabot::Clients::Azure.for_source(
+ source: ::Dependabot::Source.new(
+ provider: provider,
+ hostname: azure_hostname,
+ api_endpoint: azure_api_endpoint,
+ repo: azure_repository_path,
+ directory: (directories.any? ? nil : directory),
+ directories: (directories.any? ? directories : nil),
+ branch: branch
+ ),
+ credentials: _credentials.map { |c| ::Dependabot::Credential.new(c) }
+ )
+ end
+
+ def azure_protocol
+ ENV.fetch("AZURE_PROTOCOL", "https")
+ end
+
+ def azure_hostname
+ ENV.fetch("AZURE_HOSTNAME", "dev.azure.com")
+ end
+
+ def azure_port
+ ENV.fetch("AZURE_PORT", azure_protocol == "http" ? "80" : "443")
+ end
+
+ def azure_virtual_directory
+ ENV.fetch("AZURE_VIRTUAL_DIRECTORY", "")
+ end
+
+ def azure_api_endpoint
+ virual_directory = azure_virtual_directory.empty? ? "" : "#{azure_virtual_directory}/}"
+ "#{azure_protocol}://#{azure_hostname}:#{azure_port}/#{virual_directory}"
+ end
+
+ def azure_organization
+ ENV.fetch("AZURE_ORGANIZATION", nil)
+ end
+
+ def azure_project
+ ENV.fetch("AZURE_PROJECT", nil)
+ end
+
+ def azure_repository
+ ENV.fetch("AZURE_REPOSITORY", nil)
+ end
+
+ def azure_repository_path
+ "#{azure_organization}/#{azure_project}/_git/#{azure_repository}"
+ end
+
+ def azure_set_auto_complete
+ ENV.fetch("AZURE_SET_AUTO_COMPLETE", nil) == "true"
+ end
+
+ def azure_auto_complete_ignore_config_ids
+ JSON.parse(ENV.fetch("AZURE_AUTO_COMPLETE_IGNORE_CONFIG_IDS", "[]")).compact
+ end
+
+ def azure_set_auto_approve
+ ENV.fetch("AZURE_AUTO_APPROVE_PR", nil) == "true"
+ end
+
+ def azure_auto_approve_user_token
+ ENV.fetch("AZURE_AUTO_APPROVE_USER_TOKEN", nil) || ENV.fetch("AZURE_ACCESS_TOKEN", nil)
+ end
+
+ def azure_merge_strategy
+ ENV.fetch("AZURE_MERGE_STRATEGY", "squash")
+ end
+
+ def _existing_pull_requests
+ open_pull_requests.filter_map { |pr| pr["updated_dependencies"] }
+ .select { |d| d.is_a?(Array) }
+ end
+
+ def _existing_group_pull_requests
+ update_group_name = dependency_group_to_refresh
+ open_pull_requests.filter_map { |pr| pr["updated_dependencies"] }
+ .select { |d| d.is_a?(Hash) }
+ # If we are updating an existing group PR, we must only return PRs that match the group name
+ # of the current job. This is because "refresh_group_update_pull_request.rb" will mark all
+ # dependencies of all other group PRs as "handled" to prevent multiple PRs from being reated
+ # during the refresh. However, when we operate in the "do everything in a single job" mode,
+ # this has the side effect of causing Dependabot to think the other group PRs have already
+ # been handled; it then closes them with "update_no_longer_possible". We don't want this.
+ .select { |d| update_group_name.nil? || d["dependency-group-name"] == update_group_name }
+ end
+
+ def existing_pull_request_with_updated_dependencies(updated_dependencies)
+ open_pull_requests.find do |pr|
+ pr["updated_dependencies"] == updated_dependencies
+ end
+ end
+
+ def existing_pull_request_for_dependency_names(dependency_names)
+ open_pull_requests.find do |pr|
+ deps = pr["updated_dependencies"]
+ next if deps.nil? # Ignore PRs with no updated dependency info as we can't be sure what they are updating
+
+ dependency_names == (deps.is_a?(Array) ? deps : deps["dependencies"])&.map { |d| d["dependency-name"] }
+ end
+ end
+
+ def refresh_open_pull_requests
+ @open_pull_requests = fetch_open_pull_requests
+ end
+
+ def open_pull_requests
+ @open_pull_requests ||= fetch_open_pull_requests
+ end
+
+ def fetch_open_pull_requests
+ ::Dependabot.logger.info(
+ "Fetching pull request info for existing dependency updates."
+ )
+ user_id = azure_client.get_user_id
+ target_branch_name = branch || azure_client.fetch_default_branch(azure_repository_path)
+ azure_client.pull_requests_active_for_user_and_targeting_branch(user_id, target_branch_name).filter_map do |pr|
+ pull_request_id = pr["pullRequestId"].to_s
+ pr["properties"] = azure_client.pull_request_properties_list(pull_request_id).to_h { |k, v| [k, v["$value"]] }
+ pr["package_manager"] =
+ pr["properties"][ApiClients::AzureApiClient::PullRequest::Properties::PACKAGE_MANAGER]
+ pr["base_commit_sha"] =
+ pr["properties"][ApiClients::AzureApiClient::PullRequest::Properties::BASE_COMMIT_SHA]
+ pr["updated_dependencies"] = JSON.parse(
+ pr["properties"][ApiClients::AzureApiClient::PullRequest::Properties::UPDATED_DEPENDENCIES] || nil.to_json
+ )
+
+ # Ignore PRs that are for different package managers
+ # This avoids us trying to update a NuGet dependency in a NPM update job, for example
+ next unless pr["package_manager"] == _package_manager
+
+ pr
+ end
+ end
+
+ def open_pull_requests_limit
+ ENV.fetch("DEPENDABOT_OPEN_PULL_REQUESTS_LIMIT", "5").to_i
+ end
+
+ def open_pull_request_limit_reached?
+ open_pull_requests_limit.nonzero? && open_pull_requests.count >= open_pull_requests_limit
+ end
+
+ def security_updates_only
+ # If the pull request limit is set to zero, we assume that the user just wants security updates
+ # https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/configuring-dependabot-security-updates#overriding-the-default-behavior-with-a-configuration-file
+ return true if open_pull_requests_limit.zero?
+
+ false
+ end
+
+ def security_advisories
+ @security_advisories ||= parse_security_advisories_file
+ end
+
+ def parse_security_advisories_file
+ security_advisories_file_path = ENV.fetch("DEPENDABOT_SECURITY_ADVISORIES_FILE", nil)
+ return [] unless security_advisories_file_path && File.exist?(security_advisories_file_path)
+
+ JSON.parse(
+ File.read(security_advisories_file_path)
+ )
+ end
+
+ def pr_author_name
+ ENV.fetch("DEPENDABOT_AUTHOR_NAME", "dependabot[bot]")
+ end
+
+ def pr_author_email
+ ENV.fetch("DEPENDABOT_AUTHOR_EMAIL", "noreply@github.com")
+ end
+
+ def pr_signature_key
+ ENV.fetch("DEPENDABOT_SIGNATURE_KEY", nil)
+ end
+
+ def pr_compatibility_scores_badge
+ ENV.fetch("DEPENDABOT_COMPATIBILITY_SCORE_BADGE", nil) == "true"
+ end
+
+ def pr_message_header
+ ENV.fetch("DEPENDABOT_MESSAGE_HEADER", nil)&.dup&.gsub! "\\n", "\n"
+ end
+
+ def pr_message_footer
+ ENV.fetch("DEPENDABOT_MESSAGE_FOOTER", nil)&.dup&.gsub! "\\n", "\n"
+ end
+
+ def pr_custom_labels
+ labels = JSON.parse(ENV.fetch("DEPENDABOT_LABELS", "[]")).compact
+ return labels if labels.count.nonzero?
+
+ nil # use nil instead of empty array to ensure default labels are passed
+ end
+
+ def pr_reviewers
+ reviewers = JSON.parse(ENV.fetch("DEPENDABOT_REVIEWERS", "[]")).compact
+ return reviewers if reviewers.count.nonzero?
+
+ nil # use nil instead of empty array to avoid API rejection
+ end
+
+ def pr_assignees
+ assignees = JSON.parse(ENV.fetch("DEPENDABOT_ASSIGNEES", "[]")).compact
+ return assignees if assignees.count.nonzero?
+
+ nil # use nil instead of empty array to avoid API rejection
+ end
+
+ def pr_milestone
+ milestone = ENV.fetch("DEPENDABOT_MILESTONE", nil).to_i
+ return milestone if milestone.nonzero?
+
+ nil # use nil instead of zero
+ end
+
+ def pr_branch_name_separator
+ ENV.fetch("DEPENDABOT_BRANCH_NAME_SEPARATOR", "/")
+ end
+
+ def pr_branch_name_prefix
+ ENV.fetch("DEPENDABOT_BRANCH_NAME_PREFIX", "dependabot")
+ end
+
+ def skip_pull_requests
+ ENV.fetch("DEPENDABOT_SKIP_PULL_REQUESTS", nil) == "true"
+ end
+
+ def close_pull_requests
+ ENV.fetch("DEPENDABOT_CLOSE_PULL_REQUESTS", nil) == "true"
+ end
+
+ def comment_pull_requests
+ ENV.fetch("DEPENDABOT_COMMENT_PULL_REQUESTS", nil) == "true"
+ end
+
+ def fail_on_exception
+ ENV.fetch("DEPENDABOT_FAIL_ON_EXCEPTION", "true") == "true"
+ end
+
+ def debug_enabled?
+ ENV.fetch("DEPENDABOT_DEBUG", nil) == "true"
+ end
+ end
+ end
+end
diff --git a/updater/lib/tinglesoftware/dependabot/overrides/nuget/nuget_config_credential_helpers.rb b/updater/lib/tinglesoftware/dependabot/overrides/nuget/nuget_config_credential_helpers.rb
new file mode 100644
index 00000000..43a98d3d
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/overrides/nuget/nuget_config_credential_helpers.rb
@@ -0,0 +1,96 @@
+# typed: strong
+# frozen_string_literal: true
+
+# src: https://github.com/dependabot/dependabot-core/blob/8441dbad1bb13149f897cdbe92c11d36f98c8248/nuget/lib/dependabot/nuget/nuget_config_credential_helpers.rb
+
+#
+# This module overrides the dependabot-core nuget.config credential helper with our own implementation that fixes:
+# - https://github.com/tinglesoftware/dependabot-azure-devops/issues/1243
+#
+# Without this override, updates via MSBuild/NuGet.exe to .NET Framework projects (i.e. packages.config projects)
+# fail to authenticate with private NuGet feeds when:
+# - The NuGet feed is hosted in Azure DevOps; The password is invalid if token notation is "PAT:12345"
+# - The NuGet feed is secured with basic auth (e.g. nuget.telerik.com); The username was hardcoded to "user"
+# - The source repository already contains a nuget.config file; All feed config and package source mappings are ignored
+#
+# This module primarily fixes auth in .NET Framework projects; .NET [Core] projects auth is handled differently.
+# See: tinglesoftware/azure/artifacts_credential_provider.rb for more info.
+#
+# This credential provider is required for ALL private NuGet feeds, even if they are not hosted in Azure DevOps.
+# See README.md (Credentials for private registries and feeds) for more details.
+#
+
+# TODO: Remove this once https://github.com/dependabot/dependabot-core/pull/8927 is resolved or auth works natively.
+
+module Dependabot
+ module Nuget
+ module NuGetConfigCredentialHelpers
+ def self.add_credentials_to_nuget_config(credentials)
+ return unless File.exist?(user_nuget_config_path)
+
+ nuget_credentials = credentials.select { |cred| cred["type"] == "nuget_feed" }
+ return if nuget_credentials.empty?
+
+ # When updating via MSBuild/NuGet.exe, dependabot temporarily overrides $HOME/.nuget/NuGet/NuGet.Config.
+ # The idea here is that we should only add missing package sources and missing credentials.
+
+ File.rename(user_nuget_config_path, temporary_nuget_config_path)
+ File.write(
+ user_nuget_config_path,
+ <<~NUGET_XML
+
+
+
+ #{package_sources_xml_lines(nuget_credentials).join("\n ").strip}
+
+
+ #{package_source_credentials_xml_lines(nuget_credentials).join("\n ").strip}
+
+
+ NUGET_XML
+ )
+ end
+
+ def self.package_sources_xml_lines(credentials)
+ credentials.each_with_index.filter_map do |c, i|
+ # Ignore package sources that have a key (name), as these are already defined in the user's nuget.config file.
+ # Ensures that package source ordering and package source mappings in the user's nuget.config are respected.
+ next if c["key"]
+
+ ""
+ end
+ end
+
+ def self.package_source_credentials_xml_lines(credentials) # rubocop:disable Metrics/PerceivedComplexity
+ credentials.each_with_index.flat_map do |c, i|
+ # Ignore public package sources, no credentials required
+ next unless c["token"] || c["username"] || c["password"]
+
+ # Use the package source key (name) if provided, otherwise fallback to a auto-generated key.
+ # We want preserve the package source key (name) if it is already defined in the user's nuget.config file.
+ # This ensures that package source mappings in the user's nuget.config are respected.
+ source_key = c["key"] || "nuget_source_#{i + 1}"
+ # Use username/password auth if provided, otherwise fallback to token auth.
+ # This provides maximum compatibility with Azure DevOps, DevOps Server, and other third-party feeds.
+ # When using DevOps PATs, the token is split into username/password parts; Username is not significant.
+ # e.g. token "PAT:12345" --> { "username": "PAT", "password": "12345" }
+ # ":12345" --> { "username": "", "password": "12345" }
+ # "12345" --> { "username": "12345", "password": "12345" } # username gets redacted to "user"
+ source_username = c["username"] || c["token"]&.split(":")&.first
+ source_password = c["password"] || c["token"]&.split(":")&.last
+ # NuGet.exe will log the username in plain text to the console, which is not great for security!
+ # If the username and password are the same value, we can assume that "token" auth is being used and that the
+ # username is not significant, so redact it to something generic to avoid leaking sensitive information.
+ # e.g. { "username": "12345", "password": "12345" } --> { "username": "user", "password": "12345" }
+ source_username = "user" if source_username == source_password
+ [
+ "<#{source_key}>",
+ " ",
+ " ",
+ "#{source_key}>"
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/updater/lib/tinglesoftware/dependabot/overrides/pull_request_creator/pr_name_prefixer.rb b/updater/lib/tinglesoftware/dependabot/overrides/pull_request_creator/pr_name_prefixer.rb
new file mode 100644
index 00000000..a512f0e3
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/overrides/pull_request_creator/pr_name_prefixer.rb
@@ -0,0 +1,31 @@
+# typed: strict
+# frozen_string_literal: true
+
+# src: https://github.com/dependabot/dependabot-core/blob/8441dbad1bb13149f897cdbe92c11d36f98c8248/common/lib/dependabot/pull_request_creator/pr_name_prefixer.rb
+
+#
+# This module overrides the dependabot-core PR name prefixer with our own implementation that allows setting the styler.
+# Without this override, there is no way for the end-user to explicitly set the PR name prefix styler implementation.
+#
+
+# TODO: Remove this if/when dependabot makes this user-configurable or removes this feature entirely.
+
+require "dependabot/pull_request_creator"
+
+module Dependabot
+ class PullRequestCreator
+ class PrNamePrefixer
+ def using_angular_commit_messages?
+ ENV.fetch("DEPENDABOT_PR_NAME_PREFIX_STYLE", nil) == "angular"
+ end
+
+ def using_eslint_commit_messages?
+ ENV.fetch("DEPENDABOT_PR_NAME_PREFIX_STYLE", nil) == "eslint"
+ end
+
+ def using_gitmoji_commit_messages?
+ ENV.fetch("DEPENDABOT_PR_NAME_PREFIX_STYLE", nil) == "gitmoji"
+ end
+ end
+ end
+end
diff --git a/updater/lib/tinglesoftware/dependabot/setup.rb b/updater/lib/tinglesoftware/dependabot/setup.rb
new file mode 100644
index 00000000..5b0083a1
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/setup.rb
@@ -0,0 +1,73 @@
+# typed: strict
+# frozen_string_literal: true
+
+require "json"
+require "logger"
+require "sentry-ruby"
+
+require "dependabot"
+require "dependabot/logger"
+require "dependabot/logger/formats"
+require "dependabot/simple_instrumentor"
+require "dependabot/opentelemetry"
+require "dependabot/sentry"
+require "dependabot/environment"
+
+ENV["DEPENDABOT_JOB_ID"] = Time.now.to_i.to_s unless ENV["DEPENDABOT_JOB_ID"]
+
+Dependabot.logger = Logger.new($stdout).tap do |logger|
+ logger.level = ENV["DEPENDABOT_DEBUG"].to_s == "true" ? :debug : :info
+ logger.formatter = Dependabot::Logger::BasicFormatter.new
+end
+
+# TODO: connect error handler to the job, operation, or service so that the errors can be reported to Sentry here
+Sentry.init do |config|
+ config.dsn = "https://0abd35cde5ca89c8dbcfb766a5f5bc50@o4507686465830912.ingest.us.sentry.io/4507686484508672"
+ config.release = ENV.fetch("DEPENDABOT_UPDATER_VERSION", "unknown")
+ config.logger = Dependabot.logger
+
+ config.before_send = ->(event, hint) { Dependabot::Sentry.process_chain(event, hint) }
+ config.propagate_traces = false
+ config.instrumenter = ::Dependabot::OpenTelemetry.should_configure? ? :otel : :sentry
+end
+
+Dependabot::SimpleInstrumentor.subscribe do |*args|
+ name = args.first
+ payload = args.last
+ if name == "excon.request" || name == "excon.response"
+ error_codes = [400, 500].freeze
+ puts "đ #{name == 'excon.response' ? "<-- #{payload[:status]}" : "--> #{payload[:method].upcase}"}" \
+ " #{Excon::Utils.request_uri(payload)}"
+ puts "đ¨ #{payload[:body]}" if payload[:body] && error_codes.include?(payload[:status])
+ end
+end
+
+Dependabot::OpenTelemetry.configure
+
+# Ecosystems
+require "dependabot/python"
+require "dependabot/terraform"
+require "dependabot/elm"
+require "dependabot/docker"
+require "dependabot/git_submodules"
+require "dependabot/github_actions"
+require "dependabot/composer"
+require "dependabot/nuget"
+require "dependabot/gradle"
+require "dependabot/maven"
+require "dependabot/hex"
+require "dependabot/cargo"
+require "dependabot/go_modules"
+require "dependabot/npm_and_yarn"
+require "dependabot/bundler"
+require "dependabot/pub"
+require "dependabot/swift"
+require "dependabot/devcontainers"
+
+# Overrides for dependabot core functionality that are currently not extensible
+require "tinglesoftware/dependabot/overrides/pull_request_creator/pr_name_prefixer"
+
+# Fixes for NuGet feed auth issues
+# TODO: Remove this once https://github.com/dependabot/dependabot-core/pull/8927 is resolved or auth works natively.
+require "tinglesoftware/dependabot/overrides/nuget/nuget_config_credential_helpers"
+require "tinglesoftware/azure/artifacts_credential_provider"
diff --git a/updater/lib/tinglesoftware/dependabot/vulnerabilities.rb b/updater/lib/tinglesoftware/dependabot/vulnerabilities.rb
new file mode 100644
index 00000000..e1dc23a7
--- /dev/null
+++ b/updater/lib/tinglesoftware/dependabot/vulnerabilities.rb
@@ -0,0 +1,90 @@
+# typed: true
+# frozen_string_literal: true
+
+require "octokit"
+
+#
+# Fetches security advisory information from GitHub's Security Advisory API
+#
+module TingleSoftware
+ module Dependabot
+ module Vulnerabilities
+ class Fetcher
+ class QueryError < StandardError; end
+
+ ECOSYSTEM_LOOKUP = {
+ "github_actions" => "ACTIONS",
+ "composer" => "COMPOSER",
+ "elm" => "ERLANG",
+ "go_modules" => "GO",
+ "maven" => "MAVEN",
+ "npm_and_yarn" => "NPM",
+ "nuget" => "NUGET",
+ "pip" => "PIP",
+ "pub" => "PUB",
+ "bundler" => "RUBYGEMS",
+ "cargo" => "RUST"
+ }.freeze
+
+ GRAPHQL_QUERY = <<-GRAPHQL
+ query($ecosystem: SecurityAdvisoryEcosystem, $package: String) {
+ securityVulnerabilities(first: 100, ecosystem: $ecosystem, package: $package) {
+ nodes {
+ advisory {
+ summary,
+ description,
+ permalink
+ }
+ firstPatchedVersion {
+ identifier
+ }
+ vulnerableVersionRange
+ }
+ }
+ }
+ GRAPHQL
+
+ def initialize(package_manager, github_token)
+ @ecosystem = ECOSYSTEM_LOOKUP.fetch(package_manager, nil)
+ @client ||= Octokit::Client.new(access_token: github_token)
+ end
+
+ def fetch(dependency_name)
+ return [] unless @ecosystem
+
+ response = @client.post "/graphql", {
+ query: GRAPHQL_QUERY,
+ variables: {
+ ecosystem: @ecosystem,
+ package: dependency_name
+ }
+ }.to_json
+
+ raise(QueryError, response[:errors]&.map(&:message)&.join(", ")) if response[:errors]
+
+ response.data[:securityVulnerabilities][:nodes].map do |node|
+ # Filter out nil (using .compact), white spaces and empty strings which is necessary for situations
+ # where the API response contains null that is converted to nil, or it is an empty
+ # string. For example, npm package named faker does not have patched version as of 2023-01-16
+ # See: https://github.com/advisories/GHSA-5w9c-rv96-fr7g for npm package
+ # Fixes: https://github.com/tinglesoftware/dependabot-azure-devops/issues/453#issuecomment-1383587644
+ vulnerable_version_range = node[:vulnerableVersionRange]
+ affected_versions = [vulnerable_version_range].compact.reject { |v| v.strip.empty? }
+ first_patched_version = node.dig :firstPatchedVersion, :identifier
+ patched_versions = [first_patched_version].compact.reject { |v| v.strip.empty? }
+ {
+ "dependency-name" => dependency_name,
+ "affected-versions" => affected_versions,
+ "patched-versions" => patched_versions,
+ "unaffected-versions" => [],
+ "title" => node.dig(:advisory, :summary),
+ "description" => node.dig(:advisory, :description),
+ "source-name" => "GitHub Advisory Database",
+ "source-url" => node.dig(:advisory, :permalink)
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/updater/spec/dependabot/api_client_spec.rb b/updater/spec/dependabot/api_client_spec.rb
index 9182071a..f40c6229 100644
--- a/updater/spec/dependabot/api_client_spec.rb
+++ b/updater/spec/dependabot/api_client_spec.rb
@@ -4,10 +4,13 @@
require "spec_helper"
require "dependabot/dependency"
require "dependabot/dependency_change"
+require "dependabot/dependency_file"
+require "dependabot/pull_request_creator"
require "dependabot/api_client"
RSpec.describe Dependabot::ApiClient do
- subject(:client) { Dependabot::ApiClient.new("http://example.com", 1, "token") }
+ subject(:client) { described_class.new("http://example.com", 1, "token") }
+
let(:headers) { { "Content-Type" => "application/json" } }
describe "create_pull_request" do
@@ -18,9 +21,12 @@
updated_dependency_files: dependency_files
)
end
+ let(:source) do
+ instance_double(Dependabot::Source, provider: "github", repo: "gocardless/bump", directory: "/")
+ end
let(:job) do
instance_double(Dependabot::Job,
- source: nil,
+ source: source,
credentials: [],
commit_message_options: [],
updating_a_pull_request?: false,
@@ -112,7 +118,8 @@
"source" => nil
}
],
- "version" => "1.8.0"
+ "version" => "1.8.0",
+ "directory" => "/"
}
])
expect(data["updated-dependency-files"]).to eql([
@@ -167,16 +174,16 @@
.with(headers: { "Authorization" => "token" })
.with do |req|
data = JSON.parse(req.body)["data"]
- expect(data["dependencies"].first["removed"]).to eq(true)
- expect(data["dependencies"].first.key?("version")).to eq(false)
- expect(data["dependencies"].last.key?("removed")).to eq(false)
+ expect(data["dependencies"].first["removed"]).to be(true)
+ expect(data["dependencies"].first.key?("version")).to be(false)
+ expect(data["dependencies"].last.key?("removed")).to be(false)
expect(data["dependencies"].last["version"]).to eq("1.8.0")
true
end)
end
end
- context "grouped updates" do
+ context "when dealing with grouped updates" do
it "does not include the dependency-group key by default" do
client.create_pull_request(dependency_change, base_commit)
@@ -217,9 +224,12 @@
updated_dependency_files: dependency_files
)
end
+ let(:source) do
+ instance_double(Dependabot::Source, provider: "github", repo: "gocardless/bump", directory: "/")
+ end
let(:job) do
instance_double(Dependabot::Job,
- source: nil,
+ source: source,
credentials: [],
commit_message_options: [],
updating_a_pull_request?: true)
@@ -335,6 +345,7 @@
let(:url) { "http://example.com/update_jobs/1/record_update_job_error" }
let(:error_type) { "dependency_file_not_evaluatable" }
let(:error_detail) { { "message" => "My message" } }
+
before { stub_request(:post, url).to_return(status: 204) }
it "hits the correct endpoint" do
@@ -353,6 +364,7 @@
let(:url) { "http://example.com/update_jobs/1/record_update_job_unknown_error" }
let(:error_type) { "server_error" }
let(:error_detail) { { "message" => "My message" } }
+
before { stub_request(:post, url).to_return(status: 204) }
it "hits the correct endpoint" do
@@ -370,6 +382,7 @@
describe "mark_job_as_processed" do
let(:url) { "http://example.com/update_jobs/1/mark_as_processed" }
let(:base_commit) { "sha" }
+
before { stub_request(:patch, url).to_return(status: 204) }
it "hits the correct endpoint" do
@@ -393,6 +406,7 @@
]
)
end
+
before { stub_request(:post, url).to_return(status: 204) }
it "hits the correct endpoint" do
@@ -406,6 +420,7 @@
describe "ecosystem_versions" do
let(:url) { "http://example.com/update_jobs/1/record_ecosystem_versions" }
+
before { stub_request(:post, url).to_return(status: 204) }
it "hits the correct endpoint" do
@@ -419,6 +434,7 @@
describe "increment_metric" do
let(:url) { "http://example.com/update_jobs/1/increment_metric" }
+
before { stub_request(:post, url).to_return(status: 204) }
context "when successful" do
diff --git a/updater/spec/dependabot/dependency_change_spec.rb b/updater/spec/dependabot/dependency_change_spec.rb
index 514c3223..61d6fe0e 100644
--- a/updater/spec/dependabot/dependency_change_spec.rb
+++ b/updater/spec/dependabot/dependency_change_spec.rb
@@ -2,6 +2,9 @@
# frozen_string_literal: true
require "spec_helper"
+require "dependabot/dependency"
+require "dependabot/dependency_file"
+require "dependabot/pull_request_creator"
require "dependabot/dependency_change"
require "dependabot/job"
@@ -30,7 +33,8 @@
],
previous_requirements: [
{ file: "Gemfile", requirement: "~> 1.7.0", groups: [], source: nil }
- ]
+ ],
+ directory: "/"
)
]
end
@@ -83,13 +87,19 @@
end
let(:message_builder_mock) do
- instance_double(Dependabot::PullRequestCreator::MessageBuilder, message: "Hello World!")
+ instance_double(
+ Dependabot::PullRequestCreator::MessageBuilder,
+ message: Dependabot::PullRequestCreator::Message.new(
+ pr_name: "Title",
+ pr_message: "Hello World!",
+ commit_message: "Commit message"
+ )
+ )
end
before do
- allow(job).to receive(:source).and_return(github_source)
- allow(job).to receive(:credentials).and_return(job_credentials)
- allow(job).to receive(:commit_message_options).and_return(commit_message_options)
+ allow(job).to receive_messages(source: github_source, credentials: job_credentials,
+ commit_message_options: commit_message_options)
allow(Dependabot::PullRequestCreator::MessageBuilder).to receive(:new).and_return(message_builder_mock)
end
@@ -107,7 +117,7 @@
ignore_conditions: []
)
- expect(dependency_change.pr_message).to eql("Hello World!")
+ expect(dependency_change.pr_message.pr_message).to eql("Hello World!")
end
context "when a dependency group is assigned" do
@@ -134,7 +144,108 @@
ignore_conditions: []
)
- expect(dependency_change.pr_message).to eql("Hello World!")
+ expect(dependency_change.pr_message&.pr_message).to eql("Hello World!")
+ end
+ end
+ end
+
+ describe "#should_replace_existing_pr" do
+ context "when not updating a pull request" do
+ let(:job) do
+ instance_double(Dependabot::Job, updating_a_pull_request?: false)
+ end
+
+ it "is false" do
+ expect(dependency_change.should_replace_existing_pr?).to be false
+ end
+ end
+
+ context "when updating a pull request with all dependencies matching" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: ["business"],
+ updating_a_pull_request?: true)
+ end
+
+ it "returns false" do
+ expect(dependency_change.should_replace_existing_pr?).to be false
+ end
+ end
+
+ context "when updating a pull request with duplicate dependencies" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: %w(business business),
+ updating_a_pull_request?: true)
+ end
+
+ it "returns false" do
+ expect(dependency_change.should_replace_existing_pr?).to be false
+ end
+ end
+
+ context "when updating a pull request with non-matching casing" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: ["BuSiNeSS"],
+ updating_a_pull_request?: true)
+ end
+
+ it "returns false" do
+ expect(dependency_change.should_replace_existing_pr?).to be false
+ end
+ end
+
+ context "when updating a pull request with out of order dependencies" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: %w(PkgB PkgA),
+ updating_a_pull_request?: true)
+ end
+
+ let(:updated_dependencies) do
+ [
+ Dependabot::Dependency.new(
+ name: "PkgA",
+ package_manager: "bundler",
+ version: "1.8.0",
+ previous_version: "1.7.0",
+ requirements: [
+ { file: "Gemfile", requirement: "~> 1.8.0", groups: [], source: nil }
+ ],
+ previous_requirements: [
+ { file: "Gemfile", requirement: "~> 1.7.0", groups: [], source: nil }
+ ]
+ ),
+ Dependabot::Dependency.new(
+ name: "PkgB",
+ package_manager: "bundler",
+ version: "1.8.0",
+ previous_version: "1.7.0",
+ requirements: [
+ { file: "Gemfile", requirement: "~> 1.8.0", groups: [], source: nil }
+ ],
+ previous_requirements: [
+ { file: "Gemfile", requirement: "~> 1.7.0", groups: [], source: nil }
+ ]
+ )
+ ]
+ end
+
+ it "returns false" do
+ expect(dependency_change.should_replace_existing_pr?).to be false
+ end
+ end
+
+ context "when updating a pull request with different dependencies" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: ["contoso"],
+ updating_a_pull_request?: true)
+ end
+
+ it "returns true" do
+ expect(dependency_change.should_replace_existing_pr?).to be true
end
end
end
@@ -157,4 +268,146 @@
end
end
end
+
+ describe "#matches_existing_pr?" do
+ context "when no existing pull requests are found" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: updated_dependencies.map(&:name),
+ existing_pull_requests: [])
+ end
+ let(:dependency_change) do
+ described_class.new(
+ job: job,
+ updated_dependencies: updated_dependencies,
+ updated_dependency_files: updated_dependency_files
+ )
+ end
+
+ it "returns false" do
+ expect(dependency_change.matches_existing_pr?).to be false
+ end
+ end
+
+ context "when updating a pull request with the same dependencies" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: updated_dependencies.map(&:name),
+ existing_pull_requests: existing_pull_requests)
+ end
+ let(:existing_pull_requests) do
+ [
+ updated_dependencies.map do |dep|
+ { "dependency-name" => dep.name,
+ "dependency-version" => dep.version,
+ "directory" => dep.directory }
+ end
+ ]
+ end
+ let(:dependency_change) do
+ described_class.new(
+ job: job,
+ updated_dependencies: updated_dependencies,
+ updated_dependency_files: updated_dependency_files
+ )
+ end
+
+ it "returns true" do
+ expect(dependency_change.matches_existing_pr?).to be true
+ end
+
+ context "when there's no directory in an existing PR that otherwise matches" do
+ let(:existing_pull_requests) do
+ [
+ updated_dependencies.map do |dep|
+ { "dependency-name" => dep.name,
+ "dependency-version" => dep.version }
+ end
+ ]
+ end
+
+ it "returns true" do
+ expect(dependency_change.matches_existing_pr?).to be true
+ end
+ end
+ end
+
+ context "when updating a grouped pull request with the same dependencies" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: updated_dependencies.map(&:name),
+ existing_group_pull_requests: existing_group_pull_requests)
+ end
+ let(:existing_group_pull_requests) do
+ [
+ { "dependency-group-name" => "foo",
+ "dependencies" => updated_dependencies.map do |dep|
+ { "dependency-name" => dep.name.to_s,
+ "dependency-version" => dep.version.to_s,
+ "directory" => dep.directory.to_s }
+ end }
+ ]
+ end
+
+ let(:dependency_change) do
+ described_class.new(
+ job: job,
+ updated_dependencies: updated_dependencies,
+ updated_dependency_files: updated_dependency_files,
+ dependency_group: Dependabot::DependencyGroup.new(name: "foo", rules: { patterns: ["*"] })
+ )
+ end
+
+ it "returns true" do
+ expect(dependency_change.matches_existing_pr?).to be true
+ end
+
+ context "when there's no directory in a PR that otherwise matches" do
+ let(:existing_group_pull_requests) do
+ [
+ { "dependency-group-name" => "foo",
+ "dependencies" => updated_dependencies.map do |dep|
+ { "dependency-name" => dep.name.to_s,
+ "dependency-version" => dep.version.to_s }
+ end }
+ ]
+ end
+
+ it "returns true" do
+ expect(dependency_change.matches_existing_pr?).to be true
+ end
+ end
+ end
+
+ context "when updating a grouped pull request with the same dependencies, but in different directory" do
+ let(:job) do
+ instance_double(Dependabot::Job,
+ dependencies: updated_dependencies.map(&:name),
+ existing_group_pull_requests: existing_group_pull_requests)
+ end
+ let(:existing_group_pull_requests) do
+ [
+ { "dependency-group-name" => "foo",
+ "dependencies" => updated_dependencies.map do |dep|
+ { "dependency-name" => dep.name.to_s,
+ "dependency-version" => dep.version.to_s,
+ "directory" => "/foo" }
+ end }
+ ]
+ end
+
+ let(:dependency_change) do
+ described_class.new(
+ job: job,
+ updated_dependencies: updated_dependencies,
+ updated_dependency_files: updated_dependency_files,
+ dependency_group: Dependabot::DependencyGroup.new(name: "foo", rules: { patterns: ["*"] })
+ )
+ end
+
+ it "returns false" do
+ expect(dependency_change.matches_existing_pr?).to be false
+ end
+ end
+ end
end
diff --git a/updater/spec/dependabot/dependency_group_engine_spec.rb b/updater/spec/dependabot/dependency_group_engine_spec.rb
index 0856ec1b..2c26a446 100644
--- a/updater/spec/dependabot/dependency_group_engine_spec.rb
+++ b/updater/spec/dependabot/dependency_group_engine_spec.rb
@@ -4,6 +4,7 @@
require "spec_helper"
require "support/dependency_file_helpers"
+require "dependabot/dependency"
require "dependabot/dependency_group_engine"
require "dependabot/dependency_snapshot"
require "dependabot/job"
@@ -12,9 +13,22 @@
include DependencyFileHelpers
let(:dependency_group_engine) { described_class.from_job_config(job: job) }
-
+ let(:source) do
+ Dependabot::Source.new(
+ provider: "github",
+ repo: "gocardless/bump",
+ directory: "/",
+ branch: "master"
+ )
+ end
+ let(:security_updates_only) { false }
+ let(:dependencies) { nil }
let(:job) do
- instance_double(Dependabot::Job, dependency_groups: dependency_groups_config)
+ instance_double(Dependabot::Job,
+ dependency_groups: dependency_groups_config,
+ source: source,
+ dependencies: dependencies,
+ security_updates_only?: security_updates_only)
end
let(:dummy_pkg_a) do
@@ -81,6 +95,46 @@
)
end
+ context "when a job has grouped configured, and it's a version update" do
+ let(:dependency_groups_config) do
+ [
+ {
+ "name" => "group-a",
+ "rules" => {
+ "patterns" => ["dummy-pkg-*"],
+ "exclude-patterns" => ["dummy-pkg-b"]
+ }
+ },
+ {
+ "name" => "group-b",
+ "applies-to" => "security-updates",
+ "rules" => {
+ "patterns" => %w(dummy-pkg-b dummy-pkg-c)
+ }
+ }
+ ]
+ end
+
+ describe "::from_job_config" do
+ it "filters out the security update" do
+ expect(dependency_group_engine.dependency_groups.length).to be(1)
+ expect(dependency_group_engine.dependency_groups.map(&:name)).to eql(%w(group-a))
+ end
+ end
+
+ context "when it's a security update" do
+ let(:security_updates_only) { true }
+ let(:dependencies) { %w(dummy-pkg-a dummy-pkg-b dummy-pkg-c ungrouped_pkg) }
+
+ describe "::from_job_config" do
+ it "filters out the version update" do
+ expect(dependency_group_engine.dependency_groups.length).to be(1)
+ expect(dependency_group_engine.dependency_groups.map(&:name)).to eql(%w(group-b))
+ end
+ end
+ end
+ end
+
context "when a job has groups configured" do
let(:dependency_groups_config) do
[
@@ -102,7 +156,7 @@
describe "::from_job_config" do
it "registers the dependency groups" do
- expect(dependency_group_engine.dependency_groups.length).to eql(2)
+ expect(dependency_group_engine.dependency_groups.length).to be(2)
expect(dependency_group_engine.dependency_groups.map(&:name)).to eql(%w(group-a group-b))
expect(dependency_group_engine.dependency_groups.map(&:dependencies)).to all(be_empty)
end
@@ -141,11 +195,6 @@
it "keeps a list of any dependencies that do not match any groups" do
expect(dependency_group_engine.ungrouped_dependencies).to eql([ungrouped_pkg])
end
-
- it "raises an exception if it is called a second time" do
- expect { dependency_group_engine.assign_to_groups!(dependencies: dependencies) }
- .to raise_error(described_class::ConfigurationError, "dependency groups have already been configured!")
- end
end
context "when one group has no matching dependencies" do
@@ -158,7 +207,7 @@
- group-b
This can happen if:
- - the group's 'pattern' rules are mispelled
+ - the group's 'pattern' rules are misspelled
- your configuration's 'allow' rules do not permit any of the dependencies that match the group
- the dependencies that match the group rules have been removed from your project
WARN
@@ -179,7 +228,7 @@
- group-b
This can happen if:
- - the group's 'pattern' rules are mispelled
+ - the group's 'pattern' rules are misspelled
- your configuration's 'allow' rules do not permit any of the dependencies that match the group
- the dependencies that match the group rules have been removed from your project
WARN
diff --git a/updater/spec/dependabot/file_fetcher_command_spec.rb b/updater/spec/dependabot/file_fetcher_command_spec.rb
new file mode 100644
index 00000000..e97647bf
--- /dev/null
+++ b/updater/spec/dependabot/file_fetcher_command_spec.rb
@@ -0,0 +1,367 @@
+# typed: false
+# frozen_string_literal: true
+
+require "spec_helper"
+require "dependabot/file_fetcher_command"
+require "dependabot/errors"
+require "tmpdir"
+
+require "support/dummy_package_manager/dummy"
+
+require "dependabot/bundler"
+
+RSpec.describe Dependabot::FileFetcherCommand do
+ subject(:job) { described_class.new }
+
+ let(:api_client) { double(Dependabot::ApiClient) }
+ let(:job_id) { "123123" }
+
+ before do
+ allow(Dependabot::ApiClient).to receive(:new).and_return(api_client)
+
+ allow(api_client).to receive(:mark_job_as_processed)
+ allow(api_client).to receive(:record_update_job_error)
+ allow(api_client).to receive(:record_ecosystem_versions)
+ allow(api_client).to receive(:is_a?).with(Dependabot::ApiClient).and_return(true)
+
+ allow(Dependabot::Environment).to receive_messages(job_id: job_id, job_token: "job_token",
+ output_path: File.join(Dir.mktmpdir,
+ "output.json"),
+ job_definition: job_definition,
+ job_path: nil)
+ end
+
+ describe "#perform_job" do
+ subject(:perform_job) { job.perform_job }
+
+ let(:job_definition) do
+ JSON.parse(fixture("jobs/job_with_credentials.json"))
+ end
+
+ after do
+ # The job definition in this context loads an experiment, so reset it
+ Dependabot::Experiments.reset!
+ end
+
+ it "fetches the files and writes the fetched files to output.json", :vcr do
+ expect(api_client).not_to receive(:mark_job_as_processed)
+
+ perform_job
+
+ output = JSON.parse(File.read(Dependabot::Environment.output_path))
+ dependency_file = output["base64_dependency_files"][0]
+ expect(dependency_file["name"]).to eq(
+ "dependabot-test-ruby-package.gemspec"
+ )
+ expect(dependency_file["content_encoding"]).to eq("utf-8")
+ end
+
+ context "when the fetcher raises a ToolVersionNotSupported error", :vcr do
+ before do
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:commit).and_return("a" * 40)
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:files).and_return([])
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:ecosystem_versions)
+ .and_raise(Dependabot::ToolVersionNotSupported.new("Bundler", "1.7", "2.x"))
+ end
+
+ it "tells the backend about the error (and doesn't re-raise it)" do
+ expect(api_client)
+ .to receive(:record_update_job_error)
+ .with(
+ error_details: { "tool-name": "Bundler", "detected-version": "1.7", "supported-versions": "2.x" },
+ error_type: "tool_version_not_supported"
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+ end
+
+ context "when the fetcher raises a BranchNotFound error" do
+ before do
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:commit)
+ .and_raise(Dependabot::BranchNotFound, "my_branch")
+ end
+
+ it "tells the backend about the error (and doesn't re-raise it)" do
+ expect(api_client)
+ .to receive(:record_update_job_error)
+ .with(
+ error_details: { "branch-name": "my_branch" },
+ error_type: "branch_not_found"
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+ end
+
+ context "when the fetcher raises a RepoNotFound error" do
+ let(:provider) { job_definition.dig("job", "source", "provider") }
+ let(:repo) { job_definition.dig("job", "source", "repo") }
+ let(:source) { ::Dependabot::Source.new(provider: provider, repo: repo) }
+
+ before do
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:commit)
+ .and_raise(Dependabot::RepoNotFound, source)
+ end
+
+ it "tells the backend about the error (and doesn't re-raise it)" do
+ expect(api_client)
+ .to receive(:record_update_job_error)
+ .with(
+ error_details: { message: "Dependabot::RepoNotFound" },
+ error_type: "job_repo_not_found"
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+ end
+
+ context "when the fetcher raises a file fetcher error (cloud)", :vcr do
+ before do
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:commit)
+ .and_raise(StandardError, "my_branch")
+ Dependabot::Experiments.register(:record_update_job_unknown_error, true)
+ end
+
+ after do
+ Dependabot::Experiments.reset!
+ end
+
+ it "tells the backend about the error via update job error api (and doesn't re-raise it)" do
+ expect(api_client).to receive(:record_update_job_error).with(
+ error_type: "file_fetcher_error",
+ error_details: {
+ Dependabot::ErrorAttributes::BACKTRACE => an_instance_of(String),
+ Dependabot::ErrorAttributes::MESSAGE => "my_branch",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
+ }
+ )
+ expect(api_client).to receive(:record_update_job_unknown_error)
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+
+ it "tells the backend about the error via update job unknown error (and doesn't re-raise it)" do
+ expect(api_client).to receive(:record_update_job_unknown_error).with(
+ error_type: "unknown_error",
+ error_details: {
+ Dependabot::ErrorAttributes::BACKTRACE => an_instance_of(String),
+ Dependabot::ErrorAttributes::MESSAGE => "my_branch",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => [],
+ Dependabot::ErrorAttributes::SECURITY_UPDATE => false
+ }
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+ end
+
+ context "when the fetcher raises a file fetcher error (ghes)", :vcr do
+ before do
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:commit)
+ .and_raise(StandardError, "my_branch")
+ end
+
+ it "tells the backend about the error via update job error api (and doesn't re-raise it)" do
+ expect(api_client).to receive(:record_update_job_error).with(
+ error_type: "file_fetcher_error",
+ error_details: {
+ Dependabot::ErrorAttributes::BACKTRACE => an_instance_of(String),
+ Dependabot::ErrorAttributes::MESSAGE => "my_branch",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
+ }
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+
+ it "do not tells the backend about the error" do
+ expect(api_client).not_to receive(:record_update_job_unknown_error)
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+ end
+
+ context "when the fetcher raises a rate limited error" do
+ let(:reset_at) { Time.now + 30 }
+
+ before do
+ exception = Octokit::TooManyRequests.new(
+ response_headers: {
+ "X-RateLimit-Reset" => reset_at
+ }
+ )
+ allow_any_instance_of(Dependabot::Bundler::FileFetcher)
+ .to receive(:commit)
+ .and_raise(exception)
+ end
+
+ it "retries the job when the rate-limit is reset and reports api error" do
+ expect(Sentry).not_to receive(:capture_exception)
+ expect(api_client)
+ .to receive(:record_update_job_error)
+ .with(
+ error_details: { "rate-limit-reset": reset_at },
+ error_type: "octokit_rate_limited"
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Repository is rate limited, attempting to retry/).to_stdout_from_any_process
+ end
+ end
+
+ context "when vendoring dependencies", :vcr do
+ let(:job_definition) do
+ JSON.parse(fixture("jobs/job_with_vendor_dependencies.json"))
+ end
+
+ before do
+ allow(Dependabot::Environment).to receive(:repo_contents_path).and_return(Dir.mktmpdir)
+ end
+
+ it "clones the repo" do
+ expect(api_client).not_to receive(:mark_job_as_processed)
+
+ perform_job
+
+ root_dir_entries = Dir.entries(Dependabot::Environment.repo_contents_path)
+ expect(root_dir_entries).to include(".gitignore")
+ expect(root_dir_entries).to include(
+ "dependabot-test-ruby-package.gemspec"
+ )
+ expect(root_dir_entries).to include("README.md")
+ end
+ end
+
+ context "when package ecosystem always clones" do
+ let(:job_definition) do
+ JSON.parse(fixture("jobs/job_with_dummy.json"))
+ end
+
+ before do
+ allow(Dependabot::Environment).to receive(:repo_contents_path).and_return(Dir.mktmpdir)
+ end
+
+ it "clones the repo" do
+ perform_job
+
+ root_dir_entries = Dir.entries(Dependabot::Environment.repo_contents_path)
+ expect(root_dir_entries).to include("go.mod")
+ expect(root_dir_entries).to include("go.sum")
+ expect(root_dir_entries).to include("main.go")
+ end
+
+ context "when the fetcher raises a BranchNotFound error while cloning" do
+ before do
+ allow_any_instance_of(DummyPackageManager::FileFetcher)
+ .to receive(:clone_repo_contents)
+ .and_raise(Dependabot::BranchNotFound, "my_branch")
+ end
+
+ it "tells the backend about the error (and doesn't re-raise it)" do
+ expect(api_client)
+ .to receive(:record_update_job_error)
+ .with(
+ error_details: { "branch-name": "my_branch" },
+ error_type: "branch_not_found"
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+ end
+
+ context "when the fetcher raises a OutOfDisk error while cloning" do
+ before do
+ allow_any_instance_of(DummyPackageManager::FileFetcher)
+ .to receive(:clone_repo_contents)
+ .and_raise(Dependabot::OutOfDisk)
+ end
+
+ it "tells the backend about the error (and doesn't re-raise it)" do
+ expect(api_client)
+ .to receive(:record_update_job_error)
+ .with(
+ error_details: {},
+ error_type: "out_of_disk"
+ )
+ expect(api_client).to receive(:mark_job_as_processed)
+
+ expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
+ end
+ end
+ end
+
+ context "when the connectivity check is enabled", :vcr do
+ before do
+ allow(ENV).to receive(:[]).and_call_original
+ allow(ENV).to receive(:[]).with("ENABLE_CONNECTIVITY_CHECK").and_return("1")
+ end
+
+ it "logs connectivity is successful and does not raise an error" do
+ allow(Dependabot.logger).to receive(:info)
+
+ expect { perform_job }.not_to raise_error
+
+ expect(Dependabot.logger).to have_received(:info).with(/Connectivity check starting/)
+ expect(Dependabot.logger).to have_received(:info).with(/Connectivity check successful/)
+ end
+
+ context "when connectivity is broken" do
+ let(:mock_octokit) { instance_double(Octokit::Client) }
+
+ before do
+ allow(Octokit::Client)
+ .to receive(:new)
+ .and_call_original
+ allow(Octokit::Client)
+ .to receive(:new).with({
+ api_endpoint: "https://api.github.com/",
+ connection_options: {
+ request: {
+ open_timeout: 20,
+ timeout: 5
+ }
+ }
+ })
+ .and_return(mock_octokit)
+ allow(mock_octokit).to receive(:repository)
+ .and_raise(Octokit::Error)
+ end
+
+ it "logs connectivity failed and does not raise an error" do
+ allow(Dependabot.logger).to receive(:info)
+ allow(Dependabot.logger).to receive(:error)
+
+ expect { perform_job }.not_to raise_error
+
+ expect(Dependabot.logger).to have_received(:info).with(/Connectivity check starting/)
+ expect(Dependabot.logger).to have_received(:error).with(/Connectivity check failed/)
+ end
+ end
+ end
+ end
+end
diff --git a/updater/spec/dependabot/job_spec.rb b/updater/spec/dependabot/job_spec.rb
index d59b0e7c..6e1277ab 100644
--- a/updater/spec/dependabot/job_spec.rb
+++ b/updater/spec/dependabot/job_spec.rb
@@ -4,6 +4,8 @@
require "spec_helper"
require "dependabot/job"
require "dependabot/dependency"
+require "support/dummy_package_manager/dummy"
+
require "dependabot/bundler"
RSpec.describe Dependabot::Job do
@@ -11,7 +13,7 @@
let(:attributes) do
{
- id: 1,
+ id: "1",
token: "token",
dependencies: dependencies,
allowed_updates: allowed_updates,
@@ -22,7 +24,8 @@
source: {
"provider" => "github",
"repo" => "dependabot-fixtures/dependabot-test-ruby-package",
- "directory" => "/",
+ "directory" => directory,
+ "directories" => directories,
"api-endpoint" => "https://api.github.com/",
"hostname" => "github.com",
"branch" => nil
@@ -46,6 +49,8 @@
}
end
+ let(:directory) { "/" }
+ let(:directories) { nil }
let(:dependencies) { nil }
let(:security_advisories) { [] }
let(:package_manager) { "bundler" }
@@ -74,14 +79,14 @@
let(:new_update_job) do
described_class.new_update_job(
- job_id: anything,
+ job_id: "1",
job_definition: JSON.parse(job_json),
- repo_contents_path: anything
+ repo_contents_path: "repo"
)
end
it "correctly replaces the credentials with the credential-metadata" do
- expect(new_update_job.credentials.length).to eql(2)
+ expect(new_update_job.credentials.length).to be(2)
git_credential = new_update_job.credentials.find { |creds| creds["type"] == "git_source" }
expect(git_credential["host"]).to eql("github.com")
@@ -91,18 +96,62 @@
expect(ruby_credential["host"]).to eql("my.rubygems-host.org")
expect(ruby_credential.keys).not_to include("token")
end
+
+ context "when the directory does not start with a slash" do
+ let(:directory) { "hello" }
+
+ it "adds a slash to the directory" do
+ expect(job.source.directory).to eq("/hello")
+ end
+ end
+
+ context "when the directory uses relative path notation" do
+ let(:directory) { "hello/world/.." }
+
+ it "cleans the path" do
+ expect(job.source.directory).to eq("/hello")
+ end
+ end
+
+ context "when the directory is nil because it's a grouped security update" do
+ let(:directory) { nil }
+ let(:directories) { %w(/hello /world) }
+
+ it "doesn't raise an error" do
+ expect(job.source.directory).to be_nil
+ end
+ end
+
+ context "when neither directory nor directories are provided" do
+ let(:directory) { nil }
+ let(:directories) { nil }
+
+ it "raises a helpful error" do
+ expect { job.source.directory }.to raise_error
+ end
+ end
+
+ context "when both directory and directories are provided" do
+ let(:directory) { "hello" }
+ let(:directories) { %w(/hello /world) }
+
+ it "raises a helpful error" do
+ expect { job.source.directory }.to raise_error
+ end
+ end
end
context "when lockfile_only is passed as true" do
let(:lockfile_only) { true }
it "infers a lockfile_only requirements_update_strategy" do
- expect(subject.requirements_update_strategy).to eq("lockfile_only")
+ expect(job.requirements_update_strategy).to eq(Dependabot::RequirementsUpdateStrategy::LockfileOnly)
end
end
describe "#allowed_update?" do
subject { job.allowed_update?(dependency) }
+
let(:dependency) do
Dependabot::Dependency.new(
name: dependency_name,
@@ -143,11 +192,13 @@
requirements: []
)
end
- it { is_expected.to eq(false) }
- context "for a security update" do
+ it { is_expected.to be(false) }
+
+ context "when dealing with a security update" do
let(:security_updates_only) { true }
- it { is_expected.to eq(true) }
+
+ it { is_expected.to be(true) }
end
end
@@ -156,14 +207,15 @@
[{ file: "Gemfile", requirement: "~> 1.8.0", groups: [], source: nil }]
end
- it { is_expected.to eq(true) }
+ it { is_expected.to be(true) }
end
context "with a sub-dependency" do
let(:requirements) { [] }
- it { is_expected.to eq(false) }
- context "that is insecure" do
+ it { is_expected.to be(false) }
+
+ context "when insecure" do
let(:security_advisories) do
[
{
@@ -175,15 +227,16 @@
]
end
- it { is_expected.to eq(true) }
+ it { is_expected.to be(true) }
end
end
context "when only security fixes are allowed" do
let(:security_updates_only) { true }
- it { is_expected.to eq(false) }
- context "for a security fix" do
+ it { is_expected.to be(false) }
+
+ context "when dealing with a security fix" do
let(:security_advisories) do
[
{
@@ -195,10 +248,10 @@
]
end
- it { is_expected.to eq(true) }
+ it { is_expected.to be(true) }
end
- context "for a security fix that doesn't apply" do
+ context "when dealing with a security fix that doesn't apply" do
let(:security_advisories) do
[
{
@@ -210,10 +263,10 @@
]
end
- it { is_expected.to eq(false) }
+ it { is_expected.to be(false) }
end
- context "for a security fix that doesn't apply to some versions" do
+ context "when dealing with a security fix that doesn't apply to some versions" do
let(:security_advisories) do
[
{
@@ -225,7 +278,7 @@
]
end
- it "should be allowed" do
+ it "is allowed" do
dependency.metadata[:all_versions] = [
Dependabot::Dependency.new(
name: dependency_name,
@@ -241,33 +294,38 @@
)
]
- is_expected.to eq(true)
+ expect(job.allowed_update?(dependency)).to be(true)
end
end
end
- context "and a dependency whitelist that includes the dependency" do
+ context "when a dependency whitelist that includes the dependency" do
let(:allowed_updates) { [{ "dependency-name" => "business" }] }
- it { is_expected.to eq(true) }
+
+ it { is_expected.to be(true) }
context "with a dependency whitelist that uses a wildcard" do
let(:allowed_updates) { [{ "dependency-name" => "bus*" }] }
- it { is_expected.to eq(true) }
+
+ it { is_expected.to be(true) }
end
end
- context "and a dependency whitelist that excludes the dependency" do
+ context "when dependency whitelist that excludes the dependency" do
let(:allowed_updates) { [{ "dependency-name" => "rails" }] }
- it { is_expected.to eq(false) }
- context "that would match if we were sloppy about substrings" do
+ it { is_expected.to be(false) }
+
+ context "when matching with potential sloppiness about substrings" do
let(:allowed_updates) { [{ "dependency-name" => "bus" }] }
- it { is_expected.to eq(false) }
+
+ it { is_expected.to be(false) }
end
context "with a dependency whitelist that uses a wildcard" do
let(:allowed_updates) { [{ "dependency-name" => "b.ness*" }] }
- it { is_expected.to eq(false) }
+
+ it { is_expected.to be(false) }
end
context "when security fixes are also allowed" do
@@ -278,9 +336,9 @@
]
end
- it { is_expected.to eq(false) }
+ it { is_expected.to be(false) }
- context "for a security fix" do
+ context "when dealing with a security fix" do
let(:security_advisories) do
[
{
@@ -292,18 +350,18 @@
]
end
- it { is_expected.to eq(true) }
+ it { is_expected.to be(true) }
end
end
end
context "with dev dependencies during a security update while allowed: production is in effect" do
- let(:package_manager) { "npm_and_yarn" }
+ let(:package_manager) { "dummy" }
let(:security_updates_only) { true }
let(:dependency) do
Dependabot::Dependency.new(
name: "ansi-regex",
- package_manager: "npm_and_yarn",
+ package_manager: "dummy",
version: "6.0.0",
requirements: [
{
@@ -336,19 +394,20 @@
let(:allowed_updates) do
[{ "dependency-type" => "production" }]
end
- it { is_expected.to eq(false) }
+
+ it { is_expected.to be(false) }
end
end
describe "#security_updates_only?" do
subject { job.security_updates_only? }
- it { is_expected.to eq(false) }
+ it { is_expected.to be(false) }
context "with security only allowed updates" do
let(:security_updates_only) { true }
- it { is_expected.to eq(true) }
+ it { is_expected.to be(true) }
end
end
@@ -366,8 +425,8 @@
it "registers the experiments with Dependabot::Experiments" do
job
- expect(Dependabot::Experiments.enabled?(:kebab_case)).to be_truthy
- expect(Dependabot::Experiments.enabled?(:simpe)).to be_falsey
+ expect(Dependabot::Experiments).to be_enabled(:kebab_case)
+ expect(Dependabot::Experiments).not_to be_enabled(:simpe)
end
end
@@ -397,7 +456,7 @@
it "transforms the keys" do
expect(job.commit_message_options[:prefix]).to eq("[dev]")
expect(job.commit_message_options[:prefix_development]).to eq("[bump-dev]")
- expect(job.commit_message_options[:include_scope]).to eq(true)
+ expect(job.commit_message_options[:include_scope]).to be(true)
end
end
@@ -416,42 +475,6 @@
end
end
- describe "#clone?" do
- subject { job.clone? }
-
- it { is_expected.to eq(false) }
-
- context "with vendoring configuration enabled" do
- let(:vendor_dependencies) { true }
-
- it { is_expected.to eq(true) }
- end
-
- context "for ecosystems that always clone" do
- let(:vendor_dependencies) { false }
- let(:dependencies) do
- [
- Dependabot::Dependency.new(
- name: "github.com/pkg/errors",
- package_manager: "go_modules",
- version: "v1.8.0",
- requirements: [
- {
- file: "go.mod",
- requirement: "v1.8.0",
- groups: [],
- source: nil
- }
- ]
- )
- ]
- end
- let(:package_manager) { "go_modules" }
-
- it { is_expected.to eq(true) }
- end
- end
-
describe "#security_fix?" do
subject { job.security_fix?(dependency) }
@@ -478,25 +501,25 @@
]
end
- it { is_expected.to eq(true) }
+ it { is_expected.to be(true) }
context "when the update hasn't been patched" do
let(:dependency_version) { "1.10.0" }
- it { is_expected.to eq(false) }
+ it { is_expected.to be(false) }
end
end
describe "#reject_external_code?" do
it "defaults to false" do
- expect(job.reject_external_code?).to eq(false)
+ expect(job.reject_external_code?).to be(false)
end
it "can be enabled by job attributes" do
attrs = attributes
attrs[:reject_external_code] = true
- job = Dependabot::Job.new(attrs)
- expect(job.reject_external_code?).to eq(true)
+ job = described_class.new(attrs)
+ expect(job.reject_external_code?).to be(true)
end
end
end
diff --git a/updater/spec/dependabot/sentry/exception_sanitizer_processor_spec.rb b/updater/spec/dependabot/sentry/exception_sanitizer_processor_spec.rb
new file mode 100644
index 00000000..82b15e89
--- /dev/null
+++ b/updater/spec/dependabot/sentry/exception_sanitizer_processor_spec.rb
@@ -0,0 +1,114 @@
+# typed: false
+# frozen_string_literal: true
+
+require "sentry-ruby"
+require "spec_helper"
+
+require "dependabot/sentry/exception_sanitizer_processor"
+
+RSpec.describe ExceptionSanitizer do
+ subject { exception }
+
+ let(:message) { "kaboom" }
+ let(:exception) { instance_double(::Sentry::SingleExceptionInterface, value: message) }
+ let(:event) { instance_double(::Sentry::ErrorEvent) }
+
+ before do
+ allow(exception).to receive(:value=)
+ allow(event).to receive_message_chain("exception.values"). and_return([exception])
+ allow(event).to receive(:is_a?). and_return(true)
+ described_class.new.process(event, {})
+ end
+
+ it "does not filter messages by default" do
+ expect(exception).to have_received(:value=).with(message).at_least(:once)
+ end
+
+ context "with exception containing Bearer token" do
+ let(:message) { "Bearer SECRET_TOKEN is bad and you should feel bad" }
+
+ it "filters sensitive messages" do
+ expect(exception).to have_received(:value=).with("Bearer [FILTERED_AUTH_TOKEN] is bad and you should feel bad")
+ end
+ end
+
+ context "with exception containing Authorization: header" do
+ let(:message) { "Authorization: SECRET_TOKEN is bad" }
+
+ it "filters sensitive messages" do
+ expect(exception).to have_received(:value=).with("Authorization: [FILTERED_AUTH_TOKEN] is bad")
+ end
+ end
+
+ context "with exception containing authorization value" do
+ let(:message) { "authorization SECRET_TOKEN invalid" }
+
+ it "filters sensitive messages" do
+ expect(exception).to have_received(:value=).with("authorization [FILTERED_AUTH_TOKEN] invalid")
+ end
+ end
+
+ context "with exception secret token without an indicator" do
+ let(:message) { "SECRET_TOKEN is not filtered" }
+
+ it "filters sensitive messages" do
+ expect(exception).to have_received(:value=).with(message).at_least(:once)
+ end
+ end
+
+ context "with api repo NWO" do
+ let(:message) { "https://api.github.com/repos/foo/bar is bad" }
+
+ it "filters repo name from an api request" do
+ expect(exception).to have_received(:value=).with("https://api.github.com/repos/foo/[FILTERED_REPO] is bad")
+ end
+ end
+
+ context "with regular repo NWO" do
+ let(:message) { "https://github.com/foo/bar is bad" }
+
+ it "filters repo name from an api request" do
+ expect(exception).to have_received(:value=).with("https://github.com/foo/[FILTERED_REPO] is bad")
+ end
+ end
+
+ context "with multiple repo NWO" do
+ let(:message) do
+ "https://api.github.com/repos/foo/bar is bad, " \
+ "https://github.com/foo/baz is bad"
+ end
+
+ it "filters repo name from an api request" do
+ expect(exception).to have_received(:value=).with(
+ "https://api.github.com/repos/foo/[FILTERED_REPO] is bad, " \
+ "https://github.com/foo/[FILTERED_REPO] is bad"
+ )
+ end
+ end
+
+ context "when docs.github.com URL included" do
+ let(:message) { "https://api.github.com/repos/org/foo/contents/bar: 404 - Not Found // See: https://docs.github.com/rest/repos/contents#get-repository-content" }
+
+ it "filters repo name from an api request" do
+ expect(exception).to have_received(:value=)
+ .with("https://api.github.com/repos/org/[FILTERED_REPO]/contents/bar: 404 - Not Found // See: https://docs.github.com/rest/repos/contents#get-repository-content")
+ end
+ end
+
+ context "when docs.github.com URL included, and repo name includes 'repo'" do
+ let(:message) { "https://api.github.com/repos/org/repo/contents/bar: 404 - Not Found // See: https://docs.github.com/rest/repos/contents#get-repository-content" }
+
+ it "filters repo name from an api request" do
+ expect(exception).to have_received(:value=)
+ .with("https://api.github.com/repos/org/[FILTERED_REPO]/contents/bar: 404 - Not Found // See: https://docs.github.com/rest/repos/contents#get-repository-content")
+ end
+ end
+
+ context "with SCP-style uri" do
+ let(:message) { "git@github.com:foo/bar.git is bad" }
+
+ it "filters repo name from an api request" do
+ expect(exception).to have_received(:value=).with("git@github.com:foo/[FILTERED_REPO] is bad")
+ end
+ end
+end
diff --git a/updater/spec/dependabot/sentry/sentry_context_processor_spec.rb b/updater/spec/dependabot/sentry/sentry_context_processor_spec.rb
new file mode 100644
index 00000000..ba0ca7c3
--- /dev/null
+++ b/updater/spec/dependabot/sentry/sentry_context_processor_spec.rb
@@ -0,0 +1,42 @@
+# typed: false
+# frozen_string_literal: true
+
+require "sentry-ruby"
+require "spec_helper"
+
+require "dependabot/errors"
+require "dependabot/sentry/sentry_context_processor"
+
+RSpec.describe SentryContext do
+ subject { event }
+
+ let(:sentry_context) { { foo: "bar" } }
+ let(:exception) { double(::Dependabot::DependabotError, sentry_context: sentry_context) }
+ let(:hint) { { exception: exception } }
+ let(:event) { instance_double(::Sentry::ErrorEvent) }
+
+ before do
+ allow(event).to receive(:send)
+ described_class.new.process(event, hint)
+ end
+
+ it "adds context to the event" do
+ expect(event).to have_received(:send).with(:foo=, "bar")
+ end
+
+ context "without an exception" do
+ let(:exception) { nil }
+
+ it "does not add context" do
+ expect(event).not_to have_received(:send)
+ end
+ end
+
+ context "without sentry_context" do
+ let(:sentry_context) { nil }
+
+ it "does not add context" do
+ expect(event).not_to have_received(:send)
+ end
+ end
+end
diff --git a/updater/spec/dependabot/sentry_spec.rb b/updater/spec/dependabot/sentry_spec.rb
deleted file mode 100644
index 75df43a7..00000000
--- a/updater/spec/dependabot/sentry_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# typed: false
-# frozen_string_literal: true
-
-require "dependabot/sentry"
-require "spec_helper"
-
-RSpec.describe ExceptionSanitizer do
- let(:message) { "kaboom" }
- let(:data) do
- {
- environment: "default",
- extra: {},
- exception: {
- values: [
- { type: "StandardError", value: message }
- ]
- }
- }
- end
-
- it "does not filter messages by default" do
- expect(sanitized_message(data)).to eq(message)
- end
-
- context "with exception containing Bearer token" do
- let(:message) { "Bearer SECRET_TOKEN is bad and you should feel bad" }
-
- it "filters sensitive messages" do
- expect(sanitized_message(data)).to eq(
- "Bearer [FILTERED_AUTH_TOKEN] is bad and you should feel bad"
- )
- end
- end
-
- context "with exception containing Authorization: header" do
- let(:message) { "Authorization: SECRET_TOKEN is bad" }
-
- it "filters sensitive messages" do
- expect(sanitized_message(data)).to eq(
- "Authorization: [FILTERED_AUTH_TOKEN] is bad"
- )
- end
- end
-
- context "with exception containing authorization value" do
- let(:message) { "authorization SECRET_TOKEN invalid" }
-
- it "filters sensitive messages" do
- expect(sanitized_message(data)).to eq(
- "authorization [FILTERED_AUTH_TOKEN] invalid"
- )
- end
- end
-
- context "with exception secret token without an indicator" do
- let(:message) { "SECRET_TOKEN is not filtered" }
-
- it "filters sensitive messages" do
- expect(sanitized_message(data)).to eq("SECRET_TOKEN is not filtered")
- end
- end
-
- context "with api repo NWO" do
- let(:message) { "https://api.github.com/repos/foo/bar is bad" }
-
- it "filters repo name from an api request" do
- expect(sanitized_message(data)).to eq(
- "https://api.github.com/repos/foo/[FILTERED_REPO] is bad"
- )
- end
- end
-
- context "with regular repo NWO" do
- let(:message) { "https://github.com/foo/bar is bad" }
-
- it "filters repo name from an api request" do
- expect(sanitized_message(data)).to eq(
- "https://github.com/foo/[FILTERED_REPO] is bad"
- )
- end
- end
-
- context "with multiple repo NWO" do
- let(:message) do
- "https://api.github.com/repos/foo/bar is bad, " \
- "https://github.com/foo/baz is bad"
- end
-
- it "filters repo name from an api request" do
- expect(sanitized_message(data)).to eq(
- "https://api.github.com/repos/foo/[FILTERED_REPO] is bad, " \
- "https://github.com/foo/[FILTERED_REPO] is bad"
- )
- end
- end
-
- private
-
- def sanitized_message(data)
- filtered = ExceptionSanitizer.new.process(data)
- filtered[:exception][:values].first[:value]
- end
-end
diff --git a/updater/spec/dependabot/service_spec.rb b/updater/spec/dependabot/service_spec.rb
index 953e09de..a0f9572d 100644
--- a/updater/spec/dependabot/service_spec.rb
+++ b/updater/spec/dependabot/service_spec.rb
@@ -3,29 +3,48 @@
require "spec_helper"
require "dependabot/api_client"
+require "dependabot/dependency"
require "dependabot/dependency_change"
+require "dependabot/dependency_file"
require "dependabot/dependency_snapshot"
require "dependabot/errors"
+require "dependabot/pull_request_creator"
require "dependabot/service"
+require "dependabot/experiments"
RSpec.describe Dependabot::Service do
+ subject(:service) { described_class.new(client: mock_client) }
+
let(:base_sha) { "mock-sha" }
let(:mock_client) do
- instance_double(Dependabot::ApiClient, {
+ api_client = instance_double(Dependabot::ApiClient, {
create_pull_request: nil,
update_pull_request: nil,
close_pull_request: nil,
record_update_job_error: nil,
record_update_job_unknown_error: nil
})
+ allow(api_client).to receive(:is_a?).with(Dependabot::ApiClient).and_return(true)
+ api_client
end
- subject(:service) { described_class.new(client: mock_client) }
- shared_context :a_pr_was_created do
+ shared_context "with a created pr" do
+ let(:source) do
+ instance_double(Dependabot::Source, provider: "github", repo: "dependabot/dependabot-core", directory: "/")
+ end
+
+ let(:job) do
+ instance_double(Dependabot::Job,
+ source: source,
+ credentials: [],
+ commit_message_options: [],
+ ignore_conditions: [])
+ end
+
let(:dependency_change) do
Dependabot::DependencyChange.new(
- job: instance_double(Dependabot::Job, source: nil, credentials: [], commit_message_options: []),
+ job: job,
updated_dependencies: dependencies,
updated_dependency_files: dependency_files
)
@@ -63,22 +82,38 @@
let(:dependency_files) do
[
- { name: "Gemfile", content: "some gems" }
+ Dependabot::DependencyFile.new(name: "Gemfile", content: "some gems")
]
end
before do
allow(Dependabot::PullRequestCreator::MessageBuilder)
- .to receive_message_chain(:new, :message).and_return(pr_message)
-
- service.create_pull_request(dependency_change, base_sha)
+ .to receive_message_chain(:new, :message).and_return(
+ Dependabot::PullRequestCreator::Message.new(
+ pr_name: "Test PR",
+ pr_message: pr_message,
+ commit_message: "Commit message"
+ )
+ )
end
end
- shared_context :a_pr_was_updated do
+ shared_context "with an updated pr" do
+ let(:source) do
+ instance_double(Dependabot::Source, provider: "github", repo: "dependabot/dependabot-core", directory: "/")
+ end
+
+ let(:job) do
+ instance_double(Dependabot::Job,
+ source: source,
+ credentials: [],
+ commit_message_options: [],
+ ignore_conditions: [])
+ end
+
let(:dependency_change) do
Dependabot::DependencyChange.new(
- job: anything,
+ job: job,
updated_dependencies: dependencies,
updated_dependency_files: dependency_files
)
@@ -103,7 +138,7 @@
let(:dependency_files) do
[
- { name: "Gemfile", content: "some gems" }
+ Dependabot::DependencyFile.new(name: "Gemfile", content: "some gems")
]
end
@@ -112,7 +147,7 @@
end
end
- shared_context :a_pr_was_closed do
+ shared_context "with an closd pr" do
let(:dependency_name) { "dependabot-fortran" }
let(:reason) { :dependency_removed }
@@ -121,7 +156,7 @@
end
end
- shared_context :an_error_was_reported do
+ shared_context "with a reported error" do
before do
service.record_update_job_error(
error_type: :epoch_error,
@@ -132,7 +167,7 @@
end
end
- shared_context :a_dependency_error_was_reported do
+ shared_context "with a reported dependency error" do
let(:dependency) do
Dependabot::Dependency.new(
name: "dependabot-cobol",
@@ -183,21 +218,52 @@
end
describe "#create_pull_request" do
- include_context :a_pr_was_created
+ include_context "with a created pr"
+
+ before do
+ Dependabot::Experiments.register("dependency_change_validation", true)
+ end
it "delegates to @client" do
+ service.create_pull_request(dependency_change, base_sha)
+
expect(mock_client)
.to have_received(:create_pull_request).with(dependency_change, base_sha)
end
it "memoizes a shorthand summary of the PR" do
+ service.create_pull_request(dependency_change, base_sha)
+
expect(service.pull_requests)
.to eql([["dependabot-fortran ( from 1.7.0 to 1.8.0 ), dependabot-pascal ( from 2.7.0 to 2.8.0 )", :created]])
end
+
+ context "when the change is missing a previous version and there's no change in requirements" do
+ let(:dependencies) do
+ [
+ Dependabot::Dependency.new(
+ name: "dependabot-fortran",
+ package_manager: "bundler",
+ version: "1.8.0",
+ requirements: [
+ { file: "Gemfile", requirement: "~> 1.8.0", groups: [], source: nil }
+ ],
+ previous_requirements: [
+ { file: "Gemfile", requirement: "~> 1.8.0", groups: [], source: nil }
+ ]
+ )
+ ]
+ end
+
+ it "raises an InvalidUpdatedDependencies error" do
+ expect { service.create_pull_request(dependency_change, base_sha) }
+ .to raise_error(Dependabot::DependencyChange::InvalidUpdatedDependencies)
+ end
+ end
end
describe "#update_pull_request" do
- include_context :a_pr_was_updated
+ include_context "with an updated pr"
it "delegates to @client" do
expect(mock_client).to have_received(:update_pull_request).with(dependency_change, base_sha)
@@ -209,7 +275,7 @@
end
describe "#close_pull_request" do
- include_context :a_pr_was_closed
+ include_context "with an closd pr"
it "delegates to @client" do
expect(mock_client).to have_received(:close_pull_request).with(dependency_name, reason)
@@ -221,7 +287,7 @@
end
describe "#record_update_job_error" do
- include_context :an_error_was_reported
+ include_context "with a reported error"
it "delegates to @client" do
expect(mock_client).to have_received(:record_update_job_error).with(
@@ -241,91 +307,144 @@
describe "#capture_exception" do
before do
- allow(Raven).to receive(:capture_exception)
+ allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(true)
+ allow(mock_client).to receive(:record_update_job_unknown_error)
end
let(:error) do
Dependabot::DependabotError.new("Something went wrong")
end
- it "delegates error capture to Sentry (Raven)" do
- service.capture_exception(error: error, tags: { foo: "bar" }, extra: { baz: "qux" })
+ it "does not delegate to the service if the record_update_job_unknown_error experiment is disabled" do
+ allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(false)
+
+ service.capture_exception(error: error)
+
+ expect(mock_client)
+ .not_to have_received(:record_update_job_unknown_error)
+ end
+
+ it "delegates error capture to the service" do
+ service.capture_exception(error: error)
- expect(Raven).to have_received(:capture_exception).with(error, tags: { foo: "bar" }, extra: { baz: "qux" })
+ expect(mock_client)
+ .to have_received(:record_update_job_unknown_error)
+ .with(
+ error_type: "unknown_error",
+ error_details: hash_including(
+ Dependabot::ErrorAttributes::MESSAGE => "Something went wrong",
+ Dependabot::ErrorAttributes::CLASS => "Dependabot::DependabotError"
+ )
+ )
end
it "extracts information from a job if provided" do
- job = OpenStruct.new(id: 1234, package_manager: "bundler", repo_private?: false)
+ job = OpenStruct.new(id: 1234, package_manager: "bundler", repo_private?: false, repo_owner: "foo")
service.capture_exception(error: error, job: job)
- expect(Raven).to have_received(:capture_exception)
- .with(error,
- tags: {
- update_job_id: 1234,
- package_manager: "bundler",
- repo_private: false
- },
- extra: {})
+ expect(mock_client)
+ .to have_received(:record_update_job_unknown_error)
+ .with(
+ error_type: "unknown_error",
+ error_details: hash_including(
+ Dependabot::ErrorAttributes::CLASS => "Dependabot::DependabotError",
+ Dependabot::ErrorAttributes::MESSAGE => "Something went wrong",
+ Dependabot::ErrorAttributes::JOB_ID => job.id,
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => job.package_manager
+ )
+ )
end
it "extracts information from a dependency if provided" do
- dependency = OpenStruct.new(name: "lodash")
+ dependency = Dependabot::Dependency.new(name: "lodash", requirements: [], package_manager: "npm_and_yarn")
service.capture_exception(error: error, dependency: dependency)
- expect(Raven).to have_received(:capture_exception)
- .with(error,
- tags: {},
- extra: {
- dependency_name: "lodash"
- })
+ expect(mock_client)
+ .to have_received(:record_update_job_unknown_error)
+ .with(
+ error_type: "unknown_error",
+ error_details: hash_including(
+ Dependabot::ErrorAttributes::MESSAGE => "Something went wrong",
+ Dependabot::ErrorAttributes::CLASS => "Dependabot::DependabotError",
+ Dependabot::ErrorAttributes::DEPENDENCIES => "lodash"
+ )
+ )
+ end
+
+ it "extracts information from a security job if provided" do
+ job = OpenStruct.new(id: 1234, package_manager: "npm_and_yarn", repo_private?: false, repo_owner: "foo",
+ security_updates_only?: true)
+ service.capture_exception(error: error, job: job)
+
+ expect(mock_client)
+ .to have_received(:record_update_job_unknown_error)
+ .with(
+ error_type: "unknown_error",
+ error_details: hash_including(
+ Dependabot::ErrorAttributes::CLASS => "Dependabot::DependabotError",
+ Dependabot::ErrorAttributes::MESSAGE => "Something went wrong",
+ Dependabot::ErrorAttributes::JOB_ID => job.id,
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => job.package_manager,
+ Dependabot::ErrorAttributes::SECURITY_UPDATE => true
+ )
+ )
end
it "extracts information from a dependency_group if provided" do
dependency_group = OpenStruct.new(name: "all-the-things")
+ allow(dependency_group).to receive(:is_a?).with(Dependabot::DependencyGroup).and_return(true)
service.capture_exception(error: error, dependency_group: dependency_group)
- expect(Raven).to have_received(:capture_exception)
- .with(error,
- tags: {},
- extra: {
- dependency_group: "all-the-things"
- })
+ expect(mock_client)
+ .to have_received(:record_update_job_unknown_error)
+ .with(
+ error_type: "unknown_error",
+ error_details: hash_including(
+ Dependabot::ErrorAttributes::MESSAGE => "Something went wrong",
+ Dependabot::ErrorAttributes::CLASS => "Dependabot::DependabotError",
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => "all-the-things"
+ )
+ )
end
end
describe "#update_dependency_list" do
let(:dependency_snapshot) do
- instance_double(Dependabot::DependencySnapshot,
- dependencies: [
- Dependabot::Dependency.new(
- name: "dummy-pkg-a",
- package_manager: "bundler",
- version: "2.0.0",
- requirements: [
- { file: "Gemfile", requirement: "~> 2.0.0", groups: [:default], source: nil }
- ]
- ),
- Dependabot::Dependency.new(
- name: "dummy-pkg-b",
- package_manager: "bundler",
- version: "1.1.0",
- requirements: [
- { file: "Gemfile", requirement: "~> 1.1.0", groups: [:default], source: nil }
- ]
- )
- ],
- dependency_files: [
- Dependabot::DependencyFile.new(
- name: "Gemfile",
- content: fixture("bundler/original/Gemfile"),
- directory: "/"
- ),
- Dependabot::DependencyFile.new(
- name: "Gemfile.lock",
- content: fixture("bundler/original/Gemfile.lock"),
- directory: "/"
- )
- ])
+ dependency_snapshot = instance_double(Dependabot::DependencySnapshot,
+ all_dependencies: [
+ Dependabot::Dependency.new(
+ name: "dummy-pkg-a",
+ package_manager: "bundler",
+ version: "2.0.0",
+ requirements: [
+ { file: "Gemfile", requirement: "~> 2.0.0", groups: [:default],
+ source: nil }
+ ]
+ ),
+ Dependabot::Dependency.new(
+ name: "dummy-pkg-b",
+ package_manager: "bundler",
+ version: "1.1.0",
+ requirements: [
+ { file: "Gemfile", requirement: "~> 1.1.0", groups: [:default],
+ source: nil }
+ ]
+ )
+ ],
+ all_dependency_files: [
+ Dependabot::DependencyFile.new(
+ name: "Gemfile",
+ content: fixture("bundler/original/Gemfile"),
+ directory: "/"
+ ),
+ Dependabot::DependencyFile.new(
+ name: "Gemfile.lock",
+ content: fixture("bundler/original/Gemfile.lock"),
+ directory: "/"
+ )
+ ])
+ allow(dependency_snapshot).to receive(:is_a?).and_return(true)
+ dependency_snapshot
end
let(:expected_dependency_payload) do
@@ -415,9 +534,11 @@
end
context "when a pr was created" do
- include_context :a_pr_was_created
+ include_context "with a created pr"
it "includes the summary of the created PR" do
+ service.create_pull_request(dependency_change, base_sha)
+
expect(service.summary)
.to include("created",
"dependabot-fortran ( from 1.7.0 to 1.8.0 ), dependabot-pascal ( from 2.7.0 to 2.8.0 )")
@@ -425,7 +546,7 @@
end
context "when a pr was updated" do
- include_context :a_pr_was_updated
+ include_context "with an updated pr"
it "includes the summary of the updated PR" do
expect(service.summary)
@@ -434,7 +555,7 @@
end
context "when a pr was closed" do
- include_context :a_pr_was_closed
+ include_context "with an closd pr"
it "includes the summary of the closed PR" do
expect(service.summary)
@@ -443,7 +564,7 @@
end
context "when there was an error" do
- include_context :an_error_was_reported
+ include_context "with a reported error"
it "includes an error count" do
expect(service.summary)
@@ -457,7 +578,7 @@
end
context "when there was an dependency error" do
- include_context :a_dependency_error_was_reported
+ include_context "with a reported dependency error"
it "includes an error count" do
expect(service.summary)
@@ -473,8 +594,8 @@
end
context "when there was a mix of pr activity" do
- include_context :a_pr_was_updated
- include_context :a_pr_was_closed
+ include_context "with an updated pr"
+ include_context "with an closd pr"
it "includes the summary of the updated PR" do
expect(service.summary)
@@ -488,10 +609,14 @@
end
context "when there was a mix of pr and error activity" do
- include_context :a_pr_was_created
- include_context :a_pr_was_closed
- include_context :an_error_was_reported
- include_context :a_dependency_error_was_reported
+ include_context "with a created pr"
+ include_context "with an closd pr"
+ include_context "with a reported error"
+ include_context "with a reported dependency error"
+
+ before do
+ service.create_pull_request(dependency_change, base_sha)
+ end
it "includes the summary of the created PR" do
expect(service.summary)
diff --git a/updater/spec/dependabot/update_files_command_spec.rb b/updater/spec/dependabot/update_files_command_spec.rb
new file mode 100644
index 00000000..20a31a06
--- /dev/null
+++ b/updater/spec/dependabot/update_files_command_spec.rb
@@ -0,0 +1,395 @@
+# typed: false
+# frozen_string_literal: true
+
+require "spec_helper"
+require "dependabot/update_files_command"
+require "tmpdir"
+
+RSpec.describe Dependabot::UpdateFilesCommand do
+ subject(:job) { described_class.new }
+
+ let(:service) do
+ instance_double(Dependabot::Service,
+ capture_exception: nil,
+ mark_job_as_processed: nil,
+ record_update_job_error: nil,
+ record_update_job_unknown_error: nil,
+ update_dependency_list: nil,
+ increment_metric: nil,
+ wait_for_calls_to_finish: nil)
+ end
+ let(:job_definition) do
+ JSON.parse(fixture("file_fetcher_output/output.json"))
+ end
+ let(:job_id) { "123123" }
+
+ before do
+ allow(Dependabot::Service).to receive(:new).and_return(service)
+ allow(Dependabot::Environment).to receive_messages(job_id: job_id, job_token: "mock_token",
+ job_definition: job_definition, repo_contents_path: nil)
+ end
+
+ describe "#perform_job" do
+ subject(:perform_job) { job.perform_job }
+
+ it "delegates to Dependabot::Updater" do
+ dummy_runner = double(run: nil)
+ base_commit_sha = "1c6331732c41e4557a16dacb82534f1d1c831848"
+ expect(Dependabot::Updater)
+ .to receive(:new)
+ .with(
+ service: service,
+ job: an_object_having_attributes(id: job_id, repo_contents_path: nil),
+ dependency_snapshot: an_object_having_attributes(base_commit_sha: base_commit_sha)
+ )
+ .and_return(dummy_runner)
+ expect(dummy_runner).to receive(:run)
+ expect(service).to receive(:mark_job_as_processed)
+ .with(base_commit_sha)
+
+ perform_job
+ end
+
+ it "sends dependency metadata to the service" do
+ expect(service).to receive(:update_dependency_list)
+ .with(dependency_snapshot: an_instance_of(Dependabot::DependencySnapshot))
+
+ perform_job
+ end
+
+ context "with vendoring_dependencies" do
+ let(:snapshot) do
+ instance_double(Dependabot::DependencySnapshot,
+ base_commit_sha: "1c6331732c41e4557a16dacb82534f1d1c831848")
+ end
+ let(:repo_contents_path) { "repo/path" }
+
+ let(:job_definition) do
+ JSON.parse(fixture("file_fetcher_output/vendoring_output.json"))
+ end
+
+ before do
+ allow(Dependabot::Environment).to receive(:repo_contents_path).and_return(repo_contents_path)
+ allow(Dependabot::DependencySnapshot).to receive(:create_from_job_definition).and_return(snapshot)
+ end
+
+ it "delegates to Dependabot::Updater" do
+ dummy_runner = double(run: nil)
+ base_commit_sha = "1c6331732c41e4557a16dacb82534f1d1c831848"
+ expect(Dependabot::Updater)
+ .to receive(:new)
+ .with(
+ service: service,
+ job: an_object_having_attributes(id: job_id, repo_contents_path: repo_contents_path),
+ dependency_snapshot: snapshot
+ )
+ .and_return(dummy_runner)
+ expect(dummy_runner).to receive(:run)
+ expect(service).to receive(:mark_job_as_processed)
+ .with(base_commit_sha)
+
+ perform_job
+ end
+ end
+ end
+
+ describe "#perform_job when there is an error parsing the dependency files" do
+ subject(:perform_job) { job.perform_job }
+
+ before do
+ allow(Dependabot.logger).to receive(:info)
+ allow(Dependabot.logger).to receive(:error)
+ allow(Sentry).to receive(:capture_exception)
+ allow(Dependabot::DependencySnapshot).to receive(:create_from_job_definition).and_raise(error)
+ end
+
+ shared_examples "a fast-failed job" do
+ it "marks the job as processed without proceeding further" do
+ expect(service).to receive(:mark_job_as_processed)
+ expect(Dependabot::Updater).not_to receive(:new)
+
+ perform_job
+ end
+ end
+
+ context "with an update files error (cloud)" do
+ let(:error) { StandardError.new("hell") }
+
+ before do
+ Dependabot::Experiments.register(:record_update_job_unknown_error, true)
+ end
+
+ after do
+ Dependabot::Experiments.reset!
+ end
+
+ it_behaves_like "a fast-failed job"
+
+ it "captures the exception and records to a update job error api" do
+ expect(service).to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "update_files_error",
+ error_details: {
+ Dependabot::ErrorAttributes::BACKTRACE => an_instance_of(String),
+ Dependabot::ErrorAttributes::MESSAGE => "hell",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
+ }
+ )
+
+ perform_job
+ end
+
+ it "captures the exception and records the a update job unknown error api" do
+ expect(service).to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_unknown_error).with(
+ error_type: "update_files_error",
+ error_details: {
+ Dependabot::ErrorAttributes::BACKTRACE => an_instance_of(String),
+ Dependabot::ErrorAttributes::MESSAGE => "hell",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
+ }
+ )
+
+ perform_job
+ Dependabot::Experiments.reset!
+ end
+ end
+
+ context "with an update files error (ghes)" do
+ let(:error) { StandardError.new("hell") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "captures the exception and records to a update job error api" do
+ expect(service).to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "update_files_error",
+ error_details: {
+ Dependabot::ErrorAttributes::BACKTRACE => an_instance_of(String),
+ Dependabot::ErrorAttributes::MESSAGE => "hell",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
+ }
+ )
+
+ perform_job
+ end
+
+ it "captures the exception and does not records the update job unknown error api" do
+ expect(service).to receive(:capture_exception)
+ expect(service).not_to receive(:record_update_job_unknown_error)
+
+ perform_job
+ Dependabot::Experiments.reset!
+ end
+ end
+
+ context "with a Dependabot::RepoNotFound error" do
+ let(:error) { Dependabot::RepoNotFound.new("dependabot/four-oh-four") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "does not capture the exception or record a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).not_to receive(:record_update_job_error)
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::DependencyFileNotEvaluatable error" do
+ let(:error) { Dependabot::DependencyFileNotEvaluatable.new("message") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "dependency_file_not_evaluatable",
+ error_details: { message: "message" }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::DependencyFileNotResolvable error" do
+ let(:error) { Dependabot::DependencyFileNotResolvable.new("message") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "dependency_file_not_resolvable",
+ error_details: { message: "message" }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::BranchNotFound error" do
+ let(:error) { Dependabot::BranchNotFound.new("my_branch") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "branch_not_found",
+ error_details: { "branch-name": "my_branch" }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::DependencyFileNotParseable error" do
+ let(:error) { Dependabot::DependencyFileNotParseable.new("path/to/file", "a") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "dependency_file_not_parseable",
+ error_details: { "file-path": "path/to/file", message: "a" }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::DependencyFileNotFound error" do
+ let(:error) { Dependabot::DependencyFileNotFound.new("path/to/file") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "dependency_file_not_found",
+ error_details: {
+ message: "path/to/file not found",
+ "file-path": "path/to/file"
+ }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::PathDependenciesNotReachable error" do
+ let(:error) { Dependabot::PathDependenciesNotReachable.new(["bad_gem"]) }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "path_dependencies_not_reachable",
+ error_details: { dependencies: ["bad_gem"] }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::PrivateSourceAuthenticationFailure error" do
+ let(:error) { Dependabot::PrivateSourceAuthenticationFailure.new("some.example.com") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "private_source_authentication_failure",
+ error_details: { source: "some.example.com" }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::GitDependenciesNotReachable error" do
+ let(:error) { Dependabot::GitDependenciesNotReachable.new("https://example.com") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "git_dependencies_not_reachable",
+ error_details: { "dependency-urls": ["https://example.com"] }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with a Dependabot::NotImplemented error" do
+ let(:error) { Dependabot::NotImplemented.new("foo") }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "not_implemented",
+ error_details: { message: "foo" }
+ )
+
+ perform_job
+ end
+ end
+
+ context "with Octokit::ServerError" do
+ let(:error) { Octokit::ServerError.new }
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: "server_error",
+ error_details: nil
+ )
+
+ perform_job
+ end
+ end
+
+ Dependabot::Updater::ErrorHandler::RUN_HALTING_ERRORS.each do |error_class, error_type|
+ context "with #{error_class}" do
+ let(:error) do
+ if error_class == Octokit::Unauthorized
+ Octokit::Unauthorized.new
+ else
+ error_class.new("message")
+ end
+ end
+
+ it_behaves_like "a fast-failed job"
+
+ it "only records a job error" do
+ expect(service).not_to receive(:capture_exception)
+ expect(service).to receive(:record_update_job_error).with(
+ error_type: error_type,
+ error_details: nil
+ )
+
+ perform_job
+ end
+ end
+ end
+ end
+end
diff --git a/updater/spec/dependabot/updater/dependency_group_change_batch_spec.rb b/updater/spec/dependabot/updater/dependency_group_change_batch_spec.rb
new file mode 100644
index 00000000..08a2ab82
--- /dev/null
+++ b/updater/spec/dependabot/updater/dependency_group_change_batch_spec.rb
@@ -0,0 +1,58 @@
+# typed: false
+# frozen_string_literal: true
+
+require "dependabot/dependency_file"
+require "dependabot/job"
+require "dependabot/source"
+require "dependabot/updater/operations"
+
+require "spec_helper"
+
+RSpec.describe Dependabot::Updater::DependencyGroupChangeBatch do
+ describe "current_dependency_files" do
+ let(:files) do
+ [
+ Dependabot::DependencyFile.new(name: "Gemfile", content: "mock-gemfile", directory: "/"),
+ Dependabot::DependencyFile.new(name: "Gemfile.lock", content: "mock-gemfile-lock", directory: "/hello/.."),
+ Dependabot::DependencyFile.new(name: "Gemfile", content: "mock-package-json", directory: "/elsewhere"),
+ Dependabot::DependencyFile.new(name: "Gemfile", content: "mock-package-json", directory: "unknown"),
+ Dependabot::DependencyFile.new(name: "Gemfile", content: "mock-package-json", directory: "../../oob")
+ ]
+ end
+
+ let(:job) do
+ instance_double(Dependabot::Job, source: source, package_manager: package_manager)
+ end
+
+ let(:package_manager) { "bundler" }
+
+ let(:source) do
+ Dependabot::Source.new(provider: "github", repo: "gocardless/bump", directory: directory)
+ end
+
+ let(:directory) { "/" }
+
+ it "returns the current dependency files filtered by directory" do
+ expect(described_class.new(initial_dependency_files: files)
+ .current_dependency_files(job).map(&:name)).to eq(%w(Gemfile Gemfile.lock))
+ end
+
+ context "when the directory has a dot" do
+ let(:directory) { "/." }
+
+ it "normalizes the directory" do
+ expect(described_class.new(initial_dependency_files: files)
+ .current_dependency_files(job).map(&:name)).to eq(%w(Gemfile Gemfile.lock))
+ end
+ end
+
+ context "when the directory has a dot dot" do
+ let(:directory) { "/hello/.." }
+
+ it "normalizes the directory" do
+ expect(described_class.new(initial_dependency_files: files)
+ .current_dependency_files(job).map(&:name)).to eq(%w(Gemfile Gemfile.lock))
+ end
+ end
+ end
+end
diff --git a/updater/spec/dependabot/updater/error_handler_spec.rb b/updater/spec/dependabot/updater/error_handler_spec.rb
index 434d0449..50501530 100644
--- a/updater/spec/dependabot/updater/error_handler_spec.rb
+++ b/updater/spec/dependabot/updater/error_handler_spec.rb
@@ -5,8 +5,10 @@
require "dependabot/dependency"
require "dependabot/dependency_group"
+require "dependabot/errors"
require "dependabot/job"
require "dependabot/service"
+require "dependabot/shared_helpers"
require "dependabot/updater/error_handler"
RSpec.describe Dependabot::Updater::ErrorHandler do
@@ -64,7 +66,11 @@
end
before do
- allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(true)
+ Dependabot::Experiments.register(:record_update_job_unknown_error, true)
+ end
+
+ after do
+ Dependabot::Experiments.reset!
end
it "records the error with both update job error api services, logs the backtrace and captures the exception" do
@@ -77,13 +83,13 @@
expect(mock_service).to receive(:record_update_job_unknown_error).with(
error_type: "unknown_error",
error_details: {
- "error-backtrace" => "bees.rb:5:in `buzz`",
- "error-message" => "There are bees everywhere",
- "error-class" => "StandardError",
- "package-manager" => "bundler",
- "job-id" => "123123",
- "job-dependencies" => [],
- "job-dependency_group" => []
+ Dependabot::ErrorAttributes::BACKTRACE => "bees.rb:5:in `buzz`",
+ Dependabot::ErrorAttributes::MESSAGE => "There are bees everywhere",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCIES => [],
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
}
)
@@ -115,12 +121,8 @@
end
end
- before do
- allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(false)
- end
-
it "records error with only update job error api service, logs the backtrace and captures the exception" do
- expect(mock_service).to_not receive(:record_update_job_unknown_error)
+ expect(mock_service).not_to receive(:record_update_job_unknown_error)
expect(mock_service).to receive(:capture_exception).with(
error: error,
@@ -162,7 +164,11 @@
end
before do
- allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(true)
+ Dependabot::Experiments.register(:record_update_job_unknown_error, true)
+ end
+
+ after do
+ Dependabot::Experiments.reset!
end
it "records the error with the service and logs the backtrace" do
@@ -175,13 +181,14 @@
expect(mock_service).to receive(:record_update_job_unknown_error).with(
error_type: "unknown_error",
error_details: {
- "error-backtrace" => "****** ERROR 8335 -- 101",
- "error-message" => "the kernal is full of bees",
- "error-class" => "Dependabot::SharedHelpers::HelperSubprocessFailed",
- "package-manager" => "bundler",
- "job-id" => "123123",
- "job-dependencies" => [],
- "job-dependency_group" => []
+ Dependabot::ErrorAttributes::BACKTRACE => "****** ERROR 8335 -- 101",
+ Dependabot::ErrorAttributes::MESSAGE => "the kernal is full of bees",
+ Dependabot::ErrorAttributes::CLASS => "Dependabot::SharedHelpers::HelperSubprocessFailed",
+ Dependabot::ErrorAttributes::FINGERPRINT => anything,
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCIES => [],
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
}
)
@@ -209,7 +216,7 @@
) do |args|
expect(args[:error].message)
.to eq('Subprocess ["123456789"] failed to run. Check the job logs for error messages')
- expect(args[:error].raven_context)
+ expect(args[:error].sentry_context)
.to eq(fingerprint: ["123456789"],
extra: {
bumblebees: "many", honeybees: "few", wasps: "none"
@@ -232,10 +239,6 @@
end
end
- before do
- allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(false)
- end
-
it "records the error with the service and logs the backtrace" do
expect(mock_service).to receive(:record_update_job_error).with(
error_type: "unknown_error",
@@ -266,7 +269,7 @@
) do |args|
expect(args[:error].message)
.to eq('Subprocess ["123456789"] failed to run. Check the job logs for error messages')
- expect(args[:error].raven_context)
+ expect(args[:error].sentry_context)
.to eq(fingerprint: ["123456789"],
extra: {
bumblebees: "many", honeybees: "few", wasps: "none"
@@ -310,7 +313,11 @@
end
before do
- allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(true)
+ Dependabot::Experiments.register(:record_update_job_unknown_error, true)
+ end
+
+ after do
+ Dependabot::Experiments.reset!
end
it "records the error with the update job error services, logs the backtrace and captures the exception" do
@@ -322,13 +329,13 @@
expect(mock_service).to receive(:record_update_job_unknown_error).with(
error_type: "unknown_error",
error_details: {
- "error-backtrace" => "bees.rb:5:in `buzz`",
- "error-message" => "There are bees everywhere",
- "error-class" => "StandardError",
- "package-manager" => "bundler",
- "job-id" => "123123",
- "job-dependencies" => [],
- "job-dependency_group" => []
+ Dependabot::ErrorAttributes::BACKTRACE => "bees.rb:5:in `buzz`",
+ Dependabot::ErrorAttributes::MESSAGE => "There are bees everywhere",
+ Dependabot::ErrorAttributes::CLASS => "StandardError",
+ Dependabot::ErrorAttributes::PACKAGE_MANAGER => "bundler",
+ Dependabot::ErrorAttributes::JOB_ID => "123123",
+ Dependabot::ErrorAttributes::DEPENDENCIES => [],
+ Dependabot::ErrorAttributes::DEPENDENCY_GROUPS => []
}
)
@@ -360,10 +367,6 @@
end
end
- before do
- allow(Dependabot::Experiments).to receive(:enabled?).with(:record_update_job_unknown_error).and_return(false)
- end
-
it "records the error with the update job error services, logs the backtrace and captures the exception" do
expect(mock_service).to receive(:record_update_job_error).with(
error_type: "unknown_error",
diff --git a/updater/spec/dependabot/updater/operations_spec.rb b/updater/spec/dependabot/updater/operations_spec.rb
index 58774b1c..6afb24fe 100644
--- a/updater/spec/dependabot/updater/operations_spec.rb
+++ b/updater/spec/dependabot/updater/operations_spec.rb
@@ -16,7 +16,9 @@
# We always expect jobs that update a pull request to specify their
# existing dependency changes, a job with this set of conditions
# should never exist.
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
job = instance_double(Dependabot::Job,
+ source: source,
security_updates_only?: false,
updating_a_pull_request?: true,
dependencies: [],
@@ -27,7 +29,9 @@
end
it "returns the UpdateAllVersions class when the Job is for a fresh, non-security update with no dependencies" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
job = instance_double(Dependabot::Job,
+ source: source,
security_updates_only?: false,
updating_a_pull_request?: false,
dependencies: [],
@@ -38,7 +42,9 @@
end
it "returns the GroupUpdateAllVersions class when the Job is for a fresh, version update with no dependencies" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
job = instance_double(Dependabot::Job,
+ source: source,
security_updates_only?: false,
updating_a_pull_request?: false,
dependencies: [],
@@ -51,7 +57,9 @@
end
it "returns the RefreshGroupUpdatePullRequest class when the Job is for an existing group update" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
job = instance_double(Dependabot::Job,
+ source: source,
security_updates_only?: false,
updating_a_pull_request?: true,
dependencies: [anything],
@@ -66,7 +74,9 @@
end
it "returns the RefreshVersionUpdatePullRequest class when the Job is for an existing dependency version update" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
job = instance_double(Dependabot::Job,
+ source: source,
security_updates_only?: false,
updating_a_pull_request?: true,
dependencies: [anything],
@@ -79,10 +89,58 @@
end
it "returns the CreateSecurityUpdatePullRequest class when the Job is for a new security update for a dependency" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
job = instance_double(Dependabot::Job,
+ source: source,
+ dependency_group_to_refresh: nil,
security_updates_only?: true,
updating_a_pull_request?: false,
dependencies: [anything],
+ dependency_groups: [],
+ is_a?: true)
+
+ expect(described_class.class_for(job: job))
+ .to be(Dependabot::Updater::Operations::CreateSecurityUpdatePullRequest)
+ end
+
+ it "returns the GroupUpdateAllVersions class when Experiment flag is not provided" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
+ job = instance_double(Dependabot::Job,
+ source: source,
+ security_updates_only?: true,
+ updating_a_pull_request?: false,
+ dependencies: [anything, anything],
+ dependency_groups: [anything],
+ is_a?: true)
+
+ expect(described_class.class_for(job: job))
+ .to be(Dependabot::Updater::Operations::GroupUpdateAllVersions)
+ end
+
+ it "returns the GroupUpdateAllVersions class when Experiment flag is off" do
+ Dependabot::Experiments.register(:grouped_security_updates_disabled, false)
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
+ job = instance_double(Dependabot::Job,
+ source: source,
+ security_updates_only?: true,
+ updating_a_pull_request?: false,
+ dependencies: [anything, anything],
+ dependency_groups: [anything],
+ is_a?: true)
+
+ expect(described_class.class_for(job: job))
+ .to be(Dependabot::Updater::Operations::GroupUpdateAllVersions)
+ end
+
+ it "returns the CreateSecurityUpdatePullRequest class when Experiment flag is true" do
+ Dependabot::Experiments.register(:grouped_security_updates_disabled, true)
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
+ job = instance_double(Dependabot::Job,
+ source: source,
+ dependency_group_to_refresh: nil,
+ security_updates_only?: true,
+ updating_a_pull_request?: false,
+ dependencies: [anything, anything],
dependency_groups: [anything],
is_a?: true)
@@ -90,8 +148,27 @@
.to be(Dependabot::Updater::Operations::CreateSecurityUpdatePullRequest)
end
+ it "returns the RefreshGroupSecurityUpdatePullRequest class when the Job is for an existing security update for" \
+ " multiple dependencies" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
+ job = instance_double(Dependabot::Job,
+ source: source,
+ security_updates_only?: true,
+ updating_a_pull_request?: true,
+ dependencies: [anything, anything],
+ dependency_group_to_refresh: anything,
+ dependency_groups: [anything],
+ is_a?: true)
+
+ expect(described_class.class_for(job: job))
+ .to be(Dependabot::Updater::Operations::RefreshGroupUpdatePullRequest)
+ end
+
it "returns the RefreshSecurityUpdatePullRequest class when the Job is for an existing security update" do
+ source = instance_double(Dependabot::Source, directory: "/.", directories: nil)
job = instance_double(Dependabot::Job,
+ source: source,
+ dependency_group_to_refresh: nil,
security_updates_only?: true,
updating_a_pull_request?: true,
dependencies: [anything],
diff --git a/updater/spec/fixtures/bundler/original/sub_dep b/updater/spec/fixtures/bundler/original/sub_dep
new file mode 100644
index 00000000..d7ccf130
--- /dev/null
+++ b/updater/spec/fixtures/bundler/original/sub_dep
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "dummy-pkg-b", "~> 1.1.0"
diff --git a/updater/spec/fixtures/bundler/original/sub_dep.lock b/updater/spec/fixtures/bundler/original/sub_dep.lock
new file mode 100644
index 00000000..dae47db2
--- /dev/null
+++ b/updater/spec/fixtures/bundler/original/sub_dep.lock
@@ -0,0 +1,16 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ dummy-pkg-a (2.0.0)
+ dummy-pkg-b (1.1.0)
+ dummy-pkg-a (~> 2.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ dummy-pkg-a
+ dummy-pkg-b (~> 1.1.0)
+
+BUNDLED WITH
+ 1.14.6
diff --git a/updater/spec/fixtures/bundler2/original/Gemfile b/updater/spec/fixtures/bundler2/original/Gemfile
new file mode 100644
index 00000000..ecc7a043
--- /dev/null
+++ b/updater/spec/fixtures/bundler2/original/Gemfile
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "dummy-pkg-a", "~> 2.0.0"
+gem "dummy-pkg-b", "~> 1.1.0"
diff --git a/updater/spec/fixtures/bundler2/original/Gemfile.lock b/updater/spec/fixtures/bundler2/original/Gemfile.lock
new file mode 100644
index 00000000..e2e32c16
--- /dev/null
+++ b/updater/spec/fixtures/bundler2/original/Gemfile.lock
@@ -0,0 +1,16 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ dummy-pkg-a (2.0.0)
+ dummy-pkg-b (1.1.0)
+ dummy-pkg-a (~> 2.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ dummy-pkg-a (~> 2.0.0)
+ dummy-pkg-b (~> 1.1.0)
+
+BUNDLED WITH
+ 2.2.11
diff --git a/updater/spec/fixtures/bundler2/updated/Gemfile b/updater/spec/fixtures/bundler2/updated/Gemfile
new file mode 100644
index 00000000..607828c5
--- /dev/null
+++ b/updater/spec/fixtures/bundler2/updated/Gemfile
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "dummy-pkg-a", "~> 2.0.0"
+gem "dummy-pkg-b", "~> 1.2.0"
diff --git a/updater/spec/fixtures/bundler2/updated/Gemfile.lock b/updater/spec/fixtures/bundler2/updated/Gemfile.lock
new file mode 100644
index 00000000..8430d10f
--- /dev/null
+++ b/updater/spec/fixtures/bundler2/updated/Gemfile.lock
@@ -0,0 +1,16 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ dummy-pkg-a (2.0.0)
+ dummy-pkg-b (1.2.0)
+ dummy-pkg-a (~> 2.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ dummy-pkg-a (~> 2.0.0)
+ dummy-pkg-b (~> 1.2.0)
+
+BUNDLED WITH
+ 2.2.11
diff --git a/updater/spec/fixtures/bundler_gemspec/updated/Gemfile b/updater/spec/fixtures/bundler_gemspec/updated/Gemfile
new file mode 100644
index 00000000..b4e2a20b
--- /dev/null
+++ b/updater/spec/fixtures/bundler_gemspec/updated/Gemfile
@@ -0,0 +1,3 @@
+source "https://rubygems.org"
+
+gemspec
diff --git a/updater/spec/fixtures/bundler_gemspec/updated/Gemfile.lock b/updater/spec/fixtures/bundler_gemspec/updated/Gemfile.lock
new file mode 100644
index 00000000..29f945ec
--- /dev/null
+++ b/updater/spec/fixtures/bundler_gemspec/updated/Gemfile.lock
@@ -0,0 +1,46 @@
+PATH
+ remote: .
+ specs:
+ library (1.0.0)
+ rack (>= 2.1.4, < 3.1.0)
+ rubocop (>= 0.76, < 1.51)
+ toml-rb (~> 2.2.0)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ ast (2.4.2)
+ citrus (3.0.2)
+ json (2.6.3)
+ parallel (1.23.0)
+ parser (3.2.2.1)
+ ast (~> 2.4.1)
+ rack (3.0.7)
+ rainbow (3.1.1)
+ regexp_parser (2.8.0)
+ rexml (3.2.6)
+ rubocop (1.50.2)
+ json (~> 2.3)
+ parallel (~> 1.10)
+ parser (>= 3.2.0.0)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8, < 3.0)
+ rexml (>= 3.2.5, < 4.0)
+ rubocop-ast (>= 1.28.0, < 2.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 2.4.0, < 3.0)
+ rubocop-ast (1.28.0)
+ parser (>= 3.2.1.0)
+ ruby-progressbar (1.13.0)
+ toml-rb (2.2.0)
+ citrus (~> 3.0, > 3.0)
+ unicode-display_width (2.4.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ library!
+
+BUNDLED WITH
+ 2.4.11
diff --git a/updater/spec/fixtures/bundler_gemspec/updated/library.gemspec b/updater/spec/fixtures/bundler_gemspec/updated/library.gemspec
new file mode 100644
index 00000000..15601fe9
--- /dev/null
+++ b/updater/spec/fixtures/bundler_gemspec/updated/library.gemspec
@@ -0,0 +1,12 @@
+
+Gem::Specification.new do |s|
+ s.name = "library"
+ s.summary = "A Library"
+ s.version = "1.0.0"
+ s.homepage = "https://github.com/dependabot/dependabot-core"
+ s.authors = %w[monalisa]
+
+ s.add_runtime_dependency "rubocop", ">= 0.76", "< 1.57"
+ s.add_runtime_dependency "toml-rb", "~> 2.2.0"
+ s.add_runtime_dependency "rack", ">= 2.1.4", "< 3.1.0"
+end
diff --git a/updater/spec/fixtures/bundler_git/original/Gemfile.lock b/updater/spec/fixtures/bundler_git/original/Gemfile.lock
deleted file mode 100644
index cfe797d2..00000000
--- a/updater/spec/fixtures/bundler_git/original/Gemfile.lock
+++ /dev/null
@@ -1,21 +0,0 @@
-GIT
- remote: git@github.com:dependabot-fixtures/ruby-dummy-git-dependency.git
- revision: 20151f9b67c8a04461fa0ee28385b6187b86587b
- ref: v1.0.0
- specs:
- dummy-git-dependency (1.0.0)
-
-GEM
- remote: https://rubygems.org/
- specs:
-
-PLATFORMS
- aarch64-linux
- x86_64-darwin-19
- x86_64-linux
-
-DEPENDENCIES
- dummy-git-dependency!
-
-BUNDLED WITH
- 2.2.11
diff --git a/updater/spec/fixtures/bundler_grouped/original/Gemfile b/updater/spec/fixtures/bundler_grouped/original/Gemfile
new file mode 100644
index 00000000..01ede77b
--- /dev/null
+++ b/updater/spec/fixtures/bundler_grouped/original/Gemfile
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "dummy-pkg-a", "~> 2.0.0"
+gem "dummy-pkg-b", "~> 1.1.0"
+gem "ungrouped-dummy-pkg-c", "~> 1.0.0"
diff --git a/updater/spec/fixtures/bundler_grouped/original/Gemfile.lock b/updater/spec/fixtures/bundler_grouped/original/Gemfile.lock
new file mode 100644
index 00000000..48feef35
--- /dev/null
+++ b/updater/spec/fixtures/bundler_grouped/original/Gemfile.lock
@@ -0,0 +1,18 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ dummy-pkg-a (2.0.0)
+ dummy-pkg-b (1.1.0)
+ dummy-pkg-a (~> 2.0)
+ ungrouped-dummy-pkg-c (1.0.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ dummy-pkg-a (~> 2.0.0)
+ dummy-pkg-b (~> 1.1.0)
+ ungrouped-dummy-pkg-c (~> 1.0.0)
+
+BUNDLED WITH
+ 1.14.6
diff --git a/updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile b/updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile
deleted file mode 100644
index 75e1bd97..00000000
--- a/updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-gem "rack", "~> 2.1.3"
-gem "toml-rb", "~> 2.2.0"
-
-group :development do
- gem "rubocop", "~> 0.75.0"
-end
diff --git a/updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile.lock b/updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile.lock
deleted file mode 100644
index 4f3322cf..00000000
--- a/updater/spec/fixtures/bundler_grouped_by_types/original/Gemfile.lock
+++ /dev/null
@@ -1,35 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- ast (2.4.2)
- citrus (3.0.2)
- jaro_winkler (1.5.6)
- parallel (1.23.0)
- parser (3.2.2.3)
- ast (~> 2.4.1)
- racc
- racc (1.7.1)
- rack (2.1.3)
- rainbow (3.1.1)
- rubocop (0.75.0)
- jaro_winkler (~> 1.5.1)
- parallel (~> 1.10)
- parser (>= 2.6)
- rainbow (>= 2.2.2, < 4.0)
- ruby-progressbar (~> 1.7)
- unicode-display_width (>= 1.4.0, < 1.7)
- ruby-progressbar (1.13.0)
- toml-rb (2.2.0)
- citrus (~> 3.0, > 3.0)
- unicode-display_width (1.6.1)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- rack (~> 2.1.3)
- rubocop (~> 0.75.0)
- toml-rb (~> 2.2.0)
-
-BUNDLED WITH
- 1.14.6
diff --git a/updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-a-2.0.0.gem b/updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-a-2.0.0.gem
new file mode 100644
index 00000000..922b25aa
Binary files /dev/null and b/updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-a-2.0.0.gem differ
diff --git a/updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-b-1.1.0.gem b/updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-b-1.1.0.gem
new file mode 100644
index 00000000..059a5064
Binary files /dev/null and b/updater/spec/fixtures/bundler_vendored/original/vendor/cache/dummy-pkg-b-1.1.0.gem differ
diff --git a/updater/spec/fixtures/bundler_vendored/original/vendor/cache/ruby-dummy-git-dependency-20151f9b67c8/.bundlecache b/updater/spec/fixtures/bundler_vendored/original/vendor/cache/ruby-dummy-git-dependency-20151f9b67c8/.bundlecache
new file mode 100644
index 00000000..e69de29b
diff --git a/updater/spec/fixtures/bundler_vendored/original/vendor/cache/ruby-dummy-git-dependency-20151f9b67c8/dummy-git-dependency.gemspec b/updater/spec/fixtures/bundler_vendored/original/vendor/cache/ruby-dummy-git-dependency-20151f9b67c8/dummy-git-dependency.gemspec
new file mode 100644
index 00000000..b42e1f27
--- /dev/null
+++ b/updater/spec/fixtures/bundler_vendored/original/vendor/cache/ruby-dummy-git-dependency-20151f9b67c8/dummy-git-dependency.gemspec
@@ -0,0 +1,19 @@
+# -*- encoding: utf-8 -*-
+# stub: dummy-git-dependency 1.0.0 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = "dummy-git-dependency".freeze
+ s.version = "1.0.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib".freeze]
+ s.authors = ["Dependabot".freeze]
+ s.date = "2023-06-02"
+ s.email = "support@dependabot.com".freeze
+ s.homepage = "http://github.com/dependabot/dummy-git-dependency".freeze
+ s.licenses = ["MIT".freeze]
+ s.rubygems_version = "3.3.26".freeze
+ s.summary = "A dummy package for testing Dependabot".freeze
+
+ s.installed_by_version = "3.3.26" if s.respond_to? :installed_by_version
+end
diff --git a/updater/spec/fixtures/bundler_vendored/updated/.bundle/config b/updater/spec/fixtures/bundler_vendored/updated/.bundle/config
new file mode 100644
index 00000000..612e6ae3
--- /dev/null
+++ b/updater/spec/fixtures/bundler_vendored/updated/.bundle/config
@@ -0,0 +1,2 @@
+---
+BUNDLE_CACHE_ALL: "false"
diff --git a/updater/spec/fixtures/bundler_git/original/Gemfile b/updater/spec/fixtures/bundler_vendored/updated/Gemfile
similarity index 53%
rename from updater/spec/fixtures/bundler_git/original/Gemfile
rename to updater/spec/fixtures/bundler_vendored/updated/Gemfile
index d27ea892..8e49b738 100644
--- a/updater/spec/fixtures/bundler_git/original/Gemfile
+++ b/updater/spec/fixtures/bundler_vendored/updated/Gemfile
@@ -2,4 +2,6 @@
source "https://rubygems.org"
-gem "dummy-git-dependency", git: "git@github.com:dependabot-fixtures/ruby-dummy-git-dependency.git", ref: "v1.0.0"
+gem "dummy-pkg-a", "~> 2.0.0"
+gem "dummy-pkg-b", "~> 1.2.0"
+gem "dummy-git-dependency", git: "git@github.com:dependabot-fixtures/ruby-dummy-git-dependency.git", ref: "v1.1.0"
diff --git a/updater/spec/fixtures/bundler_vendored/updated/Gemfile.lock b/updater/spec/fixtures/bundler_vendored/updated/Gemfile.lock
new file mode 100644
index 00000000..f2fa6424
--- /dev/null
+++ b/updater/spec/fixtures/bundler_vendored/updated/Gemfile.lock
@@ -0,0 +1,26 @@
+GIT
+ remote: git@github.com:dependabot-fixtures/ruby-dummy-git-dependency.git
+ revision: c0e25c2eb332122873f73acb3b61fb2e261cfd8f
+ ref: v1.1.0
+ specs:
+ dummy-git-dependency (1.1.0)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ dummy-pkg-a (2.0.0)
+ dummy-pkg-b (1.2.0)
+ dummy-pkg-a (~> 2.0)
+
+PLATFORMS
+ aarch64-linux
+ x86_64-darwin
+ x86_64-linux
+
+DEPENDENCIES
+ dummy-git-dependency!
+ dummy-pkg-a (~> 2.0.0)
+ dummy-pkg-b (~> 1.2.0)
+
+BUNDLED WITH
+ 2.4.13
diff --git a/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-a-2.0.0.gem b/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-a-2.0.0.gem
new file mode 100644
index 00000000..922b25aa
Binary files /dev/null and b/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-a-2.0.0.gem differ
diff --git a/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-b-1.2.0.gem b/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-b-1.2.0.gem
new file mode 100644
index 00000000..e0647d5b
Binary files /dev/null and b/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/dummy-pkg-b-1.2.0.gem differ
diff --git a/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/ruby-dummy-git-dependency-c0e25c2eb332/.bundlecache b/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/ruby-dummy-git-dependency-c0e25c2eb332/.bundlecache
new file mode 100644
index 00000000..e69de29b
diff --git a/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/ruby-dummy-git-dependency-c0e25c2eb332/dummy-git-dependency.gemspec b/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/ruby-dummy-git-dependency-c0e25c2eb332/dummy-git-dependency.gemspec
new file mode 100644
index 00000000..1c067a69
--- /dev/null
+++ b/updater/spec/fixtures/bundler_vendored/updated/vendor/cache/ruby-dummy-git-dependency-c0e25c2eb332/dummy-git-dependency.gemspec
@@ -0,0 +1,19 @@
+# -*- encoding: utf-8 -*-
+# stub: dummy-git-dependency 1.1.0 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = "dummy-git-dependency".freeze
+ s.version = "1.1.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib".freeze]
+ s.authors = ["Dependabot".freeze]
+ s.date = "2023-06-05"
+ s.email = "support@dependabot.com".freeze
+ s.homepage = "http://github.com/dependabot/dummy-git-dependency".freeze
+ s.licenses = ["MIT".freeze]
+ s.rubygems_version = "3.3.26".freeze
+ s.summary = "A dummy package for testing Dependabot".freeze
+
+ s.installed_by_version = "3.3.26" if s.respond_to? :installed_by_version
+end
diff --git a/updater/spec/fixtures/composer/original/composer.json b/updater/spec/fixtures/composer/original/composer.json
new file mode 100644
index 00000000..a505225f
--- /dev/null
+++ b/updater/spec/fixtures/composer/original/composer.json
@@ -0,0 +1,7 @@
+{
+ "name": "dependabot/test-project",
+ "require": {
+ "dependabot/dummy-pkg-a": "^2.0.0",
+ "dependabot/dummy-pkg-b": "^1.1.0"
+ }
+}
diff --git a/updater/spec/fixtures/composer/original/composer.lock b/updater/spec/fixtures/composer/original/composer.lock
new file mode 100644
index 00000000..886600a5
--- /dev/null
+++ b/updater/spec/fixtures/composer/original/composer.lock
@@ -0,0 +1,65 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "8fe1579c07dcc6865cfb8300382414ea",
+ "packages": [
+ {
+ "name": "dependabot/dummy-pkg-a",
+ "version": "v2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dependabot/php-dummy-pkg-a.git",
+ "reference": "183c190aa576256c768a04ff2163c208e0dd81d1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dependabot/php-dummy-pkg-a/zipball/183c190aa576256c768a04ff2163c208e0dd81d1",
+ "reference": "183c190aa576256c768a04ff2163c208e0dd81d1",
+ "shasum": ""
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A dummy package for testing Dependabot",
+ "time": "2017-06-08T12:52:19+00:00"
+ },
+ {
+ "name": "dependabot/dummy-pkg-b",
+ "version": "v1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dependabot/php-dummy-pkg-b.git",
+ "reference": "164a01c72b834713b19f05eb0cf51130fccd9429"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dependabot/php-dummy-pkg-b/zipball/164a01c72b834713b19f05eb0cf51130fccd9429",
+ "reference": "164a01c72b834713b19f05eb0cf51130fccd9429",
+ "shasum": ""
+ },
+ "require": {
+ "dependabot/dummy-pkg-a": "~2.0"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A dummy package for testing Dependabot",
+ "time": "2017-06-08T13:03:29+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/updater/spec/fixtures/composer/updated/composer.json b/updater/spec/fixtures/composer/updated/composer.json
new file mode 100644
index 00000000..88344145
--- /dev/null
+++ b/updater/spec/fixtures/composer/updated/composer.json
@@ -0,0 +1,7 @@
+{
+ "name": "dependabot/test-project",
+ "require": {
+ "dependabot/dummy-pkg-a": "^2.0.0",
+ "dependabot/dummy-pkg-b": "^1.2.0"
+ }
+}
diff --git a/updater/spec/fixtures/composer/updated/composer.lock b/updater/spec/fixtures/composer/updated/composer.lock
new file mode 100644
index 00000000..25c4d617
--- /dev/null
+++ b/updater/spec/fixtures/composer/updated/composer.lock
@@ -0,0 +1,74 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "f3db3a6bac0d9a53cb3d7f7da5a7f535",
+ "packages": [
+ {
+ "name": "dependabot/dummy-pkg-a",
+ "version": "v2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dependabot/php-dummy-pkg-a.git",
+ "reference": "183c190aa576256c768a04ff2163c208e0dd81d1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dependabot/php-dummy-pkg-a/zipball/183c190aa576256c768a04ff2163c208e0dd81d1",
+ "reference": "183c190aa576256c768a04ff2163c208e0dd81d1",
+ "shasum": ""
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A dummy package for testing Dependabot",
+ "support": {
+ "issues": "https://github.com/dependabot/php-dummy-pkg-a/issues",
+ "source": "https://github.com/dependabot/php-dummy-pkg-a/tree/v2.0.0"
+ },
+ "time": "2017-06-08T12:52:19+00:00"
+ },
+ {
+ "name": "dependabot/dummy-pkg-b",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dependabot/php-dummy-pkg-b.git",
+ "reference": "658d78acdba4020da5da797c4e96c082be9126f0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dependabot/php-dummy-pkg-b/zipball/658d78acdba4020da5da797c4e96c082be9126f0",
+ "reference": "658d78acdba4020da5da797c4e96c082be9126f0",
+ "shasum": ""
+ },
+ "require": {
+ "dependabot/dummy-pkg-a": "~2.0"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A dummy package for testing Dependabot",
+ "support": {
+ "issues": "https://github.com/dependabot/php-dummy-pkg-b/issues",
+ "source": "https://github.com/dependabot/php-dummy-pkg-b/tree/v1.2.0"
+ },
+ "time": "2017-06-08T13:03:43+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "2.6.0"
+}
diff --git a/updater/spec/fixtures/docker/original/Dockerfile.bundler b/updater/spec/fixtures/docker/original/Dockerfile.bundler
deleted file mode 100644
index 326a6102..00000000
--- a/updater/spec/fixtures/docker/original/Dockerfile.bundler
+++ /dev/null
@@ -1 +0,0 @@
-FROM ghcr.io/dependabot/dependabot-updater-bundler:v2.0.20230317134336@sha256:f1a76307b9a43d4d25289852e2d0ee73f1bf2bbef1a935d9255c2e56f9db64fc
diff --git a/updater/spec/fixtures/docker/original/Dockerfile.cargo b/updater/spec/fixtures/docker/original/Dockerfile.cargo
deleted file mode 100644
index 3c283b98..00000000
--- a/updater/spec/fixtures/docker/original/Dockerfile.cargo
+++ /dev/null
@@ -1 +0,0 @@
-FROM ghcr.io/dependabot/dependabot-updater-cargo:v2.0.20230317130517@sha256:8913caf469d377ae28525b184b40f61b554d839082f594b31a36beb7344d12bc
diff --git a/updater/spec/fixtures/dummy/original/a.dummy b/updater/spec/fixtures/dummy/original/a.dummy
new file mode 100644
index 00000000..3b96ce63
--- /dev/null
+++ b/updater/spec/fixtures/dummy/original/a.dummy
@@ -0,0 +1 @@
+dependabot/a = 1.1.1
diff --git a/updater/spec/fixtures/dummy/original/b.dummy b/updater/spec/fixtures/dummy/original/b.dummy
new file mode 100644
index 00000000..9ef1f750
--- /dev/null
+++ b/updater/spec/fixtures/dummy/original/b.dummy
@@ -0,0 +1 @@
+dependabot/b = 2.2.2
diff --git a/updater/spec/fixtures/file_fetcher_output/output.json b/updater/spec/fixtures/file_fetcher_output/output.json
new file mode 100644
index 00000000..45777a60
--- /dev/null
+++ b/updater/spec/fixtures/file_fetcher_output/output.json
@@ -0,0 +1,70 @@
+{
+ "job": {
+ "allowed-updates": [
+ {
+ "dependency-type": "direct",
+ "update-type": "all"
+ },
+ {
+ "dependency-type": "indirect",
+ "update-type": "security"
+ }
+ ],
+ "credentials": [
+ {
+ "type": "git_source",
+ "host": "github.com",
+ "username": "x-access-token",
+ "password": "v1.exampletokenfromgithubinityesitisforsure"
+ },
+ {
+ "type": "rubygems_index",
+ "host": "my.rubygems-host.org",
+ "token": "secret"
+ }
+ ],
+ "credentials-metadata": [
+ {
+ "type": "git_source",
+ "host": "github.com"
+ },
+ {
+ "type": "rubygems_index",
+ "host": "my.rubygems-host.org"
+ }
+ ],
+ "dependencies": null,
+ "directory": "/",
+ "existing-pull-requests": [],
+ "ignore-conditions": [],
+ "security-advisories": [],
+ "package_manager": "bundler",
+ "repo-name": "dependabot-fixtures/dependabot-test-ruby-package",
+ "source": {
+ "provider": "github",
+ "repo": "dependabot-fixtures/dependabot-test-ruby-package",
+ "directory": "/",
+ "branch": null,
+ "hostname": "github.com",
+ "api-endpoint": "https://api.github.com/"
+ },
+ "lockfile-only": false,
+ "requirements-update-strategy": null,
+ "update-subdependencies": false,
+ "updating-a-pull-request": false,
+ "vendor-dependencies": false,
+ "security-updates-only": false
+ },
+ "base64_dependency_files":[
+ {
+ "name":"dependabot-test-ruby-package.gemspec",
+ "content":"IyBmcm96ZW5fc3RyaW5nX2xpdGVyYWw6IHRydWUKCkdlbTo6U3BlY2lmaWNh\ndGlvbi5uZXcgZG8gfHNwZWN8CiAgc3BlYy5uYW1lICAgICA9ICdkZXBlbmRh\nYm90LXRlc3QtcnVieS1wYWNrYWdlJwogIHNwZWMudmVyc2lvbiAgPSAnMS4w\nLjEnCiAgc3BlYy5zdW1tYXJ5ICA9ICdBIGR1bW15IHBhY2thZ2UgZm9yIHRl\nc3RpbmcgRGVwZW5kYWJvdCcKICBzcGVjLmF1dGhvciAgID0gJ0RlcGVuZGFi\nb3QnCiAgc3BlYy5saWNlbnNlICA9ICdNSVQnCiAgc3BlYy5lbWFpbCAgICA9\nICdub3JlcGx5QGdpdGh1Yi5jb20nCiAgc3BlYy5ob21lcGFnZSA9ICdodHRw\nOi8vZ2l0aHViLmNvbS9kZXBlbmRhYm90LWZpeHR1cmVzL2RlcGVuZGFib3Qt\ndGVzdC1ydWJ5LXBhY2thZ2UnCmVuZAo=\n",
+ "directory":"/",
+ "type":"file",
+ "support_file":false,
+ "content_encoding":"utf-8",
+ "deleted":false
+ }
+ ],
+ "base_commit_sha":"1c6331732c41e4557a16dacb82534f1d1c831848"
+}
diff --git a/updater/spec/fixtures/file_fetcher_output/vendoring_output.json b/updater/spec/fixtures/file_fetcher_output/vendoring_output.json
new file mode 100644
index 00000000..020585c7
--- /dev/null
+++ b/updater/spec/fixtures/file_fetcher_output/vendoring_output.json
@@ -0,0 +1,70 @@
+{
+ "job": {
+ "allowed-updates": [
+ {
+ "dependency-type": "direct",
+ "update-type": "all"
+ },
+ {
+ "dependency-type": "indirect",
+ "update-type": "security"
+ }
+ ],
+ "credentials": [
+ {
+ "type": "git_source",
+ "host": "github.com",
+ "username": "x-access-token",
+ "password": "v1.exampletokenfromgithubinityesitisforsure"
+ },
+ {
+ "type": "rubygems_index",
+ "host": "my.rubygems-host.org",
+ "token": "secret"
+ }
+ ],
+ "credentials-metadata": [
+ {
+ "type": "git_source",
+ "host": "github.com"
+ },
+ {
+ "type": "rubygems_index",
+ "host": "my.rubygems-host.org"
+ }
+ ],
+ "dependencies": null,
+ "directory": "/",
+ "existing-pull-requests": [],
+ "ignore-conditions": [],
+ "security-advisories": [],
+ "package_manager": "bundler",
+ "repo-name": "dependabot-fixtures/dependabot-test-ruby-package",
+ "source": {
+ "provider": "github",
+ "repo": "dependabot-fixtures/dependabot-test-ruby-package",
+ "directory": "/",
+ "branch": null,
+ "hostname": "github.com",
+ "api-endpoint": "https://api.github.com/"
+ },
+ "lockfile-only": false,
+ "requirements-update-strategy": null,
+ "update-subdependencies": false,
+ "updating-a-pull-request": false,
+ "vendor-dependencies": true,
+ "security-updates-only": false
+ },
+ "base64_dependency_files":[
+ {
+ "name":"dependabot-test-ruby-package.gemspec",
+ "content":"IyBmcm96ZW5fc3RyaW5nX2xpdGVyYWw6IHRydWUKCkdlbTo6U3BlY2lmaWNh\ndGlvbi5uZXcgZG8gfHNwZWN8CiAgc3BlYy5uYW1lICAgICA9ICdkZXBlbmRh\nYm90LXRlc3QtcnVieS1wYWNrYWdlJwogIHNwZWMudmVyc2lvbiAgPSAnMS4w\nLjEnCiAgc3BlYy5zdW1tYXJ5ICA9ICdBIGR1bW15IHBhY2thZ2UgZm9yIHRl\nc3RpbmcgRGVwZW5kYWJvdCcKICBzcGVjLmF1dGhvciAgID0gJ0RlcGVuZGFi\nb3QnCiAgc3BlYy5saWNlbnNlICA9ICdNSVQnCiAgc3BlYy5lbWFpbCAgICA9\nICdub3JlcGx5QGdpdGh1Yi5jb20nCiAgc3BlYy5ob21lcGFnZSA9ICdodHRw\nOi8vZ2l0aHViLmNvbS9kZXBlbmRhYm90LWZpeHR1cmVzL2RlcGVuZGFib3Qt\ndGVzdC1ydWJ5LXBhY2thZ2UnCmVuZAo=\n",
+ "directory":"/",
+ "type":"file",
+ "support_file":false,
+ "content_encoding":"utf-8",
+ "deleted":false
+ }
+ ],
+ "base_commit_sha":"1c6331732c41e4557a16dacb82534f1d1c831848"
+}
diff --git a/updater/spec/fixtures/handle_error.json b/updater/spec/fixtures/handle_error.json
new file mode 100644
index 00000000..31cce925
--- /dev/null
+++ b/updater/spec/fixtures/handle_error.json
@@ -0,0 +1,5 @@
+{
+ "data": {
+ "error_type": "some_error_class"
+ }
+}
diff --git a/updater/spec/fixtures/job_definitions/bundler/security_updates/group_update_multi_dir.yaml b/updater/spec/fixtures/job_definitions/bundler/security_updates/group_update_multi_dir.yaml
new file mode 100644
index 00000000..13ced6e7
--- /dev/null
+++ b/updater/spec/fixtures/job_definitions/bundler/security_updates/group_update_multi_dir.yaml
@@ -0,0 +1,68 @@
+job:
+ allowed-updates:
+ - dependency-type: direct
+ update-type: all
+ commit-message-options:
+ prefix:
+ prefix-development:
+ include-scope:
+ credentials-metadata:
+ - type: git_source
+ host: github.com
+ debug:
+ dependencies:
+ - sinatra
+ dependency-groups: []
+ dependency-group-to-refresh:
+ existing-pull-requests: []
+ existing-group-pull-requests: []
+ experiments:
+ grouped-updates-experimental-rules: true
+ record-ecosystem-versions: true
+ record-update-job-unknown-error: true
+ grouped-update: false
+ ignore-conditions: []
+ lockfile-only: false
+ max-updater-run-time: 2700
+ package-manager: bundler
+ proxy-log-response-body-on-auth-failure: true
+ requirements-update-strategy:
+ reject-external-code: false
+ security-advisories:
+ - dependency-name: sinatra
+ patched-versions: []
+ unaffected-versions: []
+ affected-versions:
+ - ">= 2.0.0.beta1, < 2.0.1"
+ - dependency-name: sinatra
+ patched-versions: []
+ unaffected-versions: []
+ affected-versions:
+ - ">= 2.0.0, < 2.0.2"
+ - dependency-name: sinatra
+ patched-versions: []
+ unaffected-versions: []
+ affected-versions:
+ - "< 2.2.0"
+ - dependency-name: sinatra
+ patched-versions: []
+ unaffected-versions: []
+ affected-versions:
+ - ">= 2.0.0, < 2.2.3"
+ - ">= 3.0, < 3.0.4"
+ security-updates-only: true
+ source:
+ provider: github
+ repo: dependabot-fixtures/test-gsu-multi-dir-bundler
+ directory: "/foo"
+ directories:
+ - "/foo"
+ - "/bar"
+ branch:
+ api-endpoint: https://api.github.com/
+ hostname: github.com
+ commit: 2b0328507b516c5f06a98581aa99ab1233e37429
+ updating-a-pull-request: false
+ update-subdependencies: false
+ vendor-dependencies: false
+ repo-private: false
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all.yaml
deleted file mode 100644
index b698aad4..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: everything-everywhere-all-at-once
- rules:
- patterns:
- - "*"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_by_dependency_type.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_by_dependency_type.yaml
deleted file mode 100644
index e38bdcb5..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_by_dependency_type.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions:
- - dependency-name: rubocop
- version-requirement: "> 1.56.0"
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- grouped-updates-experimental-rules: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: dev-dependencies
- rules:
- dependency-type: "development"
- - name: production-dependencies
- rules:
- dependency-type: "production"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_empty_group.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_empty_group.yaml
deleted file mode 100644
index ce148d3c..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_empty_group.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: everything-everywhere-all-at-once
- rules:
- patterns:
- - "*bagel"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_overlapping_groups.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_overlapping_groups.yaml
deleted file mode 100644
index 763524ab..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_overlapping_groups.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: my-group
- rules:
- patterns:
- - "dummy-pkg-*"
- - name: my-overlapping-group
- rules:
- patterns:
- - "dummy-pkg-*"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping.yaml
deleted file mode 100644
index 75c3d2c0..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- grouped-updates-experimental-rules: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: small-bumps
- rules:
- update-types:
- - "minor"
- - "patch"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping_with_global_ignores.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping_with_global_ignores.yaml
deleted file mode 100644
index d08e0f69..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_semver_grouping_with_global_ignores.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions:
- - dependency-name: "*"
- update-types: ["version-update:semver-major"]
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- grouped-updates-experimental-rules: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: patches
- rules:
- update-types:
- - "patch"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_existing_pr.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_existing_pr.yaml
deleted file mode 100644
index 0d92199d..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_existing_pr.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- existing-group-pull-requests:
- - dependency-group-name: "group-b"
- dependencies:
- - dependency-name: "dummy-pkg-b"
- dependency-version: "1.2.0"
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: group-b
- rules:
- patterns:
- - "dummy-pkg-b"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_ungrouped.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_ungrouped.yaml
deleted file mode 100644
index 51c66fbf..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_ungrouped.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: group-b
- rules:
- patterns:
- - "dummy-pkg-b"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_vendoring.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_vendoring.yaml
deleted file mode 100644
index 9540fd73..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_all_with_vendoring.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: true
- experiments:
- grouped-updates-prototype: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
- dependency-groups:
- - name: everything-everywhere-all-at-once
- rules:
- patterns:
- - "*"
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_peer_manifests.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_peer_manifests.yaml
deleted file mode 100644
index 1becba2b..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_peer_manifests.yaml
+++ /dev/null
@@ -1 +0,0 @@
-404: Not Found
\ No newline at end of file
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh.yaml
index b0f9fd2a..d33aac07 100644
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh.yaml
+++ b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh.yaml
@@ -3,7 +3,7 @@ job:
source:
provider: github
repo: dependabot/smoke-tests
- directory: "/bundler"
+ directory: "/"
branch:
api-endpoint: https://api.github.com/
hostname: github.com
@@ -15,6 +15,7 @@ job:
dependencies:
- dependency-name: dummy-pkg-b
dependency-version: 1.2.0
+ directory: "/"
updating-a-pull-request: true
lockfile-only: false
update-subdependencies: false
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_dependencies_changed.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_dependencies_changed.yaml
index 66546c9d..a21e1c2f 100644
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_dependencies_changed.yaml
+++ b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_dependencies_changed.yaml
@@ -3,7 +3,7 @@ job:
source:
provider: github
repo: dependabot/smoke-tests
- directory: "/bundler"
+ directory: "/"
branch:
api-endpoint: https://api.github.com/
hostname: github.com
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_empty_group.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_empty_group.yaml
index 77614afe..b1862d00 100644
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_empty_group.yaml
+++ b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_empty_group.yaml
@@ -3,7 +3,7 @@ job:
source:
provider: github
repo: dependabot/smoke-tests
- directory: "/bundler"
+ directory: "/"
branch:
api-endpoint: https://api.github.com/
hostname: github.com
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_missing_group.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_missing_group.yaml
index 64ece40d..3764128a 100644
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_missing_group.yaml
+++ b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_missing_group.yaml
@@ -3,7 +3,7 @@ job:
source:
provider: github
repo: dependabot/smoke-tests
- directory: "/bundler"
+ directory: "/"
branch:
api-endpoint: https://api.github.com/
hostname: github.com
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_similar_pr.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_similar_pr.yaml
index 5c7de7b2..06256150 100644
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_similar_pr.yaml
+++ b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_similar_pr.yaml
@@ -3,7 +3,7 @@ job:
source:
provider: github
repo: dependabot/smoke-tests
- directory: "/bundler"
+ directory: "/"
branch:
api-endpoint: https://api.github.com/
hostname: github.com
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_versions_changed.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_versions_changed.yaml
index 79e42f2c..107d7668 100644
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_versions_changed.yaml
+++ b/updater/spec/fixtures/job_definitions/bundler/version_updates/group_update_refresh_versions_changed.yaml
@@ -3,7 +3,7 @@ job:
source:
provider: github
repo: dependabot/smoke-tests
- directory: "/bundler"
+ directory: "/"
branch:
api-endpoint: https://api.github.com/
hostname: github.com
diff --git a/updater/spec/fixtures/job_definitions/bundler/version_updates/update_all_simple.yaml b/updater/spec/fixtures/job_definitions/bundler/version_updates/update_all_simple.yaml
deleted file mode 100644
index 2be7d5ec..00000000
--- a/updater/spec/fixtures/job_definitions/bundler/version_updates/update_all_simple.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-job:
- package-manager: bundler
- source:
- provider: github
- repo: dependabot/smoke-tests
- directory: "/bundler"
- branch:
- api-endpoint: https://api.github.com/
- hostname: github.com
- dependencies:
- existing-pull-requests: []
- updating-a-pull-request: false
- lockfile-only: false
- update-subdependencies: false
- ignore-conditions: []
- requirements-update-strategy:
- allowed-updates:
- - dependency-type: direct
- update-type: all
- credentials-metadata:
- - type: git_source
- host: github.com
- security-advisories: []
- max-updater-run-time: 2700
- vendor-dependencies: false
- experiments:
- grouped-updates-prototype: true
- reject-external-code: false
- commit-message-options:
- prefix:
- prefix-development:
- include-scope:
- security-updates-only: false
diff --git a/updater/spec/fixtures/job_definitions/docker/version_updates/group_update_peer_manifests.yaml b/updater/spec/fixtures/job_definitions/dummy/version_updates/group_update_peer_manifests.yaml
similarity index 94%
rename from updater/spec/fixtures/job_definitions/docker/version_updates/group_update_peer_manifests.yaml
rename to updater/spec/fixtures/job_definitions/dummy/version_updates/group_update_peer_manifests.yaml
index a7d9ab31..59c1f26a 100644
--- a/updater/spec/fixtures/job_definitions/docker/version_updates/group_update_peer_manifests.yaml
+++ b/updater/spec/fixtures/job_definitions/dummy/version_updates/group_update_peer_manifests.yaml
@@ -1,9 +1,9 @@
job:
- package-manager: docker
+ package-manager: dummy
source:
provider: github
repo: github/dependabot-action
- directory: "/docker"
+ directory: "/dummy"
branch:
api-endpoint: https://api.github.com/
hostname: github.com
diff --git a/updater/spec/fixtures/jobs/job_with_dummy.json b/updater/spec/fixtures/jobs/job_with_dummy.json
new file mode 100644
index 00000000..03606302
--- /dev/null
+++ b/updater/spec/fixtures/jobs/job_with_dummy.json
@@ -0,0 +1,41 @@
+{
+ "job": {
+ "allowed-updates": [
+ {
+ "dependency-type": "direct",
+ "update-type": "all"
+ },
+ {
+ "dependency-type": "indirect",
+ "update-type": "security"
+ }
+ ],
+ "credentials-metadata": [
+ {
+ "type": "git_source",
+ "host": "github.com"
+ }
+ ],
+ "dependencies": null,
+ "directory": "/",
+ "existing-pull-requests": [],
+ "ignore-conditions": [],
+ "security-advisories": [],
+ "package_manager": "dummy",
+ "repo-name": "dependabot-fixtures/go-modules-lib",
+ "source": {
+ "provider": "github",
+ "repo": "dependabot-fixtures/go-modules-lib",
+ "directory": "/",
+ "branch": null,
+ "hostname": "github.com",
+ "api-endpoint": "https://api.github.com/"
+ },
+ "lockfile-only": false,
+ "requirements-update-strategy": null,
+ "update-subdependencies": false,
+ "updating-a-pull-request": false,
+ "vendor-dependencies": false,
+ "security-updates-only": false
+ }
+}
diff --git a/updater/spec/fixtures/jobs/job_with_vendor_dependencies.json b/updater/spec/fixtures/jobs/job_with_vendor_dependencies.json
new file mode 100644
index 00000000..24f7d2cf
--- /dev/null
+++ b/updater/spec/fixtures/jobs/job_with_vendor_dependencies.json
@@ -0,0 +1,58 @@
+{
+ "job": {
+ "allowed-updates": [
+ {
+ "dependency-type": "direct",
+ "update-type": "all"
+ },
+ {
+ "dependency-type": "indirect",
+ "update-type": "security"
+ }
+ ],
+ "credentials-metadata": [
+ {
+ "type": "git_source",
+ "host": "github.com"
+ },
+ {
+ "type": "rubygems_index",
+ "host": "my.rubygems-host.org"
+ }
+ ],
+ "dependencies": null,
+ "directory": "/",
+ "existing-pull-requests": [],
+ "ignore-conditions": [],
+ "security-advisories": [],
+ "package_manager": "bundler",
+ "repo-name": "dependabot-fixtures/dependabot-test-ruby-package",
+ "source": {
+ "provider": "github",
+ "repo": "dependabot-fixtures/dependabot-test-ruby-package",
+ "directory": "/",
+ "branch": null,
+ "hostname": "github.com",
+ "api-endpoint": "https://api.github.com/"
+ },
+ "lockfile-only": false,
+ "requirements-update-strategy": null,
+ "update-subdependencies": false,
+ "updating-a-pull-request": false,
+ "vendor-dependencies": true,
+ "security-updates-only": false
+ },
+ "credentials": [
+ {
+ "type": "git_source",
+ "host": "github.com",
+ "username": "x-access-token",
+ "password": "v1.exampletokenfromgithubinityesitisforsure"
+ },
+ {
+ "type": "rubygems_index",
+ "host": "my.rubygems-host.org",
+ "token": "secret"
+ }
+ ]
+}
diff --git a/updater/spec/fixtures/jobs/job_without_credentials.json b/updater/spec/fixtures/jobs/job_without_credentials.json
new file mode 100644
index 00000000..0044aba5
--- /dev/null
+++ b/updater/spec/fixtures/jobs/job_without_credentials.json
@@ -0,0 +1,45 @@
+{
+ "job": {
+ "allowed-updates": [
+ {
+ "dependency-type": "direct",
+ "update-type": "all"
+ },
+ {
+ "dependency-type": "indirect",
+ "update-type": "security"
+ }
+ ],
+ "credentials-metadata": [
+ {
+ "type": "git_source",
+ "host": "github.com"
+ },
+ {
+ "type": "rubygems_index",
+ "host": "my.rubygems-host.org"
+ }
+ ],
+ "dependencies": null,
+ "directory": "/",
+ "existing-pull-requests": [],
+ "ignore-conditions": [],
+ "security-advisories": [],
+ "package_manager": "bundler",
+ "repo-name": "dependabot-fixtures/dependabot-test-ruby-package",
+ "source": {
+ "provider": "github",
+ "repo": "dependabot-fixtures/dependabot-test-ruby-package",
+ "directory": "/",
+ "branch": null,
+ "hostname": "github.com",
+ "api-endpoint": "https://api.github.com/"
+ },
+ "lockfile-only": false,
+ "requirements-update-strategy": null,
+ "update-subdependencies": false,
+ "updating-a-pull-request": false,
+ "vendor-dependencies": false,
+ "security-updates-only": false
+ }
+}
diff --git a/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/does_not_clone_the_repo.yml b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/does_not_clone_the_repo.yml
new file mode 100644
index 00000000..4bba419b
--- /dev/null
+++ b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/does_not_clone_the_repo.yml
@@ -0,0 +1,302 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.19.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 29 Oct 2020 19:20:31 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"c7014303ad99dbf7e02ae891e64a18881566da6a75b8b457cb0cf96705f6eca6"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
+ X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '52'
+ X-Ratelimit-Reset:
+ - '1603999545'
+ X-Ratelimit-Used:
+ - '8'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '1254'
+ X-Github-Request-Id:
+ - C80F:51E5:12C5A2:20FA0C:5F9B15FF
+ body:
+ encoding: ASCII-8BIT
+ string: '{"id":267290099,"node_id":"MDEwOlJlcG9zaXRvcnkyNjcyOTAwOTk=","name":"dependabot-test-ruby-package","full_name":"dependabot-fixtures/dependabot-test-ruby-package","private":false,"owner":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars0.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","description":"A
+ ruby gem for testing dependabot","fork":false,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package","forks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/forks","keys_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/teams","hooks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/hooks","issue_events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/events{/number}","events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/events","assignees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/assignees{/user}","branches_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/branches{/branch}","tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/tags","blobs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs{/sha}","trees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/statuses/{sha}","languages_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/languages","stargazers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/stargazers","contributors_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contributors","subscribers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscribers","subscription_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscription","commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/commits{/sha}","git_commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits{/sha}","comments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/comments{/number}","issue_comment_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/comments{/number}","contents_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/{+path}","compare_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/merges","archive_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/downloads","issues_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues{/number}","pulls_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/pulls{/number}","milestones_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/milestones{/number}","notifications_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/labels{/name}","releases_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/releases{/id}","deployments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/deployments","created_at":"2020-05-27T10:32:22Z","updated_at":"2020-05-27T11:17:06Z","pushed_at":"2020-05-27T11:23:26Z","git_url":"git://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","ssh_url":"git@github.com:dependabot-fixtures/dependabot-test-ruby-package.git","clone_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","svn_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","homepage":null,"size":1,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master","temp_clone_token":null,"organization":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars0.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"network_count":0,"subscribers_count":0}'
+ recorded_at: Thu, 29 Oct 2020 19:20:32 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.19.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 29 Oct 2020 19:20:32 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"4ec4b41160548cc0f86c2459b25f9dd74f27df28fc425f9786cdb4ab70be7e01"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Poll-Interval:
+ - '300'
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
+ X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '51'
+ X-Ratelimit-Reset:
+ - '1603999545'
+ X-Ratelimit-Used:
+ - '9'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '237'
+ X-Github-Request-Id:
+ - C810:0BE6:144B46:2232C8:5F9B1600
+ body:
+ encoding: ASCII-8BIT
+ string: '{"ref":"refs/heads/master","node_id":"MDM6UmVmMjY3MjkwMDk5OnJlZnMvaGVhZHMvbWFzdGVy","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master","object":{"sha":"1c6331732c41e4557a16dacb82534f1d1c831848","type":"commit","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits/1c6331732c41e4557a16dacb82534f1d1c831848"}}'
+ recorded_at: Thu, 29 Oct 2020 19:20:32 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.19.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 29 Oct 2020 19:20:32 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"7b44807c792639c3b05064b3493694c6b4cce370"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
+ X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '50'
+ X-Ratelimit-Reset:
+ - '1603999545'
+ X-Ratelimit-Used:
+ - '10'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '496'
+ X-Github-Request-Id:
+ - C811:0357:123458:1FE6AE:5F9B1600
+ body:
+ encoding: ASCII-8BIT
+ string: '[{"name":".gitignore","path":".gitignore","sha":"c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","size":6,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore"}},{"name":"README.md","path":"README.md","sha":"f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","size":85,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md"}},{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}]'
+ recorded_at: Thu, 29 Oct 2020 19:20:32 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.19.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 29 Oct 2020 19:20:32 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"c5fd208850ed1bf1334b4b9cd4910950bed0c497"
+ Last-Modified:
+ - Wed, 27 May 2020 10:39:57 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
+ X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '49'
+ X-Ratelimit-Reset:
+ - '1603999545'
+ X-Ratelimit-Used:
+ - '11'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '703'
+ X-Github-Request-Id:
+ - C812:0357:123474:1FE6DE:5F9B1600
+ body:
+ encoding: ASCII-8BIT
+ string: '{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","content":"IyBmcm96ZW5fc3RyaW5nX2xpdGVyYWw6IHRydWUKCkdlbTo6U3BlY2lmaWNh\ndGlvbi5uZXcgZG8gfHNwZWN8CiAgc3BlYy5uYW1lICAgICA9ICdkZXBlbmRh\nYm90LXRlc3QtcnVieS1wYWNrYWdlJwogIHNwZWMudmVyc2lvbiAgPSAnMS4w\nLjEnCiAgc3BlYy5zdW1tYXJ5ICA9ICdBIGR1bW15IHBhY2thZ2UgZm9yIHRl\nc3RpbmcgRGVwZW5kYWJvdCcKICBzcGVjLmF1dGhvciAgID0gJ0RlcGVuZGFi\nb3QnCiAgc3BlYy5saWNlbnNlICA9ICdNSVQnCiAgc3BlYy5lbWFpbCAgICA9\nICdub3JlcGx5QGdpdGh1Yi5jb20nCiAgc3BlYy5ob21lcGFnZSA9ICdodHRw\nOi8vZ2l0aHViLmNvbS9kZXBlbmRhYm90LWZpeHR1cmVzL2RlcGVuZGFib3Qt\ndGVzdC1ydWJ5LXBhY2thZ2UnCmVuZAo=\n","encoding":"base64","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}'
+ recorded_at: Thu, 29 Oct 2020 19:20:32 GMT
+recorded_with: VCR 6.0.0
diff --git a/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/fetches_the_files_and_writes_the_fetched_files_to_output_json.yml b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/fetches_the_files_and_writes_the_fetched_files_to_output_json.yml
new file mode 100644
index 00000000..a57dd2ac
--- /dev/null
+++ b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/fetches_the_files_and_writes_the_fetched_files_to_output_json.yml
@@ -0,0 +1,294 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.18.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 20 Aug 2020 15:19:20 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"05138d3b8df2fc6f68facbbc84776e4d"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
+ X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '42'
+ X-Ratelimit-Reset:
+ - '1597938283'
+ Accept-Ranges:
+ - bytes
+ Transfer-Encoding:
+ - chunked
+ X-Github-Request-Id:
+ - FED9:4A5A:67DBD2:F5E8C6:5F3E9478
+ body:
+ encoding: ASCII-8BIT
+ string: '{"id":267290099,"node_id":"MDEwOlJlcG9zaXRvcnkyNjcyOTAwOTk=","name":"dependabot-test-ruby-package","full_name":"dependabot-fixtures/dependabot-test-ruby-package","private":false,"owner":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars0.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","description":"A
+ ruby gem for testing dependabot","fork":false,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package","forks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/forks","keys_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/teams","hooks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/hooks","issue_events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/events{/number}","events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/events","assignees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/assignees{/user}","branches_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/branches{/branch}","tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/tags","blobs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs{/sha}","trees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/statuses/{sha}","languages_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/languages","stargazers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/stargazers","contributors_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contributors","subscribers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscribers","subscription_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscription","commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/commits{/sha}","git_commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits{/sha}","comments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/comments{/number}","issue_comment_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/comments{/number}","contents_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/{+path}","compare_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/merges","archive_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/downloads","issues_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues{/number}","pulls_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/pulls{/number}","milestones_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/milestones{/number}","notifications_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/labels{/name}","releases_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/releases{/id}","deployments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/deployments","created_at":"2020-05-27T10:32:22Z","updated_at":"2020-05-27T11:17:06Z","pushed_at":"2020-05-27T11:23:26Z","git_url":"git://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","ssh_url":"git@github.com:dependabot-fixtures/dependabot-test-ruby-package.git","clone_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","svn_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","homepage":null,"size":1,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master","temp_clone_token":null,"organization":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars0.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"network_count":0,"subscribers_count":0}'
+ recorded_at: Thu, 20 Aug 2020 15:19:20 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.18.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 20 Aug 2020 15:19:20 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"65d96789fd44ca17a64d185184c47a55"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Poll-Interval:
+ - '300'
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
+ X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '41'
+ X-Ratelimit-Reset:
+ - '1597938283'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '237'
+ X-Github-Request-Id:
+ - FEDA:29AB:71BB21:FD5860:5F3E9478
+ body:
+ encoding: ASCII-8BIT
+ string: '{"ref":"refs/heads/master","node_id":"MDM6UmVmMjY3MjkwMDk5OnJlZnMvaGVhZHMvbWFzdGVy","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master","object":{"sha":"1c6331732c41e4557a16dacb82534f1d1c831848","type":"commit","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits/1c6331732c41e4557a16dacb82534f1d1c831848"}}'
+ recorded_at: Thu, 20 Aug 2020 15:19:20 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.18.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 20 Aug 2020 15:19:21 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"7b44807c792639c3b05064b3493694c6b4cce370"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
+ X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '40'
+ X-Ratelimit-Reset:
+ - '1597938284'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '496'
+ X-Github-Request-Id:
+ - FEDB:549C:3517BB:8B1654:5F3E9479
+ body:
+ encoding: ASCII-8BIT
+ string: '[{"name":".gitignore","path":".gitignore","sha":"c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","size":6,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore"}},{"name":"README.md","path":"README.md","sha":"f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","size":85,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md"}},{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}]'
+ recorded_at: Thu, 20 Aug 2020 15:19:21 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.18.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Thu, 20 Aug 2020 15:19:21 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Status:
+ - 200 OK
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"c5fd208850ed1bf1334b4b9cd4910950bed0c497"
+ Last-Modified:
+ - Wed, 27 May 2020 10:39:57 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
+ X-GitHub-Media-Type, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '39'
+ X-Ratelimit-Reset:
+ - '1597938283'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '703'
+ X-Github-Request-Id:
+ - FEDC:455E:E95A4A:1861D80:5F3E9479
+ body:
+ encoding: ASCII-8BIT
+ string: '{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","content":"IyBmcm96ZW5fc3RyaW5nX2xpdGVyYWw6IHRydWUKCkdlbTo6U3BlY2lmaWNh\ndGlvbi5uZXcgZG8gfHNwZWN8CiAgc3BlYy5uYW1lICAgICA9ICdkZXBlbmRh\nYm90LXRlc3QtcnVieS1wYWNrYWdlJwogIHNwZWMudmVyc2lvbiAgPSAnMS4w\nLjEnCiAgc3BlYy5zdW1tYXJ5ICA9ICdBIGR1bW15IHBhY2thZ2UgZm9yIHRl\nc3RpbmcgRGVwZW5kYWJvdCcKICBzcGVjLmF1dGhvciAgID0gJ0RlcGVuZGFi\nb3QnCiAgc3BlYy5saWNlbnNlICA9ICdNSVQnCiAgc3BlYy5lbWFpbCAgICA9\nICdub3JlcGx5QGdpdGh1Yi5jb20nCiAgc3BlYy5ob21lcGFnZSA9ICdodHRw\nOi8vZ2l0aHViLmNvbS9kZXBlbmRhYm90LWZpeHR1cmVzL2RlcGVuZGFib3Qt\ndGVzdC1ydWJ5LXBhY2thZ2UnCmVuZAo=\n","encoding":"base64","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}'
+ recorded_at: Thu, 20 Aug 2020 15:19:21 GMT
+recorded_with: VCR 6.0.0
diff --git a/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/logs_connectivity_is_successful_and_does_not_raise_an_error.yml b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/logs_connectivity_is_successful_and_does_not_raise_an_error.yml
new file mode 100644
index 00000000..f6ffdd02
--- /dev/null
+++ b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/logs_connectivity_is_successful_and_does_not_raise_an_error.yml
@@ -0,0 +1,382 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:30:05 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"d041f3a6129802958b654fb48b93bc9dc7ccb7d70f223019425d4c0d579491c6"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '43'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '17'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '1298'
+ X-Github-Request-Id:
+ - 0400:5036:3954D:41410:6230E93D
+ body:
+ encoding: ASCII-8BIT
+ string: '{"id":267290099,"node_id":"MDEwOlJlcG9zaXRvcnkyNjcyOTAwOTk=","name":"dependabot-test-ruby-package","full_name":"dependabot-fixtures/dependabot-test-ruby-package","private":false,"owner":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","description":"A
+ ruby gem for testing dependabot","fork":false,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package","forks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/forks","keys_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/teams","hooks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/hooks","issue_events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/events{/number}","events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/events","assignees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/assignees{/user}","branches_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/branches{/branch}","tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/tags","blobs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs{/sha}","trees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/statuses/{sha}","languages_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/languages","stargazers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/stargazers","contributors_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contributors","subscribers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscribers","subscription_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscription","commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/commits{/sha}","git_commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits{/sha}","comments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/comments{/number}","issue_comment_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/comments{/number}","contents_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/{+path}","compare_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/merges","archive_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/downloads","issues_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues{/number}","pulls_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/pulls{/number}","milestones_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/milestones{/number}","notifications_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/labels{/name}","releases_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/releases{/id}","deployments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/deployments","created_at":"2020-05-27T10:32:22Z","updated_at":"2020-05-27T11:17:06Z","pushed_at":"2020-05-27T11:23:26Z","git_url":"git://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","ssh_url":"git@github.com:dependabot-fixtures/dependabot-test-ruby-package.git","clone_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","svn_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","homepage":null,"size":1,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":null,"allow_forking":true,"is_template":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master","temp_clone_token":null,"organization":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"network_count":0,"subscribers_count":1}'
+ recorded_at: Tue, 15 Mar 2022 19:30:05 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:30:05 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"d041f3a6129802958b654fb48b93bc9dc7ccb7d70f223019425d4c0d579491c6"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '42'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '18'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '1298'
+ X-Github-Request-Id:
+ - 0401:2EA6:1406B3:14B558:6230E93D
+ body:
+ encoding: ASCII-8BIT
+ string: '{"id":267290099,"node_id":"MDEwOlJlcG9zaXRvcnkyNjcyOTAwOTk=","name":"dependabot-test-ruby-package","full_name":"dependabot-fixtures/dependabot-test-ruby-package","private":false,"owner":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","description":"A
+ ruby gem for testing dependabot","fork":false,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package","forks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/forks","keys_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/teams","hooks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/hooks","issue_events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/events{/number}","events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/events","assignees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/assignees{/user}","branches_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/branches{/branch}","tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/tags","blobs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs{/sha}","trees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/statuses/{sha}","languages_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/languages","stargazers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/stargazers","contributors_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contributors","subscribers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscribers","subscription_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscription","commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/commits{/sha}","git_commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits{/sha}","comments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/comments{/number}","issue_comment_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/comments{/number}","contents_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/{+path}","compare_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/merges","archive_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/downloads","issues_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues{/number}","pulls_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/pulls{/number}","milestones_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/milestones{/number}","notifications_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/labels{/name}","releases_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/releases{/id}","deployments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/deployments","created_at":"2020-05-27T10:32:22Z","updated_at":"2020-05-27T11:17:06Z","pushed_at":"2020-05-27T11:23:26Z","git_url":"git://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","ssh_url":"git@github.com:dependabot-fixtures/dependabot-test-ruby-package.git","clone_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","svn_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","homepage":null,"size":1,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":null,"allow_forking":true,"is_template":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master","temp_clone_token":null,"organization":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"network_count":0,"subscribers_count":1}'
+ recorded_at: Tue, 15 Mar 2022 19:30:05 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:30:05 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"4ec4b41160548cc0f86c2459b25f9dd74f27df28fc425f9786cdb4ab70be7e01"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Poll-Interval:
+ - '300'
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '41'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '19'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '237'
+ X-Github-Request-Id:
+ - 0402:19A3:158342:163172:6230E93D
+ body:
+ encoding: ASCII-8BIT
+ string: '{"ref":"refs/heads/master","node_id":"MDM6UmVmMjY3MjkwMDk5OnJlZnMvaGVhZHMvbWFzdGVy","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master","object":{"sha":"1c6331732c41e4557a16dacb82534f1d1c831848","type":"commit","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits/1c6331732c41e4557a16dacb82534f1d1c831848"}}'
+ recorded_at: Tue, 15 Mar 2022 19:30:05 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:30:05 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"7b44807c792639c3b05064b3493694c6b4cce370"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '40'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '20'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '496'
+ X-Github-Request-Id:
+ - 0403:7088:17566:1EFBA:6230E93D
+ body:
+ encoding: ASCII-8BIT
+ string: '[{"name":".gitignore","path":".gitignore","sha":"c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","size":6,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore"}},{"name":"README.md","path":"README.md","sha":"f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","size":85,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md"}},{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}]'
+ recorded_at: Tue, 15 Mar 2022 19:30:05 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:30:05 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"c5fd208850ed1bf1334b4b9cd4910950bed0c497"
+ Last-Modified:
+ - Wed, 27 May 2020 10:39:57 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '39'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '21'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '703'
+ X-Github-Request-Id:
+ - 0404:19A2:D5755:DF679:6230E93D
+ body:
+ encoding: ASCII-8BIT
+ string: '{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","content":"IyBmcm96ZW5fc3RyaW5nX2xpdGVyYWw6IHRydWUKCkdlbTo6U3BlY2lmaWNh\ndGlvbi5uZXcgZG8gfHNwZWN8CiAgc3BlYy5uYW1lICAgICA9ICdkZXBlbmRh\nYm90LXRlc3QtcnVieS1wYWNrYWdlJwogIHNwZWMudmVyc2lvbiAgPSAnMS4w\nLjEnCiAgc3BlYy5zdW1tYXJ5ICA9ICdBIGR1bW15IHBhY2thZ2UgZm9yIHRl\nc3RpbmcgRGVwZW5kYWJvdCcKICBzcGVjLmF1dGhvciAgID0gJ0RlcGVuZGFi\nb3QnCiAgc3BlYy5saWNlbnNlICA9ICdNSVQnCiAgc3BlYy5lbWFpbCAgICA9\nICdub3JlcGx5QGdpdGh1Yi5jb20nCiAgc3BlYy5ob21lcGFnZSA9ICdodHRw\nOi8vZ2l0aHViLmNvbS9kZXBlbmRhYm90LWZpeHR1cmVzL2RlcGVuZGFib3Qt\ndGVzdC1ydWJ5LXBhY2thZ2UnCmVuZAo=\n","encoding":"base64","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}'
+ recorded_at: Tue, 15 Mar 2022 19:30:05 GMT
+recorded_with: VCR 6.0.0
diff --git a/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/when_connectivity_is_broken/logs_connectivity_failed_and_does_not_raise_an_error.yml b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/when_connectivity_is_broken/logs_connectivity_failed_and_does_not_raise_an_error.yml
new file mode 100644
index 00000000..98e5e2a8
--- /dev/null
+++ b/updater/spec/fixtures/vcr_cassettes/Dependabot_FileFetcherCommand/_perform_job/when_the_connectivity_check_is_enabled/when_connectivity_is_broken/logs_connectivity_failed_and_does_not_raise_an_error.yml
@@ -0,0 +1,306 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:23:50 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"d041f3a6129802958b654fb48b93bc9dc7ccb7d70f223019425d4c0d579491c6"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '47'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '13'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '1298'
+ X-Github-Request-Id:
+ - 0401:2EA6:132466:13CA55:6230E7C7
+ body:
+ encoding: ASCII-8BIT
+ string: '{"id":267290099,"node_id":"MDEwOlJlcG9zaXRvcnkyNjcyOTAwOTk=","name":"dependabot-test-ruby-package","full_name":"dependabot-fixtures/dependabot-test-ruby-package","private":false,"owner":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","description":"A
+ ruby gem for testing dependabot","fork":false,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package","forks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/forks","keys_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/teams","hooks_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/hooks","issue_events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/events{/number}","events_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/events","assignees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/assignees{/user}","branches_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/branches{/branch}","tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/tags","blobs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs{/sha}","trees_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/statuses/{sha}","languages_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/languages","stargazers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/stargazers","contributors_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contributors","subscribers_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscribers","subscription_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/subscription","commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/commits{/sha}","git_commits_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits{/sha}","comments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/comments{/number}","issue_comment_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues/comments{/number}","contents_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/{+path}","compare_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/merges","archive_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/downloads","issues_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/issues{/number}","pulls_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/pulls{/number}","milestones_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/milestones{/number}","notifications_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/labels{/name}","releases_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/releases{/id}","deployments_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/deployments","created_at":"2020-05-27T10:32:22Z","updated_at":"2020-05-27T11:17:06Z","pushed_at":"2020-05-27T11:23:26Z","git_url":"git://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","ssh_url":"git@github.com:dependabot-fixtures/dependabot-test-ruby-package.git","clone_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package.git","svn_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package","homepage":null,"size":1,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":null,"allow_forking":true,"is_template":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master","temp_clone_token":null,"organization":{"login":"dependabot-fixtures","id":44116593,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ0MTE2NTkz","avatar_url":"https://avatars.githubusercontent.com/u/44116593?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot-fixtures","html_url":"https://github.com/dependabot-fixtures","followers_url":"https://api.github.com/users/dependabot-fixtures/followers","following_url":"https://api.github.com/users/dependabot-fixtures/following{/other_user}","gists_url":"https://api.github.com/users/dependabot-fixtures/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot-fixtures/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot-fixtures/subscriptions","organizations_url":"https://api.github.com/users/dependabot-fixtures/orgs","repos_url":"https://api.github.com/users/dependabot-fixtures/repos","events_url":"https://api.github.com/users/dependabot-fixtures/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot-fixtures/received_events","type":"Organization","site_admin":false},"network_count":0,"subscribers_count":1}'
+ recorded_at: Tue, 15 Mar 2022 19:23:51 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:23:50 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"4ec4b41160548cc0f86c2459b25f9dd74f27df28fc425f9786cdb4ab70be7e01"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Poll-Interval:
+ - '300'
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '46'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '14'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '237'
+ X-Github-Request-Id:
+ - 0403:7084:1D95:8E8F:6230E7C7
+ body:
+ encoding: ASCII-8BIT
+ string: '{"ref":"refs/heads/master","node_id":"MDM6UmVmMjY3MjkwMDk5OnJlZnMvaGVhZHMvbWFzdGVy","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/refs/heads/master","object":{"sha":"1c6331732c41e4557a16dacb82534f1d1c831848","type":"commit","url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/commits/1c6331732c41e4557a16dacb82534f1d1c831848"}}'
+ recorded_at: Tue, 15 Mar 2022 19:23:51 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:23:50 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"7b44807c792639c3b05064b3493694c6b4cce370"
+ Last-Modified:
+ - Wed, 27 May 2020 11:17:06 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '45'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '15'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '496'
+ X-Github-Request-Id:
+ - 0402:199D:2FB1E:37377:6230E7C7
+ body:
+ encoding: ASCII-8BIT
+ string: '[{"name":".gitignore","path":".gitignore","sha":"c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","size":6,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/.gitignore?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c111b331371ae211d3bc2e3a9e34ad2a7d6b3982","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/.gitignore"}},{"name":"README.md","path":"README.md","sha":"f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","size":85,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/README.md","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/README.md?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/f71de17053d17a9a5d135b3bfff3f6aa4409dc4b","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/README.md"}},{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}]'
+ recorded_at: Tue, 15 Mar 2022 19:23:51 GMT
+- request:
+ method: get
+ uri: https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - Octokit Ruby Gem 4.22.0
+ Accept:
+ - application/vnd.github.v3+json
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub.com
+ Date:
+ - Tue, 15 Mar 2022 19:23:51 GMT
+ Content-Type:
+ - application/json; charset=utf-8
+ Cache-Control:
+ - public, max-age=60, s-maxage=60
+ Vary:
+ - Accept, Accept-Encoding, Accept, X-Requested-With
+ Etag:
+ - W/"c5fd208850ed1bf1334b4b9cd4910950bed0c497"
+ Last-Modified:
+ - Wed, 27 May 2020 10:39:57 GMT
+ X-Github-Media-Type:
+ - github.v3; format=json
+ Access-Control-Expose-Headers:
+ - ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
+ X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes,
+ X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO,
+ X-GitHub-Request-Id, Deprecation, Sunset
+ Access-Control-Allow-Origin:
+ - "*"
+ Strict-Transport-Security:
+ - max-age=31536000; includeSubdomains; preload
+ X-Frame-Options:
+ - deny
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - '0'
+ Referrer-Policy:
+ - origin-when-cross-origin, strict-origin-when-cross-origin
+ Content-Security-Policy:
+ - default-src 'none'
+ X-Ratelimit-Limit:
+ - '60'
+ X-Ratelimit-Remaining:
+ - '44'
+ X-Ratelimit-Reset:
+ - '1647374033'
+ X-Ratelimit-Resource:
+ - core
+ X-Ratelimit-Used:
+ - '16'
+ Accept-Ranges:
+ - bytes
+ Content-Length:
+ - '703'
+ X-Github-Request-Id:
+ - 0405:19A2:CC582:D5CB7:6230E7C7
+ body:
+ encoding: ASCII-8BIT
+ string: '{"name":"dependabot-test-ruby-package.gemspec","path":"dependabot-test-ruby-package.gemspec","sha":"c5fd208850ed1bf1334b4b9cd4910950bed0c497","size":383,"url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","html_url":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","git_url":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","download_url":"https://raw.githubusercontent.com/dependabot-fixtures/dependabot-test-ruby-package/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec","type":"file","content":"IyBmcm96ZW5fc3RyaW5nX2xpdGVyYWw6IHRydWUKCkdlbTo6U3BlY2lmaWNh\ndGlvbi5uZXcgZG8gfHNwZWN8CiAgc3BlYy5uYW1lICAgICA9ICdkZXBlbmRh\nYm90LXRlc3QtcnVieS1wYWNrYWdlJwogIHNwZWMudmVyc2lvbiAgPSAnMS4w\nLjEnCiAgc3BlYy5zdW1tYXJ5ICA9ICdBIGR1bW15IHBhY2thZ2UgZm9yIHRl\nc3RpbmcgRGVwZW5kYWJvdCcKICBzcGVjLmF1dGhvciAgID0gJ0RlcGVuZGFi\nb3QnCiAgc3BlYy5saWNlbnNlICA9ICdNSVQnCiAgc3BlYy5lbWFpbCAgICA9\nICdub3JlcGx5QGdpdGh1Yi5jb20nCiAgc3BlYy5ob21lcGFnZSA9ICdodHRw\nOi8vZ2l0aHViLmNvbS9kZXBlbmRhYm90LWZpeHR1cmVzL2RlcGVuZGFib3Qt\ndGVzdC1ydWJ5LXBhY2thZ2UnCmVuZAo=\n","encoding":"base64","_links":{"self":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/contents/dependabot-test-ruby-package.gemspec?ref=1c6331732c41e4557a16dacb82534f1d1c831848","git":"https://api.github.com/repos/dependabot-fixtures/dependabot-test-ruby-package/git/blobs/c5fd208850ed1bf1334b4b9cd4910950bed0c497","html":"https://github.com/dependabot-fixtures/dependabot-test-ruby-package/blob/1c6331732c41e4557a16dacb82534f1d1c831848/dependabot-test-ruby-package.gemspec"}}'
+ recorded_at: Tue, 15 Mar 2022 19:23:51 GMT
+recorded_with: VCR 6.0.0
diff --git a/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_contains_a_git_dependency/creates_individual_PRs_since_git_dependencies_cannot_be_grouped_as_semver.yml b/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_contains_a_git_dependency/creates_individual_PRs_since_git_dependencies_cannot_be_grouped_as_semver.yml
new file mode 100644
index 00000000..97056cfe
--- /dev/null
+++ b/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_contains_a_git_dependency/creates_individual_PRs_since_git_dependencies_cannot_be_grouped_as_semver.yml
@@ -0,0 +1,505 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/versions/dummy-git-dependency.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '384'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Last-Modified:
+ - Mon, 14 Dec 2020 17:08:06 GMT
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A14ac2664a203aa4e56def33f9dc1ac9d9bf71c88%2Cenv%3Aproduction%2Ctrace_id%3A93572498929242322
+ X-Request-Id:
+ - '05101318-b593-4f70-a9ec-752da6231f5b'
+ X-Runtime:
+ - '0.017212'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 35.81.98.222:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Tue, 15 Aug 2023 12:07:29 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '0'
+ X-Served-By:
+ - cache-ams21038-AMS
+ X-Cache:
+ - MISS
+ X-Cache-Hits:
+ - '0'
+ X-Timer:
+ - S1692101249.628574,VS0,VE437
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"f59670cf8622242bfb55afc03a027238"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '[{"authors":"Dependabot","built_at":"2019-11-22T00:00:00.000Z","created_at":"2019-11-22T15:24:24.964Z","description":null,"downloads_count":2090,"metadata":{},"number":"1.1.0","summary":"A
+ dummy package for testing Dependabot","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3c1b71569ef54c120b0c1d47704409bf9c63de0d581ad6bbb9521634063d7e2d"},{"authors":"Dependabot","built_at":"2019-11-22T00:00:00.000Z","created_at":"2019-11-22T15:24:41.638Z","description":null,"downloads_count":1517,"metadata":{},"number":"1.0.0","summary":"A
+ dummy package for testing Dependabot","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c020db324e8d21ee9f0e16ffcdc3c025b6da796c4d62e24e952725b76e2b0088"}]'
+ recorded_at: Tue, 15 Aug 2023 12:07:29 GMT
+- request:
+ method: get
+ uri: https://github.com/dependabot-fixtures/ruby-dummy-git-dependency.git/info/refs?service=git-upload-pack
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub-Babel/3.0
+ Content-Type:
+ - application/x-git-upload-pack-advertisement
+ Content-Security-Policy:
+ - default-src 'none'; sandbox
+ Expires:
+ - Fri, 01 Jan 1980 00:00:00 GMT
+ Pragma:
+ - no-cache
+ Cache-Control:
+ - no-cache, max-age=0, must-revalidate
+ Vary:
+ - Accept-Encoding
+ Date:
+ - Tue, 15 Aug 2023 12:07:28 GMT
+ X-Frame-Options:
+ - DENY
+ X-Github-Request-Id:
+ - D311:B3FC:4DE913E:4F14650:64DB6A81
+ body:
+ encoding: ASCII-8BIT
+ string: "001e# service=git-upload-pack\n00000155f229d172c826b95f464a82d8ddf8b3efce3825b0
+ HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since
+ deepen-not deepen-relative no-progress include-tag multi_ack_detailed allow-tip-sha1-in-want
+ allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/master filter
+ object-format=sha1 agent=git/github-32046c9f808f\n003ff229d172c826b95f464a82d8ddf8b3efce3825b0
+ refs/heads/master\n003e20151f9b67c8a04461fa0ee28385b6187b86587b refs/tags/v1.0.0\n003ec0e25c2eb332122873f73acb3b61fb2e261cfd8f
+ refs/tags/v1.1.0\n0000"
+ recorded_at: Tue, 15 Aug 2023 12:07:29 GMT
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/gems/dummy-git-dependency.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '445'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Access-Control-Allow-Origin:
+ - "*"
+ Access-Control-Allow-Methods:
+ - GET
+ Access-Control-Max-Age:
+ - '1728000'
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A14ac2664a203aa4e56def33f9dc1ac9d9bf71c88%2Cenv%3Aproduction%2Ctrace_id%3A2302961586161028526
+ X-Request-Id:
+ - 448e293b-7426-45ea-a71b-7de7631116b1
+ X-Runtime:
+ - '0.023034'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 52.24.74.243:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Tue, 15 Aug 2023 12:07:30 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '0'
+ X-Served-By:
+ - cache-ams21059-AMS
+ X-Cache:
+ - MISS
+ X-Cache-Hits:
+ - '0'
+ X-Timer:
+ - S1692101249.438226,VS0,VE621
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"b2b2df644984fa222ec351016a6a0037"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '{"name":"dummy-git-dependency","downloads":3607,"version":"1.1.0","version_created_at":"2019-11-22T15:24:24.964Z","version_downloads":2090,"platform":"ruby","authors":"Dependabot","info":"A
+ dummy package for testing Dependabot","licenses":["MIT"],"metadata":{},"yanked":false,"sha":"3c1b71569ef54c120b0c1d47704409bf9c63de0d581ad6bbb9521634063d7e2d","project_uri":"https://rubygems.org/gems/dummy-git-dependency","gem_uri":"https://rubygems.org/gems/dummy-git-dependency-1.1.0.gem","homepage_uri":"http://github.com/dependabot/dummy-git-dependency","wiki_uri":null,"documentation_uri":"https://www.rubydoc.info/gems/dummy-git-dependency/1.1.0","mailing_list_uri":null,"source_code_uri":null,"bug_tracker_uri":null,"changelog_uri":null,"funding_uri":null,"dependencies":{"development":[],"runtime":[]}}'
+ recorded_at: Tue, 15 Aug 2023 12:07:29 GMT
+- request:
+ method: get
+ uri: https://github.com/dependabot/dummy-git-dependency.git/info/refs?service=git-upload-pack
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 401
+ message: Unauthorized
+ headers:
+ Server:
+ - GitHub-Babel/3.0
+ Content-Security-Policy:
+ - default-src 'none'; sandbox
+ Content-Type:
+ - text/plain; charset=UTF-8
+ Www-Authenticate:
+ - Basic realm="GitHub"
+ Content-Length:
+ - '21'
+ Date:
+ - Tue, 15 Aug 2023 12:07:30 GMT
+ X-Frame-Options:
+ - DENY
+ X-Github-Request-Id:
+ - D313:CB32:4F223FA:504D973:64DB6A82
+ body:
+ encoding: ASCII-8BIT
+ string: Repository not found.
+ recorded_at: Tue, 15 Aug 2023 12:07:30 GMT
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/versions/dummy-git-dependency.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '384'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Last-Modified:
+ - Mon, 14 Dec 2020 17:08:06 GMT
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A14ac2664a203aa4e56def33f9dc1ac9d9bf71c88%2Cenv%3Aproduction%2Ctrace_id%3A93572498929242322
+ X-Request-Id:
+ - '05101318-b593-4f70-a9ec-752da6231f5b'
+ X-Runtime:
+ - '0.017212'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 35.81.98.222:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Tue, 15 Aug 2023 12:07:30 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '2'
+ X-Served-By:
+ - cache-ams21065-AMS
+ X-Cache:
+ - HIT
+ X-Cache-Hits:
+ - '1'
+ X-Timer:
+ - S1692101251.759070,VS0,VE1
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"f59670cf8622242bfb55afc03a027238"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '[{"authors":"Dependabot","built_at":"2019-11-22T00:00:00.000Z","created_at":"2019-11-22T15:24:24.964Z","description":null,"downloads_count":2090,"metadata":{},"number":"1.1.0","summary":"A
+ dummy package for testing Dependabot","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3c1b71569ef54c120b0c1d47704409bf9c63de0d581ad6bbb9521634063d7e2d"},{"authors":"Dependabot","built_at":"2019-11-22T00:00:00.000Z","created_at":"2019-11-22T15:24:41.638Z","description":null,"downloads_count":1517,"metadata":{},"number":"1.0.0","summary":"A
+ dummy package for testing Dependabot","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c020db324e8d21ee9f0e16ffcdc3c025b6da796c4d62e24e952725b76e2b0088"}]'
+ recorded_at: Tue, 15 Aug 2023 12:07:30 GMT
+- request:
+ method: get
+ uri: https://github.com/dependabot-fixtures/ruby-dummy-git-dependency.git/info/refs?service=git-upload-pack
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub-Babel/3.0
+ Content-Type:
+ - application/x-git-upload-pack-advertisement
+ Content-Security-Policy:
+ - default-src 'none'; sandbox
+ Expires:
+ - Fri, 01 Jan 1980 00:00:00 GMT
+ Pragma:
+ - no-cache
+ Cache-Control:
+ - no-cache, max-age=0, must-revalidate
+ Vary:
+ - Accept-Encoding
+ Date:
+ - Tue, 15 Aug 2023 12:07:30 GMT
+ X-Frame-Options:
+ - DENY
+ X-Github-Request-Id:
+ - D316:E95B:4F937F3:50BEC2D:64DB6A82
+ body:
+ encoding: ASCII-8BIT
+ string: "001e# service=git-upload-pack\n00000155f229d172c826b95f464a82d8ddf8b3efce3825b0
+ HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since
+ deepen-not deepen-relative no-progress include-tag multi_ack_detailed allow-tip-sha1-in-want
+ allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/master filter
+ object-format=sha1 agent=git/github-32046c9f808f\n003ff229d172c826b95f464a82d8ddf8b3efce3825b0
+ refs/heads/master\n003e20151f9b67c8a04461fa0ee28385b6187b86587b refs/tags/v1.0.0\n003ec0e25c2eb332122873f73acb3b61fb2e261cfd8f
+ refs/tags/v1.1.0\n0000"
+ recorded_at: Tue, 15 Aug 2023 12:07:30 GMT
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/gems/dummy-git-dependency.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '445'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Access-Control-Allow-Origin:
+ - "*"
+ Access-Control-Allow-Methods:
+ - GET
+ Access-Control-Max-Age:
+ - '1728000'
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A14ac2664a203aa4e56def33f9dc1ac9d9bf71c88%2Cenv%3Aproduction%2Ctrace_id%3A2302961586161028526
+ X-Request-Id:
+ - 448e293b-7426-45ea-a71b-7de7631116b1
+ X-Runtime:
+ - '0.023034'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 52.24.74.243:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Tue, 15 Aug 2023 12:07:31 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '1'
+ X-Served-By:
+ - cache-ams21066-AMS
+ X-Cache:
+ - HIT
+ X-Cache-Hits:
+ - '1'
+ X-Timer:
+ - S1692101251.086141,VS0,VE2
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"b2b2df644984fa222ec351016a6a0037"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '{"name":"dummy-git-dependency","downloads":3607,"version":"1.1.0","version_created_at":"2019-11-22T15:24:24.964Z","version_downloads":2090,"platform":"ruby","authors":"Dependabot","info":"A
+ dummy package for testing Dependabot","licenses":["MIT"],"metadata":{},"yanked":false,"sha":"3c1b71569ef54c120b0c1d47704409bf9c63de0d581ad6bbb9521634063d7e2d","project_uri":"https://rubygems.org/gems/dummy-git-dependency","gem_uri":"https://rubygems.org/gems/dummy-git-dependency-1.1.0.gem","homepage_uri":"http://github.com/dependabot/dummy-git-dependency","wiki_uri":null,"documentation_uri":"https://www.rubydoc.info/gems/dummy-git-dependency/1.1.0","mailing_list_uri":null,"source_code_uri":null,"bug_tracker_uri":null,"changelog_uri":null,"funding_uri":null,"dependencies":{"development":[],"runtime":[]}}'
+ recorded_at: Tue, 15 Aug 2023 12:07:30 GMT
+- request:
+ method: get
+ uri: https://github.com/dependabot/dummy-git-dependency.git/info/refs?service=git-upload-pack
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 401
+ message: Unauthorized
+ headers:
+ Server:
+ - GitHub-Babel/3.0
+ Content-Security-Policy:
+ - default-src 'none'; sandbox
+ Content-Type:
+ - text/plain; charset=UTF-8
+ Www-Authenticate:
+ - Basic realm="GitHub"
+ Content-Length:
+ - '21'
+ Date:
+ - Tue, 15 Aug 2023 12:07:30 GMT
+ X-Frame-Options:
+ - DENY
+ X-Github-Request-Id:
+ - D318:E95B:4F939CA:50BEE1A:64DB6A83
+ body:
+ encoding: ASCII-8BIT
+ string: Repository not found.
+ recorded_at: Tue, 15 Aug 2023 12:07:31 GMT
+recorded_with: VCR 6.2.0
diff --git a/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_a_gemspec/creates_a_DependencyChange_for_just_the_modified_files_without_reporting_errors.yml b/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_a_gemspec/creates_a_DependencyChange_for_just_the_modified_files_without_reporting_errors.yml
new file mode 100644
index 00000000..11035e06
--- /dev/null
+++ b/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_a_gemspec/creates_a_DependencyChange_for_just_the_modified_files_without_reporting_errors.yml
@@ -0,0 +1,2557 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/versions/rubocop.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (aarch64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '18348'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Last-Modified:
+ - Wed, 09 Aug 2023 06:34:52 GMT
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A34907a6b36a81c276c9cc8a53a7534170f401308%2Cenv%3Aproduction%2Ctrace_id%3A432763313615770357
+ X-Request-Id:
+ - d8045972-4d46-423e-b53e-ebbe5fd6ebb2
+ X-Runtime:
+ - '0.102663'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 35.81.98.222:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Wed, 09 Aug 2023 17:40:27 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '697'
+ X-Served-By:
+ - cache-stl760081-STL
+ X-Cache:
+ - HIT
+ X-Cache-Hits:
+ - '2'
+ X-Timer:
+ - S1691602827.217070,VS0,VE0
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"8b940b33fc8a9445cdbb1a3d81206175"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '[{"authors":"Bozhidar Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-08-09T00:00:00.000Z","created_at":"2023-08-09T06:34:52.156Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":20407,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.56/","rubygems_mfa_required":"true"},"number":"1.56.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"96152bc00f2bd09df20a48133cb2b5c34267414d665f424d7cc127470c1fe2c5"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-07-31T00:00:00.000Z","created_at":"2023-07-31T05:20:13.164Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":378694,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.55/","rubygems_mfa_required":"true"},"number":"1.55.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"35e7ab338bcfd883122a3cb828b33aaa32c6759ab423ed872e10198c152e3769"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-07-25T00:00:00.000Z","created_at":"2023-07-25T15:27:10.822Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":237388,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.55/","rubygems_mfa_required":"true"},"number":"1.55.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"71defdb44c840b580db541900e02194d87ab7e6f3519221d711f2f252827899d"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-07-13T00:00:00.000Z","created_at":"2023-07-13T11:02:41.121Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":648288,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.54/","rubygems_mfa_required":"true"},"number":"1.54.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f9884d2335026b22c6341355da508cecd3762ea4180e2cf65edcdd07553284c1"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-07-04T00:00:00.000Z","created_at":"2023-07-04T07:34:06.364Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":882738,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.54/","rubygems_mfa_required":"true"},"number":"1.54.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e4bc497a45928beb95cf5d2688a4bfe8258b4b778bf41921d6118402da5274ee"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-07-01T00:00:00.000Z","created_at":"2023-07-01T08:14:24.868Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":180029,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.54/","rubygems_mfa_required":"true"},"number":"1.54.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"1fe1fd463dfe1cae2c57b9ea60c29c780bbdc7ff8b36173a9197cd17e292da0b"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-06-26T00:00:00.000Z","created_at":"2023-06-26T10:56:26.570Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":408828,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.53/","rubygems_mfa_required":"true"},"number":"1.53.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"cf1844ecc1e610dc72116dbfeb39ca1d74c609913203c91f539db313e625bed4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-06-23T00:00:00.000Z","created_at":"2023-06-23T09:44:11.779Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":113748,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.53/","rubygems_mfa_required":"true"},"number":"1.53.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"01e56decdd30c12163c3ec5784ee6f579e61c939c04edb64d2f74c131272f72c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-06-12T00:00:00.000Z","created_at":"2023-06-12T08:02:12.517Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1150337,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.52/","rubygems_mfa_required":"true"},"number":"1.52.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"908718035c4771f414280412be0d9168a7abd310da1ab859a50d41bece0dac2f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-06-02T00:00:00.000Z","created_at":"2023-06-02T09:27:27.204Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":627975,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.52/","rubygems_mfa_required":"true"},"number":"1.52.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a9860af191f6d51696de9ece6ca8c072643ee6c04af4310242b13e642b11ef91"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-05-13T00:00:00.000Z","created_at":"2023-05-13T07:54:17.418Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1477707,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.51/","rubygems_mfa_required":"true"},"number":"1.51.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.7.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"beba4c57242d3dfd9561d67f6588e2568a818445d51d172dda836223bfad2300"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-04-17T00:00:00.000Z","created_at":"2023-04-17T08:12:58.522Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":3579674,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.50/","rubygems_mfa_required":"true"},"number":"1.50.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7cfeb0616f686ac61d049beae89f31446792d7e9f5728152657548f70aa78650"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-04-12T00:00:00.000Z","created_at":"2023-04-12T12:52:35.268Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":327587,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.50/","rubygems_mfa_required":"true"},"number":"1.50.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"acfcbf5a410f7afe00d42fa696e752646057731396411d5066078ec26b909242"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-04-11T00:00:00.000Z","created_at":"2023-04-11T07:14:22.682Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":205132,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.50/","rubygems_mfa_required":"true"},"number":"1.50.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ad8ce5c13982a66c4e9c0d54040e96d210f9f88405e903b0e31081bd53e11dbd"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-04-03T00:00:00.000Z","created_at":"2023-04-03T07:00:18.540Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":668777,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.49/","rubygems_mfa_required":"true"},"number":"1.49.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d4c09409555ae24bca5b80cce20954544b057c1e7ec89c361f676aaba4b705b6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-03-13T00:00:00.000Z","created_at":"2023-03-13T06:59:23.990Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2476799,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.48/","rubygems_mfa_required":"true"},"number":"1.48.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"18cf7216ba8febb2f8a2a5978f36c54cfdf0de4427cb190a20fd0ee38ccdbee8"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-03-06T00:00:00.000Z","created_at":"2023-03-06T09:50:13.745Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":762065,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.48/","rubygems_mfa_required":"true"},"number":"1.48.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2a90d242c2155c6d72cfaaf86d68bbbe58a6816cc8b192ac8c6702466c40c231"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-03-01T00:00:00.000Z","created_at":"2023-03-01T11:21:14.919Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":360010,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.47/","rubygems_mfa_required":"true"},"number":"1.47.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"90c159d8584eca251e90c45112301c4519dea61ecdda11a1ff6e65e4d1f49241"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-02-22T00:00:00.000Z","created_at":"2023-02-22T18:46:08.467Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":706391,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.46/","rubygems_mfa_required":"true"},"number":"1.46.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ad46816d898ceec42babb5564f73d48d95cda7c3950cb4dba61b8252f0404c98"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-02-08T00:00:00.000Z","created_at":"2023-02-08T17:34:23.239Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1326475,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.45/","rubygems_mfa_required":"true"},"number":"1.45.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f5863c6aa3954e62d583f13a51d100bc45040454adca92b5e85ab0e247f251cb"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-02-08T00:00:00.000Z","created_at":"2023-02-08T12:27:53.350Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":38182,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.45/","rubygems_mfa_required":"true"},"number":"1.45.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"283f2aaa2bc2a2e9a14f290ac735c284473c47ec0451d539bf55b0cc7f444d5f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-01-25T00:00:00.000Z","created_at":"2023-01-25T12:34:55.402Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2299679,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.44/","rubygems_mfa_required":"true"},"number":"1.44.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d734ba640caea1b6de27ed49e9ef7c6206f230aa8aa5e35a5b598aec09419638"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-01-23T00:00:00.000Z","created_at":"2023-01-23T10:46:02.014Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1493506,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.44/","rubygems_mfa_required":"true"},"number":"1.44.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6152012525035a7cdd62c7a6acede84ac7a25f1880f33a3fc5cfee6bf2295228"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-01-10T00:00:00.000Z","created_at":"2023-01-10T10:32:39.737Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":3965068,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.43/","rubygems_mfa_required":"true"},"number":"1.43.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d08127ff6d99b7217b9ac27b51be872270bc7f19151706d799e18a5e4777adc6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2023-01-01T00:00:00.000Z","created_at":"2023-01-01T14:16:25.547Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1507980,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.42/","rubygems_mfa_required":"true"},"number":"1.42.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a8eaa22c3e5c2741229d65804211a9867699fc03e17d3a150fe654b986aa0b6a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-12-22T00:00:00.000Z","created_at":"2022-12-22T08:40:32.261Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":988446,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.41/","rubygems_mfa_required":"true"},"number":"1.41.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"23fdc9ed8f1f78acda597a4c6d54ace2feafdffc4871fba207ea0afbcc566d57"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-12-20T00:00:00.000Z","created_at":"2022-12-20T08:41:26.726Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":173877,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.41/","rubygems_mfa_required":"true"},"number":"1.41.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f53c9bbee7ad00e3294379e0f3e702bf4b9a8733bb95c5ff82de6bdd27c77184"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-12-08T00:00:00.000Z","created_at":"2022-12-08T07:50:52.498Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1080174,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.40/","rubygems_mfa_required":"true"},"number":"1.40.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"031881f824594fdb08713d5187c7bf07a11ff83fda869a7dd2d7765f92846a35"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-11-14T00:00:00.000Z","created_at":"2022-11-14T06:09:18.582Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2495233,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.39/","rubygems_mfa_required":"true"},"number":"1.39.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7ce41a7778f3b65d3b8ca9d39ea360bbe58551fe3b7b2ab2f5b5b7860c9efd3d"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-11-01T00:00:00.000Z","created_at":"2022-11-01T07:26:18.895Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":3168203,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.38/","rubygems_mfa_required":"true"},"number":"1.38.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"64a64a66d746bd417224c0292d08d8bf5affcfe8fbfc3d50a36810ee8c8a1eba"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-10-24T00:00:00.000Z","created_at":"2022-10-24T06:34:17.041Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1057324,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.37/","rubygems_mfa_required":"true"},"number":"1.37.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c1a5dc9b54913531ae03b6856216487734fba881d5673b77249fe8ff054215f6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-10-20T00:00:00.000Z","created_at":"2022-10-20T07:55:22.076Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":270540,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.37/","rubygems_mfa_required":"true"},"number":"1.37.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"285b6e8ac2ba7d7651122974669839cfd64b8a960d3ce29270bd1cb598be250f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-09-01T00:00:00.000Z","created_at":"2022-09-01T07:59:06.750Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":6795626,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.36/","rubygems_mfa_required":"true"},"number":"1.36.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"368e47dcab8417419949bbadb11ec41fd94e6b785f8bff4f9cc56a1ddf60ffac"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-08-22T00:00:00.000Z","created_at":"2022-08-22T05:48:01.573Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1772634,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.35/","rubygems_mfa_required":"true"},"number":"1.35.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9d5cf370e27d5e44ed4cd6eeabd5224b21faafa677e6ec0cc0836c847ae3db8d"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-08-12T00:00:00.000Z","created_at":"2022-08-12T12:50:07.105Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":738570,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.35/","rubygems_mfa_required":"true"},"number":"1.35.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7fdcffa6eff2272e0e31993743caec05d1d48e9e6de8d0f6c1a57b4e849183f1"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-08-09T00:00:00.000Z","created_at":"2022-08-09T12:00:48.363Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":394017,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.34/","rubygems_mfa_required":"true"},"number":"1.34.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"eed2747f54da1414f6a163442ad9c02d07b93c8b3d5a571e52b22b7cb4e508d8"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-08-09T00:00:00.000Z","created_at":"2022-08-09T05:25:37.049Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":44642,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.34/","rubygems_mfa_required":"true"},"number":"1.34.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c794b2713af8017c3e17941ae42c99000d4188fdfc164fb033c67f8dec1e3f0a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-08-04T00:00:00.000Z","created_at":"2022-08-04T09:25:43.239Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":668540,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.33/","rubygems_mfa_required":"true"},"number":"1.33.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7613b5d7bced82209ec8d8455f9f0910732dc623e6717a6c21aec45e6f3a389a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-07-21T00:00:00.000Z","created_at":"2022-07-21T10:33:44.211Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2250801,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.32/","rubygems_mfa_required":"true"},"number":"1.32.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"82d2a9c2995324ee0d27b75f4396d30a021e965c0e82c39162e7041a6a386326"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-07-07T00:00:00.000Z","created_at":"2022-07-07T08:04:49.754Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1603979,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.31/","rubygems_mfa_required":"true"},"number":"1.31.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"641f15809e4850d86fc9337f8b783b1accd23c16f19e347608ff35fa270dd2f2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-06-29T00:00:00.000Z","created_at":"2022-06-29T06:55:06.791Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":784026,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.31/","rubygems_mfa_required":"true"},"number":"1.31.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"bc8cbc2ca284d31785e3379bad610447e4cb43688a6db38c0c4f50c0c3afca19"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-06-27T00:00:00.000Z","created_at":"2022-06-27T06:28:07.483Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":538447,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.31/","rubygems_mfa_required":"true"},"number":"1.31.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a20b666d1702649efff1d27f95cf6fbb412a8d162441afce2def2276b7a104f4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-06-06T00:00:00.000Z","created_at":"2022-06-06T07:58:39.398Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1948133,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.30/","rubygems_mfa_required":"true"},"number":"1.30.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e1fcbed368d823ff8bbda00819f0abf0d522a914dfe62499884fcb6df0ff1d21"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-05-26T00:00:00.000Z","created_at":"2022-05-26T06:05:15.705Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1061849,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.30/","rubygems_mfa_required":"true"},"number":"1.30.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"665a7539677efb17bd644106a463047bd4c6a38963fca98fe50189de504109a2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-05-12T00:00:00.000Z","created_at":"2022-05-12T11:19:28.982Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2042877,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.29/","rubygems_mfa_required":"true"},"number":"1.29.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2880817bba3d1f7f3418a8f50f12de84580f34750aa33b4560be5ddc75ba5c4c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-05-06T00:00:00.000Z","created_at":"2022-05-06T15:51:42.728Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":582277,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.29/","rubygems_mfa_required":"true"},"number":"1.29.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.6.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"eb47f76dbc87b5b6eb449ace51a6f2819df2f1ee49734a866554ef80b8f478cc"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-04-25T00:00:00.000Z","created_at":"2022-04-25T06:44:26.428Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":4417812,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.28/","rubygems_mfa_required":"true"},"number":"1.28.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0d9bf4108fd86ede43c6cf30adcb3dd2e0c6750941c5ec2a00621478157cb377"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-04-21T00:00:00.000Z","created_at":"2022-04-21T12:36:57.304Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":342774,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.28/","rubygems_mfa_required":"true"},"number":"1.28.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d608991e7f0038b0bd3012ba5c6e59aba587dc0451a36ee3f970330c380b59c4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-04-21T00:00:00.000Z","created_at":"2022-04-21T08:07:06.255Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":51783,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.28/","rubygems_mfa_required":"true"},"number":"1.28.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"40934c4b94f39b9cd1108b4ace5e39584971bd47ab2fe8a806da08d5078f553d"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-04-08T00:00:00.000Z","created_at":"2022-04-08T06:05:25.410Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1279397,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.27/","rubygems_mfa_required":"true"},"number":"1.27.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"936a444b2915697e8f1f0e97d92e1682260d15cb6209e5279623f765e9b7a901"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-03-22T00:00:00.000Z","created_at":"2022-03-22T11:02:36.495Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2661539,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.26/","rubygems_mfa_required":"true"},"number":"1.26.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f4c91b087a10596529fb82146f214824f952e6b2e15977002df54a85b32f2018"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-03-09T00:00:00.000Z","created_at":"2022-03-09T16:40:38.227Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1512988,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.26/","rubygems_mfa_required":"true"},"number":"1.26.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9939f5ded335c505b4f34d856dbbc11138a74ed7c045a09add8837ec96d9860d"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-02-03T00:00:00.000Z","created_at":"2022-02-03T06:44:47.250Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":4394904,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.25/","rubygems_mfa_required":"true"},"number":"1.25.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"330b7674fd5242a8f10beaebe91098b7f766b3abbbd34000fda57f44a34978d0"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2022-01-18T00:00:00.000Z","created_at":"2022-01-18T07:45:28.499Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2051694,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.25/","rubygems_mfa_required":"true"},"number":"1.25.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"723149a1d1809a13e61e7352f59b98ecd0f8219b88630030b20a45dc6a712e90"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-12-31T00:00:00.000Z","created_at":"2021-12-31T10:33:10.186Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":3521938,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.24/","rubygems_mfa_required":"true"},"number":"1.24.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e01ede58cb413c08e6e5b8c6f4b92ec282e646158774b3f98595ae92c453c7ea"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-12-23T00:00:00.000Z","created_at":"2021-12-23T11:06:39.276Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":636312,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.24/","rubygems_mfa_required":"true"},"number":"1.24.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"104848c84b18417e0c0e7c96670320d028a15a918fe2327ffc86d3c1b83be2c3"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-11-15T00:00:00.000Z","created_at":"2021-11-15T08:10:45.792Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":4516948,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.23/","rubygems_mfa_required":"true"},"number":"1.23.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0b0b110eb9309a750d02f4549dfdc5399d3384ddfd6758cb3e4bd3551a5e3b0e"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-10-27T00:00:00.000Z","created_at":"2021-10-27T13:02:09.306Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2109830,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.22/"},"number":"1.22.3","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5e10a1a63db9028b173f2b65549477d087a9a23de4d94eb1b89d79db31ee306b"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-10-22T00:00:00.000Z","created_at":"2021-10-22T05:45:22.726Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":511068,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.22/"},"number":"1.22.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"bb760c298b15e5dc1bbbb4e0fb225abf2598b5b30a0c059e805370ce586d0b67"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-10-04T00:00:00.000Z","created_at":"2021-10-04T03:20:23.304Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2024908,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.22/"},"number":"1.22.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9171b4a7cea4fc98cb7053df4467ec2ae0bac03e1b6b65802404c84e6a154fa6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-09-29T00:00:00.000Z","created_at":"2021-09-29T07:26:49.236Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":500253,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.22/"},"number":"1.22.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2784830587b80e019661015ee5c9e030248985dabc590ddd84d7aca0ddc26a81"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-09-13T00:00:00.000Z","created_at":"2021-09-13T13:09:37.203Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1425971,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.21/"},"number":"1.21.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9501707e5d72476e72843242fcd29332a1ccb656a163042462d4b18f2f531abf"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-08-26T00:00:00.000Z","created_at":"2021-08-26T07:51:17.209Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1639791,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.20/"},"number":"1.20.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"46f6c5d45a25f2c75cf8c92149e91e8680dac7f6056ebb92d979f36ff890d17f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-08-19T00:00:00.000Z","created_at":"2021-08-19T06:55:01.167Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":925414,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.19/"},"number":"1.19.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8c4f8cd8abfd8bb24f4f30a9d9d70118b3bc64a455750c742414b6b540acacbe"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-08-12T00:00:00.000Z","created_at":"2021-08-12T10:31:19.249Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":488236,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.19/"},"number":"1.19.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4ea67b3e0581623e40cfcb58cdb946d11d2aa92b78d42cd050ab6d786d36a716"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-07-23T00:00:00.000Z","created_at":"2021-07-23T06:12:42.555Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2925846,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.18/"},"number":"1.18.4","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"12c63a283dc90816072392118f7dbfc358febc0648655abd23690905ecbd68d2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-07-06T00:00:00.000Z","created_at":"2021-07-06T09:15:19.404Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1940171,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.18/"},"number":"1.18.3","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3d68b45c160faab94cc544f94d31c0625305ee5faf49415c49edfaa9a9cab110"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-07-02T00:00:00.000Z","created_at":"2021-07-02T12:18:10.848Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":361132,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.18/"},"number":"1.18.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6b45980977a3adf83ebea10ed31b81611db4713c910b9d4f37144d7e6cff25c2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-06-30T00:00:00.000Z","created_at":"2021-06-30T05:26:23.167Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":309987,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.18/"},"number":"1.18.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"33ae45f78d826a5ef9613eebe354a841cee55eb02b826daa4ba7af1fb7d6689c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-06-29T00:00:00.000Z","created_at":"2021-06-29T05:37:50.088Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":120921,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.18/"},"number":"1.18.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"b5de58755d97ca72d25a3cd057cd61ac519a9021787da927f20337ef6676b130"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-06-15T00:00:00.000Z","created_at":"2021-06-15T10:36:25.983Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1393211,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.17/"},"number":"1.17.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e69e12da57c04241dd7978b43e570e1eed589d486271842b10d9c921a330d052"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-06-09T00:00:00.000Z","created_at":"2021-06-09T10:46:29.434Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":447418,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.16/"},"number":"1.16.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"fd0feb97e7249b890af0d5bf1078d94ac587741a2c88dd0ddfc959b3aa35729a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-06-01T00:00:00.000Z","created_at":"2021-06-01T07:05:05.867Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1300536,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.16/"},"number":"1.16.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"223c4d1187f34a224ab38e1f180cb390d9209191d268d9040b6315c0bbe65746"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-05-17T00:00:00.000Z","created_at":"2021-05-17T07:17:56.288Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1737542,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.15/"},"number":"1.15.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3c783af32a3950d5b5d7b3a59d2517bccc2fce80df44d4cd1baedc6131f20af6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-05-05T00:00:00.000Z","created_at":"2021-05-05T07:54:36.043Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1683392,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.14/"},"number":"1.14.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f73a95a3aa4999d617678fd5c3559b531d77ef408d44d8ce5dd99d07a2c91232"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-04-20T00:00:00.000Z","created_at":"2021-04-20T08:04:40.949Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1592133,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.13/"},"number":"1.13.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.5.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4d24d2557ad91ebf0c316c7da4e4b96c2e293ae32ab6760510bc650e8e91f931"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-04-04T00:00:00.000Z","created_at":"2021-04-04T13:59:28.208Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":3413699,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.12/"},"number":"1.12.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2963dc4b3b8e9b2b2d39e8986e5bacbb0246bfb439da03ba4fca5365d4602242"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-03-24T00:00:00.000Z","created_at":"2021-03-24T13:37:11.465Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1122587,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.12/"},"number":"1.12.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"dc9150badc782e37fbf572357b132ad3db2a11485635595b269d7bae0c047ec4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-03-01T00:00:00.000Z","created_at":"2021-03-01T07:52:18.929Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2358637,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop/rubocop/issues","source_code_uri":"https://github.com/rubocop/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.11/"},"number":"1.11.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f954e6889e6a5e0b64e973b531e673ec597ff430ddbf50584099d532fad33f7f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-02-15T00:00:00.000Z","created_at":"2021-02-15T13:10:10.167Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1565678,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.10/"},"number":"1.10.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9fe2014e760ddef3ba9f822a8b6643d175ea8ee622c8240d922204a609378dd9"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-02-01T00:00:00.000Z","created_at":"2021-02-01T07:37:07.234Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1252206,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.9/"},"number":"1.9.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"42a43aeea3d87ea429b897c3f18d8b6fddcf99c37d47f413f859f7ebe5f2d71a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-01-28T00:00:00.000Z","created_at":"2021-01-28T08:18:31.958Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":198283,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.9/"},"number":"1.9.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a99ce92e7b5b2050b75a8885b1ca2a32f7c690222bba3357d566f4495ebee0f3"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-01-11T00:00:00.000Z","created_at":"2021-01-11T11:18:45.376Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1758035,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.8/"},"number":"1.8.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"bffc58b0a398bd3fd46ad6f6310befb981e5cd1d706a8f8de18251e981620299"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2021-01-07T00:00:00.000Z","created_at":"2021-01-07T08:56:11.791Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":196242,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.8/"},"number":"1.8.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2d7a5c2eacd9e1b322a8b910acc1f16c109e793b76f56af435228821b5755989"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-12-25T00:00:00.000Z","created_at":"2020-12-25T07:22:09.105Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2112037,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.7/"},"number":"1.7.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"343c1b2ebf906a0e889c5135a65227548538e50d8c8aa27b8a150cf8fdf7738a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-12-10T00:00:00.000Z","created_at":"2020-12-10T07:36:17.340Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1503282,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.6/"},"number":"1.6.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"89b638e3975463d2b0749617ec7a372f3a597bfa2465e1e396aee82824839626"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-12-09T00:00:00.000Z","created_at":"2020-12-09T12:10:09.487Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":69443,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.6/"},"number":"1.6.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"de769122490a39a283aa99519e54358a4ae84b5a3773d4ed135da750f59dbdef"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-12-04T00:00:00.000Z","created_at":"2020-12-04T08:48:50.982Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":538481,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.5/"},"number":"1.5.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e552e6c2a0765a5faea5b0def4c13a6004bcdd2e947eb5693ee3900c5535444c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-12-02T00:00:00.000Z","created_at":"2020-12-02T16:00:42.960Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":143427,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.5/"},"number":"1.5.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"481149f40d9e4b45e81ba9df462d943fb1dddadc60b88bf5632e1dcb5278168d"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-12-01T00:00:00.000Z","created_at":"2020-12-01T17:18:15.865Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":42212,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.5/"},"number":"1.5.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2b1c5101afc230126dc5be1d9a8afa5deda2e9b6a8eb87f032863ab195113ad2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-11-25T00:00:00.000Z","created_at":"2020-11-25T08:13:43.899Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2377457,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.4/"},"number":"1.4.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5e58c81eb570336047380f2cc179b412eb50ee7560d128395ea535f6e1877fcf"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-11-23T00:00:00.000Z","created_at":"2020-11-23T15:47:06.586Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":274848,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.4/"},"number":"1.4.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c502ab34cfdffafca465b8ab4338209eaf86f3ac2a015e87caeb49b69e0979f6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-11-23T00:00:00.000Z","created_at":"2020-11-23T09:00:24.039Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":26784,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.4/"},"number":"1.4.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c01c4658123427d9198c3d20e6fede1d0be555703a84bd8023efdf91dd8a6187"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-11-16T00:00:00.000Z","created_at":"2020-11-16T08:54:41.526Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":777950,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.3/"},"number":"1.3.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4054ee99368d9e479ef82869e731eccde3055b1a5bcdba02ea077f2411b3eab1"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-11-12T00:00:00.000Z","created_at":"2020-11-12T08:03:01.026Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":233414,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.3/"},"number":"1.3.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7cf281b5e63b87b6caf26a0fc44f98af32b0507898e360ac3a0d8fd824a947ef"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-11-05T00:00:00.000Z","created_at":"2020-11-05T07:35:20.077Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":609851,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.2/"},"number":"1.2.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"599bb8a001cd4cb0195b8b0b8f055158b93f7bd77fc3a232e5adbdf3bca28715"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-10-29T00:00:00.000Z","created_at":"2020-10-29T15:18:10.951Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1144309,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.1/"},"number":"1.1.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"146a44a1d0baba33cac6274c0be6a98098cabe38684dff6e1b3929c29f3d88db"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-10-21T00:00:00.000Z","created_at":"2020-10-21T11:02:00.444Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1157578,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/1.0/"},"number":"1.0.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c16df6a0a14e370a64ac7de209bba6e8466a0283761373c82b9eff9d0bf988b5"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-10-12T00:00:00.000Z","created_at":"2020-10-12T07:04:17.359Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":17570406,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/0.93/"},"number":"0.93.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"73b44fbbe872edbd3f14487175b6369a0f48e952c155f305896ffa56c48b195e"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-10-08T00:00:00.000Z","created_at":"2020-10-08T14:53:41.340Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":232421,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/0.93/"},"number":"0.93.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6a3c6b8e5287ea7b7c729ab51406a163a9894fe0f925f0626b2a9112503c3bdb"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-09-25T00:00:00.000Z","created_at":"2020-09-25T08:12:20.931Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2834641,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/rubocop/0.92/"},"number":"0.92.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3653dec299db6290c1f4dd3c4afd47ef76c484185f3642605552547c74672e4b"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-09-23T00:00:00.000Z","created_at":"2020-09-23T09:46:14.960Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2128361,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.91.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"06b08219c476a9507eb8a7f6b026d33f5d463d2a32488a3b80aab06a3e6fd5a6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-09-15T00:00:00.000Z","created_at":"2020-09-15T05:45:14.063Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":7480215,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.91.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0a836a303d959ba4d35b9c3eb848e4ed54952a4d0037874f0c1067da4721781d"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-09-01T00:00:00.000Z","created_at":"2020-09-01T06:44:59.545Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1652573,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.90.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9d3d3acf7dde11cea1c7c18c1baf8cc80124ad02db802eee2a96e03f38c58cf0"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-08-10T00:00:00.000Z","created_at":"2020-08-10T12:17:06.265Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":6424554,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.89.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"30794116b2804aab1abc74780a201fae5160c1d6a21550ce9786abd3ca0e07fa"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-08-05T00:00:00.000Z","created_at":"2020-08-05T19:05:18.634Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":280008,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.89.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ef1aba0c16b4610bfc8e9fdd233707719872b387f4bc9d3fc66829572a9008c2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-07-13T00:00:00.000Z","created_at":"2020-07-13T12:22:19.339Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":3644319,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.88.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8d7da9340fce8b64be15077e6ba1f7c60b5b5999901ce2eda9837f9ca08b2fe4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-07-07T00:00:00.000Z","created_at":"2020-07-07T19:14:41.214Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":631211,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.87.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3df1a0c641feed9004d0dd787e220283cf8a82f8ba24a04a284c4f841dad6029"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-07-06T00:00:00.000Z","created_at":"2020-07-06T16:12:40.171Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":2324512,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.87.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a928b5acff012d15df735ef34978bc099397b12fcaa4025e46c565397e179430"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-06-22T00:00:00.000Z","created_at":"2020-06-22T07:29:21.381Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":10205881,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.86.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"babe641b554e28d53bad32fd94f90e6d65410fa06fe8a2c511f2aec03b7c83ca"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-06-07T00:00:00.000Z","created_at":"2020-06-07T15:40:00.351Z","description":" RuboCop
+ is a Ruby code style checking and code formatting tool.\n It aims to enforce
+ the community-driven Ruby Style Guide.\n","downloads_count":1912351,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.85.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d0a0b8a7cf84ed0c5f3a305a48c738b1b8ec8a08affd19e7c5986fd6d5a21bbe"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-06-01T00:00:00.000Z","created_at":"2020-06-01T15:47:28.436Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":407188,"metadata":{"homepage_uri":"https://rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.85.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3963352c8187a06dbf3f65358ab91eff3502792da8dbb0e8329eeb7fb74715bc"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-05-21T00:00:00.000Z","created_at":"2020-05-21T06:57:11.953Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1763468,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.84.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"069f1497ef67aefa360a80aef66375fab2941b85dd09a2a68dcaab3d17a4e5c6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-05-11T00:00:00.000Z","created_at":"2020-05-11T12:28:44.160Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1474486,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.83.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3397a3778ed0fa4d43e69b19f9c421da1925e7a85acc07f5b852aafc7a3c7224"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-04-16T00:00:00.000Z","created_at":"2020-04-16T08:19:59.835Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":5420222,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.82.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2d6c5bc7d9b9c1ac1e8bb8a724ea761d724661ebec143eb0b92345b5a8e8d25f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-04-01T00:00:00.000Z","created_at":"2020-04-01T07:55:38.383Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":4461435,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.81.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3d1ff65d3acf3a4ef1babb4624255268460f5d56ddb8f9c985a731d8ebe80d32"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-02-29T00:00:00.000Z","created_at":"2020-02-29T18:05:39.532Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":4091837,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.80.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"485291465f908c08de184932a6ae7a796c8a37dd46020dd5ed21cc46eee117c5"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-02-18T00:00:00.000Z","created_at":"2020-02-18T11:59:08.971Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1657923,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.80.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"cb7ec63f27324162a4d2021104b2d94ea611e7c47995cc8b6f65436ecebeae29"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2020-01-06T00:00:00.000Z","created_at":"2020-01-06T09:57:25.274Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3881112,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.79.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"325b331102897bd19d08f4e4d18dde806f24cbf5768110663ae16fab1f17a792"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-12-18T00:00:00.000Z","created_at":"2019-12-18T20:51:20.075Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2086611,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.78.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a013cddb1b1292833fada241aea59b450c2a51d730c556e829572ba69d862bdc"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-11-27T00:00:00.000Z","created_at":"2019-11-27T18:03:03.812Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2513680,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.77.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a95b032eeeb52bfd83db802d992a2e98484023b06677f16d5bb5c2f556580855"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-10-28T00:00:00.000Z","created_at":"2019-10-28T14:54:29.237Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3241279,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.76.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"00837450d0eb3d85c7eb32afe909f9536135172df06bdd490ade9c4e7b0ca51f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-10-14T00:00:00.000Z","created_at":"2019-10-14T16:58:16.713Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2156709,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.75.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"360c1d4941881064611feae6b3b9f1535a5e455dd174ced9a4d39b734e0cb868"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-09-30T00:00:00.000Z","created_at":"2019-09-30T17:05:51.523Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1094590,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.75.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4be504c51e6bfbce5070bc853d9bd770a273600eca31820ca1aa3695d66010f4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-07-31T00:00:00.000Z","created_at":"2019-07-31T19:12:04.545Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":10008882,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.74.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d3abbf59133f5fce2bea961bb363a3c2f7d2e36ffc30fd11de5c2c9cb456fe72"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-07-16T00:00:00.000Z","created_at":"2019-07-16T08:57:10.832Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1290752,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.73.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0ed89317eba64db6327896303dafad5c1520983e12cb2c21cbb32cac3a62bfac"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-06-25T00:00:00.000Z","created_at":"2019-06-25T14:36:09.976Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2979244,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.72.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a20e5b9fe52b337b491d77faea871fe90f8bf2fd5a71b594e76419a852ad5ba4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-05-30T00:00:00.000Z","created_at":"2019-05-30T13:53:44.134Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2595408,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.71.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"cb40a9fb646368c867dd3a41753169dee24aa4b819e91f0189a8b8da82cb5e56"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-05-21T00:00:00.000Z","created_at":"2019-05-21T10:26:46.421Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1383246,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.70.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9c938c50fe39ed55458ecf600f316ccbaf51cf5584d1aa83b621ba27ba94f86c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-05-13T00:00:00.000Z","created_at":"2019-05-13T08:57:55.837Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2841242,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.69.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"b1ccb922caf3eb21a97a92fe8cbf62950e4adc215052cde4cfbbc5a8a442bcb2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-04-30T00:00:00.000Z","created_at":"2019-04-30T19:46:32.378Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2467915,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.68.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"af830b7ddf6459700b35225db789e828015df5d96ca40ee438da1115383a39d4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-04-29T00:00:00.000Z","created_at":"2019-04-29T13:53:42.552Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":444126,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.68.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8a1d642e344ef81164915cf00f021724a2ff1b17ff552369f8be36a7dc672b9c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-04-05T00:00:00.000Z","created_at":"2019-04-05T07:54:59.405Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1425061,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.67.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0029e66eb5c02fca2befdcba5c12c81ca83620c4ad5e1d197fcd35f7a549efb1"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-04-04T00:00:00.000Z","created_at":"2019-04-04T17:13:15.415Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":110653,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.67.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c1e929a0aae8b28d75c8ca093a4401e66c2fc91e84f6a8ccb8ad5f22016fd7a2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-04-04T00:00:00.000Z","created_at":"2019-04-04T15:23:11.383Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":69380,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.67.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"fe1d716afc92a59180e30e9d62b398a3c069c4ae5bca97b5de6e4d65c6cf9f8c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-03-18T00:00:00.000Z","created_at":"2019-03-18T09:27:01.934Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2661515,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.66.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f05a6896f367765b3f0fba663d0add120444f8de604dada405662b10a0860f5a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-02-19T00:00:00.000Z","created_at":"2019-02-19T08:43:14.568Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2547696,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.65.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c6ffcb57bab1cecbcd0240948c2bba65f422abc1e72bef461fb4f31cd9bbd19a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-02-10T00:00:00.000Z","created_at":"2019-02-10T13:54:58.751Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3552284,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.64.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"726debef2d860b3855f683c5051121046b99197b39459620dd137266a789501f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-01-22T00:00:00.000Z","created_at":"2019-01-22T03:02:01.872Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2269935,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.63.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5e8a8fadbe248ff9c3727b603d66f23658fe1e1965ae07571365b34a390600df"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-01-16T00:00:00.000Z","created_at":"2019-01-16T16:32:03.430Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":455917,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.63.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"81b091cf498be83ecb01be8ea5c265c0231eed14d9ee00b872cd10859dca8d65"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2019-01-01T00:00:00.000Z","created_at":"2019-01-01T08:40:22.886Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1155198,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.62.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"73bb7e9b97e35444c37a9164e5a644518807fba613a790e1e234ae9b7fcfca0e"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-12-06T00:00:00.000Z","created_at":"2018-12-06T08:18:53.311Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1916902,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.61.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8650301567ee5a4867dbb9ba9ca9987d7dc8a9166ee3136c41c03906cc935ada"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-12-05T00:00:00.000Z","created_at":"2018-12-05T12:42:54.009Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":117778,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.61.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"18a30bb68d42e8cc3fd1a0aac37c86db46c99fae2edae7bb9dc49eed8f41864c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-10-26T00:00:00.000Z","created_at":"2018-10-26T10:41:04.209Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2218016,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.60.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"31d8b34585456ce0f0e79d6411c3b7e705ac571996876d9815e1d6f1130173c7"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-09-24T00:00:00.000Z","created_at":"2018-09-24T05:19:49.887Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2856290,"metadata":{"homepage_uri":"https://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://docs.rubocop.org/"},"number":"0.59.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a6888aef273e586226013355db553bca6c7acd81ca5771d749d69a883dc92004"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-09-15T00:00:00.000Z","created_at":"2018-09-15T07:45:31.993Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":809484,"metadata":{"homepage_uri":"http://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"http://docs.rubocop.org/"},"number":"0.59.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4b449177a369193559dabbbbd4fff967c1b2bd817bd78134b6082f1d1dd5e443"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-09-09T00:00:00.000Z","created_at":"2018-09-09T16:24:47.204Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":815608,"metadata":{"homepage_uri":"http://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"http://docs.rubocop.org/"},"number":"0.59.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"455597f026940948d5c46a84660a0a944f07d6cf27f497d7147bc49626ced183"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-07-23T00:00:00.000Z","created_at":"2018-07-23T18:01:18.681Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":5843604,"metadata":{"homepage_uri":"http://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"http://docs.rubocop.org/"},"number":"0.58.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"43dab6c9d6c72844cbd028140ee7c60190c5ee6e30417d63480da3f413778139"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-07-10T00:00:00.000Z","created_at":"2018-07-10T07:31:15.576Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":853005,"metadata":{"homepage_uri":"http://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"http://docs.rubocop.org/"},"number":"0.58.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"26ca690212ae00b00435e767f9b3f46ffb1f1143a5f25fe728e638d15ba65868"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-07-07T00:00:00.000Z","created_at":"2018-07-07T12:38:26.296Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":186241,"metadata":{"homepage_uri":"http://www.rubocop.org/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"http://docs.rubocop.org/"},"number":"0.58.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e3b24a143239f4752f4a4e5e09ebb6ff41bb302db34771489db6ef4b728d3a24"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-06-12T00:00:00.000Z","created_at":"2018-06-12T09:05:03.272Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2274204,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.57.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"468ca03be186a4c39f75535ad445bb073408cf2c75035105ac6b91dc04d9cbac"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-06-06T00:00:00.000Z","created_at":"2018-06-06T22:57:40.803Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":274997,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.57.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ad25fe52d9be12bb0662dd32e84cc72067f2ab516a14bb5d557dfec15a74a2e6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-06-06T00:00:00.000Z","created_at":"2018-06-06T00:53:30.394Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":118755,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rubocop-hq/rubocop/issues","source_code_uri":"https://github.com/rubocop-hq/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.57.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"cbce208bac27d4368ebe1a7892b37674399ad274b18888259be8b305a368e644"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-05-14T00:00:00.000Z","created_at":"2018-05-14T15:17:56.916Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1916629,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.56.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"687f9418a1475911fdd0cf7c57009620daef2caffe5692b621dc6d1abfb04212"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-04-16T00:00:00.000Z","created_at":"2018-04-16T09:20:00.851Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3910741,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.55.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"21fc1b25eee37a6b601144b02f2b90d608555aa09aafbf57f03636828d99169f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-03-21T00:00:00.000Z","created_at":"2018-03-21T03:01:01.664Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":4986826,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.54.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3a2208fe1166b22e109b3a184aa0bd26e04d16e78587b9c714e63980694ade80"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2018-03-05T00:00:00.000Z","created_at":"2018-03-05T01:51:12.010Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1134869,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.53.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ce9862b128c49183b2bf2b3416825557ca99bddd504eb1a9f815e8039f201b28"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-12-27T00:00:00.000Z","created_at":"2017-12-27T13:31:06.690Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":7053869,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.52.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4ec659892e86c64ec25e7a543b4a717f9ee6e9450bdb9541e0d3492b43ce4234"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-12-12T00:00:00.000Z","created_at":"2017-12-12T13:56:04.078Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":945343,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.52.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"291bcca376e76b5bada3ee91c938a4354e384d3d87278ab54b22bde660b520bd"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-10-18T00:00:00.000Z","created_at":"2017-10-18T19:13:19.013Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3328257,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.51.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.1.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c131a5c063600cd31cf49c69130c16b94a6bd7d6a35f6f00c587ac6330bdc233"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-09-14T00:00:00.000Z","created_at":"2017-09-14T18:13:15.476Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3356510,"metadata":{"homepage_uri":"https://rubocop.readthedocs.io/","changelog_uri":"https://github.com/bbatsov/rubocop/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/bbatsov/rubocop/issues","source_code_uri":"https://github.com/bbatsov/rubocop/","documentation_uri":"https://rubocop.readthedocs.io/"},"number":"0.50.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9f7610b37b6746609c932ec8ac5b1a9b4b56f7526018c941393e79b2d93fedc2"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-05-29T00:00:00.000Z","created_at":"2017-05-29T12:34:28.077Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":10749870,"metadata":{},"number":"0.49.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"bcb37220633a570611b68bf8d4649414624d90fad83a7bf8310940f61df51ed7"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-05-24T00:00:00.000Z","created_at":"2017-05-24T05:33:44.287Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":343189,"metadata":{},"number":"0.49.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3af20292590127c5e5a67a08e84642bb9d1f555cb8ed16717980c8b524ed038f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-04-03T00:00:00.000Z","created_at":"2017-04-03T11:29:58.977Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2592511,"metadata":{},"number":"0.48.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"002f6b49013abdc05c68ae75433c48d3ee7f1baa70674d60bf1cc310e210fbd7"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-03-26T00:00:00.000Z","created_at":"2017-03-26T09:53:19.789Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":198982,"metadata":{},"number":"0.48.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"cca38e2b3ba3496fa63dbe747baa9eff7f9717631df1221467618b54773fd690"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-01-18T00:00:00.000Z","created_at":"2017-01-18T02:22:18.664Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3109698,"metadata":{},"number":"0.47.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a7066a0c553e3bad1f118062deef5f05d050a6cf11cb9c9cda067b2a891a7916"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2017-01-16T00:00:00.000Z","created_at":"2017-01-16T01:37:21.113Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":94971,"metadata":{},"number":"0.47.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"55d32c0b5b42f7ac5b6ede9ef4f6a1e6605bc28f8cb38db1c796042ad71f5bd3"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-11-30T00:00:00.000Z","created_at":"2016-11-30T06:56:04.929Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":2007313,"metadata":{},"number":"0.46.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0c5087c157b6070319d06cab7594f9f72b5478344a2568b7029875a081c20418"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-10-31T00:00:00.000Z","created_at":"2016-10-31T09:31:11.908Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":913542,"metadata":{},"number":"0.45.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c579b99be005868127c26d18d8ebfc2e71ed4ebacf781896a837cac6f128248f"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-10-13T00:00:00.000Z","created_at":"2016-10-13T14:55:04.417Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":452524,"metadata":{},"number":"0.44.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2f702937dce43bed412c186dadd3727a769e837bd82263ee7687f37050c324ec"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-10-13T00:00:00.000Z","created_at":"2016-10-13T14:05:27.902Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":5856,"metadata":{},"number":"0.44.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"b124c8255b6570964a53e9d17d3861970d71788f8caa7819335cfac291eb8093"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-09-19T00:00:00.000Z","created_at":"2016-09-19T08:02:45.931Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":936565,"metadata":{},"number":"0.43.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"125e352d5ad65cae73f33572fde0f4793ca8ef7823263935e93ff0c2cd265764"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-07-25T00:00:00.000Z","created_at":"2016-07-25T09:16:51.982Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1763676,"metadata":{},"number":"0.42.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.0.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"1bfd76aa1f6e266d459a7776e5de13956595aca4718758f593b5800c9d809918"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-07-07T00:00:00.000Z","created_at":"2016-07-07T11:55:00.995Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1677885,"metadata":{},"number":"0.41.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9ef6e6a8de0ee0f45305beaae85645bb050e443c237ce91ab488268540ca4d09"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-06-26T00:00:00.000Z","created_at":"2016-06-26T08:50:26.134Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":377270,"metadata":{},"number":"0.41.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7f6c7d8a9a9b4fecbe89a851c38bc549c8d1d4744f57bde383aa33884d4ef661"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-06-25T00:00:00.000Z","created_at":"2016-06-25T17:50:26.038Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":51431,"metadata":{},"number":"0.41.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9580eb20ce098b7a0c06730f92e07ff71da25b803b5a28580ea571b17422e008"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-05-09T00:00:00.000Z","created_at":"2016-05-09T17:45:53.078Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1493315,"metadata":{},"number":"0.40.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ddca83f353721240eb3563c2d9b5fb7e9928845058a6a49f23aab79d25ca83f6"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-03-27T00:00:00.000Z","created_at":"2016-03-27T11:16:21.158Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1658242,"metadata":{},"number":"0.39.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5600c0f7268778520598394cc9ceed69ca3df2a874f2881dfd1d17ba336427bb"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-03-09T00:00:00.000Z","created_at":"2016-03-09T15:10:05.281Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":643355,"metadata":{},"number":"0.38.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4094e2c4b9e0827191c55ab59fd8813e41f40f5c83717b5a143f108b4ac1fc61"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-02-11T00:00:00.000Z","created_at":"2016-02-11T12:50:57.031Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":885969,"metadata":{},"number":"0.37.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d65255d1f072bf737cef74c0cfca50de448bd8428644fa3c4e8880698856be2a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-02-09T00:00:00.000Z","created_at":"2016-02-09T16:45:33.535Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":63445,"metadata":{},"number":"0.37.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a1dbc993cd8c0f1afb90a913b4ef6fa83400809d2b30079a877f52358e498bcc"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-02-04T00:00:00.000Z","created_at":"2016-02-04T06:37:12.148Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":110900,"metadata":{},"number":"0.37.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c8979d3c94088d7e7f38f412efb91a74b2b0c97b3eadf35d7d2645b676508ae1"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2016-01-14T00:00:00.000Z","created_at":"2016-01-14T18:22:21.651Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1306723,"metadata":{},"number":"0.36.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e613b68a72930726a8aab8fba7afffef0b162edaa67b271b491fd18c6c350fec"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-11-10T00:00:00.000Z","created_at":"2015-11-10T17:09:13.947Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1590757,"metadata":{},"number":"0.35.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4f0b3ce1e3f9e6bb2b1409a3c49dbf7e4a682b7b083ee5ff20d5cee6846a38bf"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-11-07T00:00:00.000Z","created_at":"2015-11-07T06:46:41.231Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":252146,"metadata":{},"number":"0.35.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c7b4d9f1bfd1dfb4730519979f6fb283518b945ebe3bb7fc21b2a89ecb7979ff"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-09-21T00:00:00.000Z","created_at":"2015-09-21T14:50:42.260Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":1222555,"metadata":{},"number":"0.34.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d387d22b07c8ba54043d742ee7738dd31440808e371e86502e6d06fbf5d22b7a"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-09-09T00:00:00.000Z","created_at":"2015-09-09T11:29:19.149Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":144599,"metadata":{},"number":"0.34.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0d904489dca12ab4005c358d74057e197266a2571c9feafc15a37bbe3c3b13f8"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-09-05T00:00:00.000Z","created_at":"2015-09-05T05:06:00.263Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":60655,"metadata":{},"number":"0.34.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e704f43bc99114ca93d78cef7937d5a7aebde105613bf82ec86612a8efb44bc3"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-08-05T00:00:00.000Z","created_at":"2015-08-05T12:17:28.842Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":549818,"metadata":{},"number":"0.33.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8910c66466538d01d0abbf21aa099b15901e4fc9c20394cf1ba9ef9f357b4a8c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-06-24T00:00:00.000Z","created_at":"2015-06-24T06:17:18.900Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":564168,"metadata":{},"number":"0.32.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"fa2a1ea1a069436b991ce4d4c4c45682d1e9e83cc9e609dc181eb786e93641d5"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-06-06T00:00:00.000Z","created_at":"2015-06-06T09:02:54.433Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":209457,"metadata":{},"number":"0.32.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"091830914416431bd8217855ccf2e23a82865519e97dbe309501184529efe25e"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-05-05T00:00:00.000Z","created_at":"2015-05-05T17:06:13.868Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":458000,"metadata":{},"number":"0.31.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f44b741edaa71337b8bce7a08e966630035a178034a4308de483e92cb02989e3"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-04-21T00:00:00.000Z","created_at":"2015-04-21T11:54:36.285Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":403414,"metadata":{},"number":"0.30.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f6fbe1c3236e4472365a7c726c2bf4c0f066b241dbecd274a3b296cc19dfbe50"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-04-06T00:00:00.000Z","created_at":"2015-04-06T15:38:28.162Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":264151,"metadata":{},"number":"0.30.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ac19d6befb873d5b16dd10f8000ed5b579708e3a883ba5140bc4c21ae5a93923"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-02-13T00:00:00.000Z","created_at":"2015-02-13T09:44:57.178Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":746127,"metadata":{},"number":"0.29.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"fdef20af47f52e8130d39665fb5770fdc7cb72d9c5a5ba56078e0fe2c325f50c"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2015-02-05T00:00:00.000Z","created_at":"2015-02-05T20:28:53.282Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":68184,"metadata":{},"number":"0.29.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ed2b625e9884ff66a606f60d4b725f5bba747c05591c3dca0e426afd35f8135e"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-12-10T00:00:00.000Z","created_at":"2014-12-10T17:17:29.029Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":836598,"metadata":{},"number":"0.28.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8db05151c0af7fbb334d0326c587f6515380122a17ed726d0936dc16147cc41e"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-11-08T00:00:00.000Z","created_at":"2014-11-08T11:26:10.904Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":538776,"metadata":{},"number":"0.27.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e0f5190a96b82917bd2a15de027d252218830efdfee98bcca3cd3fd8a0e38d24"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-10-30T00:00:00.000Z","created_at":"2014-10-30T16:43:32.213Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":43707,"metadata":{},"number":"0.27.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5729bcce45721e6c9a9139e44df8c26872ff97927fa0df382ed82d1b932593e4"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-09-18T00:00:00.000Z","created_at":"2014-09-18T12:10:31.079Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":686475,"metadata":{},"number":"0.26.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c49f376e77808c8b07578c3d4cdfb6c73f1fd530941ba724303a354498d2cabb"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-09-03T00:00:00.000Z","created_at":"2014-09-03T12:35:37.840Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":104416,"metadata":{},"number":"0.26.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"773ed161beab33976b5760a2f5db27db3447726dd1389212c60668889f9e6091"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-08-15T00:00:00.000Z","created_at":"2014-08-15T11:19:31.470Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":84208,"metadata":{},"number":"0.25.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8ec2267e73031a0056e3cda5332d81774c3102acb8afb0ec5131f28de26dd662"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-07-03T00:00:00.000Z","created_at":"2014-07-03T11:40:30.994Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":243728,"metadata":{},"number":"0.24.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"1c58201dcd9e9cb364a5865b71d29c1371379c3c6c6896d6aaef292d0468bc54"},{"authors":"Bozhidar
+ Batsov, Jonas Arvidsson, Yuji Nakayama","built_at":"2014-06-25T00:00:00.000Z","created_at":"2014-06-25T06:50:13.105Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":39988,"metadata":{},"number":"0.24.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d27a16864c907fd7a1ea463c6cc4ccbda6671b12255e497fceaa080315f0c90d"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-06-02T00:00:00.000Z","created_at":"2014-06-02T12:19:23.545Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":221393,"metadata":{},"number":"0.23.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"82de5ffe9e7acd0a4d5846fbad8805303cf7764955ccba375640ff9d6871a2f1"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-05-20T00:00:00.000Z","created_at":"2014-05-20T14:36:31.896Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":39383,"metadata":{},"number":"0.22.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"888b5a1aba5991169ccb8ba81b9880ef1a190f431252c4174dbcd05c366dcb15"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-04-24T00:00:00.000Z","created_at":"2014-04-24T09:41:19.853Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":143109,"metadata":{},"number":"0.21.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"93667e72149fd96897c5e410827dab5b260a95666549b7885d5b334b1eaadd1e"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-04-05T00:00:00.000Z","created_at":"2014-04-05T12:16:27.467Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":86329,"metadata":{},"number":"0.20.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"42577cc435ac444c8f9fb8659c99721416b6046a3db499f6434464cd7cb09aa5"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-04-02T00:00:00.000Z","created_at":"2014-04-02T14:03:48.747Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":18787,"metadata":{},"number":"0.20.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"1d9bf34022495497bafc86d30443ba5d9732553d3e966c26250956dc33431627"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-03-17T00:00:00.000Z","created_at":"2014-03-17T15:57:49.176Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":159095,"metadata":{},"number":"0.19.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"271e424a97876d4c94ba07f8b65fc56356d19f1ae8b2c0ef4e767e970dafb352"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-03-13T00:00:00.000Z","created_at":"2014-03-13T16:07:32.092Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":11167,"metadata":{},"number":"0.19.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"37f30df2bfabf7d2e66b6efcbbc6d646db9389c26e94bbc2fa86c1d8e62e563f"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-02-02T00:00:00.000Z","created_at":"2014-02-02T10:11:48.216Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":193045,"metadata":{},"number":"0.18.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a8e35b2f2b25cf5306866b821002f35b2c35d221598c1d376df8d497940ba253"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-01-30T00:00:00.000Z","created_at":"2014-01-30T16:11:12.247Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":15554,"metadata":{},"number":"0.18.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4b0e26be25eb9154938b665685aec57fc1b6956d825e7a05d17373bb4b729681"},{"authors":"Bozhidar
+ Batsov","built_at":"2014-01-23T00:00:00.000Z","created_at":"2014-01-23T15:44:06.875Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":29958,"metadata":{},"number":"0.17.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"529e1af94a20544424c6d7603ca62459acdfe10466503416a6eae5c0d3fb67e3"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-12-25T00:00:00.000Z","created_at":"2013-12-25T18:01:42.589Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":58764,"metadata":{},"number":"0.16.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c14fd2a1f918227e105be122e2500b62b94ef9ac454b2e01fc528bbd4da66aa0"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-11-06T00:00:00.000Z","created_at":"2013-11-06T14:55:07.694Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":58847,"metadata":{},"number":"0.15.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"794a5f1839bac8bf728a44291598ba4040a90dd164878adb0d82c192dcd10279"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-10-10T00:00:00.000Z","created_at":"2013-10-10T09:22:35.405Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":63013,"metadata":{},"number":"0.14.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6e164c3d0a55e543012eb814e11b25c431d32a04393b6f86ebbad6d21a493f2c"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-10-07T00:00:00.000Z","created_at":"2013-10-07T11:55:17.164Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":8084,"metadata":{},"number":"0.14.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f5d16dd4e7e012b4414afc57691e6ae4902a96cd63f2a3462ac5ca5423a6c78e"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-09-19T00:00:00.000Z","created_at":"2013-09-19T11:17:32.222Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":30944,"metadata":{},"number":"0.13.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"53a38480d2217ea4f0076485cc3eddb3baece8e69179e144177af38ab860e4f0"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-09-13T00:00:00.000Z","created_at":"2013-09-13T14:12:11.377Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":10582,"metadata":{},"number":"0.13.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"693f24feec332394ff817e95c1d27000ab843802de02ee232c47711e497bddd2"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-08-23T00:00:00.000Z","created_at":"2013-08-23T08:20:14.993Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":23339,"metadata":{},"number":"0.12.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6935abc7d1cc704bf2c96646b1441270c3415ab8be1a7776686ff36ad3cd38bc"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-08-12T00:00:00.000Z","created_at":"2013-08-12T08:41:14.761Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":16338,"metadata":{},"number":"0.11.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8b8155e9b786c8e2bb8699875c4351e50b093b9dced4097d1be176b426306981"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-08-09T00:00:00.000Z","created_at":"2013-08-09T14:44:40.288Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":5017,"metadata":{},"number":"0.11.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"edf8fcf8682ccdbf9ff65e4365fcba0f203777471f6afdb58f31a5327881636d"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-07-17T00:00:00.000Z","created_at":"2013-07-17T18:38:32.352Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":15268,"metadata":{},"number":"0.10.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"5542215006b235d0ade4dda74b23d5d22d47cad02100a0e31bb097d80d7c8431"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-07-05T00:00:00.000Z","created_at":"2013-07-05T15:13:09.528Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":10002,"metadata":{},"number":"0.9.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"cea12e9561a37ca065cd05c613735406fe8ff86d9015cc80cb02d8f9fc4c3131"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-07-01T00:00:00.000Z","created_at":"2013-07-01T12:57:41.924Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":13669,"metadata":{},"number":"0.9.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"846a289554c4716fd4ff3f890a954ecce2895a9a6e913d4617f21dea5f522361"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-06-18T12:41:29.955Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":9742,"metadata":{},"number":"0.8.3","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"30178ff21ee90e5e760c7ea40f448c46efab17c5ab9263e4f9df470d8ef008e3"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-06-05T11:46:36.612Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":6441,"metadata":{},"number":"0.8.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"a853697357734fa301a3594bcc457b068be4056fe19cc420abe68531a771936c"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-05-30T08:11:17.673Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":5358,"metadata":{},"number":"0.8.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"7d008670bf5a3aa378e78ea78024670bd83f38b188c82b1b64d1858ab5d6cf29"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-05-28T09:06:01.240Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":5396,"metadata":{},"number":"0.8.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"2e7d746c66b0c8d3ab9d1ed9c3ccb827b8c4325cb8ea835d8becec870be2b70f"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-05-13T07:00:10.330Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":10469,"metadata":{},"number":"0.7.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"36c0574cc728e120e593fcf84fa0a01a36aa948096cdccdbd9f3eb3f9a0d3011"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-05-11T12:01:12.103Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3989,"metadata":{},"number":"0.7.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"5a8fb4c2cb9270b9fc5c325d8bb7f556233e472a82d1b02ab8501041c7c35d16"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-05-11T05:40:16.929Z","description":" Automatic
+ Ruby code style checking tool.\n Aims to enforce the community-driven Ruby
+ Style Guide.\n","downloads_count":3978,"metadata":{},"number":"0.7.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"3a9096d62151fb4dffebc3fd2f672b238172af945890156923ffc60ebe1eef7a"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-04-28T12:44:09.481Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":5381,"metadata":{},"number":"0.6.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"2cc2d86791a143c791ac99b566d99e920873d086e7b7a9daed69fe5546d4dad6"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-04-23T11:23:04.364Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":4914,"metadata":{},"number":"0.6.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 1.9.2","prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"53702a3cd38c916ab3b57e2a84a5a1af68350dedd83e1b5f5a2dfd786612789a"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-17T00:00:00.000Z","created_at":"2013-04-17T15:09:32.991Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":10870,"metadata":{},"number":"0.5.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"3ba57b2986af5eb7521aa4f5b4fbc2b6b9b5177b398eecee02bbb1411983d7a6"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-15T00:00:00.000Z","created_at":"2013-04-15T13:21:26.422Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":4802,"metadata":{},"number":"0.4.6","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"714b451a1e3cdc7e11e407c06028e3cad0d77c74aa5f1ba623029d2d12c1eca7"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-15T00:00:00.000Z","created_at":"2013-04-15T13:18:31.196Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":3815,"metadata":{},"number":"0.4.5","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"e95b05fa17998d7eb195244d8be36d836f28b60d23cd754349684309a5cd7999"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-14T00:00:00.000Z","created_at":"2013-04-14T14:26:04.168Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":3952,"metadata":{},"number":"0.4.4","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"aeacff29f694b02465fbff9c089f3bb57c2f27d15734935764e14d3beadfce7b"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-14T00:00:00.000Z","created_at":"2013-04-14T06:10:31.699Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":3923,"metadata":{},"number":"0.4.3","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"81c55e8dae6e379f92ea47898daf0e138ba9d84102225e26a82fa9d51f75e4ec"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-13T00:00:00.000Z","created_at":"2013-04-13T19:20:43.281Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":3873,"metadata":{},"number":"0.4.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"a4086d3db38c363a0143a8d4ff7b00f03eb91ddc01081604b9812524d50d5991"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-13T00:00:00.000Z","created_at":"2013-04-13T11:20:04.189Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":3840,"metadata":{},"number":"0.4.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"7173b29a39b02640c4ce5d9b285527978b5f256056b3f85c0f33c894ad476570"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-11T00:00:00.000Z","created_at":"2013-04-11T09:45:55.167Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":4394,"metadata":{},"number":"0.4.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"ce4270b896e61800e286def6324c8d471cc13185cc9270ebe98b9390da0ba480"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-04-06T00:00:00.000Z","created_at":"2013-04-06T17:24:51.540Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":3907,"metadata":{},"number":"0.3.2","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"d3c2ae557b75d78c05624c51fc2ce6e42e41102da11323f164f25581f82a5d4b"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-02-28T00:00:00.000Z","created_at":"2013-02-28T20:45:07.073Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":10405,"metadata":{},"number":"0.3.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"10be88926012cf90ecb86e1289252fd0cd4fd7973e7c34854d03ced3f219f68e"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-02-11T00:00:00.000Z","created_at":"2013-02-11T15:55:45.093Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":4259,"metadata":{},"number":"0.3.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"a54c7d286347e81af34c0381aa41bd5bfeba13f19d27fdd7b6fc9d81a18faf65"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-01-12T00:00:00.000Z","created_at":"2013-01-12T15:56:50.441Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":4309,"metadata":{},"number":"0.2.1","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"7c6fb4f739334b6ab3312b8efe99dc8e8c4ec4965657146287d25f82f4e0459e"},{"authors":"Bozhidar
+ Batsov","built_at":"2013-01-02T00:00:00.000Z","created_at":"2013-01-02T19:42:27.672Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":4015,"metadata":{},"number":"0.2.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"b4f367fbe644fc6947c07172b7a0a022c726efb5efcfa3390dd1c69e6ee808cc"},{"authors":"Bozhidar
+ Batsov","built_at":"2012-12-20T00:00:00.000Z","created_at":"2012-12-20T09:56:20.974Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":11260,"metadata":{},"number":"0.1.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"68930de9ca68b1efbe8c39c7835b818bfed303c0b13fdd9682b5d2b0f170310c"},{"authors":"Bozhidar
+ Batsov","built_at":"2012-05-03T00:00:00.000Z","created_at":"2012-05-03T12:36:58.944Z","description":"Automatic
+ Ruby code style checking tool. Aims to enforce the community-driven Ruby Style
+ Guide.","downloads_count":4281,"metadata":{},"number":"0.0.0","summary":"Automatic
+ Ruby code style checking tool.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"4d4405e8735e7cc4f310c02eb0085f394f5c235ff6777dfd4cc0e36ba1e5b94f"}]'
+ recorded_at: Wed, 09 Aug 2023 17:40:27 GMT
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/versions/toml-rb.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (aarch64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '2642'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Last-Modified:
+ - Fri, 15 Jul 2022 09:00:06 GMT
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A34907a6b36a81c276c9cc8a53a7534170f401308%2Cenv%3Aproduction%2Ctrace_id%3A1052983647522675522
+ X-Request-Id:
+ - ec920637-1658-4b57-b527-2d6e317ffdaa
+ X-Runtime:
+ - '0.022905'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 35.81.98.222:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Wed, 09 Aug 2023 17:40:29 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '700'
+ X-Served-By:
+ - cache-stl760032-STL
+ X-Cache:
+ - HIT
+ X-Cache-Hits:
+ - '1'
+ X-Timer:
+ - S1691602829.296721,VS0,VE1
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"7b4f06968d50f51f6a290ce7dacf9e78"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '[{"authors":"Emiliano Mancuso, Lucas Tolchinsky","built_at":"2022-07-15T00:00:00.000Z","created_at":"2022-07-15T09:00:06.684Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":6386413,"metadata":{},"number":"2.2.0","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 2.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a1e2c54ac3cc9d49861004f75f0648b3622ac03a76abe105358c31553227d9a6"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2022-01-27T00:00:00.000Z","created_at":"2022-01-27T10:12:59.502Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":803589,"metadata":{},"number":"2.1.2","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 2.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ced902c8de778cf3556b0c335a383ce24af7b214b57140a2bace51cd2c5f30a2"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2022-01-19T00:00:00.000Z","created_at":"2022-01-19T17:59:09.198Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":28019,"metadata":{},"number":"2.1.1","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 2.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"915ef9d397a0b58413bcffc1a2303e5be1223a34d5debe2330ee4a4b30faca0c"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2021-11-01T00:00:00.000Z","created_at":"2021-11-01T10:37:10.046Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":413438,"metadata":{},"number":"2.1.0","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 2.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"595bc99fda33682dd7e9d18f632444a00bc33ed6960ea35fa2c9d5a7124339d7"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2021-11-01T00:00:00.000Z","created_at":"2021-11-01T10:28:17.330Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":38061,"metadata":{},"number":"2.0.2","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 2.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f5715daa5d05ca424829fb54d9e049bf9b2f3687ab1c0261540c027e9945596e"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2019-11-18T00:00:00.000Z","created_at":"2019-11-18T09:46:24.255Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":10361654,"metadata":{},"number":"2.0.1","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 2.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5016c6c77ac72bca5fe67c372722bdfdd4479a6fe1a1c4ff2a486e247849b274"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2019-10-25T00:00:00.000Z","created_at":"2019-10-25T15:21:19.133Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":17629,"metadata":{},"number":"2.0.0","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 2.3","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"967f44df7882d6494ec773f7126b26803704bc04a20c0cfb78d654220620aa43"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2018-08-16T00:00:00.000Z","created_at":"2018-08-16T10:38:02.132Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":766709,"metadata":{},"number":"1.1.2","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 1.9","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"e70993afeddfee6fc5f01cd168870603a2878011baa0d2c0fcb997724a96047d"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2017-11-25T00:00:00.000Z","created_at":"2017-11-25T12:26:27.634Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":463723,"metadata":{},"number":"1.1.1","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c43f188f68a8cefa790950e8eb02100164710479c6f6d189cb30098e6b212665"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2017-10-01T00:00:00.000Z","created_at":"2017-10-02T02:15:21.004Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":248684,"metadata":{},"number":"1.1.0","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4a674f4d815aabf20f2ed97f7271261f77aabe3b2380b1174fabb5875954c727"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2017-06-14T00:00:00.000Z","created_at":"2017-06-14T14:54:10.984Z","description":"A
+ Toml parser using Citrus parsing library. ","downloads_count":10799838,"metadata":{},"number":"1.0.0","summary":"Toml
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8d440e2c075ba681d73c0537938a04f7d280896d75f4352123dbe6c36af8e65f"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2016-10-22T00:00:00.000Z","created_at":"2016-10-22T21:03:58.526Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":1474633,"metadata":{},"number":"0.3.15","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2e937e6a2ffbe094e166cd662079bd8a4e99703cec9397e02a39c491c21c590f"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2016-04-18T00:00:00.000Z","created_at":"2016-04-18T12:36:25.934Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":32329,"metadata":{},"number":"0.3.14","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ea2824e01479b653e4648c4a66e1cf5620af8f87e67614b75346b269c23bd5dd"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2016-04-02T00:00:00.000Z","created_at":"2016-04-02T21:31:13.069Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":4030,"metadata":{},"number":"0.3.13","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a5442186b21abb3c9b20e08f1aef4ba469c302c13f0f9c3c3f7d39334d1b6c2b"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2016-03-10T00:00:00.000Z","created_at":"2016-03-10T13:52:13.108Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":18859,"metadata":{},"number":"0.3.12","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5c605fded9badfd6ee84ef25cda294084235b47312e3b0e5d38560a871ab84f2"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2016-03-08T00:00:00.000Z","created_at":"2016-03-09T00:00:11.277Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2319,"metadata":{},"number":"0.3.11","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"caf6b55302f4de162fa6a3aeb806792cf3017672ffafae7c7139e0a2632ab48d"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2016-02-23T00:00:00.000Z","created_at":"2016-02-23T15:47:50.855Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":3490,"metadata":{},"number":"0.3.10","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3db4a604458446d2372a0923f38e40eae7d158991c4bf59948a1dc162ec808cf"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-12-28T00:00:00.000Z","created_at":"2015-12-28T15:06:15.159Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":6357,"metadata":{},"number":"0.3.9","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"799894ebffe778b4e7ee121a9aec890548581bb546bd10e7812c3a58886b8c8d"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-06-12T00:00:00.000Z","created_at":"2015-06-12T12:34:31.349Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":8250,"metadata":{},"number":"0.3.8","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5c2bf7d0b6545dacef67832aa6008abfb1f8d1faf15f2a93879bcab2dfac7cf3"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-06-08T00:00:00.000Z","created_at":"2015-06-09T01:20:16.618Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2051,"metadata":{},"number":"0.3.7","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"aa66498e2e5ddf60be95ddcdf93e847738d1b430f0d5efac4fde5154c6ae6624"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-05-22T00:00:00.000Z","created_at":"2015-05-22T20:49:50.980Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2376,"metadata":{},"number":"0.3.6","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8d1accad4caa1f0ae44475a04cdad11240b66a38df974898c0db0a7e222bd929"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-05-14T00:00:00.000Z","created_at":"2015-05-15T00:22:52.510Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2174,"metadata":{},"number":"0.3.5","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"b8b137b7e741ce53865cf19898342584ef79e6dbc7d37e3ec40c77eebc52da72"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-05-13T00:00:00.000Z","created_at":"2015-05-13T23:11:35.823Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2053,"metadata":{},"number":"0.3.4","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"af8e3e5894ad875fb3a46cacef4ddece4eba24b6f03e97cb9af7efe4d5414834"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-04-17T00:00:00.000Z","created_at":"2015-04-18T01:08:02.325Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2931,"metadata":{},"number":"0.3.3","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3652aaa990a837ddbe1cd0269ba9d9f1a57e03e7e929eb48877f41ab7f9df103"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-02-19T00:00:00.000Z","created_at":"2015-02-19T14:04:46.429Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":4672,"metadata":{},"number":"0.3.0","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"39ab52eb9575a8d7c6e07250cf3fb2194a0dfab72a93233f4dea42e6012d76e8"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-02-05T00:00:00.000Z","created_at":"2015-02-05T12:02:24.579Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2357,"metadata":{},"number":"0.2.1","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"542f9a144200c00fad32ed6d9bd81bf2c4f0a42091b3b159c54ed514a33b5499"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2015-01-31T00:00:00.000Z","created_at":"2015-01-31T13:21:32.125Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2075,"metadata":{},"number":"0.2.0","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4ce285781f13c04dcfff200925c893cdf59f421bb959882683c58482062c4d8b"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2014-03-26T00:00:00.000Z","created_at":"2014-03-26T15:10:52.601Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":4854,"metadata":{},"number":"0.1.6","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":"\u003e=
+ 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0541beef8203204a243603f42964958810d33629a6c38a3231936dc28afe6e2d"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2014-03-16T00:00:00.000Z","created_at":"2014-03-16T16:57:58.913Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":2459,"metadata":{},"number":"0.1.5","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f8df352a62984084c26764adfb0a4a048e8bfa4617ed90edf57063ac65ae05a1"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2013-10-15T00:00:00.000Z","created_at":"2013-10-15T14:08:12.587Z","description":"A
+ TOML parser using Citrus parsing library. ","downloads_count":5769,"metadata":{},"number":"0.1.4","summary":"TOML
+ parser in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f4812e0672c7beacb291d4a13b08f0d6ce8c5f392453145896a92b626356f737"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2013-05-07T00:00:00.000Z","created_at":"2013-05-07T03:49:35.472Z","description":"A
+ TOML parser using Citrus parsing library. Formerly known as ''toml_parser-ruby''.
+ ","downloads_count":3236,"metadata":{},"number":"0.1.3","summary":"TOML parser
+ in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"04f0a7a1c46ecde6c89bf4bb1f9556bf4336d0fc850333836837b513fa2cae29"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2013-04-12T00:00:00.000Z","created_at":"2013-04-12T20:23:35.014Z","description":"A
+ TOML parser using Citrus parsing library. Formerly known as ''toml_parser-ruby''.
+ ","downloads_count":2550,"metadata":{},"number":"0.1.2","summary":"TOML parser
+ in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"509881e2e820c77145f1258669f93b8eea9e5d0dfd4cd1ce3d806077732c386a"},{"authors":"Emiliano
+ Mancuso, Lucas Tolchinsky","built_at":"2013-04-03T00:00:00.000Z","created_at":"2013-04-03T14:37:25.812Z","description":"A
+ TOML parser using Citrus parsing library. Formerly known as ''toml_parser-ruby''.
+ ","downloads_count":2487,"metadata":{},"number":"0.1.0","summary":"TOML parser
+ in ruby, for ruby.","platform":"ruby","rubygems_version":"\u003e= 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"2bbf858d9f55251df8522671c54972d7b6a3a43e407cd6baa3f7d0a5a5862b2a"}]'
+ recorded_at: Wed, 09 Aug 2023 17:40:29 GMT
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/versions/rack.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.225.0 excon/0.100.0 ruby/3.1.4 (aarch64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '9209'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Last-Modified:
+ - Mon, 31 Jul 2023 02:43:44 GMT
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A34907a6b36a81c276c9cc8a53a7534170f401308%2Cenv%3Aproduction%2Ctrace_id%3A699312993194980926
+ X-Request-Id:
+ - 000d3dad-d218-444d-82fc-140918fadae8
+ X-Runtime:
+ - '0.052328'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 35.164.180.222:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Wed, 09 Aug 2023 17:40:29 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '701'
+ X-Served-By:
+ - cache-stl760073-STL
+ X-Cache:
+ - HIT
+ X-Cache-Hits:
+ - '1'
+ X-Timer:
+ - S1691602830.614498,VS0,VE1
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"06d8052ca76f90cd682e9a3c3002cb6a"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '[{"authors":"Leah Neukirchen","built_at":"2023-06-14T00:00:00.000Z","created_at":"2023-06-14T02:01:53.401Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":2057123,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.8","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9c2c912fb7bb367ddeea98193fc51f2aa526733066d0846911aaddb13a457248"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-16T00:00:00.000Z","created_at":"2023-03-16T02:22:59.785Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":3789411,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.7","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4383a019926cfd250c3027f8cc7064f6859e478871b4e09b9cf967800947e7ce"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-13T00:00:00.000Z","created_at":"2023-03-13T18:10:08.055Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":274579,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.6.1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"a3f89e07502bda2f4c866a2fe37f416458c9e1defadc05cd6d8e3991ca63f960"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-13T00:00:00.000Z","created_at":"2023-03-13T06:00:02.662Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":37345,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.6","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"431f49518ddcd70913767aef2327830388eff1de94b3c5be40044af297784d20"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-12T00:00:00.000Z","created_at":"2023-03-12T06:28:17.816Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":25709,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.5","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"fb590ecd5ba8a047b18fc991c5235d1501e2dc1be2976e6dac14a63599847adc"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-02T00:00:00.000Z","created_at":"2023-03-02T22:57:31.330Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":449621,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.4.2","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f5e3f987030d83c8e9477e2002410531943f441df6b8224113039eb3f91fdbb4"},{"authors":"Leah
+ Neukirchen","built_at":"2023-01-17T00:00:00.000Z","created_at":"2023-01-17T20:48:39.596Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":1826800,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.4.1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6fe0042b786617e966ebc082f76dba624f5167f70acb741209805b216e0d07d7"},{"authors":"Leah
+ Neukirchen","built_at":"2023-01-16T00:00:00.000Z","created_at":"2023-01-16T22:41:09.758Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":42264,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.4","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"93008e4e29af4ac220ec19ed814f288f0cc27e14b1a4b27216e6e4b7e1e73cf6"},{"authors":"Leah
+ Neukirchen","built_at":"2022-12-26T00:00:00.000Z","created_at":"2022-12-26T20:20:10.238Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":687494,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.3","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3a3b430e04eb9c5eb1e93502ce80e1c534eb20586eca8d2fbfb1b99960aad300"},{"authors":"Leah
+ Neukirchen","built_at":"2022-12-05T00:00:00.000Z","created_at":"2022-12-05T05:13:22.496Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":804455,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.2","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7313e1a28e8301e08f86de3ee0c82d9e3e1602586683007fdd018abe5e818420"},{"authors":"Leah
+ Neukirchen","built_at":"2022-11-18T00:00:00.000Z","created_at":"2022-11-18T20:59:51.381Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":589564,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0fe3e8d68b2a4684bcdff0b1fecb0a5e201b7ea3f6fac868fa18d596035eff3c"},{"authors":"Leah
+ Neukirchen","built_at":"2022-09-06T00:00:00.000Z","created_at":"2022-09-06T16:28:59.486Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":2917585,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.0","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.4.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"197adbbe5d585dca1909fe6c544f369919d795d54fc76f86b6ce458008f6e29c"},{"authors":"Leah
+ Neukirchen","built_at":"2022-09-04T00:00:00.000Z","created_at":"2022-09-04T23:52:01.005Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":903,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.0.rc1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":"\u003e= 2.4.0","prerelease":true,"licenses":["MIT"],"requirements":[],"sha":"9ded32f01d520c16076c72649873229fcd83a18e79d9ebd696fc22d34e484088"},{"authors":"Leah
+ Neukirchen","built_at":"2022-08-08T00:00:00.000Z","created_at":"2022-08-08T20:34:51.637Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":1962,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/main/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"3.0.0.beta1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":"\u003e= 2.4.0","prerelease":true,"licenses":["MIT"],"requirements":[],"sha":"293cd882966e417d38329ff96b62f3ce1be14cc534cdec22a9150e93ec803cc9"},{"authors":"Leah
+ Neukirchen","built_at":"2023-07-31T00:00:00.000Z","created_at":"2023-07-31T02:43:44.620Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":754768,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.8","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"7b83a1f1304a8f5554c67bc83632d29ecd2ed1daeb88d276b7898533fde22d97"},{"authors":"Leah
+ Neukirchen","built_at":"2023-04-24T00:00:00.000Z","created_at":"2023-04-24T23:22:24.296Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":12588819,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.7","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"b3377e8b2227b8ffa6b617ef8649ffb5e265e46ca8fa1f31244c809fe609829b"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-13T00:00:00.000Z","created_at":"2023-03-13T18:10:14.661Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":12426721,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.6.4","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d3d92be402b5881058caccc0975e6d67a1e0ba929d1d144a43daf689169bfce1"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-02T00:00:00.000Z","created_at":"2023-03-02T22:57:25.059Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":3306045,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.6.3","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"421648340bfe81e20fc766304129e08c92d7a661a856466ec2f1c224784a8f9f"},{"authors":"Leah
+ Neukirchen","built_at":"2023-01-17T00:00:00.000Z","created_at":"2023-01-17T21:22:23.931Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":11613635,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.6.2","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4be320c0fdea6651f0247dbd4182c1bd8acc06606a6b8935a19ad6a504347763"},{"authors":"Leah
+ Neukirchen","built_at":"2023-01-17T00:00:00.000Z","created_at":"2023-01-17T20:48:33.530Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":18538,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.6.1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"252ebd625fa53bbc5aa5aa43cb658d3d92342850898b5f0592dc170d20b8ba88"},{"authors":"Leah
+ Neukirchen","built_at":"2023-01-16T00:00:00.000Z","created_at":"2023-01-16T21:05:52.483Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":251379,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.6","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"d903a6529095f624bb7bd3b6b0e29a1aa0fdcd85d476033648d93e7a68760308"},{"authors":"Leah
+ Neukirchen","built_at":"2022-12-26T00:00:00.000Z","created_at":"2022-12-26T20:19:38.461Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":2731754,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.5","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"724426d0d1dd60f35247024413af93f8e1071c7cfe2c012e59503e5bd7f4b293"},{"authors":"Leah
+ Neukirchen","built_at":"2022-06-30T00:00:00.000Z","created_at":"2022-06-30T22:22:23.466Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":40843275,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.4","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ea2232b638cbd919129c8c8ad8012ecaccc09f848152a7e705d2139d0137ac2b"},{"authors":"Leah
+ Neukirchen","built_at":"2022-05-27T00:00:00.000Z","created_at":"2022-05-27T15:31:55.752Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":29927209,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.3.1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8c728da28f6d800c3839d226fdbce702c94eeb68e25e752b6c2f2be7c1d338ac"},{"authors":"Leah
+ Neukirchen","built_at":"2020-06-15T00:00:00.000Z","created_at":"2020-06-15T22:25:14.574Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":170247890,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.3","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2638e7eb6689a5725c7e16f30cc4aa4e31694dc3ca30d790952526781bd0bb44"},{"authors":"Leah
+ Neukirchen","built_at":"2020-02-10T00:00:00.000Z","created_at":"2020-02-10T22:25:17.510Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":17033347,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.2","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"77f51f9a1409e388a7c002612311fc8cef06f70309eba90800dc6a8d884eb782"},{"authors":"Leah
+ Neukirchen","built_at":"2020-02-09T00:00:00.000Z","created_at":"2020-02-09T06:20:24.822Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":194947,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.1","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"da7f8ccc5dcdc4a25a6fc7086e6969f32ded6d70ae3d79bb8fe6c30c4bd67fbc"},{"authors":"Leah
+ Neukirchen","built_at":"2020-02-08T00:00:00.000Z","created_at":"2020-02-08T18:26:43.205Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n","downloads_count":46498,"metadata":{"changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.2.0","summary":"A
+ modular Ruby webserver interface.","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.3.0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"740433e3a52f9862f508db646e7d324eb209db02572a633de01e406483c29cf3"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-02T00:00:00.000Z","created_at":"2023-03-02T22:57:19.576Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":46740,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.4.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9b421416c3dd3ea788bcfe642ce9da7622cd5a7fd684fdc7262f5f6028f50f85"},{"authors":"Leah
+ Neukirchen","built_at":"2023-01-17T00:00:00.000Z","created_at":"2023-01-17T20:48:28.897Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":86727,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.4.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"42eff000e8df7e9ac284deb30b9140570592be565582d84768825e2354a9b77c"},{"authors":"Leah
+ Neukirchen","built_at":"2022-05-27T00:00:00.000Z","created_at":"2022-05-27T15:31:49.864Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":433014,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.4.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"af609439fca4ac98fb3d9a5f82800d37c2758ebe50c2bbeb4d4d1db568ebe47d"},{"authors":"Leah
+ Neukirchen","built_at":"2020-06-15T00:00:00.000Z","created_at":"2020-06-15T22:24:45.579Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":3094055,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"bfae1fd43aab62bb60b268329c860f214d2ffb15c4bca48931135a2dea52e2f4"},{"authors":"Leah
+ Neukirchen","built_at":"2020-05-12T00:00:00.000Z","created_at":"2020-05-12T21:44:50.346Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":137152,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f8088454a5ad6974e5710cb7441c233773bb2871610aa802009c6e86c0f2f916"},{"authors":"Leah
+ Neukirchen","built_at":"2020-01-27T00:00:00.000Z","created_at":"2020-01-27T22:42:41.077Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":3342175,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"1c45c0dac286ae71afe8d74265ccfb39a2ba36950e44b8ebe4ae43237c060a13"},{"authors":"Leah
+ Neukirchen","built_at":"2020-01-11T00:00:00.000Z","created_at":"2020-01-11T22:18:36.652Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":1688804,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"dc6653775cc44febfc82132783e0a7d7284cf248d42c0c13bd77ecc327b79375"},{"authors":"Leah
+ Neukirchen","built_at":"2020-01-10T00:00:00.000Z","created_at":"2020-01-10T17:49:19.823Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":93460,"metadata":{"homepage_uri":"https://rack.github.io","changelog_uri":"https://github.com/rack/rack/blob/master/CHANGELOG.md","bug_tracker_uri":"https://github.com/rack/rack/issues","source_code_uri":"https://github.com/rack/rack","mailing_list_uri":"https://groups.google.com/forum/#!forum/rack-devel","documentation_uri":"https://rubydoc.info/github/rack/rack"},"number":"2.1.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"9d0a52989ce13125be491dd30e6b6c23c539ecf0cc404b2cb5d862dddbcb3e68"},{"authors":"Leah
+ Neukirchen","built_at":"2023-03-02T00:00:00.000Z","created_at":"2023-03-02T22:57:12.585Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":89877,"metadata":{},"number":"2.0.9.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3c38013104cf9f83d645bd7ad9c036e96d0e8ee4dfc6891cde4480274bfbbd79"},{"authors":"Leah
+ Neukirchen","built_at":"2023-01-17T00:00:00.000Z","created_at":"2023-01-17T20:48:23.635Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":51700,"metadata":{},"number":"2.0.9.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4b1cfbe2f35832ce9d06e9bf52d5d5b2ef75f0960ce90f3d8c8890ed71bdd93e"},{"authors":"Leah
+ Neukirchen","built_at":"2022-05-27T00:00:00.000Z","created_at":"2022-05-27T15:31:43.757Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":665245,"metadata":{},"number":"2.0.9.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3cc510b7943eb9d963ad028438501f06c6f909377ffe58206339fa2b73cd61d3"},{"authors":"Leah
+ Neukirchen","built_at":"2020-02-08T00:00:00.000Z","created_at":"2020-02-08T18:21:51.799Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":6474747,"metadata":{},"number":"2.0.9","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"733a9e53a7470c472d58b94532d70d6e31edb49245ca6449333f53df4598bfd7"},{"authors":"Leah
+ Neukirchen","built_at":"2019-12-18T00:00:00.000Z","created_at":"2019-12-18T18:08:51.732Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":10829683,"metadata":{},"number":"2.0.8","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f98171fb30e104950abe1e9fb97c177d8bb5643dd649bc2ed837864eb596a0c5"},{"authors":"Leah
+ Neukirchen","built_at":"2019-04-02T00:00:00.000Z","created_at":"2019-04-02T16:54:37.554Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":36381631,"metadata":{},"number":"2.0.7","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5158fb64313c17fb2535c8e5def3de7e8b38baf2ab9e4c90155ebed5a9db207d"},{"authors":"Leah
+ Neukirchen","built_at":"2018-11-05T00:00:00.000Z","created_at":"2018-11-05T20:00:33.151Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":25314306,"metadata":{},"number":"2.0.6","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f5874ac9c2223ecc65fcad3120c884fc2a868c1c18f548ff676a6eb21bda8fdd"},{"authors":"Leah
+ Neukirchen","built_at":"2018-04-23T00:00:00.000Z","created_at":"2018-04-23T17:47:56.538Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":20630903,"metadata":{},"number":"2.0.5","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"81e3ece3d36e7507ff6a05666cc2ff759bdd08a96aefb8c5fd6c309a8f5d1095"},{"authors":"Christian
+ Neukirchen","built_at":"2018-01-31T00:00:00.000Z","created_at":"2018-01-31T18:17:19.593Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see https://rack.github.io/.\n","downloads_count":7679067,"metadata":{},"number":"2.0.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"2700d52bdc681b103e161d1da2b3767850e58f3de80e87d16e232491058fd9d5"},{"authors":"Christian
+ Neukirchen","built_at":"2017-05-15T00:00:00.000Z","created_at":"2017-05-15T16:50:19.287Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":16201010,"metadata":{},"number":"2.0.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"8c1c9bbafd74f11c78a29bd87c72a70e7b5b872712d1768ab83b33fec57d9fcd"},{"authors":"Christian
+ Neukirchen","built_at":"2017-05-08T00:00:00.000Z","created_at":"2017-05-08T17:08:46.316Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":24833871,"metadata":{},"number":"2.0.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"b9009b9acbefd85bea220ca13011e6f0f76630169289d9e433a0558dc73542d2"},{"authors":"Christian
+ Neukirchen","built_at":"2016-06-30T00:00:00.000Z","created_at":"2016-06-30T17:34:18.298Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":9689863,"metadata":{},"number":"2.0.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 2.2.2","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"61f78033bf5b1cd0221549ecce7c7b73205d4634b0e63c66e1f295dcf3c26b14"},{"authors":"Christian
+ Neukirchen","built_at":"2016-05-06T00:00:00.000Z","created_at":"2016-05-06T20:52:56.316Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":162419,"metadata":{},"number":"2.0.0.rc1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":"\u003e= 2.2.2","prerelease":true,"licenses":["MIT"],"requirements":[],"sha":"f5b00b0977166f79073c79b2b1d11fc7fe335697e05eafde380379ddf253ba77"},{"authors":"Christian
+ Neukirchen","built_at":"2015-12-17T00:00:00.000Z","created_at":"2015-12-17T21:34:53.915Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":249285,"metadata":{},"number":"2.0.0.alpha","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":"\u003e= 2.2.2","prerelease":true,"licenses":["MIT"],"requirements":[],"sha":"f6d4e7b7673f1d3d09e52c9b0d7fbfd7702f23f6d14f82e8283e19460bb223f9"},{"authors":"Christian
+ Neukirchen","built_at":"2020-02-08T00:00:00.000Z","created_at":"2020-02-08T18:19:58.581Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":18967125,"metadata":{},"number":"1.6.13","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"207e60f917a7b47cb858a6e813500bc6042a958c2ca9eeb64631b19cde702173"},{"authors":"Christian
+ Neukirchen","built_at":"2019-12-18T00:00:00.000Z","created_at":"2019-12-18T18:08:57.819Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":2235335,"metadata":{},"number":"1.6.12","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"928527a0897796d2575e8cacdfa526341dc4c55d05e09b31c39b3704c80738e6"},{"authors":"Christian
+ Neukirchen","built_at":"2018-11-05T00:00:00.000Z","created_at":"2018-11-05T20:00:22.853Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":19130463,"metadata":{},"number":"1.6.11","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ee2016b1ddf820f6e5ee437d10a85319506b60c0d493da1d15815361a91129bd"},{"authors":"Christian
+ Neukirchen","built_at":"2018-04-23T00:00:00.000Z","created_at":"2018-04-23T17:52:31.754Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":9674179,"metadata":{},"number":"1.6.10","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"58e8ae09c502f219a6ad2e7f932072faf2d2b38ce6fd0251ac7ff3096c55c046"},{"authors":"Christian
+ Neukirchen","built_at":"2018-02-27T00:00:00.000Z","created_at":"2018-02-27T17:19:24.605Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":2485395,"metadata":{},"number":"1.6.9","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"0764d8edafbd52a1055966045a27d1c73fb572a18db5151c000887444bcc810f"},{"authors":"Christian
+ Neukirchen","built_at":"2017-05-16T00:00:00.000Z","created_at":"2017-05-16T21:29:10.758Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":23136033,"metadata":{},"number":"1.6.8","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"eae37ccb7686b2c672f64bc6be366cfda4d828ea58e1086cb82766b17a54a7a6"},{"authors":"Christian
+ Neukirchen","built_at":"2017-05-15T00:00:00.000Z","created_at":"2017-05-15T16:47:41.052Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":49597,"metadata":{},"number":"1.6.7","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"485c1bf52521ff354be7dd2a9f529423ee976a51ddec5aace4cf2eebc2ce9c59"},{"authors":"Christian
+ Neukirchen","built_at":"2017-05-08T00:00:00.000Z","created_at":"2017-05-08T17:07:35.278Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":1612071,"metadata":{},"number":"1.6.6","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"5d098ff67ab8c44a9a9007a445fac67db7f53075d943bda0ec6439fc64366d1c"},{"authors":"Christian
+ Neukirchen","built_at":"2016-11-10T00:00:00.000Z","created_at":"2016-11-10T21:55:00.409Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":10726360,"metadata":{},"number":"1.6.5","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"ff9d8fc9e89af3f59ba1708d5dec642e3ec421dbeca567bc460b5b8ba0efe48c"},{"authors":"Christian
+ Neukirchen","built_at":"2015-06-18T00:00:00.000Z","created_at":"2015-06-18T21:51:38.677Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":38351024,"metadata":{},"number":"1.6.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"455ec4545a54b40dae9937bc5f61ee0e32134191cc1ef9a7959a19ec4b127a25"},{"authors":"Christian
+ Neukirchen","built_at":"2015-06-18T00:00:00.000Z","created_at":"2015-06-18T18:45:14.478Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":16042,"metadata":{},"number":"1.6.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4da0c396487e0914cd380c9ec43d4511992756d2c0fa079f70de0a03651cd783"},{"authors":"Christian
+ Neukirchen","built_at":"2015-06-16T00:00:00.000Z","created_at":"2015-06-16T17:59:02.851Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":499570,"metadata":{},"number":"1.6.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"89278d4842d0ecdd1e79cbf7a894dc86f976e6d25debc6814343298fd19ed017"},{"authors":"Christian
+ Neukirchen","built_at":"2015-05-06T00:00:00.000Z","created_at":"2015-05-06T18:37:48.080Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":2791756,"metadata":{},"number":"1.6.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"f4017a0a84dd36f1a6b38baa081731e3696a356f8f83ed74a09ff109afd9e338"},{"authors":"Christian
+ Neukirchen","built_at":"2014-12-18T00:00:00.000Z","created_at":"2014-12-18T22:45:11.060Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":25592578,"metadata":{},"number":"1.6.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6b6941d48013bc605538fc453006a9df18114ddf0757a3cd69cfbd5c3b72a7b8"},{"authors":"Christian
+ Neukirchen","built_at":"2014-11-27T00:00:00.000Z","created_at":"2014-11-27T18:52:53.658Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":102047,"metadata":{},"number":"1.6.0.beta2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":"\u003e= 0","prerelease":true,"licenses":["MIT"],"requirements":[],"sha":"078f2babefc7c659f1833f08e03ca79cb1a8ab80f2a6cb6fec057b9f60e7a494"},{"authors":"Christian
+ Neukirchen","built_at":"2014-08-18T00:00:00.000Z","created_at":"2014-08-18T19:02:15.332Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.io/.\n","downloads_count":350200,"metadata":{},"number":"1.6.0.beta","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":"\u003e= 0","prerelease":true,"licenses":["MIT"],"requirements":[],"sha":"fbfe6d3f321a863809ade0c8fb5db95721ddd95831d01342623123789c596125"},{"authors":"Christian
+ Neukirchen","built_at":"2015-06-18T00:00:00.000Z","created_at":"2015-06-18T18:46:19.771Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":9158509,"metadata":{},"number":"1.5.5","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"4ae4a74f555008ecc541060515c37baa9e16f131538447a668c0bf52117c43b7"},{"authors":"Christian
+ Neukirchen","built_at":"2015-06-16T00:00:00.000Z","created_at":"2015-06-16T17:58:54.690Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":258372,"metadata":{},"number":"1.5.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"401f8725be81ca60a4c8366fca674a9f18e9bc577b6ad4f42c9f66d763107e6e"},{"authors":"Christian
+ Neukirchen","built_at":"2015-05-06T00:00:00.000Z","created_at":"2015-05-06T18:43:23.519Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":498872,"metadata":{},"number":"1.5.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"6b4cbe46b77cd1887c8175bd5f08b1644ab4397122edc1bb1551c9e14390100f"},{"authors":"Christian
+ Neukirchen","built_at":"2013-02-08T00:00:00.000Z","created_at":"2013-02-08T03:14:13.517Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":45819782,"metadata":{},"number":"1.5.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":["MIT"],"requirements":null,"sha":"e64af00234e8faaa69ea81ef4e3800f40743c69560f0dda8fc9969660e775fa7"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-28T00:00:00.000Z","created_at":"2013-01-28T22:52:21.850Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":477969,"metadata":{},"number":"1.5.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"398896c8a109b402d4f62b7fc3b93f65249b3ffd8024ca8127a763a1a0fa6f84"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-22T00:00:00.000Z","created_at":"2013-01-22T07:40:50.789Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":209973,"metadata":{},"number":"1.5.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"d0ac19e607aae98dc2ebe9e45b0d07405efae90a43874cfa5b7a47e4f76af624"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-13T00:00:00.000Z","created_at":"2013-01-13T22:10:44.503Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":5191,"metadata":{},"number":"1.5.0.beta.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":null,"prerelease":true,"licenses":[],"requirements":null,"sha":"9e6b45061429c21a9c50fe5252efb83f3faf1f54e2bfcf629d1a9d5a2340c2ed"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-11T00:00:00.000Z","created_at":"2013-01-11T22:58:13.295Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":4995,"metadata":{},"number":"1.5.0.beta.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":null,"prerelease":true,"licenses":[],"requirements":null,"sha":"92a8748082af00c11b47df71f66ce04e96b724d8a67c51dc301b56a955312468"},{"authors":"Christian
+ Neukirchen","built_at":"2015-06-18T00:00:00.000Z","created_at":"2015-06-18T21:12:18.862Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":11925377,"metadata":{},"number":"1.4.7","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":[],"requirements":[],"sha":"fa06e970605808834fc7e5a8b9babd4871d7d4c23a4d9d61cb94cbd4c15de5e6"},{"authors":"Christian
+ Neukirchen","built_at":"2015-06-16T00:00:00.000Z","created_at":"2015-06-16T20:47:05.112Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":99471,"metadata":{},"number":"1.4.6","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":[],"requirements":[],"sha":"f65ad9022e83e6517d6e3e37cecb781cd172061e769068334bab142d3435f13d"},{"authors":"Christian
+ Neukirchen","built_at":"2013-02-08T00:00:00.000Z","created_at":"2013-02-08T03:13:08.844Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":17832473,"metadata":{},"number":"1.4.5","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"f7bf3faa8e09a2ff26475372de36a724e7470d6bdc33d189a0ec34b49605f308"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-13T00:00:00.000Z","created_at":"2013-01-13T22:07:50.276Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":872377,"metadata":{},"number":"1.4.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"7f8b61b0d1f65279474f09e0a92d121dfd0d0651843755a0f152e8f02c281dc0"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-07T00:00:00.000Z","created_at":"2013-01-07T18:49:46.932Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":582786,"metadata":{},"number":"1.4.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"e16392baa87833c0eb51afcec13f96a521339af183032fa211b6d31e57f320df"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-07T00:00:00.000Z","created_at":"2013-01-07T02:43:24.200Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":34447,"metadata":{},"number":"1.4.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"cf09934534722129318d8049fdc98bf319a3e9254565e0edc31529fed889b840"},{"authors":"Christian
+ Neukirchen","built_at":"2012-01-23T00:00:00.000Z","created_at":"2012-01-23T06:51:48.577Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":9622155,"metadata":{},"number":"1.4.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"2005d0cee536e76b5d0dc853778e3f7840e98c38380265d6d2c45e44dee7a3b3"},{"authors":"Christian
+ Neukirchen","built_at":"2011-12-28T00:00:00.000Z","created_at":"2011-12-28T02:56:39.698Z","description":"Rack
+ provides a minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":225586,"metadata":{},"number":"1.4.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"09b3fbc957e182b00db573b0693014065745432f4bc53920e8d019e4a7cfb896"},{"authors":"Christian
+ Neukirchen","built_at":"2013-02-08T00:00:00.000Z","created_at":"2013-02-08T03:11:09.654Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":817709,"metadata":{},"number":"1.3.10","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"7a7e3e07181d05dc39bdfa566c4025c126a1eb9ac0f434848a9ea0a9c127d9b6"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-13T00:00:00.000Z","created_at":"2013-01-13T22:07:05.217Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":53184,"metadata":{},"number":"1.3.9","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"c75023e17509f4fdee8f62f86140175c8dd279e0e0b489fd9e2662226640243f"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-07T00:00:00.000Z","created_at":"2013-01-07T18:49:00.051Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":47052,"metadata":{},"number":"1.3.8","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"977aef187206d8706b074a3f900b1ac1dcdf86eccbfc7fc4b234d821ee1b8015"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-07T00:00:00.000Z","created_at":"2013-01-07T02:42:07.617Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.github.com/.\n","downloads_count":6196,"metadata":{},"number":"1.3.7","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"258d673aedab569c5f9ed6f6bd5c19907b6d948aa92a25aac83afc8a35cc46b9"},{"authors":"Christian
+ Neukirchen","built_at":"2011-12-28T00:00:00.000Z","created_at":"2011-12-28T02:52:24.315Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":1064118,"metadata":{},"number":"1.3.6","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"d4090f47205a8af4e602be73cf6e5f94f0c91951cfb4e4682e93fc17b2e670e2"},{"authors":"Christian
+ Neukirchen","built_at":"2011-10-18T00:00:00.000Z","created_at":"2011-10-18T05:33:18.912Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":1280322,"metadata":{},"number":"1.3.5","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"1722c36ea1200116560f7e8973a7675a27e6fc2e08a5cc1c146523351ab6e5e1"},{"authors":"Christian
+ Neukirchen","built_at":"2011-10-01T00:00:00.000Z","created_at":"2011-10-01T20:50:43.592Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":238474,"metadata":{},"number":"1.3.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"347f9bc51d2ecba71caa31e0fe2a0cddf88c0877ba0047ffaa365ee474a0919f"},{"authors":"Christian
+ Neukirchen","built_at":"2011-09-16T00:00:00.000Z","created_at":"2011-09-16T23:32:21.895Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":233325,"metadata":{},"number":"1.3.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"8a32cf05128c1d89f6899ef67826ead71ec44a9c9ff4b7cafcb82eae50cc7e47"},{"authors":"Christian
+ Neukirchen","built_at":"2011-07-26T00:00:00.000Z","created_at":"2011-07-26T01:40:42.197Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":1880670,"metadata":{},"number":"1.3.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"f3fc568ecab28b26473c97e5e3a3ff36368128db157d95931b8c28247d263ae3"},{"authors":"Christian
+ Neukirchen","built_at":"2011-07-13T00:00:00.000Z","created_at":"2011-07-13T23:20:57.656Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":79822,"metadata":{},"number":"1.3.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"d64b700e33f156fb6e7ddfbafcd6a962b441394f04c31f559e2078c45ceb6b68"},{"authors":"Christian
+ Neukirchen","built_at":"2011-05-22T07:00:00.000Z","created_at":"2011-05-23T06:08:16.127Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":384390,"metadata":{},"number":"1.3.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"98c4aa69ea449c54bcb6f5a7417e2bdad1b34a0de20608c0fe8c621b01914aa4"},{"authors":"Christian
+ Neukirchen","built_at":"2011-05-19T04:00:00.000Z","created_at":"2011-05-19T17:16:00.870Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":71032,"metadata":{},"number":"1.3.0.beta2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":null,"prerelease":true,"licenses":null,"requirements":null,"sha":"2ba51abc21a6543d117f1a31318bc3acf41ed90cc5397090857746c01f187555"},{"authors":"Christian
+ Neukirchen","built_at":"2011-05-03T07:00:00.000Z","created_at":"2011-05-03T10:39:20.440Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":15025,"metadata":{},"number":"1.3.0.beta","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":null,"prerelease":true,"licenses":null,"requirements":null,"sha":"a5f8a8a2233e43636c4164c35fe837364a1fb673e52a578c5a73485e5acd2057"},{"authors":"Christian
+ Neukirchen","built_at":"2013-02-08T00:00:00.000Z","created_at":"2013-02-08T03:09:59.888Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":1099355,"metadata":{},"number":"1.2.8","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"4b0414a7267d6426d61a105f0ccd75ed0c94cf3ceebb25a0740d3894dbca5cc6"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-13T00:00:00.000Z","created_at":"2013-01-13T22:05:30.848Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":106782,"metadata":{},"number":"1.2.7","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"107cee2cda7b9cd4231c849dc9dcf06867beb7c67b64a4066502452908847ac0"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-07T00:00:00.000Z","created_at":"2013-01-07T02:39:13.764Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":68277,"metadata":{},"number":"1.2.6","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"673af82991bd3f51f7f063b701abe910c4eca7711b34821a31cee743e48357d7"},{"authors":"Christian
+ Neukirchen","built_at":"2011-12-28T00:00:00.000Z","created_at":"2011-12-28T02:48:19.240Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":849098,"metadata":{},"number":"1.2.5","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"2722169809a6fcd73b395a405e43985af24d0cc005a3f9ec389967ecfc6f2c6c"},{"authors":"Christian
+ Neukirchen","built_at":"2011-09-16T00:00:00.000Z","created_at":"2011-09-17T00:00:17.782Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":516069,"metadata":{},"number":"1.2.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"2f0d158a077e40f2150922d0049e33fb21854b1b2d4a795c0bed0a2872fda002"},{"authors":"Christian
+ Neukirchen","built_at":"2011-05-23T07:00:00.000Z","created_at":"2011-05-23T07:42:19.332Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":1056409,"metadata":{},"number":"1.2.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"d1c6f65e54facecd0cd3f06ea60c63a81c8f36497e6ebb575d48cf015b13847f"},{"authors":"Christian
+ Neukirchen","built_at":"2011-03-12T23:00:00.000Z","created_at":"2011-03-13T14:03:14.786Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":4952146,"metadata":{},"number":"1.2.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"6463a86f1d86fa9850f24d32bb398889103c4bca0a1ad34c3f1e6a1cd77e51b3"},{"authors":"Christian
+ Neukirchen","built_at":"2010-06-14T22:00:00.000Z","created_at":"2010-06-15T09:57:28.836Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":3175206,"metadata":{},"number":"1.2.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"72014a9f5b565bd088735f54e6d601a715c5d4d89c89bc387f9ddb9d6f749a2f"},{"authors":"Christian
+ Neukirchen","built_at":"2010-06-12T22:00:00.000Z","created_at":"2010-06-13T17:53:23.974Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":24319,"metadata":{},"number":"1.2.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"dede6fbef9c9f00435d68c90e404e18988dd9e22d4bf3317e7d29b0fa4562f2d"},{"authors":"Christian
+ Neukirchen","built_at":"2013-02-08T00:00:00.000Z","created_at":"2013-02-08T03:08:42.626Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":1637287,"metadata":{},"number":"1.1.6","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"fd0aaca346d52758e4b152783418c5b6e04861862001c54a0d3715ed08e0ecb8"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-13T00:00:00.000Z","created_at":"2013-01-13T22:03:48.342Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":68469,"metadata":{},"number":"1.1.5","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"49cf6108fb5aa606cd121d63a16ba40bd56af83b8e12d9ea06edafdd58a4926d"},{"authors":"Christian
+ Neukirchen","built_at":"2013-01-07T00:00:00.000Z","created_at":"2013-01-07T02:21:38.382Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":43667,"metadata":{},"number":"1.1.4","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":[],"requirements":null,"sha":"4d8a7504ac93a872712275e4d5a2b6274ea6e17b7e30dd92d49ec73f329b1909"},{"authors":"Christian
+ Neukirchen","built_at":"2011-12-28T00:00:00.000Z","created_at":"2011-12-28T02:37:38.280Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":506617,"metadata":{},"number":"1.1.3","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"e774004d0229a82835d21fcfed2feda1b0df1fe7e609d3d4324660890a57ac3e"},{"authors":"Christian
+ Neukirchen","built_at":"2011-03-12T23:00:00.000Z","created_at":"2011-03-13T14:02:46.405Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":506232,"metadata":{},"number":"1.1.2","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"0caaa479982622042c4af7411a3f9b9748c9b3a5a03b60a0991c7e44a22f15f5"},{"authors":"Christian
+ Neukirchen","built_at":"2011-02-28T08:00:00.000Z","created_at":"2011-03-01T06:04:51.617Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":86855,"metadata":{},"number":"1.1.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"89c18ffce62358c17ba8a4674b500635f6a9debb0bd612d816c998ae16be7148"},{"authors":"Christian
+ Neukirchen","built_at":"2011-02-09T08:00:00.000Z","created_at":"2011-02-10T03:12:48.548Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":5477,"metadata":{},"number":"1.1.1.pre","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e
+ 1.3.1","ruby_version":null,"prerelease":true,"licenses":null,"requirements":null,"sha":"ea26b77b9b55d364ec38d649a98901abbd5ab2317906a947532fd96facfc568c"},{"authors":"Christian
+ Neukirchen","built_at":"2010-01-03T23:00:00.000Z","created_at":"2010-01-03T23:15:29.326Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing\nweb applications
+ in Ruby. By wrapping HTTP requests and responses in\nthe simplest way possible,
+ it unifies and distills the API for web\nservers, web frameworks, and software
+ in between (the so-called\nmiddleware) into a single method call.\n\nAlso
+ see http://rack.rubyforge.org.\n","downloads_count":1850210,"metadata":{},"number":"1.1.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"96c618247e5f53c20ad51de0b7682ca589abe7070ce5325502054f80ed29011f"},{"authors":"Christian
+ Neukirchen","built_at":"2009-10-18T01:00:00.000Z","created_at":"2009-10-18T22:45:05.531Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":1793950,"metadata":{},"number":"1.0.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"78b9d7d82d40a3c2e361fdc93db8652c527d397b4a4eda99e6873e14190ec612"},{"authors":"Christian
+ Neukirchen","built_at":"2009-04-24T22:00:00.000Z","created_at":"2009-07-25T18:02:12.000Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":88849,"metadata":{},"number":"1.0.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"b1147fd2991884dfbac9b101b0c88a0f0fc71abbd1bd24fb29cde3fdfc8ebd77"},{"authors":"Christian
+ Neukirchen","built_at":"2009-01-08T23:00:00.000Z","created_at":"2009-07-25T18:02:13.000Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":23655,"metadata":{},"number":"0.9.1","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"0b10d9a9ae7dec712abb18a4e684a660e80c095ee03062dfa48a15f7a643720b"},{"authors":"Christian
+ Neukirchen","built_at":"2009-01-05T23:00:00.000Z","created_at":"2009-07-25T18:02:13.000Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":5245,"metadata":{},"number":"0.9.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"412426b5b2c95d60f014469baabf5ed50fc6cca2ec098b5ad32c24eddfab63de"},{"authors":"Christian
+ Neukirchen","built_at":"2008-08-20T22:00:00.000Z","created_at":"2009-07-25T18:02:13.000Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":8602,"metadata":{},"number":"0.4.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":null,"prerelease":false,"licenses":null,"requirements":null,"sha":"720ce53dfe04ab32724871f135d9fe6b9af79070ab4c2b2b22aaf93aa7fa52dd"},{"authors":"Christian
+ Neukirchen","built_at":"2008-02-25T23:00:00.000Z","created_at":"2009-07-25T18:02:13.000Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":6343,"metadata":{},"number":"0.3.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e 0.0.0","prerelease":false,"licenses":null,"requirements":null,"sha":"344a154a0aeaeff1b7114a62263cd42e902521ff6869e6d7159d76e3df066d82"},{"authors":"Christian
+ Neukirchen","built_at":"2007-05-15T22:00:00.000Z","created_at":"2009-07-25T18:02:13.000Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":4877,"metadata":{},"number":"0.2.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e 0.0.0","prerelease":false,"licenses":null,"requirements":null,"sha":"865666f11c3016e89e689078358edb895691170b11482ec252e42ae9c08b0772"},{"authors":"Christian
+ Neukirchen","built_at":"2007-03-02T23:00:00.000Z","created_at":"2009-07-25T18:02:13.000Z","description":"Rack
+ provides minimal, modular and adaptable interface for developing web applications
+ in Ruby. By wrapping HTTP requests and responses in the simplest way possible,
+ it unifies and distills the API for web servers, web frameworks, and software
+ in between (the so-called middleware) into a single method call. Also see
+ http://rack.rubyforge.org.","downloads_count":6926,"metadata":{},"number":"0.1.0","summary":"a
+ modular Ruby webserver interface","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e 0.0.0","prerelease":false,"licenses":null,"requirements":null,"sha":"ae2a16ef7744acf003a2f1adc0d8c5cbb3dfa614f6f70f7b99165ba5fad3c0ac"}]'
+ recorded_at: Wed, 09 Aug 2023 17:40:29 GMT
diff --git a/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_vendored_dependencies/creates_a_pull_request_that_includes_changes_to_the_vendored_files.yml b/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_vendored_dependencies/creates_a_pull_request_that_includes_changes_to_the_vendored_files.yml
new file mode 100644
index 00000000..04ecc3b2
--- /dev/null
+++ b/updater/spec/fixtures/vcr_cassettes/Dependabot_Updater_Operations_GroupUpdateAllVersions/when_the_snapshot_is_updating_vendored_dependencies/creates_a_pull_request_that_includes_changes_to_the_vendored_files.yml
@@ -0,0 +1,254 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/versions/dummy-git-dependency.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.218.0 excon/0.99.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '384'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Last-Modified:
+ - Mon, 14 Dec 2020 17:08:06 GMT
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A146af3740941a790a743a2e699036f0d7137b99e%2Cenv%3Aproduction%2Ctrace_id%3A4285453584986361312
+ X-Request-Id:
+ - cb7aac61-54b3-42e9-aadf-50389d975e32
+ X-Runtime:
+ - '0.014696'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 44.238.31.117:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Fri, 02 Jun 2023 15:59:46 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '0'
+ X-Served-By:
+ - cache-lcy-eglc8600048-LCY
+ X-Cache:
+ - MISS
+ X-Cache-Hits:
+ - '0'
+ X-Timer:
+ - S1685721586.853325,VS0,VE168
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"f59670cf8622242bfb55afc03a027238"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '[{"authors":"Dependabot","built_at":"2019-11-22T00:00:00.000Z","created_at":"2019-11-22T15:24:24.964Z","description":null,"downloads_count":2033,"metadata":{},"number":"1.1.0","summary":"A
+ dummy package for testing Dependabot","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"3c1b71569ef54c120b0c1d47704409bf9c63de0d581ad6bbb9521634063d7e2d"},{"authors":"Dependabot","built_at":"2019-11-22T00:00:00.000Z","created_at":"2019-11-22T15:24:41.638Z","description":null,"downloads_count":1476,"metadata":{},"number":"1.0.0","summary":"A
+ dummy package for testing Dependabot","platform":"ruby","rubygems_version":"\u003e=
+ 0","ruby_version":"\u003e= 0","prerelease":false,"licenses":["MIT"],"requirements":[],"sha":"c020db324e8d21ee9f0e16ffcdc3c025b6da796c4d62e24e952725b76e2b0088"}]'
+ recorded_at: Fri, 02 Jun 2023 15:59:45 GMT
+- request:
+ method: get
+ uri: https://github.com/dependabot-fixtures/ruby-dummy-git-dependency.git/info/refs?service=git-upload-pack
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.218.0 excon/0.99.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Server:
+ - GitHub-Babel/3.0
+ Content-Type:
+ - application/x-git-upload-pack-advertisement
+ Content-Security-Policy:
+ - default-src 'none'; sandbox
+ Expires:
+ - Fri, 01 Jan 1980 00:00:00 GMT
+ Pragma:
+ - no-cache
+ Cache-Control:
+ - no-cache, max-age=0, must-revalidate
+ Vary:
+ - Accept-Encoding
+ Date:
+ - Fri, 02 Jun 2023 15:59:46 GMT
+ X-Frame-Options:
+ - DENY
+ X-Github-Request-Id:
+ - DD50:EC5D:56DC982:581CFB3:647A11F2
+ body:
+ encoding: ASCII-8BIT
+ string: "001e# service=git-upload-pack\n00000155f229d172c826b95f464a82d8ddf8b3efce3825b0
+ HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since
+ deepen-not deepen-relative no-progress include-tag multi_ack_detailed allow-tip-sha1-in-want
+ allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/master filter
+ object-format=sha1 agent=git/github-aebc4fa63a74\n003ff229d172c826b95f464a82d8ddf8b3efce3825b0
+ refs/heads/master\n003e20151f9b67c8a04461fa0ee28385b6187b86587b refs/tags/v1.0.0\n003ec0e25c2eb332122873f73acb3b61fb2e261cfd8f
+ refs/tags/v1.1.0\n0000"
+ recorded_at: Fri, 02 Jun 2023 15:59:46 GMT
+- request:
+ method: get
+ uri: https://rubygems.org/api/v1/gems/dummy-git-dependency.json
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.218.0 excon/0.99.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Connection:
+ - keep-alive
+ Content-Length:
+ - '445'
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - '0'
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Access-Control-Allow-Origin:
+ - "*"
+ Access-Control-Allow-Methods:
+ - GET
+ Access-Control-Max-Age:
+ - '1728000'
+ Cache-Control:
+ - max-age=60, public
+ Content-Encoding:
+ - ''
+ Content-Security-Policy:
+ - default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
+ https://secure.gaug.es https://gravatar.com https://www.gravatar.com https://secure.gravatar.com
+ https://*.fastly-insights.com https://avatars.githubusercontent.com; object-src
+ 'none'; script-src 'self' https://secure.gaug.es https://www.fastly-insights.com
+ 'nonce-'; style-src 'self' https://fonts.googleapis.com; connect-src 'self'
+ https://s3-us-west-2.amazonaws.com/rubygems-dumps/ https://*.fastly-insights.com
+ https://fastly-insights.com https://api.github.com http://localhost:*; form-action
+ 'self' https://github.com/login/oauth/authorize; frame-ancestors 'self'; report-uri
+ https://csp-report.browser-intake-datadoghq.com/api/v2/logs?dd-api-key=pub852fa3e2312391fafa5640b60784e660&dd-evp-origin=content-security-policy&ddsource=csp-report&ddtags=service%3Arubygems.org%2Cversion%3A146af3740941a790a743a2e699036f0d7137b99e%2Cenv%3Aproduction%2Ctrace_id%3A2719169708493862752
+ X-Request-Id:
+ - b3f6e55c-f0e9-4b2c-8deb-e44b382fb5b8
+ X-Runtime:
+ - '0.022959'
+ Strict-Transport-Security:
+ - max-age=31536000
+ X-Backend:
+ - F_Rails 44.238.31.117:443
+ Accept-Ranges:
+ - bytes
+ Date:
+ - Fri, 02 Jun 2023 15:59:46 GMT
+ Via:
+ - 1.1 varnish
+ Age:
+ - '0'
+ X-Served-By:
+ - cache-lhr7376-LHR
+ X-Cache:
+ - MISS
+ X-Cache-Hits:
+ - '0'
+ X-Timer:
+ - S1685721586.359029,VS0,VE629
+ Vary:
+ - Accept-Encoding
+ Etag:
+ - '"8540b24d44eb668142e74c17caf70060"'
+ Server:
+ - RubyGems.org
+ body:
+ encoding: UTF-8
+ string: '{"name":"dummy-git-dependency","downloads":3509,"version":"1.1.0","version_created_at":"2019-11-22T15:24:24.964Z","version_downloads":2033,"platform":"ruby","authors":"Dependabot","info":"A
+ dummy package for testing Dependabot","licenses":["MIT"],"metadata":{},"yanked":false,"sha":"3c1b71569ef54c120b0c1d47704409bf9c63de0d581ad6bbb9521634063d7e2d","project_uri":"https://rubygems.org/gems/dummy-git-dependency","gem_uri":"https://rubygems.org/gems/dummy-git-dependency-1.1.0.gem","homepage_uri":"http://github.com/dependabot/dummy-git-dependency","wiki_uri":null,"documentation_uri":"https://www.rubydoc.info/gems/dummy-git-dependency/1.1.0","mailing_list_uri":null,"source_code_uri":null,"bug_tracker_uri":null,"changelog_uri":null,"funding_uri":null,"dependencies":{"development":[],"runtime":[]}}'
+ recorded_at: Fri, 02 Jun 2023 15:59:46 GMT
+- request:
+ method: get
+ uri: https://github.com/dependabot/dummy-git-dependency.git/info/refs?service=git-upload-pack
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ User-Agent:
+ - dependabot-core/0.218.0 excon/0.99.0 ruby/3.1.4 (x86_64-linux) (+https://github.com/dependabot/dependabot-core)
+ response:
+ status:
+ code: 401
+ message: Unauthorized
+ headers:
+ Server:
+ - GitHub-Babel/3.0
+ Content-Security-Policy:
+ - default-src 'none'; sandbox
+ Content-Type:
+ - text/plain; charset=UTF-8
+ Www-Authenticate:
+ - Basic realm="GitHub"
+ Content-Length:
+ - '21'
+ Date:
+ - Fri, 02 Jun 2023 15:59:46 GMT
+ X-Frame-Options:
+ - DENY
+ X-Github-Request-Id:
+ - DD52:35CF:108CDB83:10C1816C:647A11F3
+ body:
+ encoding: ASCII-8BIT
+ string: Repository not found.
+ recorded_at: Fri, 02 Jun 2023 15:59:47 GMT
+recorded_with: VCR 6.1.0
diff --git a/updater/spec/spec_helper.rb b/updater/spec/spec_helper.rb
index bc945574..b75c2a0d 100644
--- a/updater/spec/spec_helper.rb
+++ b/updater/spec/spec_helper.rb
@@ -1,23 +1,9 @@
+# typed: false
# frozen_string_literal: true
require "dependabot/logger"
-require "dependabot/python"
-require "dependabot/terraform"
-require "dependabot/elm"
-require "dependabot/docker"
-require "dependabot/git_submodules"
-require "dependabot/github_actions"
-require "dependabot/composer"
-require "dependabot/nuget"
-require "dependabot/gradle"
-require "dependabot/maven"
-require "dependabot/hex"
-require "dependabot/cargo"
-require "dependabot/go_modules"
-require "dependabot/npm_and_yarn"
-require "dependabot/bundler"
-require "dependabot/pub"
require "logger"
+require "rspec/sorbet"
require "vcr"
require "webmock/rspec"
require "yaml"
@@ -32,6 +18,12 @@
WebMock.disable_net_connect!
+# Set git envvars so we can commit to repos during test setup if required
+ENV["GIT_AUTHOR_NAME"] = "dependabot-ci"
+ENV["GIT_AUTHOR_EMAIL"] = "no-reply@github.com"
+ENV["GIT_COMMITTER_NAME"] = "dependabot-ci"
+ENV["GIT_COMMITTER_EMAIL"] = "no-reply@github.com"
+
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
@@ -62,16 +54,14 @@ def job_definition_fixture(path)
end
end
+RSpec::Sorbet.allow_doubles!
+
VCR.configure do |config|
config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
config.hook_into :webmock
config.configure_rspec_metadata!
config.allow_http_connections_when_no_cassette = false
- config.filter_sensitive_data("") do
- ENV.fetch("AZURE_ACCESS_TOKEN", nil)
- end
-
config.filter_sensitive_data("") do
ENV.fetch("AWS_ACCESS_KEY_ID", nil)
end
diff --git a/updater/spec/support/dummy_package_manager/dummy.rb b/updater/spec/support/dummy_package_manager/dummy.rb
new file mode 100644
index 00000000..f0fc7bf5
--- /dev/null
+++ b/updater/spec/support/dummy_package_manager/dummy.rb
@@ -0,0 +1,22 @@
+# typed: true
+# frozen_string_literal: true
+
+require_relative "version"
+require_relative "requirement"
+
+require_relative "file_fetcher"
+require_relative "file_parser"
+require_relative "update_checker"
+require_relative "file_updater"
+
+require "dependabot/dependency"
+Dependabot::Dependency.register_production_check(
+ "dummy",
+ lambda do |groups|
+ return true if groups.empty?
+ return true if groups.include?("runtime")
+ return true if groups.include?("default")
+
+ groups.any? { |g| g.include?("prod") }
+ end
+)
diff --git a/updater/spec/support/dummy_package_manager/file_fetcher.rb b/updater/spec/support/dummy_package_manager/file_fetcher.rb
new file mode 100644
index 00000000..ac0ded17
--- /dev/null
+++ b/updater/spec/support/dummy_package_manager/file_fetcher.rb
@@ -0,0 +1,26 @@
+# typed: true
+# frozen_string_literal: true
+
+require "dependabot/file_fetchers"
+require "dependabot/file_fetchers/base"
+
+module DummyPackageManager
+ class FileFetcher < Dependabot::FileFetchers::Base
+ def fetch_files
+ [a_dummy, b_dummy].compact
+ end
+
+ private
+
+ def a_dummy
+ fetch_file_if_present("a.dummy")
+ end
+
+ def b_dummy
+ fetch_file_if_present("b.dummy")
+ end
+ end
+end
+
+Dependabot::FileFetchers
+ .register("dummy", DummyPackageManager::FileFetcher)
diff --git a/updater/spec/support/dummy_package_manager/file_parser.rb b/updater/spec/support/dummy_package_manager/file_parser.rb
new file mode 100644
index 00000000..38573a10
--- /dev/null
+++ b/updater/spec/support/dummy_package_manager/file_parser.rb
@@ -0,0 +1,47 @@
+# typed: true
+# frozen_string_literal: true
+
+require "dependabot/dependency"
+require "dependabot/file_parsers"
+require "dependabot/file_parsers/base"
+
+module DummyPackageManager
+ class FileParser < Dependabot::FileParsers::Base
+ require "dependabot/file_parsers/base/dependency_set"
+
+ def parse
+ dependency_set = DependencySet.new
+
+ dependency_files.each do |dependency_file|
+ dependency_file.content.each_line do |line|
+ name, version = line.strip.split(" = ")
+
+ dependency_set << Dependabot::Dependency.new(
+ name: name,
+ version: version,
+ package_manager: "dummy",
+ requirements: [
+ requirement: version.to_s,
+ groups: [],
+ file: dependency_file.name,
+ source: nil
+ ]
+ )
+ end
+ end
+
+ dependency_set.dependencies
+ end
+
+ private
+
+ def check_required_files
+ # Just check if there are any files at all.
+ return if dependency_files.any?
+
+ raise "No dependency files!"
+ end
+ end
+end
+
+Dependabot::FileParsers.register("dummy", DummyPackageManager::FileParser)
diff --git a/updater/spec/support/dummy_package_manager/file_updater.rb b/updater/spec/support/dummy_package_manager/file_updater.rb
new file mode 100644
index 00000000..dbee588b
--- /dev/null
+++ b/updater/spec/support/dummy_package_manager/file_updater.rb
@@ -0,0 +1,60 @@
+# typed: true
+# frozen_string_literal: true
+
+require "dependabot/file_updaters"
+require "dependabot/file_updaters/base"
+
+module DummyPackageManager
+ class FileUpdater < Dependabot::FileUpdaters::Base
+ def updated_dependency_files
+ updated_files = []
+ dependency_files.each do |file|
+ next unless requirement_changed?(file, dependency)
+
+ updated_files << updated_file(file: file, content: updated_file_content(file))
+ end
+
+ updated_files.reject! { |f| dependency_files.include?(f) }
+ raise "No files changed!" if updated_files.none?
+
+ updated_files
+ end
+
+ private
+
+ def dependency
+ # Dockerfiles will only ever be updating a single dependency
+ dependencies.first
+ end
+
+ def check_required_files
+ # Just check if there are any files at all.
+ return if dependency_files.any?
+
+ raise "No dependency files!"
+ end
+
+ def updated_file_content(file)
+ updated_content = file.content.gsub(
+ /#{dependency.name} = #{previous_requirements(file).first[:requirement]}/,
+ "#{dependency.name} = #{requirements(file).first[:requirement]}"
+ )
+
+ raise "Expected content to change!" if updated_content == file.content
+
+ updated_content
+ end
+
+ def requirements(file)
+ dependency.requirements
+ .select { |r| r[:file] == file.name }
+ end
+
+ def previous_requirements(file)
+ dependency.previous_requirements
+ .select { |r| r[:file] == file.name }
+ end
+ end
+end
+
+Dependabot::FileUpdaters.register("dummy", DummyPackageManager::FileUpdater)
diff --git a/updater/spec/support/dummy_package_manager/requirement.rb b/updater/spec/support/dummy_package_manager/requirement.rb
new file mode 100644
index 00000000..23f1fce9
--- /dev/null
+++ b/updater/spec/support/dummy_package_manager/requirement.rb
@@ -0,0 +1,20 @@
+# typed: true
+# frozen_string_literal: true
+
+require "dependabot/requirement"
+require "dependabot/utils"
+
+module DummyPackageManager
+ class Requirement < Dependabot::Requirement
+ AND_SEPARATOR = /(?<=[a-zA-Z0-9*])\s+(?:&+\s+)?(?!\s*[|-])/
+
+ def self.requirements_array(requirement_string)
+ requirements = requirement_string.split(AND_SEPARATOR).map(&:strip)
+
+ [new(*requirements)]
+ end
+ end
+end
+
+Dependabot::Utils
+ .register_requirement_class("dummy", DummyPackageManager::Requirement)
diff --git a/updater/spec/support/dummy_package_manager/update_checker.rb b/updater/spec/support/dummy_package_manager/update_checker.rb
new file mode 100644
index 00000000..5a06114f
--- /dev/null
+++ b/updater/spec/support/dummy_package_manager/update_checker.rb
@@ -0,0 +1,34 @@
+# typed: true
+# frozen_string_literal: true
+
+require "dependabot/update_checkers"
+require "dependabot/update_checkers/base"
+require "dependabot/errors"
+
+module DummyPackageManager
+ class UpdateChecker < Dependabot::UpdateCheckers::Base
+ def latest_version
+ "9.9.9"
+ end
+
+ def up_to_date?
+ false
+ end
+
+ def can_update?(*)
+ true
+ end
+
+ def latest_resolvable_version
+ latest_version
+ end
+
+ def updated_requirements
+ dependency.requirements.map do |req|
+ req.merge(requirement: "9.9.9")
+ end
+ end
+ end
+end
+
+Dependabot::UpdateCheckers.register("dummy", DummyPackageManager::UpdateChecker)
diff --git a/updater/spec/support/dummy_package_manager/version.rb b/updater/spec/support/dummy_package_manager/version.rb
new file mode 100644
index 00000000..727f3ba9
--- /dev/null
+++ b/updater/spec/support/dummy_package_manager/version.rb
@@ -0,0 +1,13 @@
+# typed: true
+# frozen_string_literal: true
+
+require "dependabot/version"
+require "dependabot/utils"
+
+module DummyPackageManager
+ class Version < Dependabot::Version
+ end
+end
+
+Dependabot::Utils
+ .register_version_class("dummy", DummyPackageManager::Version)
diff --git a/updater/spec/support/dummy_pkg_helpers.rb b/updater/spec/support/dummy_pkg_helpers.rb
index 76e62ed7..de4b572f 100644
--- a/updater/spec/support/dummy_pkg_helpers.rb
+++ b/updater/spec/support/dummy_pkg_helpers.rb
@@ -1,6 +1,8 @@
# typed: false
# frozen_string_literal: true
+require "dependabot/dependency_file"
+
# This module provides some shortcuts for working with our two mock RubyGems packages:
# - https://rubygems.org/gems/dummy-pkg-a
# - https://rubygems.org/gems/dummy-pkg-b