require 'yaml' require 'tmpdir' require 'system' require 'ostruct' require 'fileutils' require 'open3' # make for: the user, system, package # as regular user, if dependencies provided # user: $HOME/.local # as super user # system: / # package: /pkg/$name/$version/ module Make def self.rostruct(obj) case obj when Hash OpenStruct.new(obj.transform_values { |v| rostruct(v) }) when Array obj.map { |v| rostruct(v) } else obj end end class Context attr_accessor :name, :use_cache, :environment, :steps, :packages, :repository attr_accessor :target def initialize(options: OpenStruct.new) @target = options.target || :user @name = options.name @use_cache = options.use_cache || false #System.detect_os # rbenv: brew install rbenv && rbenv install 3.0.0 # asdf: asdf install ruby 3.0.0 makefile_path = "#{ENV["DAT_ROOT"]}/recipes/#{@name}.yml" unless File.file?(makefile_path) makefile_path = "#{ENV["DAT_ROOT"]}/recipes/#{@name}/#{System.detect_os}.yml" end puts "recipe at: #{makefile_path}" makefile = YAML.load_file(makefile_path) # puts makefile_path # puts makefile @packages = makefile["packages"] || [] @repository = Make.rostruct(makefile["repository"] || OpenStruct.new) @steps = makefile["steps"] || [] @environment = ENV.to_h.merge( "PREFIX" => get_prefix ) environment = makefile["environment"] if environment != nil @environment = environment.merge(@environment) end end def get_prefix case @target when :user "#{ENV["HOME"]}/.local" when :package "/pkg/#{@name}/#{@repository.branch}" when :system "/" end end # Root project directory def rpd puts @repository if @use_cache path = "#{ENV["HOME"]}/.cache/dat/build/#{@name}/#{@repository.branch}" FileUtils.mkdir_p(path) yield path else Dir.mktmpdir do |tmp_path| yield tmp_path end end end def local_repo "#{ENV["HOME"]}/.cache/dat/repo/#{@name}.git" end def to_s vars = instance_variables.map do |var| "#{var.to_s.delete('@')}: #{instance_variable_get(var).inspect}" end "Context(#{vars.join(', ')})" end end class Builder attr_accessor :context, :cwd def initialize(context) @context = context end def execute(command) if command.strip.start_with?("cd ") eval_cmd = command.sub(/^cd /, 'echo ') # new_dir = `#{eval_cmd}`.strip new_dir, stderr, status = Open3.capture3(@context.environment, eval_cmd) begin puts "Dir: #{Dir.pwd}" # new_dir = File.expand_path(new_dir) new_dir = new_dir.strip puts "Exists #{new_dir} => #{Dir.exist?(new_dir)}" Dir.chdir(new_dir) puts "Changed directory to #{Dir.pwd}" rescue Errno::ENOENT puts "Directory not found: #{new_dir}" end else system(@context.environment, command) end end def build install @context.rpd do | path | @cwd = path checkout puts "path: #{path}" Dir.chdir(path) @context.steps.each do |command| # system(env, command) execute(command) # need to be refreshed # rehash # hash -r # https://chatgpt.com/c/6880b3d6-b190-8330-9623-0458254d2881 end end end def checkout repo_path = @context.local_repo repo_url = @context.repository.url branch = @context.repository.branch puts "Local bare git repo path: #{repo_path}" if Dir.exist?(repo_path) && !Dir.empty?(repo_path) puts "Bare repo exists, fetching updates..." Dir.chdir(repo_path) do system("git fetch origin") end else puts "Cloning bare repository..." FileUtils.mkdir_p(repo_path) system("git clone --bare #{repo_url} #{repo_path}") end system("git --git-dir=#{repo_path} --work-tree=#{@cwd} checkout -f #{branch}") end def install System.install(context.packages) end end # dat make -t pkg --cache --name dry-run # dat make --name dry-run def self.command(options) context = Context.new(options: options) builder = Builder.new(context) builder.build end end