# frozen_string_literal: true require "rake/clean" require "logger" require "json" require "yaml" %w[makit makit/v1 makit/cli makit/content makit/mp].each do |dir| Dir[File.join(__dir__, dir, "*.rb")].each do |file| require_relative file end end module Makit class Error < StandardError; end # Constants if File.exist? ".makit.project.json" PROJECT = Makit::Serializer.open(".makit.project.json", Makit::V1::Project) else if File.exist? ".makit.project.yml" PROJECT = Makit::Serializer.open(".makit.project.yml", Makit::V1::Project) else PROJECT = Makit::V1::Project.new PROJECT.set_default_values end end STARTTIME = Time.now SHOW = Makit::Show.new IS_GIT_REPO = Dir.exist? ".git" DETACHED = `git status`.include?("detached") IS_READ_ONLY = !IS_GIT_REPO || DETACHED RUNTIME_IDENTIFIER = Makit::Environment::get_runtime_identifier DEVICE = Socket.gethostname LOGGER = Makit::Logging::MultiLogger.create_logger # Git Commit and Branch/Tag constants ENV["COMMIT_SHA"] = Makit::Git::commitsha ENV["COMMIT_BRANCH"] = Makit::Git::branch RUNNER = CommandRunner.new GIT_FILE_INFOS = Makit::Git::get_file_infos #RUNNER.log_to_artifacts = true # Variables log_level = Logger::INFO package_type = Makit::V1::PackageType::GEM commands = Makit::Commands.new # methods # # initialize a git repository def self.init(directory) if !Dir.exist?(directory) FileUtils.mkdir_p(directory) end raise Makit::Error.new("directory does not exist: #{directory}") if !Dir.exist?(directory) Dir.chdir(directory) do File.write(".gitignore", Makit::Content::GITIGNORE) unless File.exist?(".gitignore") init = Makit::RUNNER.execute "git init" if init.exit_code != 0 raise Makit::Error.new("failed to initialize local repository: #{directory}\n#{Makit::Humanize.get_command_summary(init)}") end init end end # clone a git repository to a local directory in Directories::CLONE # returns the Makit::V1::Command for 'git clone ...' def self.clone(git_repository) commands = [] # make sure a local clone of the repository exists clone_dir = Directories::get_clone_directory(git_repository) if (!Dir.exist?(clone_dir)) commands << Makit::RUNNER.execute("git clone #{git_repository} #{clone_dir}") end commands end # pull the latest changes from the remote repository to a local clone in Directories::CLONE def self.pull(git_repository) clone_dir = Directories::get_clone_directory(git_repository) raise Makit::Error.new("clone directory does not exist: #{clone_dir}") if !Dir.exist?(clone_dir) Dir.chdir(clone_dir) do request = Makit::V1::CommandRequest.new( name: "git", arguments: ["pull"], directory: clone_dir, ) pull_command = Makit::RUNNER.execute(request) raise Makit::Error.new(Makit::Humanize::get_command_details(pull_command)) if pull_command.exit_code != 0 return pull_command end end def self.clone_or_pull(git_repository) commands = [] clone_dir = Directories::get_clone_directory(git_repository) if Dir.exist?(clone_dir) commands << pull(git_repository) else commands << clone(git_repository) end commands end # log information about a specific repository # return an array of GitLogEntry objects def self.log(git_repository, limit, skip) entries = [] clone_dir = Directories::get_clone_directory(git_repository) raise Makit::Error.new("clone directory does not exist: #{clone_dir}") if !Dir.exist?(clone_dir) Dir.chdir(clone_dir) do log_command = Makit::RUNNER.execute("git log -n #{limit} --skip #{skip} --date=iso") if log_command.exit_code != 0 lines = log_command.stderr.split("\n") # iterate over the lines, generating a GitLogEntry for each commit lines.each do |line| if line.start_with?("commit") commit = line.split(" ")[1] entries << GitLogEntry.new(commit) end if line.start_with?("Author:") entries.last.author = line.split(" ")[1..-1].join(" ") end if line.start_with?("Date:") entries.last.date = line.split(" ")[1..-1].join(" ") end if line.start_with?(" ") entries.last.message += line[4..-1] end end end end entries end # work on a local clone of a git repository # if the repository does not exist, clone it # if the repository exists, pull the latest changes # if a build command can be found, execute it and return the result Makit::V1::WorkResult def self.work(repository) commands = [] work_dir = Makit::Directories::get_work_directory(repository) commands << clone_or_pull(repository) clone_dir = Makit::Directories::get_clone_directory(repository) if !Dir.exist?(work_dir) # make the parent directory for work_dir if it does not exist FileUtils.mkdir_p(File.dirname(work_dir)) unless Dir.exist?(File.dirname(work_dir)) Makit::RUNNER::execute "git clone #{clone_dir} #{work_dir}" end Dir.chdir(work_dir) do # if there is no .gitignore file, create one File.write(".gitignore", Makit::Content::GITIGNORE) unless File.exist?(".gitignore") end nil? end def self.enable_monkey_patch %w[makit/mp].each do |dir| Dir[File.join(__dir__, dir, "*.rb")].each do |file| require_relative file end end end # Given a git repository URL and a commit id, create a new MakeResult object. def self.make(url, commit, force = false) log_filename = File.join(Directories::get_log_directory(url), commit, +"#{RUNTIME_IDENTIFIER}.#{DEVICE}.json") if File.exist?(log_filename) && !force && commit != "latest" begin # deserialize the log file to a Makite::V1::MakeResult object make_result = Makit::V1::MakeResult.decode_json(File.read(log_filename)) return make_result rescue => e # if deserialization fails, delete the log file and continue FileUtils.rm(log_filename) end else commands = [] begin clone_or_pull(url).each do |command| commands << command end # make sure a local clone of the repository exists clone_dir = Directories::get_clone_directory(url) raise Makit::Error.new("clone directory does not exist: #{clone_dir}") if !Dir.exist?(clone_dir) if (commit == "latest") Dir.chdir(clone_dir) do git_log = Makit::RUNNER.execute("git log -n 1 --date=iso") commands << git_log # assert that the commit is valid commit = git_log.output.match(/^commit ([0-9a-f]{40})$/i)[1] raise Makit::Error.new("invalid commit: #{commit}") if commit.nil? || commit.empty? || !commit.match?(/\A[0-9a-f]{40}\z/i) log_filename = File.join(Directories::get_log_directory(url), commit, +"#{RUNTIME_IDENTIFIER}.#{DEVICE}.json") end end # clone a fresh copy of the repository to a make directory make_dir = Directories::get_make_commit_directory(url, commit) FileUtils.rm_rf(make_dir) if Dir.exist?(make_dir) commands << Makit::RUNNER.execute("git clone #{clone_dir} #{make_dir}") raise Makit::Error.new("failed to clone repository: #{url} to #{make_dir}") if !Dir.exist?(make_dir) Dir.chdir(make_dir) do commands << Makit::RUNNER.execute("git reset --hard #{commit}") commands << Makit::RUNNER.execute("git log -n 1") commands << Makit::RUNNER.execute("bundle install") if File.exist? "Gemfile" if File.exist? ("Rakefile") commands << Makit::RUNNER.execute("rake default") else commands << Makit::RUNNER.execute("rake default") if File.exist? "rakefile.rb" end make_result = Makit::V1::MakeResult.new( repository: url, commit: commit, branch: "?", tag: "?", device: DEVICE, runtime_identifier: RUNTIME_IDENTIFIER, ) commands.flatten.each do |command| make_result.commands << command end # save the MakeResult object to a log file as pretty printed json FileUtils.mkdir_p(File.dirname(log_filename)) unless Dir.exist?(File.dirname(log_filename)) File.write(log_filename, make_result.to_json) return make_result end rescue => e message = "error raised attempting to make repository: #{url} commit: #{commit}\n\n" message += "#{e.message}\n" backtrace = e.backtrace.join("\n") message += "#{backtrace}\n\n" message += "commands:\n" commands.flatten.each do |command| message += Makit::Humanize::get_command_details(command) end raise Makit::Error.new(message) end end end end if !File.exist?(".gitignore") Makit::LOGGER.info("added .gitignore file") File.open(".gitignore", "w") do |file| file.puts Makit::Content::GITIGNORE end end