dependabot-devbox 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/exe/dependabot-devbox-update +101 -0
- data/lib/dependabot/devbox/file_fetcher.rb +58 -0
- data/lib/dependabot/devbox/file_parser.rb +119 -0
- data/lib/dependabot/devbox/file_updater.rb +131 -0
- data/lib/dependabot/devbox/helpers.rb +67 -0
- data/lib/dependabot/devbox/metadata_finder.rb +59 -0
- data/lib/dependabot/devbox/package/package_details_fetcher.rb +86 -0
- data/lib/dependabot/devbox/requirement.rb +57 -0
- data/lib/dependabot/devbox/update_checker/latest_version_finder.rb +33 -0
- data/lib/dependabot/devbox/update_checker.rb +89 -0
- data/lib/dependabot/devbox/version.rb +64 -0
- data/lib/dependabot/devbox.rb +41 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 11b87d3ad83c9c545c69e83345c8173e7bd9de6873c1b2a789dd03b93998c563
|
|
4
|
+
data.tar.gz: 107abd1dd96105c923866884b0624878c3640a7f4fa82b9009ed7b4dc3e518f4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f0f380423454eb53418a9eb2d45d60cfcb8a8d1162567d16904367c9f02156fdfbfc1c6c40f5dc2b067d896140cbe911e9ec0a8d94b99b63026b2b1adc2b267c
|
|
7
|
+
data.tar.gz: 7b0fa0c022e3bd8c38dd2442057fcf721c30b6fd3386b447d798d6e6f2935bc95f120effec80c8bd5897c6e3ebc6e1dbd38ce382bc4916249578630a84d48bcf
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/file_fetchers"
|
|
5
|
+
require "dependabot/file_parsers"
|
|
6
|
+
require "dependabot/update_checkers"
|
|
7
|
+
require "dependabot/file_updaters"
|
|
8
|
+
require "dependabot/pull_request_creator"
|
|
9
|
+
require "dependabot/devbox"
|
|
10
|
+
|
|
11
|
+
repo = ENV.fetch("GITHUB_REPOSITORY")
|
|
12
|
+
token = ENV["GITHUB_ACCESS_TOKEN"] || ENV.fetch("GITHUB_TOKEN")
|
|
13
|
+
directory = ENV.fetch("DIRECTORY_PATH", "/")
|
|
14
|
+
branch = ENV.fetch("BASE_BRANCH", nil)
|
|
15
|
+
|
|
16
|
+
credentials = [
|
|
17
|
+
{
|
|
18
|
+
"type" => "git_source",
|
|
19
|
+
"host" => "github.com",
|
|
20
|
+
"username" => "x-access-token",
|
|
21
|
+
"password" => token
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
source = Dependabot::Source.new(
|
|
26
|
+
provider: "github",
|
|
27
|
+
repo: repo,
|
|
28
|
+
directory: directory,
|
|
29
|
+
branch: branch
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
location = directory == "/" ? repo : "#{repo} (#{directory})"
|
|
33
|
+
puts "Fetching devbox files from #{location}"
|
|
34
|
+
|
|
35
|
+
fetcher = Dependabot::FileFetchers.for_package_manager("devbox").new(
|
|
36
|
+
source: source,
|
|
37
|
+
credentials: credentials
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
files = fetcher.files
|
|
41
|
+
commit = fetcher.commit
|
|
42
|
+
|
|
43
|
+
parser = Dependabot::FileParsers.for_package_manager("devbox").new(
|
|
44
|
+
dependency_files: files,
|
|
45
|
+
source: source,
|
|
46
|
+
credentials: credentials
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
dependencies = parser.parse
|
|
50
|
+
puts "Found #{dependencies.length} devbox package(s)"
|
|
51
|
+
|
|
52
|
+
dependencies.each do |dep| # rubocop:disable Metrics/BlockLength
|
|
53
|
+
puts "Checking #{dep.name} (#{dep.version})..."
|
|
54
|
+
|
|
55
|
+
checker = Dependabot::UpdateCheckers.for_package_manager("devbox").new(
|
|
56
|
+
dependency: dep,
|
|
57
|
+
dependency_files: files,
|
|
58
|
+
credentials: credentials
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if checker.up_to_date?
|
|
62
|
+
puts " up to date"
|
|
63
|
+
next
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
requirements_to_unlock = checker.requirements_unlocked_or_can_be? ? :own : :none
|
|
67
|
+
|
|
68
|
+
updated_deps =
|
|
69
|
+
begin
|
|
70
|
+
checker.updated_dependencies(requirements_to_unlock: requirements_to_unlock)
|
|
71
|
+
rescue Dependabot::AllVersionsIgnored
|
|
72
|
+
puts " all versions ignored"
|
|
73
|
+
next
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
updater = Dependabot::FileUpdaters.for_package_manager("devbox").new(
|
|
77
|
+
dependencies: updated_deps,
|
|
78
|
+
dependency_files: files,
|
|
79
|
+
credentials: credentials
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
updated_files = updater.updated_dependency_files
|
|
83
|
+
|
|
84
|
+
pr_creator = Dependabot::PullRequestCreator.new(
|
|
85
|
+
source: source,
|
|
86
|
+
base_commit: commit,
|
|
87
|
+
dependencies: updated_deps,
|
|
88
|
+
files: updated_files,
|
|
89
|
+
credentials: credentials,
|
|
90
|
+
label_language: true
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
pr = pr_creator.create
|
|
94
|
+
if pr
|
|
95
|
+
puts " PR created: #{pr.html_url}"
|
|
96
|
+
else
|
|
97
|
+
puts " PR already exists or no changes needed"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
puts "Done."
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/file_fetchers"
|
|
5
|
+
require "dependabot/file_fetchers/base"
|
|
6
|
+
|
|
7
|
+
module Dependabot
|
|
8
|
+
module Devbox
|
|
9
|
+
class FileFetcher < Dependabot::FileFetchers::Base
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
MANIFEST_FILENAME = T.let("devbox.json", String)
|
|
13
|
+
LOCKFILE_FILENAME = T.let("devbox.lock", String)
|
|
14
|
+
|
|
15
|
+
sig { override.returns(String) }
|
|
16
|
+
def self.required_files_message
|
|
17
|
+
"Repo must contain a devbox.json."
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
|
21
|
+
def self.required_files_in?(filenames)
|
|
22
|
+
filenames.include?(MANIFEST_FILENAME)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
sig { override.returns(T::Array[DependencyFile]) }
|
|
26
|
+
def fetch_files
|
|
27
|
+
fetched_files = [manifest_file]
|
|
28
|
+
fetched_files << T.must(lockfile) if lockfile
|
|
29
|
+
fetched_files
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
sig { returns(DependencyFile) }
|
|
35
|
+
def manifest_file
|
|
36
|
+
@manifest_file ||= T.let(
|
|
37
|
+
begin
|
|
38
|
+
file = fetch_file_if_present(MANIFEST_FILENAME)
|
|
39
|
+
raise Dependabot::DependencyFileNotFound.new(nil, self.class.required_files_message) unless file
|
|
40
|
+
|
|
41
|
+
file
|
|
42
|
+
end,
|
|
43
|
+
T.nilable(DependencyFile)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
sig { returns(T.nilable(DependencyFile)) }
|
|
48
|
+
def lockfile
|
|
49
|
+
@lockfile ||= T.let(
|
|
50
|
+
fetch_file_if_present(LOCKFILE_FILENAME),
|
|
51
|
+
T.nilable(DependencyFile)
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
Dependabot::FileFetchers.register("devbox", Dependabot::Devbox::FileFetcher)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "json"
|
|
5
|
+
require "dependabot/dependency"
|
|
6
|
+
require "dependabot/file_parsers"
|
|
7
|
+
require "dependabot/file_parsers/base"
|
|
8
|
+
require "dependabot/devbox/helpers"
|
|
9
|
+
require "dependabot/devbox/version"
|
|
10
|
+
|
|
11
|
+
module Dependabot
|
|
12
|
+
module Devbox
|
|
13
|
+
class FileParser < Dependabot::FileParsers::Base
|
|
14
|
+
extend T::Sig
|
|
15
|
+
|
|
16
|
+
ECOSYSTEM = T.let("devbox", String)
|
|
17
|
+
MANIFEST_FILENAME = T.let("devbox.json", String)
|
|
18
|
+
LOCKFILE_FILENAME = T.let("devbox.lock", String)
|
|
19
|
+
# An entry without an "@constraint" suffix tracks the newest release.
|
|
20
|
+
DEFAULT_CONSTRAINT = T.let("latest", String)
|
|
21
|
+
SOURCE_TYPE = T.let("nixhub", String)
|
|
22
|
+
|
|
23
|
+
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
|
24
|
+
def parse
|
|
25
|
+
package_entries.filter_map do |entry|
|
|
26
|
+
next unless entry.is_a?(String)
|
|
27
|
+
|
|
28
|
+
name, constraint = split_package_entry(entry)
|
|
29
|
+
next if name.empty?
|
|
30
|
+
|
|
31
|
+
Dependabot::Dependency.new(
|
|
32
|
+
name: name,
|
|
33
|
+
version: resolved_versions[entry],
|
|
34
|
+
requirements: [{
|
|
35
|
+
requirement: constraint,
|
|
36
|
+
file: MANIFEST_FILENAME,
|
|
37
|
+
groups: [],
|
|
38
|
+
source: { type: SOURCE_TYPE }
|
|
39
|
+
}],
|
|
40
|
+
package_manager: ECOSYSTEM
|
|
41
|
+
)
|
|
42
|
+
end.sort_by(&:name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
sig { override.void }
|
|
48
|
+
def check_required_files
|
|
49
|
+
return if manifest
|
|
50
|
+
|
|
51
|
+
raise "No devbox.json found!"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
sig { returns(T.nilable(DependencyFile)) }
|
|
55
|
+
def manifest
|
|
56
|
+
@manifest ||= T.let(
|
|
57
|
+
dependency_files.find { |f| File.basename(f.name) == MANIFEST_FILENAME },
|
|
58
|
+
T.nilable(DependencyFile)
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
sig { returns(T.nilable(DependencyFile)) }
|
|
63
|
+
def lockfile
|
|
64
|
+
@lockfile ||= T.let(
|
|
65
|
+
dependency_files.find { |f| File.basename(f.name) == LOCKFILE_FILENAME },
|
|
66
|
+
T.nilable(DependencyFile)
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The "packages" field in devbox.json is an array of `name@constraint`
|
|
71
|
+
# strings — the only form supported in beta. Any other shape (e.g. the
|
|
72
|
+
# object form) yields no dependencies.
|
|
73
|
+
sig { returns(T::Array[Object]) }
|
|
74
|
+
def package_entries
|
|
75
|
+
packages = Helpers.parse_json_or_jsonc(manifest&.content).fetch("packages", [])
|
|
76
|
+
return [] unless packages.is_a?(Array)
|
|
77
|
+
|
|
78
|
+
packages
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Splits a package entry on the LAST "@" so future scoped names
|
|
82
|
+
# (e.g. "@org/pkg@1.0") resolve correctly. An entry with no constraint
|
|
83
|
+
# (or a leading-"@" scoped name with none) defaults to "latest".
|
|
84
|
+
sig { params(entry: String).returns([String, String]) }
|
|
85
|
+
def split_package_entry(entry)
|
|
86
|
+
index = entry.rindex("@")
|
|
87
|
+
return [entry, DEFAULT_CONSTRAINT] if index.nil? || index.zero?
|
|
88
|
+
|
|
89
|
+
[T.must(entry[0...index]), T.must(entry[(index + 1)..])]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Maps each manifest package entry (the full `name@constraint` string) to
|
|
93
|
+
# the resolved version recorded in devbox.lock. devbox.lock is strict JSON.
|
|
94
|
+
sig { returns(T::Hash[String, String]) }
|
|
95
|
+
def resolved_versions
|
|
96
|
+
@resolved_versions ||= T.let(parse_lockfile_versions, T.nilable(T::Hash[String, String]))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
sig { returns(T::Hash[String, String]) }
|
|
100
|
+
def parse_lockfile_versions
|
|
101
|
+
content = lockfile&.content
|
|
102
|
+
return {} unless content
|
|
103
|
+
|
|
104
|
+
parsed = JSON.parse(content)
|
|
105
|
+
packages = parsed.is_a?(Hash) ? parsed.fetch("packages", {}) : {}
|
|
106
|
+
return {} unless packages.is_a?(Hash)
|
|
107
|
+
|
|
108
|
+
packages.each_with_object({}) do |(entry, data), versions|
|
|
109
|
+
version = data.is_a?(Hash) ? data["version"] : nil
|
|
110
|
+
versions[entry] = version if version.is_a?(String)
|
|
111
|
+
end
|
|
112
|
+
rescue JSON::ParserError
|
|
113
|
+
{}
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
Dependabot::FileParsers.register("devbox", Dependabot::Devbox::FileParser)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/dependency_file"
|
|
6
|
+
require "dependabot/errors"
|
|
7
|
+
require "dependabot/shared_helpers"
|
|
8
|
+
require "dependabot/file_updaters"
|
|
9
|
+
require "dependabot/file_updaters/base"
|
|
10
|
+
require "dependabot/devbox/helpers"
|
|
11
|
+
|
|
12
|
+
module Dependabot
|
|
13
|
+
module Devbox
|
|
14
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
15
|
+
extend T::Sig
|
|
16
|
+
|
|
17
|
+
MANIFEST_FILENAME = T.let("devbox.json", String)
|
|
18
|
+
LOCKFILE_FILENAME = T.let("devbox.lock", String)
|
|
19
|
+
LATEST = T.let("latest", String)
|
|
20
|
+
|
|
21
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
|
22
|
+
def updated_dependency_files
|
|
23
|
+
updated_files = []
|
|
24
|
+
|
|
25
|
+
new_manifest_content = updated_manifest_content
|
|
26
|
+
if new_manifest_content != manifest.content
|
|
27
|
+
updated_files << updated_file(file: manifest, content: new_manifest_content)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
updated_files << lockfile_dependency_file(regenerated_lockfile_content(new_manifest_content))
|
|
31
|
+
|
|
32
|
+
updated_files
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
sig { override.void }
|
|
38
|
+
def check_required_files
|
|
39
|
+
return if dependency_files.any? { |f| File.basename(f.name) == MANIFEST_FILENAME }
|
|
40
|
+
|
|
41
|
+
raise "No devbox.json found!"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
sig { returns(Dependabot::DependencyFile) }
|
|
45
|
+
def manifest
|
|
46
|
+
@manifest ||= T.let(
|
|
47
|
+
T.must(dependency_files.find { |f| File.basename(f.name) == MANIFEST_FILENAME }),
|
|
48
|
+
T.nilable(Dependabot::DependencyFile)
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
53
|
+
def lockfile
|
|
54
|
+
@lockfile ||= T.let(
|
|
55
|
+
dependency_files.find { |f| File.basename(f.name) == LOCKFILE_FILENAME },
|
|
56
|
+
T.nilable(Dependabot::DependencyFile)
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Rewrites each changed `name@constraint` entry in the raw manifest text so
|
|
61
|
+
# surrounding comments/formatting survive. A `latest` constraint never
|
|
62
|
+
# changes, so those entries are left untouched (lockfile-only update).
|
|
63
|
+
sig { returns(String) }
|
|
64
|
+
def updated_manifest_content
|
|
65
|
+
content = T.must(manifest.content).dup
|
|
66
|
+
|
|
67
|
+
dependencies.each do |dep|
|
|
68
|
+
prev_reqs = (dep.previous_requirements || []).select { |r| r[:file] == manifest.name }
|
|
69
|
+
new_reqs = dep.requirements.select { |r| r[:file] == manifest.name }
|
|
70
|
+
|
|
71
|
+
prev_reqs.zip(new_reqs).each do |prev_req, new_req|
|
|
72
|
+
next unless new_req
|
|
73
|
+
|
|
74
|
+
old_constraint = prev_req[:requirement]
|
|
75
|
+
new_constraint = new_req[:requirement]
|
|
76
|
+
next if old_constraint.nil? || old_constraint == new_constraint
|
|
77
|
+
|
|
78
|
+
content = content.gsub(%("#{dep.name}@#{old_constraint}"), %("#{dep.name}@#{new_constraint}"))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
content
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Regenerates devbox.lock by running `devbox update <pkg> --no-install`
|
|
86
|
+
# (metadata-only: resolves nixpkgs commits/hashes without downloading store
|
|
87
|
+
# paths) against the updated manifest in an isolated temp directory.
|
|
88
|
+
sig { params(manifest_content: String).returns(String) }
|
|
89
|
+
def regenerated_lockfile_content(manifest_content)
|
|
90
|
+
original = lockfile&.content
|
|
91
|
+
|
|
92
|
+
new_content =
|
|
93
|
+
begin
|
|
94
|
+
SharedHelpers.in_a_temporary_directory do |dir|
|
|
95
|
+
dir = dir.to_s
|
|
96
|
+
File.write(File.join(dir, MANIFEST_FILENAME), manifest_content)
|
|
97
|
+
File.write(File.join(dir, LOCKFILE_FILENAME), original) if original
|
|
98
|
+
dependencies.each do |dep|
|
|
99
|
+
Helpers.run_devbox_command("update", "--no-install", dep.name, dir: dir)
|
|
100
|
+
end
|
|
101
|
+
File.read(File.join(dir, LOCKFILE_FILENAME))
|
|
102
|
+
end
|
|
103
|
+
rescue SharedHelpers::HelperSubprocessFailed, Errno::ENOENT => e
|
|
104
|
+
raise Dependabot::DependencyFileNotResolvable, e.message
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
if original && new_content == original
|
|
108
|
+
raise Dependabot::DependencyFileContentNotChanged,
|
|
109
|
+
"devbox update did not change #{LOCKFILE_FILENAME}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
new_content
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
sig { params(content: String).returns(Dependabot::DependencyFile) }
|
|
116
|
+
def lockfile_dependency_file(content)
|
|
117
|
+
existing = lockfile
|
|
118
|
+
return updated_file(file: existing, content: content) if existing
|
|
119
|
+
|
|
120
|
+
Dependabot::DependencyFile.new(
|
|
121
|
+
name: LOCKFILE_FILENAME,
|
|
122
|
+
content: content,
|
|
123
|
+
directory: manifest.directory,
|
|
124
|
+
operation: Dependabot::DependencyFile::Operation::CREATE
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
Dependabot::FileUpdaters.register("devbox", Dependabot::Devbox::FileUpdater)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "json"
|
|
5
|
+
require "sorbet-runtime"
|
|
6
|
+
|
|
7
|
+
require "dependabot/shared_helpers"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Devbox
|
|
11
|
+
module Helpers
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
# Matches either a JSON string literal (with escapes), a line comment, a
|
|
15
|
+
# block comment, or a trailing comma. The alternation lets gsub preserve
|
|
16
|
+
# strings while stripping the JSONC-only constructs, so e.g. "//" inside a
|
|
17
|
+
# URL value is not mistaken for the start of a comment.
|
|
18
|
+
JSONC_TOKEN = T.let(
|
|
19
|
+
%r{
|
|
20
|
+
("(?:\\.|[^"\\])*") # JSON string literal
|
|
21
|
+
| //[^\n]* # line comment
|
|
22
|
+
| /\*.*?\*/ # block comment
|
|
23
|
+
| ,(?=\s*[\}\]]) # trailing comma
|
|
24
|
+
}mx,
|
|
25
|
+
Regexp
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
sig { params(content: T.nilable(String)).returns(T::Hash[String, Object]) }
|
|
29
|
+
def self.parse_json_or_jsonc(content)
|
|
30
|
+
return {} unless content
|
|
31
|
+
|
|
32
|
+
cleaned = content.gsub(JSONC_TOKEN) { ::Regexp.last_match(1) || "" }
|
|
33
|
+
|
|
34
|
+
parsed = JSON.parse(cleaned)
|
|
35
|
+
# A devbox.json must be a JSON object. Guard here so a malformed manifest
|
|
36
|
+
# (e.g. a top-level array) surfaces as a clear parse error rather than an
|
|
37
|
+
# opaque sorbet-runtime type error at the call site.
|
|
38
|
+
raise JSON::ParserError, "Expected a JSON object, got #{parsed.class}" unless parsed.is_a?(Hash)
|
|
39
|
+
|
|
40
|
+
parsed
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Wraps `devbox <args>` via Dependabot's standard subprocess helper, so
|
|
44
|
+
# failures surface as Dependabot::SharedHelpers::HelperSubprocessFailed
|
|
45
|
+
# (consistent with cargo / bun / npm_and_yarn). The Devbox/Nix caches are
|
|
46
|
+
# scoped to the working directory so concurrent jobs don't trample each
|
|
47
|
+
# other's state.
|
|
48
|
+
sig do
|
|
49
|
+
params(
|
|
50
|
+
args: String,
|
|
51
|
+
dir: String
|
|
52
|
+
).returns(String)
|
|
53
|
+
end
|
|
54
|
+
def self.run_devbox_command(*args, dir:)
|
|
55
|
+
Dependabot::SharedHelpers.run_shell_command(
|
|
56
|
+
"devbox #{args.join(' ')}",
|
|
57
|
+
cwd: dir,
|
|
58
|
+
env: {
|
|
59
|
+
"DEVBOX_CACHE" => File.join(dir, ".devbox_cache"),
|
|
60
|
+
"XDG_CACHE_HOME" => File.join(dir, ".cache"),
|
|
61
|
+
"HOME" => dir
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "cgi"
|
|
5
|
+
require "json"
|
|
6
|
+
require "dependabot/metadata_finders"
|
|
7
|
+
require "dependabot/metadata_finders/base"
|
|
8
|
+
require "dependabot/registry_client"
|
|
9
|
+
|
|
10
|
+
module Dependabot
|
|
11
|
+
module Devbox
|
|
12
|
+
class MetadataFinder < Dependabot::MetadataFinders::Base
|
|
13
|
+
extend T::Sig
|
|
14
|
+
|
|
15
|
+
SEARCH_URL = T.let("https://search.devbox.sh/v1/search", String)
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# nixpkgs packages expose a `homepage` in the Nixhub search response. When
|
|
20
|
+
# it points at a recognised git host (e.g. GitHub) we can surface changelog
|
|
21
|
+
# and release metadata; otherwise there is no usable source.
|
|
22
|
+
sig { override.returns(T.nilable(Dependabot::Source)) }
|
|
23
|
+
def look_up_source
|
|
24
|
+
homepage = nixhub_homepage
|
|
25
|
+
return nil unless homepage
|
|
26
|
+
|
|
27
|
+
Source.from_url(homepage)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
sig { returns(T.nilable(String)) }
|
|
31
|
+
def nixhub_homepage
|
|
32
|
+
homepage = package_versions.filter_map { |v| v["homepage"] if v.is_a?(Hash) }.first
|
|
33
|
+
homepage.is_a?(String) && !homepage.empty? ? homepage : nil
|
|
34
|
+
rescue JSON::ParserError, Excon::Error::Timeout, Excon::Error::Socket
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The versions list for the exact-name package in the Nixhub search
|
|
39
|
+
# response (search is fuzzy, so match the name precisely).
|
|
40
|
+
sig { returns(T::Array[Object]) }
|
|
41
|
+
def package_versions
|
|
42
|
+
response = Dependabot::RegistryClient.get(
|
|
43
|
+
url: "#{SEARCH_URL}?q=#{CGI.escape(dependency.name)}"
|
|
44
|
+
)
|
|
45
|
+
return [] unless response.status == 200
|
|
46
|
+
|
|
47
|
+
data = JSON.parse(response.body)
|
|
48
|
+
packages = data.is_a?(Hash) ? data["packages"] : nil
|
|
49
|
+
return [] unless packages.is_a?(Array)
|
|
50
|
+
|
|
51
|
+
package = packages.find { |pkg| pkg.is_a?(Hash) && pkg["name"] == dependency.name }
|
|
52
|
+
versions = package.is_a?(Hash) ? package["versions"] : nil
|
|
53
|
+
versions.is_a?(Array) ? versions : []
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Dependabot::MetadataFinders.register("devbox", Dependabot::Devbox::MetadataFinder)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "cgi"
|
|
5
|
+
require "json"
|
|
6
|
+
require "time"
|
|
7
|
+
require "sorbet-runtime"
|
|
8
|
+
require "dependabot/registry_client"
|
|
9
|
+
require "dependabot/package/package_release"
|
|
10
|
+
require "dependabot/devbox/version"
|
|
11
|
+
|
|
12
|
+
module Dependabot
|
|
13
|
+
module Devbox
|
|
14
|
+
module Package
|
|
15
|
+
class PackageDetailsFetcher
|
|
16
|
+
extend T::Sig
|
|
17
|
+
|
|
18
|
+
SEARCH_URL = T.let("https://search.devbox.sh/v1/search", String)
|
|
19
|
+
|
|
20
|
+
sig { params(dependency: Dependabot::Dependency).void }
|
|
21
|
+
def initialize(dependency:)
|
|
22
|
+
@dependency = dependency
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
sig { returns(T::Array[Dependabot::Package::PackageRelease]) }
|
|
26
|
+
def available_versions
|
|
27
|
+
package = fetch_package
|
|
28
|
+
return [] unless package
|
|
29
|
+
|
|
30
|
+
versions = package["versions"]
|
|
31
|
+
return [] unless versions.is_a?(Array)
|
|
32
|
+
|
|
33
|
+
versions.filter_map do |version_data|
|
|
34
|
+
next unless version_data.is_a?(Hash)
|
|
35
|
+
|
|
36
|
+
version_str = version_data["version"]
|
|
37
|
+
next unless version_str.is_a?(String) && Devbox::Version.correct?(version_str)
|
|
38
|
+
|
|
39
|
+
Dependabot::Package::PackageRelease.new(
|
|
40
|
+
version: Devbox::Version.new(version_str),
|
|
41
|
+
released_at: release_time(version_data)
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
rescue JSON::ParserError, Excon::Error::Timeout, Excon::Error::Socket
|
|
45
|
+
[]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
sig { returns(Dependabot::Dependency) }
|
|
51
|
+
attr_reader :dependency
|
|
52
|
+
|
|
53
|
+
# Nixhub search is fuzzy and can return several packages; keep only the
|
|
54
|
+
# one whose name matches the dependency exactly.
|
|
55
|
+
sig { returns(T.nilable(T::Hash[String, Object])) }
|
|
56
|
+
def fetch_package
|
|
57
|
+
response = Dependabot::RegistryClient.get(
|
|
58
|
+
url: "#{SEARCH_URL}?q=#{CGI.escape(dependency.name)}"
|
|
59
|
+
)
|
|
60
|
+
return nil unless response.status == 200
|
|
61
|
+
|
|
62
|
+
data = JSON.parse(response.body)
|
|
63
|
+
packages = data.is_a?(Hash) ? data["packages"] : nil
|
|
64
|
+
return nil unless packages.is_a?(Array)
|
|
65
|
+
|
|
66
|
+
packages.find { |pkg| pkg.is_a?(Hash) && pkg["name"] == dependency.name }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Approximates a version's release date with the earliest per-system
|
|
70
|
+
# `last_updated` (Unix epoch seconds), falling back to the top-level
|
|
71
|
+
# `last_updated`. `filter_by_cooldown` treats a nil result gracefully.
|
|
72
|
+
sig { params(version_data: T::Hash[String, Object]).returns(T.nilable(Time)) }
|
|
73
|
+
def release_time(version_data)
|
|
74
|
+
timestamps = []
|
|
75
|
+
|
|
76
|
+
systems = version_data["systems"]
|
|
77
|
+
timestamps.concat(systems.values.filter_map { |s| s["last_updated"] if s.is_a?(Hash) }) if systems.is_a?(Hash)
|
|
78
|
+
timestamps << version_data["last_updated"]
|
|
79
|
+
|
|
80
|
+
epoch = timestamps.grep(Integer).min
|
|
81
|
+
epoch ? Time.at(epoch).utc : nil
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/requirement"
|
|
6
|
+
require "dependabot/utils"
|
|
7
|
+
require "dependabot/devbox/version"
|
|
8
|
+
|
|
9
|
+
# Devbox constraints are nixpkgs version prefixes declared as `name@constraint`
|
|
10
|
+
# in devbox.json. We translate the prefix form into Gem::Requirement strings:
|
|
11
|
+
# latest -> ">= 0" (track the newest release)
|
|
12
|
+
# 3 -> "~> 3.0" (pin the major line)
|
|
13
|
+
# 3.10 -> "~> 3.10.0" (pin the minor line)
|
|
14
|
+
# 3.10.19 -> "= 3.10.19" (pin an exact version)
|
|
15
|
+
|
|
16
|
+
module Dependabot
|
|
17
|
+
module Devbox
|
|
18
|
+
class Requirement < Dependabot::Requirement
|
|
19
|
+
extend T::Sig
|
|
20
|
+
|
|
21
|
+
sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
|
|
22
|
+
def self.requirements_array(requirement_string)
|
|
23
|
+
[new(requirement_string)]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
sig { params(requirements: T.nilable(T.any(String, T::Array[String]))).void }
|
|
27
|
+
def initialize(*requirements)
|
|
28
|
+
constraints = requirements.flatten.compact.flat_map do |req_string|
|
|
29
|
+
req_string.strip.split(/\s*,\s*/).map do |part|
|
|
30
|
+
convert_devbox_constraint_to_ruby_constraint(part.strip)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
constraints = [">= 0"] if constraints.empty?
|
|
34
|
+
|
|
35
|
+
super(constraints)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
sig { params(constraint: String).returns(String) }
|
|
41
|
+
def convert_devbox_constraint_to_ruby_constraint(constraint)
|
|
42
|
+
return ">= 0" if constraint.empty? || constraint == Version::LATEST
|
|
43
|
+
# Already a Ruby/Gem requirement string (e.g. ">= 0" from base-class callers
|
|
44
|
+
# like UpdateChecker::Base#can_update? or ignored_versions entries).
|
|
45
|
+
return constraint if constraint.match?(/\A[><=~!]/)
|
|
46
|
+
|
|
47
|
+
segments = constraint.split(".")
|
|
48
|
+
case segments.length
|
|
49
|
+
when 1, 2 then "~> #{constraint}.0"
|
|
50
|
+
else "= #{constraint}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Dependabot::Utils.register_requirement_class("devbox", Dependabot::Devbox::Requirement)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/package/package_latest_version_finder"
|
|
6
|
+
require "dependabot/package/package_details"
|
|
7
|
+
require "dependabot/devbox/update_checker"
|
|
8
|
+
require "dependabot/devbox/package/package_details_fetcher"
|
|
9
|
+
|
|
10
|
+
module Dependabot
|
|
11
|
+
module Devbox
|
|
12
|
+
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
13
|
+
class LatestVersionFinder < Dependabot::Package::PackageLatestVersionFinder
|
|
14
|
+
extend T::Sig
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
sig { override.returns(T.nilable(Dependabot::Package::PackageDetails)) }
|
|
19
|
+
def package_details
|
|
20
|
+
@package_details ||= T.let(
|
|
21
|
+
Dependabot::Package::PackageDetails.new(
|
|
22
|
+
dependency: dependency,
|
|
23
|
+
releases: Package::PackageDetailsFetcher.new(
|
|
24
|
+
dependency: dependency
|
|
25
|
+
).available_versions
|
|
26
|
+
),
|
|
27
|
+
T.nilable(Dependabot::Package::PackageDetails)
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/update_checkers"
|
|
5
|
+
require "dependabot/update_checkers/base"
|
|
6
|
+
require "dependabot/devbox/version"
|
|
7
|
+
require "dependabot/devbox/requirement"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Devbox
|
|
11
|
+
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
require_relative "update_checker/latest_version_finder"
|
|
15
|
+
|
|
16
|
+
LATEST = T.let("latest", String)
|
|
17
|
+
|
|
18
|
+
sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
|
|
19
|
+
def latest_version
|
|
20
|
+
latest_version_finder.latest_version
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
|
|
24
|
+
def latest_resolvable_version
|
|
25
|
+
latest_version
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
sig { override.returns(T.nilable(String)) }
|
|
29
|
+
def latest_resolvable_version_with_no_unlock
|
|
30
|
+
dependency.version
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
sig { override.returns(T::Array[Dependabot::DependencyRequirement]) }
|
|
34
|
+
def updated_requirements
|
|
35
|
+
latest = latest_version
|
|
36
|
+
return dependency.requirements unless latest
|
|
37
|
+
|
|
38
|
+
updated = dependency.requirements.map do |req|
|
|
39
|
+
req.merge(requirement: updated_constraint(req[:requirement], latest.to_s))
|
|
40
|
+
end
|
|
41
|
+
wrap_requirements(updated)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
sig { override.returns(T::Boolean) }
|
|
47
|
+
def latest_version_resolvable_with_full_unlock?
|
|
48
|
+
false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
|
52
|
+
def updated_dependencies_after_full_unlock
|
|
53
|
+
[]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
sig { returns(LatestVersionFinder) }
|
|
57
|
+
def latest_version_finder
|
|
58
|
+
@latest_version_finder ||= T.let(
|
|
59
|
+
LatestVersionFinder.new(
|
|
60
|
+
dependency: dependency,
|
|
61
|
+
dependency_files: dependency_files,
|
|
62
|
+
credentials: credentials,
|
|
63
|
+
ignored_versions: ignored_versions,
|
|
64
|
+
security_advisories: security_advisories,
|
|
65
|
+
cooldown_options: update_cooldown
|
|
66
|
+
),
|
|
67
|
+
T.nilable(LatestVersionFinder)
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Recomputes the `name@constraint` constraint for the target version,
|
|
72
|
+
# preserving the original constraint's precision:
|
|
73
|
+
# - "latest" stays "latest" (the lockfile alone advances)
|
|
74
|
+
# - a pinned-minor constraint ("3.10") keeps its two segments, so a patch
|
|
75
|
+
# bump leaves it unchanged and only a minor/major bump rewrites it
|
|
76
|
+
# - a pinned-exact constraint ("3.10.15") tracks every segment, so any
|
|
77
|
+
# bump rewrites it
|
|
78
|
+
sig { params(old_constraint: T.nilable(String), latest: String).returns(T.nilable(String)) }
|
|
79
|
+
def updated_constraint(old_constraint, latest)
|
|
80
|
+
return old_constraint if old_constraint.nil? || old_constraint == LATEST
|
|
81
|
+
|
|
82
|
+
segment_count = old_constraint.split(".").length
|
|
83
|
+
latest.split(".").first(segment_count).join(".")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
Dependabot::UpdateCheckers.register("devbox", Dependabot::Devbox::UpdateChecker)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/version"
|
|
5
|
+
require "dependabot/utils"
|
|
6
|
+
require "sorbet-runtime"
|
|
7
|
+
|
|
8
|
+
# Devbox package versions are nixpkgs versions. Alongside standard numeric
|
|
9
|
+
# versions ("3.10.19") and short prefixes ("3", "3.10"), Devbox supports a
|
|
10
|
+
# "latest" sentinel that always resolves to the newest release, so it must
|
|
11
|
+
# sort above any concrete version.
|
|
12
|
+
|
|
13
|
+
module Dependabot
|
|
14
|
+
module Devbox
|
|
15
|
+
class Version < Dependabot::Version
|
|
16
|
+
extend T::Sig
|
|
17
|
+
|
|
18
|
+
LATEST = "latest"
|
|
19
|
+
|
|
20
|
+
# A value that sorts above any realistic nixpkgs version, used as the
|
|
21
|
+
# internal representation of the "latest" sentinel since Gem::Version
|
|
22
|
+
# cannot parse the word itself.
|
|
23
|
+
LATEST_SENTINEL = T.let("999999", String)
|
|
24
|
+
|
|
25
|
+
sig { override.params(version: VersionParameter).returns(T::Boolean) }
|
|
26
|
+
def self.correct?(version)
|
|
27
|
+
return false if version.nil?
|
|
28
|
+
return true if version.to_s.strip == LATEST
|
|
29
|
+
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
sig { override.params(version: VersionParameter).void }
|
|
34
|
+
def initialize(version)
|
|
35
|
+
@version_string = T.let(version.to_s.strip, String)
|
|
36
|
+
@latest = T.let(@version_string == LATEST, T::Boolean)
|
|
37
|
+
|
|
38
|
+
super(@latest ? LATEST_SENTINEL : version)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
sig { override.params(version: VersionParameter).returns(Dependabot::Devbox::Version) }
|
|
42
|
+
def self.new(version)
|
|
43
|
+
T.cast(super, Dependabot::Devbox::Version)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
sig { returns(T::Boolean) }
|
|
47
|
+
def latest?
|
|
48
|
+
@latest
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
sig { override.returns(String) }
|
|
52
|
+
def to_s
|
|
53
|
+
@version_string
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
sig { override.returns(String) }
|
|
57
|
+
def inspect
|
|
58
|
+
"#<#{self.class} #{@version_string}>"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
Dependabot::Utils.register_version_class("devbox", Dependabot::Devbox::Version)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/utils"
|
|
5
|
+
require "dependabot/config/file"
|
|
6
|
+
|
|
7
|
+
# The published dependabot-common gem predates the devbox entry being added
|
|
8
|
+
# to PACKAGE_MANAGER_LOOKUP. Patch validate_package_manager! to allow "devbox"
|
|
9
|
+
# until the upstream PR merges and a new gem version is published.
|
|
10
|
+
module Dependabot
|
|
11
|
+
module Utils
|
|
12
|
+
class << self
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def validate_package_manager!(package_manager)
|
|
16
|
+
return if package_manager == "devbox"
|
|
17
|
+
return if Config::File::REVERSE_PACKAGE_MANAGER_LOOKUP.key?(package_manager)
|
|
18
|
+
return if %w[dummy silent].include?(package_manager)
|
|
19
|
+
|
|
20
|
+
raise "Unsupported package_manager #{package_manager}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "dependabot/devbox/file_fetcher"
|
|
27
|
+
require "dependabot/devbox/file_parser"
|
|
28
|
+
require "dependabot/devbox/update_checker"
|
|
29
|
+
require "dependabot/devbox/file_updater"
|
|
30
|
+
require "dependabot/devbox/metadata_finder"
|
|
31
|
+
require "dependabot/devbox/package/package_details_fetcher"
|
|
32
|
+
require "dependabot/devbox/helpers"
|
|
33
|
+
require "dependabot/devbox/version"
|
|
34
|
+
require "dependabot/devbox/requirement"
|
|
35
|
+
|
|
36
|
+
require "dependabot/pull_request_creator/labeler"
|
|
37
|
+
Dependabot::PullRequestCreator::Labeler
|
|
38
|
+
.register_label_details("devbox", name: "devbox", colour: "5c4ee5")
|
|
39
|
+
|
|
40
|
+
require "dependabot/dependency"
|
|
41
|
+
Dependabot::Dependency.register_production_check("devbox", ->(_) { true })
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dependabot-devbox
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Andoni A.
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-06-29 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: dependabot-common
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.383'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.383'
|
|
27
|
+
description: Automatically update Devbox (devbox.json) package versions via Dependabot.
|
|
28
|
+
Standalone gem for use before official dependabot-core support lands.
|
|
29
|
+
email:
|
|
30
|
+
- andonialonsof@gmail.com
|
|
31
|
+
executables:
|
|
32
|
+
- dependabot-devbox-update
|
|
33
|
+
extensions: []
|
|
34
|
+
extra_rdoc_files: []
|
|
35
|
+
files:
|
|
36
|
+
- exe/dependabot-devbox-update
|
|
37
|
+
- lib/dependabot/devbox.rb
|
|
38
|
+
- lib/dependabot/devbox/file_fetcher.rb
|
|
39
|
+
- lib/dependabot/devbox/file_parser.rb
|
|
40
|
+
- lib/dependabot/devbox/file_updater.rb
|
|
41
|
+
- lib/dependabot/devbox/helpers.rb
|
|
42
|
+
- lib/dependabot/devbox/metadata_finder.rb
|
|
43
|
+
- lib/dependabot/devbox/package/package_details_fetcher.rb
|
|
44
|
+
- lib/dependabot/devbox/requirement.rb
|
|
45
|
+
- lib/dependabot/devbox/update_checker.rb
|
|
46
|
+
- lib/dependabot/devbox/update_checker/latest_version_finder.rb
|
|
47
|
+
- lib/dependabot/devbox/version.rb
|
|
48
|
+
homepage: https://github.com/andoniaf/dependabot-devbox
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata:
|
|
52
|
+
bug_tracker_uri: https://github.com/andoniaf/dependabot-devbox/issues
|
|
53
|
+
changelog_uri: https://github.com/andoniaf/dependabot-devbox/releases
|
|
54
|
+
source_code_uri: https://github.com/andoniaf/dependabot-devbox
|
|
55
|
+
rubygems_mfa_required: 'true'
|
|
56
|
+
post_install_message:
|
|
57
|
+
rdoc_options: []
|
|
58
|
+
require_paths:
|
|
59
|
+
- lib
|
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: 3.3.0
|
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
requirements: []
|
|
71
|
+
rubygems_version: 3.5.22
|
|
72
|
+
signing_key:
|
|
73
|
+
specification_version: 4
|
|
74
|
+
summary: Dependabot support for Devbox
|
|
75
|
+
test_files: []
|