Files

241 lines
7.2 KiB
Ruby
Raw Permalink Normal View History

2022-08-23 15:03:43 -07:00
#!/usr/bin/env ruby
2022-09-14 11:27:10 -07:00
require "date"
2022-08-23 15:03:43 -07:00
require "json"
require "net/http"
2022-09-14 11:27:10 -07:00
require "open3"
2022-08-23 15:03:43 -07:00
require "optparse"
require "pathname"
2022-09-14 11:27:10 -07:00
require "tmpdir"
2022-08-23 15:03:43 -07:00
options = {
2022-09-14 11:27:10 -07:00
assets_file: "azure_devops.tgz",
2022-09-14 13:25:27 -07:00
pipeline_assets_dir: "pipelines",
2022-09-01 15:14:01 -04:00
root_dir: File.join(ENV["CODESPACE_VSCODE_FOLDER"], "azure_devops/bootstrap"),
2022-09-14 11:27:10 -07:00
forecast_source_file: "jobs.json"
2022-08-23 15:03:43 -07:00
}
OptionParser.new do |opt|
opt.on('--organization ORGANIZATION') { |o| options[:organization] = o }
opt.on('--project PROJECT') { |o| options[:project] = o }
opt.on('--access-token ACCESS_TOKEN') { |o| options[:access_token] = o }
end.parse!
[:organization, :project, :access_token].each do |key|
raise OptionParser::MissingArgument, key if options[key].nil?
end
def post_request(uri, body, access_token)
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Post.new(uri)
request.basic_auth "", access_token
request.body = body.to_json
request.content_type = 'application/json'
response = http.request request
abort_on_ado_error!(response, uri)
2022-08-23 15:03:43 -07:00
yield(response) if block_given?
sleep(5)
JSON.parse(response.body)
end
end
def get_request(uri, access_token)
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri)
request.basic_auth "", access_token
response = http.request request
yield(response) if block_given?
2022-08-23 15:03:43 -07:00
JSON.parse(response.body)
end
end
def abort_on_ado_error!(response, uri)
return if response.code.start_with?("20")
message = case response.code
when "401"
2023-01-30 13:58:29 -05:00
"Please ensure that your Azure DevOps access token has the correct permissions."
when "404"
JSON.parse(response.body)["message"]
when "302"
2023-01-30 13:58:29 -05:00
"Please ensure that your Azure DevOps access token is entered correctly"
else
begin
JSON.parse(response.body)["message"]
rescue JSON::ParserError
response.body
end
end
abort "Error encountered on request:#{uri}\n StatusCode:#{response.code}\n Message: '#{message}'"
end
def project_exists?(options)
organization = options[:organization]
project = options[:project]
access_token = options[:access_token]
uri = URI("https://dev.azure.com/#{organization}/_apis/projects/#{project}?api-version=6.0")
get_request(uri, access_token) do |response|
return response.code.start_with?("20")
end
end
2022-08-23 15:03:43 -07:00
def create_project(options)
organization = options[:organization]
project = options[:project]
access_token = options[:access_token]
puts "Creating project #{project} in organization #{organization}..."
uri = URI("https://dev.azure.com/#{organization}/_apis/projects?api-version=6.0")
project_to_create = {
name: project,
2022-11-03 12:47:40 -07:00
description: "Project to be used for GitHub Actions Importer Labs",
2022-08-23 15:03:43 -07:00
capabilities: {
versioncontrol: {
sourceControlType: "Git"
},
processTemplate: {
templateTypeId: "6b724908-ef14-45cf-84f8-768b5384da45"
}
}
}
post_request(uri, project_to_create, access_token)
2022-08-23 15:03:43 -07:00
end
def create_repository(options)
organization = options[:organization]
project = options[:project]
# use the default repository created when the project is created
repository = options[:project]
access_token = options[:access_token]
root_dir = options[:root_dir]
2022-09-14 11:27:10 -07:00
assets_file = options[:assets_file]
2022-09-14 13:25:27 -07:00
tmp_dir = Dir.mktmpdir
puts "Extracting assets..."
_, _, st = Open3.capture3("tar", "-xzf", File.join(root_dir, assets_file), "-C", tmp_dir)
abort unless st.exitstatus == 0
uri = URI("https://dev.azure.com/#{organization}/#{project}/_apis/git/repositories/#{repository}/pushes?api-version=7.1-preview.2")
puts "Creating repository #{repository}..."
root = Pathname.new(root_dir)
repository_to_create = {
refUpdates: [{
name: "refs/heads/main",
oldObjectId: "0000000000000000000000000000000000000000"
}],
commits: [{
comment: "Initial commit.",
changes: Dir[File.join(tmp_dir, "**/*.{cs,csproj,sln,yml}")].reject {|f| File.directory?(f) }.map do |entry|
2022-09-14 13:25:27 -07:00
{
changeType: "add",
item: {
path: File.join("/", Pathname.new(entry).relative_path_from(tmp_dir).to_s)
},
newContent: {
content: File.read(entry),
contentType: "rawText"
2022-09-14 11:27:10 -07:00
}
2022-09-14 13:25:27 -07:00
}
end.flatten
}]
}
2022-08-23 15:03:43 -07:00
result = post_request(uri, repository_to_create, access_token)
2022-09-14 13:25:27 -07:00
[result, tmp_dir]
2022-08-23 15:03:43 -07:00
end
2022-09-14 13:25:27 -07:00
def create_yaml_pipelines(options, repository, tmp_dir)
2022-08-23 15:03:43 -07:00
organization = options[:organization]
project = options[:project]
repository_name = repository.dig("repository", "name")
repository_id = repository.dig("repository", "id")
access_token = options[:access_token]
pipeline_assets_dir = options[:pipeline_assets_dir]
puts "Creating yaml pipelines..."
uri = URI("https://dev.azure.com/#{organization}/#{project}/_apis/pipelines?api-version=6.0-preview.1")
2022-09-14 13:25:27 -07:00
root = Pathname.new(tmp_dir)
Dir[File.join(tmp_dir, pipeline_assets_dir, "yml", "*")].each do |entry|
2022-08-23 15:03:43 -07:00
puts "Creating pipeline #{entry}..."
pipeline_to_create = {
folder: "pipelines",
name: File.basename(entry, ".yml"),
configuration: {
type: "yaml",
path: File.join("/", Pathname.new(entry).relative_path_from(root).to_s),
repository: {
id: repository_id,
name: repository_name,
type: "azureReposGit"
}
}
}
post_request(uri, pipeline_to_create, access_token)
2022-08-23 15:03:43 -07:00
end
end
def create_classic_pipelines(options, repository)
organization = options[:organization]
project = options[:project]
repository_name = repository.dig("repository", "name")
repository_id = repository.dig("repository", "id")
access_token = options[:access_token]
root_dir = options[:root_dir]
pipeline_assets_dir = options[:pipeline_assets_dir]
puts "Creating classic pipelines..."
uri = URI("https://dev.azure.com/#{organization}/#{project}/_apis/build/definitions?api-version=7.1-preview.7")
root = Pathname.new(root_dir)
Dir[File.join(root_dir, pipeline_assets_dir, "classic", "*")].each do |entry|
puts "Creating pipeline #{entry}..."
pipeline_to_create = JSON.parse(File.read(entry))
pipeline_to_create["repository"]["name"] = repository_name
pipeline_to_create["repository"]["id"] = repository_id
post_request(uri, pipeline_to_create, access_token)
2022-08-23 15:03:43 -07:00
end
end
2022-09-14 13:25:27 -07:00
def update_forecast_source_file(options, tmp_dir)
2022-09-01 15:14:01 -04:00
puts "Updating forecast data"
2022-09-14 11:27:10 -07:00
root_dir = options[:root_dir]
jobs_data_file = options[:forecast_source_file]
2022-09-14 13:25:27 -07:00
jobs_data = File.read(File.join(tmp_dir, jobs_data_file))
2022-09-01 15:14:01 -04:00
today = Date.today.strftime("%Y-%m-%d")
jobs_data.gsub!(/20[0-2][0-9]-[0-1][0-9]-[0-3][0-9]/, today)
2022-09-14 13:25:27 -07:00
File.write(File.join(root_dir, jobs_data_file), jobs_data)
2022-09-01 15:14:01 -04:00
end
2022-08-23 15:03:43 -07:00
if project_exists?(options)
print "Project '#{options[:project]}' already exists. Would you like to continue? [y/N]"
abort unless gets.chomp.casecmp? "y"
else
create_project(options)
end
2022-09-14 13:25:27 -07:00
repository, tmp_dir = create_repository(options)
create_yaml_pipelines(options, repository, tmp_dir)
2022-08-23 15:03:43 -07:00
create_classic_pipelines(options, repository)
2022-09-14 13:25:27 -07:00
update_forecast_source_file(options, tmp_dir)
2022-08-23 15:03:43 -07:00
2022-09-01 15:14:01 -04:00
puts "Success!"