dependabot-devcontainers 0.242.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/lib/dependabot/devcontainers/file_fetcher.rb +84 -0
- data/lib/dependabot/devcontainers/file_parser/feature_dependency_parser.rb +97 -0
- data/lib/dependabot/devcontainers/file_parser.rb +51 -0
- data/lib/dependabot/devcontainers/file_updater/config_updater.rb +78 -0
- data/lib/dependabot/devcontainers/file_updater.rb +85 -0
- data/lib/dependabot/devcontainers/metadata_finder.rb +21 -0
- data/lib/dependabot/devcontainers/requirement.rb +35 -0
- data/lib/dependabot/devcontainers/update_checker.rb +115 -0
- data/lib/dependabot/devcontainers/utils.rb +24 -0
- data/lib/dependabot/devcontainers/version.rb +35 -0
- data/lib/dependabot/devcontainers.rb +23 -0
- metadata +266 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bcfeb30425ec7f1f8d1c65ae2f3640b4e9448f95c1e45f8b48f40b57bc99d2a9
|
|
4
|
+
data.tar.gz: b031b0d36b8cc9102b8b27ea79217132caf4f4f37922b97479222528a483ce06
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b9875fd79c9ead1bd13008967af50ebade976a937ac2f964de80e87bb768a3ee490f49de8738b81b6aef43cd38c8d89869ebe82e2fdffb3f8a667f0fa63339f2
|
|
7
|
+
data.tar.gz: 63c9c39203c2187262bf05f1a637cfbd475f1d05f50d5f5e7ab5469011aa7fdba4de5dce9109d81dd573799282f623b283624dafad9c5ffb4f11bd13e5a508d1
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/file_fetchers"
|
|
5
|
+
require "dependabot/file_fetchers/base"
|
|
6
|
+
require "dependabot/devcontainers/utils"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Devcontainers
|
|
10
|
+
class FileFetcher < Dependabot::FileFetchers::Base
|
|
11
|
+
def self.required_files_in?(filenames)
|
|
12
|
+
# There's several other places a devcontainer.json can be checked into
|
|
13
|
+
# See: https://containers.dev/implementors/spec/#devcontainerjson
|
|
14
|
+
filenames.any? { |f| f.end_with?("devcontainer.json") }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.required_files_message
|
|
18
|
+
"Repo must contain a dev container configuration file."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def fetch_files
|
|
22
|
+
fetched_files = []
|
|
23
|
+
fetched_files += root_files
|
|
24
|
+
fetched_files += scoped_files
|
|
25
|
+
fetched_files += custom_directory_files
|
|
26
|
+
return fetched_files if fetched_files.any?
|
|
27
|
+
|
|
28
|
+
raise Dependabot::DependencyFileNotFound.new(
|
|
29
|
+
nil,
|
|
30
|
+
"Neither .devcontainer.json nor .devcontainer/devcontainer.json nor " \
|
|
31
|
+
".devcontainer/<anything>/devcontainer.json found in #{directory}"
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def root_files
|
|
38
|
+
fetch_config_and_lockfile_from(".")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def scoped_files
|
|
42
|
+
return [] unless devcontainer_directory
|
|
43
|
+
|
|
44
|
+
fetch_config_and_lockfile_from(".devcontainer")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def custom_directory_files
|
|
48
|
+
return [] unless devcontainer_directory
|
|
49
|
+
|
|
50
|
+
custom_directories.flat_map do |directory|
|
|
51
|
+
fetch_config_and_lockfile_from(directory.path)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def custom_directories
|
|
56
|
+
repo_contents(dir: ".devcontainer").select { |f| f.type == "dir" && f.name != ".devcontainer" }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def devcontainer_directory
|
|
60
|
+
return @devcontainer_directory if defined?(@devcontainer_directory)
|
|
61
|
+
|
|
62
|
+
@devcontainer_directory = repo_contents.find { |f| f.type == "dir" && f.name == ".devcontainer" }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def fetch_config_and_lockfile_from(directory)
|
|
66
|
+
files = []
|
|
67
|
+
|
|
68
|
+
config_name = Utils.expected_config_basename(directory)
|
|
69
|
+
config_file = fetch_file_if_present(File.join(directory, config_name))
|
|
70
|
+
return files unless config_file
|
|
71
|
+
|
|
72
|
+
files << config_file
|
|
73
|
+
|
|
74
|
+
lockfile_name = Utils.expected_lockfile_name(File.basename(config_file.name))
|
|
75
|
+
lockfile = fetch_support_file(File.join(directory, lockfile_name))
|
|
76
|
+
files << lockfile if lockfile
|
|
77
|
+
|
|
78
|
+
files
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
Dependabot::FileFetchers.register("devcontainers", Dependabot::Devcontainers::FileFetcher)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/devcontainers/requirement"
|
|
5
|
+
require "dependabot/file_parsers/base"
|
|
6
|
+
require "dependabot/shared_helpers"
|
|
7
|
+
require "dependabot/dependency"
|
|
8
|
+
require "json"
|
|
9
|
+
require "uri"
|
|
10
|
+
|
|
11
|
+
module Dependabot
|
|
12
|
+
module Devcontainers
|
|
13
|
+
class FileParser < Dependabot::FileParsers::Base
|
|
14
|
+
class FeatureDependencyParser
|
|
15
|
+
def initialize(config_dependency_file:, repo_contents_path:, credentials:)
|
|
16
|
+
@config_dependency_file = config_dependency_file
|
|
17
|
+
@repo_contents_path = repo_contents_path
|
|
18
|
+
@credentials = credentials
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parse
|
|
22
|
+
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
|
23
|
+
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
24
|
+
parse_cli_json(evaluate_with_cli)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def base_dir
|
|
32
|
+
File.dirname(config_dependency_file.path)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def config_name
|
|
36
|
+
File.basename(config_dependency_file.path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def config_contents
|
|
40
|
+
config_dependency_file.content
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# https://github.com/devcontainers/cli/blob/9444540283b236298c28f397dea879e7ec222ca1/src/spec-node/devContainersSpecCLI.ts#L1072
|
|
44
|
+
def evaluate_with_cli
|
|
45
|
+
raise "config_name must be a string" unless config_name.is_a?(String) && !config_name.empty?
|
|
46
|
+
|
|
47
|
+
cmd = "devcontainer outdated --workspace-folder . --config #{config_name} --output-format json"
|
|
48
|
+
Dependabot.logger.info("Running command: #{cmd}")
|
|
49
|
+
|
|
50
|
+
json = SharedHelpers.run_shell_command(
|
|
51
|
+
cmd,
|
|
52
|
+
stderr_to_stdout: false
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
JSON.parse(json)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def parse_cli_json(json)
|
|
59
|
+
dependencies = []
|
|
60
|
+
|
|
61
|
+
features = json["features"]
|
|
62
|
+
features.each do |feature, versions_object|
|
|
63
|
+
name, requirement = feature.split(":")
|
|
64
|
+
|
|
65
|
+
# Skip sha pinned tags for now. Ideally the devcontainers CLI would give us updated SHA info
|
|
66
|
+
next if name.end_with?("@sha256")
|
|
67
|
+
|
|
68
|
+
# Skip deprecated features until `devcontainer features info tag`
|
|
69
|
+
# and `devcontainer upgrade` work with them. See https://github.com/devcontainers/cli/issues/712
|
|
70
|
+
next unless name.include?("/")
|
|
71
|
+
|
|
72
|
+
current = versions_object["current"]
|
|
73
|
+
|
|
74
|
+
dep = Dependency.new(
|
|
75
|
+
name: name,
|
|
76
|
+
version: current,
|
|
77
|
+
package_manager: "devcontainers",
|
|
78
|
+
requirements: [
|
|
79
|
+
{
|
|
80
|
+
requirement: requirement,
|
|
81
|
+
file: config_dependency_file.name,
|
|
82
|
+
groups: ["feature"],
|
|
83
|
+
source: nil
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
dependencies << dep
|
|
89
|
+
end
|
|
90
|
+
dependencies
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
attr_reader :config_dependency_file, :repo_contents_path, :credentials
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/file_parsers"
|
|
5
|
+
require "dependabot/file_parsers/base"
|
|
6
|
+
require "dependabot/devcontainers/version"
|
|
7
|
+
require "dependabot/devcontainers/file_parser/feature_dependency_parser"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Devcontainers
|
|
11
|
+
class FileParser < Dependabot::FileParsers::Base
|
|
12
|
+
require "dependabot/file_parsers/base/dependency_set"
|
|
13
|
+
|
|
14
|
+
def parse
|
|
15
|
+
dependency_set = DependencySet.new
|
|
16
|
+
|
|
17
|
+
config_dependency_files.each do |config_dependency_file|
|
|
18
|
+
parse_features(config_dependency_file).each do |dep|
|
|
19
|
+
dependency_set << dep
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
dependency_set.dependencies
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def check_required_files
|
|
29
|
+
return if config_dependency_files.any?
|
|
30
|
+
|
|
31
|
+
raise "No dev container configuration!"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_features(config_dependency_file)
|
|
35
|
+
FeatureDependencyParser.new(
|
|
36
|
+
config_dependency_file: config_dependency_file,
|
|
37
|
+
repo_contents_path: repo_contents_path,
|
|
38
|
+
credentials: credentials
|
|
39
|
+
).parse
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def config_dependency_files
|
|
43
|
+
@config_dependency_files ||= dependency_files.select do |f|
|
|
44
|
+
f.name.end_with?("devcontainer.json")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Dependabot::FileParsers.register("devcontainers", Dependabot::Devcontainers::FileParser)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/file_updaters/base"
|
|
5
|
+
require "dependabot/shared_helpers"
|
|
6
|
+
require "dependabot/logger"
|
|
7
|
+
require "dependabot/devcontainers/utils"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Devcontainers
|
|
11
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
12
|
+
class ConfigUpdater
|
|
13
|
+
def initialize(feature:, requirement:, version:, manifest:, repo_contents_path:, credentials:)
|
|
14
|
+
@feature = feature
|
|
15
|
+
@requirement = requirement
|
|
16
|
+
@version = version
|
|
17
|
+
@manifest = manifest
|
|
18
|
+
@repo_contents_path = repo_contents_path
|
|
19
|
+
@credentials = credentials
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update
|
|
23
|
+
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
|
24
|
+
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
25
|
+
update_manifests(
|
|
26
|
+
target_requirement: requirement,
|
|
27
|
+
target_version: version
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
[File.read(manifest_name), File.read(lockfile_name)].compact
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def base_dir
|
|
38
|
+
File.dirname(manifest.path)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def manifest_name
|
|
42
|
+
File.basename(manifest.path)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def lockfile_name
|
|
46
|
+
Utils.expected_lockfile_name(manifest_name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def update_manifests(target_requirement:, target_version:)
|
|
50
|
+
# First force target version to upgrade lockfile.
|
|
51
|
+
run_devcontainer_upgrade(target_version)
|
|
52
|
+
|
|
53
|
+
# Now replace specific version back with target requirement
|
|
54
|
+
force_target_requirement(manifest_name, from: target_version, to: target_requirement)
|
|
55
|
+
force_target_requirement(lockfile_name, from: target_version, to: target_requirement)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def force_target_requirement(file_name, from:, to:)
|
|
59
|
+
File.write(file_name, File.read(file_name).gsub("#{feature}:#{from}", "#{feature}:#{to}"))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def run_devcontainer_upgrade(target_version)
|
|
63
|
+
cmd = "devcontainer upgrade " \
|
|
64
|
+
"--workspace-folder . " \
|
|
65
|
+
"--feature #{feature} " \
|
|
66
|
+
"--config #{manifest_name} " \
|
|
67
|
+
"--target-version #{target_version}"
|
|
68
|
+
|
|
69
|
+
Dependabot.logger.info("Running command: `#{cmd}`")
|
|
70
|
+
|
|
71
|
+
SharedHelpers.run_shell_command(cmd, stderr_to_stdout: false)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
attr_reader :feature, :requirement, :version, :manifest, :repo_contents_path, :credentials
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/file_updaters"
|
|
5
|
+
require "dependabot/file_updaters/base"
|
|
6
|
+
require "dependabot/devcontainers/file_updater/config_updater"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Devcontainers
|
|
10
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
11
|
+
def self.updated_files_regex
|
|
12
|
+
[
|
|
13
|
+
/^\.?devcontainer\.json$/,
|
|
14
|
+
/^\.?devcontainer-lock\.json$/
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def updated_dependency_files
|
|
19
|
+
updated_files = []
|
|
20
|
+
|
|
21
|
+
manifests.each do |manifest|
|
|
22
|
+
requirement = dependency.requirements.find { |req| req[:file] == manifest.name }
|
|
23
|
+
next unless requirement
|
|
24
|
+
|
|
25
|
+
config_contents, lockfile_contents = update(manifest, requirement)
|
|
26
|
+
|
|
27
|
+
updated_files << updated_file(file: manifest, content: config_contents) if file_changed?(manifest)
|
|
28
|
+
|
|
29
|
+
lockfile = lockfile_for(manifest)
|
|
30
|
+
|
|
31
|
+
updated_files << updated_file(file: lockfile, content: lockfile_contents) if lockfile && lockfile_contents
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
updated_files
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def dependency
|
|
40
|
+
# TODO: Handle one dependency at a time
|
|
41
|
+
dependencies.first
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def check_required_files
|
|
45
|
+
return if dependency_files.any?
|
|
46
|
+
|
|
47
|
+
raise "No dev container configuration!"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def manifests
|
|
51
|
+
@manifests ||= dependency_files.select do |f|
|
|
52
|
+
f.name.end_with?("devcontainer.json")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def lockfile_for(manifest)
|
|
57
|
+
lockfile_name = lockfile_name_for(manifest)
|
|
58
|
+
|
|
59
|
+
dependency_files.find do |f|
|
|
60
|
+
f.name == lockfile_name
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def lockfile_name_for(manifest)
|
|
65
|
+
basename = File.basename(manifest.name)
|
|
66
|
+
lockfile_name = Utils.expected_lockfile_name(basename)
|
|
67
|
+
|
|
68
|
+
manifest.name.delete_suffix(basename).concat(lockfile_name)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def update(manifest, requirement)
|
|
72
|
+
ConfigUpdater.new(
|
|
73
|
+
feature: dependency.name,
|
|
74
|
+
requirement: requirement[:requirement],
|
|
75
|
+
version: dependency.version,
|
|
76
|
+
manifest: manifest,
|
|
77
|
+
repo_contents_path: repo_contents_path,
|
|
78
|
+
credentials: credentials
|
|
79
|
+
).update
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
Dependabot::FileUpdaters.register("devcontainers", Dependabot::Devcontainers::FileUpdater)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/metadata_finders"
|
|
5
|
+
require "dependabot/metadata_finders/base"
|
|
6
|
+
|
|
7
|
+
module Dependabot
|
|
8
|
+
module Devcontainers
|
|
9
|
+
class MetadataFinder < Dependabot::MetadataFinders::Base
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def look_up_source
|
|
13
|
+
# TODO: Make upstream changes to dev container CLI to point to docs.
|
|
14
|
+
# Specifically, 'devcontainers features info' can be augmented to expose documentationUrl
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Dependabot::MetadataFinders.register("devcontainers", Dependabot::Devcontainers::MetadataFinder)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
require "dependabot/requirement"
|
|
7
|
+
require "dependabot/utils"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Devcontainers
|
|
11
|
+
class Requirement < Dependabot::Requirement
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
# For consistency with other languages, we define a requirements array.
|
|
15
|
+
# Devcontainers don't have an `OR` separator for requirements, so it
|
|
16
|
+
# always contains a single element.
|
|
17
|
+
sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
|
|
18
|
+
def self.requirements_array(requirement_string)
|
|
19
|
+
[new(requirement_string)]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Patches Gem::Requirement to make it accept requirement strings like
|
|
23
|
+
# "~> 4.2.5, >= 4.2.5.1" without first needing to split them.
|
|
24
|
+
def initialize(*requirements)
|
|
25
|
+
requirements = requirements.flatten.flat_map do |req_string|
|
|
26
|
+
req_string.split(",").map(&:strip)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
super(requirements)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Dependabot::Utils.register_requirement_class("devcontainers", Dependabot::Devcontainers::Requirement)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/update_checkers"
|
|
5
|
+
require "dependabot/update_checkers/base"
|
|
6
|
+
require "dependabot/devcontainers/version"
|
|
7
|
+
require "dependabot/update_checkers/version_filters"
|
|
8
|
+
require "dependabot/devcontainers/requirement"
|
|
9
|
+
|
|
10
|
+
module Dependabot
|
|
11
|
+
module Devcontainers
|
|
12
|
+
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
13
|
+
def latest_version
|
|
14
|
+
@latest_version ||= fetch_latest_version
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def latest_resolvable_version
|
|
18
|
+
latest_version # TODO
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def updated_requirements
|
|
22
|
+
dependency.requirements.map do |requirement|
|
|
23
|
+
required_version = version_class.new(requirement[:requirement])
|
|
24
|
+
updated_requirement = remove_precision_changes(viable_candidates, required_version).last
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
file: requirement[:file],
|
|
28
|
+
requirement: updated_requirement,
|
|
29
|
+
groups: requirement[:groups],
|
|
30
|
+
source: requirement[:source]
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def latest_resolvable_version_with_no_unlock
|
|
36
|
+
raise NotImplementedError
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def viable_candidates
|
|
42
|
+
@viable_candidates ||= fetch_viable_candidates
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def fetch_viable_candidates
|
|
46
|
+
candidates = comparable_versions_from_registry
|
|
47
|
+
candidates = filter_ignored(candidates)
|
|
48
|
+
candidates.sort
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def fetch_latest_version
|
|
52
|
+
return current_version unless viable_candidates.any?
|
|
53
|
+
|
|
54
|
+
viable_candidates.last
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def remove_precision_changes(versions, required_version)
|
|
58
|
+
versions.select do |version|
|
|
59
|
+
version.same_precision?(required_version)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def filter_ignored(versions)
|
|
64
|
+
filtered =
|
|
65
|
+
versions.reject do |version|
|
|
66
|
+
ignore_requirements.any? { |r| version.satisfies?(r) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if @raise_on_ignored &&
|
|
70
|
+
filter_lower_versions(filtered).empty? &&
|
|
71
|
+
filter_lower_versions(versions).any?
|
|
72
|
+
raise AllVersionsIgnored
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
filtered
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def comparable_versions_from_registry
|
|
79
|
+
tags_from_registry.filter_map do |tag|
|
|
80
|
+
version_class.correct?(tag) && version_class.new(tag)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def tags_from_registry
|
|
85
|
+
@tags_from_registry ||= fetch_tags_from_registry
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def fetch_tags_from_registry
|
|
89
|
+
cmd = "devcontainer features info tags #{dependency.name} --output-format json"
|
|
90
|
+
|
|
91
|
+
Dependabot.logger.info("Running command: `#{cmd}`")
|
|
92
|
+
|
|
93
|
+
output = SharedHelpers.run_shell_command(cmd, stderr_to_stdout: false)
|
|
94
|
+
|
|
95
|
+
JSON.parse(output).fetch("publishedTags")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def filter_lower_versions(versions)
|
|
99
|
+
versions.select do |version|
|
|
100
|
+
version > current_version
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def latest_version_resolvable_with_full_unlock?
|
|
105
|
+
false # TODO
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def updated_dependencies_after_full_unlock
|
|
109
|
+
raise NotImplementedError
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
Dependabot::UpdateCheckers.register("devcontainers", Dependabot::Devcontainers::UpdateChecker)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Devcontainers
|
|
6
|
+
module Utils
|
|
7
|
+
def self.expected_config_basename(directory)
|
|
8
|
+
root_directory?(directory) ? ".devcontainer.json" : "devcontainer.json"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.root_directory?(directory)
|
|
12
|
+
Pathname.new(directory).cleanpath.to_path == Pathname.new(".").cleanpath.to_path
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.expected_lockfile_name(config_file_name)
|
|
16
|
+
if config_file_name.start_with?(".")
|
|
17
|
+
".devcontainer-lock.json"
|
|
18
|
+
else
|
|
19
|
+
"devcontainer-lock.json"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/version"
|
|
5
|
+
require "dependabot/utils"
|
|
6
|
+
|
|
7
|
+
module Dependabot
|
|
8
|
+
module Devcontainers
|
|
9
|
+
class Version < Dependabot::Version
|
|
10
|
+
def same_precision?(other)
|
|
11
|
+
precision == other.precision
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def satisfies?(requirement)
|
|
15
|
+
requirement.satisfied_by?(self)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def <=>(other)
|
|
19
|
+
if self == other
|
|
20
|
+
precision <=> other.precision
|
|
21
|
+
else
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
def precision
|
|
29
|
+
segments.size
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Dependabot::Utils.register_version_class("devcontainers", Dependabot::Devcontainers::Version)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# These all need to be required so the various classes can be registered in a
|
|
5
|
+
# lookup table of package manager names to concrete classes.
|
|
6
|
+
require "dependabot/devcontainers/file_fetcher"
|
|
7
|
+
require "dependabot/devcontainers/file_parser"
|
|
8
|
+
require "dependabot/devcontainers/update_checker"
|
|
9
|
+
require "dependabot/devcontainers/file_updater"
|
|
10
|
+
require "dependabot/devcontainers/metadata_finder"
|
|
11
|
+
require "dependabot/devcontainers/requirement"
|
|
12
|
+
require "dependabot/devcontainers/version"
|
|
13
|
+
|
|
14
|
+
require "dependabot/pull_request_creator/labeler"
|
|
15
|
+
Dependabot::PullRequestCreator::Labeler
|
|
16
|
+
.register_label_details("devcontainers", name: "devcontainers_package_manager", colour: "2753E3")
|
|
17
|
+
|
|
18
|
+
require "dependabot/dependency"
|
|
19
|
+
Dependabot::Dependency
|
|
20
|
+
.register_production_check("devcontainers", ->(_) { true })
|
|
21
|
+
|
|
22
|
+
require "dependabot/utils"
|
|
23
|
+
Dependabot::Utils.register_always_clone("devcontainers")
|
metadata
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dependabot-devcontainers
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.242.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Dependabot
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2024-01-22 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.242.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.242.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: debug
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 1.8.0
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 1.8.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: gpgme
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '13'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '13'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.12'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.12'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rspec-its
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.3'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.3'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rspec-sorbet
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: 1.9.2
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: 1.9.2
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rubocop
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: 1.58.0
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: 1.58.0
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: rubocop-performance
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: 1.19.0
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: 1.19.0
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: rubocop-sorbet
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - "~>"
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: 0.7.3
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - "~>"
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: 0.7.3
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: stackprof
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - "~>"
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: 0.2.16
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - "~>"
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: 0.2.16
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: turbo_tests
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - "~>"
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: 2.2.0
|
|
174
|
+
type: :development
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - "~>"
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: 2.2.0
|
|
181
|
+
- !ruby/object:Gem::Dependency
|
|
182
|
+
name: vcr
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - "~>"
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '6.1'
|
|
188
|
+
type: :development
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - "~>"
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '6.1'
|
|
195
|
+
- !ruby/object:Gem::Dependency
|
|
196
|
+
name: webmock
|
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
|
198
|
+
requirements:
|
|
199
|
+
- - "~>"
|
|
200
|
+
- !ruby/object:Gem::Version
|
|
201
|
+
version: '3.18'
|
|
202
|
+
type: :development
|
|
203
|
+
prerelease: false
|
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
205
|
+
requirements:
|
|
206
|
+
- - "~>"
|
|
207
|
+
- !ruby/object:Gem::Version
|
|
208
|
+
version: '3.18'
|
|
209
|
+
- !ruby/object:Gem::Dependency
|
|
210
|
+
name: webrick
|
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
|
212
|
+
requirements:
|
|
213
|
+
- - ">="
|
|
214
|
+
- !ruby/object:Gem::Version
|
|
215
|
+
version: '1.7'
|
|
216
|
+
type: :development
|
|
217
|
+
prerelease: false
|
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
219
|
+
requirements:
|
|
220
|
+
- - ">="
|
|
221
|
+
- !ruby/object:Gem::Version
|
|
222
|
+
version: '1.7'
|
|
223
|
+
description: Dependabot-Devcontainers provides support for managing dev container
|
|
224
|
+
versioning via Dependabot.
|
|
225
|
+
email: opensource@github.com
|
|
226
|
+
executables: []
|
|
227
|
+
extensions: []
|
|
228
|
+
extra_rdoc_files: []
|
|
229
|
+
files:
|
|
230
|
+
- lib/dependabot/devcontainers.rb
|
|
231
|
+
- lib/dependabot/devcontainers/file_fetcher.rb
|
|
232
|
+
- lib/dependabot/devcontainers/file_parser.rb
|
|
233
|
+
- lib/dependabot/devcontainers/file_parser/feature_dependency_parser.rb
|
|
234
|
+
- lib/dependabot/devcontainers/file_updater.rb
|
|
235
|
+
- lib/dependabot/devcontainers/file_updater/config_updater.rb
|
|
236
|
+
- lib/dependabot/devcontainers/metadata_finder.rb
|
|
237
|
+
- lib/dependabot/devcontainers/requirement.rb
|
|
238
|
+
- lib/dependabot/devcontainers/update_checker.rb
|
|
239
|
+
- lib/dependabot/devcontainers/utils.rb
|
|
240
|
+
- lib/dependabot/devcontainers/version.rb
|
|
241
|
+
homepage: https://github.com/dependabot/dependabot-core
|
|
242
|
+
licenses:
|
|
243
|
+
- Nonstandard
|
|
244
|
+
metadata:
|
|
245
|
+
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
246
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.242.0
|
|
247
|
+
post_install_message:
|
|
248
|
+
rdoc_options: []
|
|
249
|
+
require_paths:
|
|
250
|
+
- lib
|
|
251
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
252
|
+
requirements:
|
|
253
|
+
- - ">="
|
|
254
|
+
- !ruby/object:Gem::Version
|
|
255
|
+
version: 3.1.0
|
|
256
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
257
|
+
requirements:
|
|
258
|
+
- - ">="
|
|
259
|
+
- !ruby/object:Gem::Version
|
|
260
|
+
version: 3.1.0
|
|
261
|
+
requirements: []
|
|
262
|
+
rubygems_version: 3.3.26
|
|
263
|
+
signing_key:
|
|
264
|
+
specification_version: 4
|
|
265
|
+
summary: Provides Dependabot support for Dev Containers
|
|
266
|
+
test_files: []
|