-
Notifications
You must be signed in to change notification settings - Fork 971
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8445 from joshspicer/joshspicer/devcontainers
Add `devcontainers` ecosystem
- Loading branch information
Showing
28 changed files
with
1,264 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
devcontainers/lib/dependabot/devcontainers/file_parser/feature_dependency_parser.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# typed: true | ||
# frozen_string_literal: true | ||
|
||
require "dependabot/devcontainers/requirement" | ||
require "dependabot/file_parsers/base" | ||
require "dependabot/shared_helpers" | ||
require "dependabot/dependency" | ||
require "json" | ||
require "uri" | ||
|
||
module Dependabot | ||
module Devcontainers | ||
class FileParser < Dependabot::FileParsers::Base | ||
class FeatureDependencyParser | ||
def initialize(config_dependency_file:, repo_contents_path:, credentials:) | ||
@config_dependency_file = config_dependency_file | ||
@repo_contents_path = repo_contents_path | ||
@credentials = credentials | ||
end | ||
|
||
def parse | ||
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do | ||
SharedHelpers.with_git_configured(credentials: credentials) do | ||
parse_cli_json(evaluate_with_cli) | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
def base_dir | ||
File.dirname(config_dependency_file.path) | ||
end | ||
|
||
def config_name | ||
File.basename(config_dependency_file.path) | ||
end | ||
|
||
def config_contents | ||
config_dependency_file.content | ||
end | ||
|
||
# https://github.com/devcontainers/cli/blob/9444540283b236298c28f397dea879e7ec222ca1/src/spec-node/devContainersSpecCLI.ts#L1072 | ||
def evaluate_with_cli | ||
raise "config_name must be a string" unless config_name.is_a?(String) && !config_name.empty? | ||
|
||
cmd = "devcontainer outdated --workspace-folder . --config #{config_name} --output-format json" | ||
Dependabot.logger.info("Running command: #{cmd}") | ||
|
||
json = SharedHelpers.run_shell_command( | ||
cmd, | ||
stderr_to_stdout: false | ||
) | ||
|
||
JSON.parse(json) | ||
end | ||
|
||
def parse_cli_json(json) | ||
dependencies = [] | ||
|
||
features = json["features"] | ||
features.each do |feature, versions_object| | ||
name, requirement = feature.split(":") | ||
|
||
# Skip sha pinned tags for now. Ideally the devcontainers CLI would give us updated SHA info | ||
next if name.end_with?("@sha256") | ||
|
||
# Skip deprecated features until `devcontainer features info tag` | ||
# and `devcontainer upgrade` work with them. See https://github.com/devcontainers/cli/issues/712 | ||
next unless name.include?("/") | ||
|
||
current = versions_object["current"] | ||
|
||
dep = Dependency.new( | ||
name: name, | ||
version: current, | ||
package_manager: "devcontainers", | ||
requirements: [ | ||
{ | ||
requirement: requirement, | ||
file: config_dependency_file.name, | ||
groups: ["feature"], | ||
source: nil | ||
} | ||
] | ||
) | ||
|
||
dependencies << dep | ||
end | ||
dependencies | ||
end | ||
|
||
attr_reader :config_dependency_file, :repo_contents_path, :credentials | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
devcontainers/lib/dependabot/devcontainers/file_updater/config_updater.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# typed: true | ||
# frozen_string_literal: true | ||
|
||
require "dependabot/file_updaters/base" | ||
require "dependabot/shared_helpers" | ||
require "dependabot/logger" | ||
require "dependabot/devcontainers/utils" | ||
|
||
module Dependabot | ||
module Devcontainers | ||
class FileUpdater < Dependabot::FileUpdaters::Base | ||
class ConfigUpdater | ||
def initialize(feature:, requirement:, version:, manifest:, repo_contents_path:, credentials:) | ||
@feature = feature | ||
@requirement = requirement | ||
@version = version | ||
@manifest = manifest | ||
@repo_contents_path = repo_contents_path | ||
@credentials = credentials | ||
end | ||
|
||
def update | ||
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do | ||
SharedHelpers.with_git_configured(credentials: credentials) do | ||
update_manifests( | ||
target_requirement: requirement, | ||
target_version: version | ||
) | ||
|
||
[File.read(manifest_name), File.read(lockfile_name)].compact | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
def base_dir | ||
File.dirname(manifest.path) | ||
end | ||
|
||
def manifest_name | ||
File.basename(manifest.path) | ||
end | ||
|
||
def lockfile_name | ||
Utils.expected_lockfile_name(manifest_name) | ||
end | ||
|
||
def update_manifests(target_requirement:, target_version:) | ||
# First force target version to upgrade lockfile. | ||
run_devcontainer_upgrade(target_version) | ||
|
||
# Now replace specific version back with target requirement | ||
force_target_requirement(manifest_name, from: target_version, to: target_requirement) | ||
force_target_requirement(lockfile_name, from: target_version, to: target_requirement) | ||
end | ||
|
||
def force_target_requirement(file_name, from:, to:) | ||
File.write(file_name, File.read(file_name).gsub("#{feature}:#{from}", "#{feature}:#{to}")) | ||
end | ||
|
||
def run_devcontainer_upgrade(target_version) | ||
cmd = "devcontainer upgrade " \ | ||
"--workspace-folder . " \ | ||
"--feature #{feature} " \ | ||
"--config #{manifest_name} " \ | ||
"--target-version #{target_version}" | ||
|
||
Dependabot.logger.info("Running command: `#{cmd}`") | ||
|
||
SharedHelpers.run_shell_command(cmd, stderr_to_stdout: false) | ||
end | ||
|
||
attr_reader :feature, :requirement, :version, :manifest, :repo_contents_path, :credentials | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.