lib/gdk/package_helper.rb (155 lines of code) (raw):

# frozen_string_literal: true require 'fileutils' require 'rubygems/package' require 'zlib' require 'net/http' require 'uri' require 'tmpdir' require_relative '../gdk' module GDK class PackageHelper API_V4_URL = 'https://gitlab.com/api/v4' GDK_PROJECT_ID = '74823' SUPPORTED_ARCHS = %w[darwin-arm64 linux-amd64 linux-arm64].freeze EXCLUDED_FILES = %w[build/metadata.txt build/checksums.txt].freeze FALLBACK_BRANCHES = %w[main master].freeze def self.supported_os_arch?(os_arch_name = GDK::Machine.package_platform) SUPPORTED_ARCHS.include?(os_arch_name) end attr_reader :package_basename, :package_name, :package_path, :package_version, :project_path, :upload_path, :download_paths, :platform_specific, :project_id, :token def initialize(package:, project_id: GDK_PROJECT_ID, token: ENV.fetch('CI_JOB_TOKEN', '')) config = GDK::PackageConfig.project(package) { raise "Unknown package: #{package}" } @platform_specific = config[:platform_specific] @package_basename = config[:package_name] @package_name = @platform_specific ? "#{@package_basename}-#{GDK::Machine.package_platform}" : @package_basename @package_path = config[:package_path] @package_version = ENV['PACKAGE_VERSION'] || config[:package_version] @project_path = config[:project_path] @upload_path = config[:upload_path] @download_paths = config[:download_paths] @project_id = project_id @token = token end def create_package File.open(package_path, 'wb') do |file| Zlib::GzipWriter.wrap(file) do |gzip| Gem::Package::TarWriter.new(gzip) do |tar| upload_path.find do |path| next if path == upload_path if path.directory? tar.mkdir(path.to_s, path.stat.mode) else add_file_to_tar(tar, path) end end end end end GDK::Output.success("Package created at #{package_path}") rescue StandardError => e raise "Package creation failed: #{e.message}" end def upload_package create_package base_uri = "#{API_V4_URL}/projects/#{project_id}/packages/generic/#{package_name}" versions = [package_version, 'latest'] versions.each do |version| uri = URI.parse("#{base_uri}/#{version}/#{File.basename(package_path)}") request = Net::HTTP::Put.new(uri) request['JOB-TOKEN'] = token request.body = File.read(package_path) response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end raise "Upload failed for version '#{version}': #{response.body}" unless response.is_a?(Net::HTTPSuccess) GDK::Output.success("Package uploaded successfully to #{uri}") end end def download_package if platform_specific && !GDK::PackageHelper.supported_os_arch? GDK::Output.info("Unsupported OS or architecture detected in #{GDK::Machine.package_platform}. To continue, please enable local compilation and then update by running `gdk config set #{package_basename}.skip_compile false && gdk update`. ") return end return GDK::Output.success("No changes detected in #{project_path}, skipping package download and extraction.") if current_commit_sha == stored_commit_sha uri = URI.parse("#{API_V4_URL}/projects/#{project_id}/packages/generic/#{package_name}/#{package_version}/#{File.basename(package_path)}") GDK::Output.info("Downloading package from #{uri}") response = Net::HTTP.get_response(uri) if response.is_a?(Net::HTTPNotFound) GDK::Output.warn("Package not found for version '#{package_version}', trying 'latest' instead.") uri = URI.parse("#{API_V4_URL}/projects/#{project_id}/packages/generic/#{package_name}/latest/#{File.basename(package_path)}") GDK::Output.info("Retrying download from #{uri}") response = Net::HTTP.get_response(uri) end raise "Download failed: #{response.body}" unless response.is_a?(Net::HTTPSuccess) File.write(package_path, response.body) GDK::Output.success("Package downloaded successfully to #{package_path}") @download_paths.each { |path| extract_package(path) } FileUtils.rm_f(package_path) store_commit_sha(current_commit_sha) end def extract_package(destination_path) destination_path.mkpath Dir.mktmpdir do |tmp_dir| tmp_download_path = Pathname.new(tmp_dir) File.open(package_path, 'rb') do |file| Zlib::GzipReader.wrap(file) do |gzip| Gem::Package::TarReader.new(gzip) do |tar| tar.each do |entry| extract_entry(entry, tmp_download_path) end end end end copy_entries(tmp_download_path, destination_path) GDK::Output.success("Package extracted successfully to #{destination_path}") end end private def add_file_to_tar(tar, file_path) stat = file_path.stat tar.add_file_simple(file_path.to_s, stat.mode, stat.size) do |io| io.write(file_path.binread) print '.' end end def copy_entries(source_dir, destination_dir) source_dir.find do |entry| next if entry.directory? && entry.find(&:file?).nil? destination_path = destination_dir.join(entry.relative_path_from(source_dir)) FileUtils.mkdir_p(destination_path.dirname) FileUtils.cp_r(entry, destination_path, remove_destination: true) end end def current_commit_sha Shellout.new(%W[git -C #{project_path} rev-parse HEAD]).run end def extract_entry(entry, destination_path) return if entry.full_name.include?('..') target_path = destination_path.join(File.basename(entry.full_name)) parent_dir = target_path.dirname if entry.directory? FileUtils.mkdir_p(parent_dir, mode: entry.header.mode) return end return unless entry.file? return if EXCLUDED_FILES.any? { |excluded| entry.full_name.end_with?(excluded) } File.binwrite(target_path, entry.read) File.chmod(entry.header.mode, target_path) end def sha_file_path File.join(sha_file_root, '.cache', ".#{package_name.gsub(/[^a-z0-9]+/i, '_')}_commit_sha") end def sha_file_root GDK.config.gdk_root end def store_commit_sha(commit_sha) FileUtils.mkdir_p(File.dirname(sha_file_path)) File.write(sha_file_path, commit_sha) end def stored_commit_sha return nil unless File.exist?(sha_file_path) File.read(sha_file_path).chomp end end end