#!/usr/bin/env ruby require 'find' require 'net/http' require 'uri' require 'digest/sha1' require 'json' require 'fileutils' @command = ARGV[0] @pkgName = ARGV[1] CREW_PREFIX = '/usr/local' CREW_LIB_PATH = CREW_PREFIX + '/lib/crew/' CREW_CONFIG_PATH = CREW_PREFIX + '/etc/crew/' CREW_BREW_DIR = CREW_PREFIX + '/tmp/crew/' CREW_DEST_DIR = CREW_BREW_DIR + '/dest' ARCH = `uname -m`.strip #SHORTARCH = `getconf LONG_BIT`.strip # will refactor this line in the future case ARCH when "i686" SHORTARCH="32" when "x86_64" SHORTARCH="64" when "armv7l" SHORTARCH="32" else SHORTARCH="32" end $LOAD_PATH.unshift "#{CREW_LIB_PATH}lib" USER = `whoami`.chomp #disallow sudo abort "Chromebrew should not be run as root." if Process.uid == 0 && @command != "remove" @device = JSON.parse(File.read(CREW_CONFIG_PATH + 'device.json'), symbolize_names: true) #symbolize also values @device.each do |key, elem| @device[key] = @device[key].to_sym rescue @device[key] end def setPkg (pkgName, silent = false) require CREW_LIB_PATH + 'packages/' + pkgName @pkg = Object.const_get(pkgName.capitalize) @pkg.name = pkgName puts "Found #{pkgName}, version #{@pkg.version}" unless silent end def list_packages Find.find (CREW_LIB_PATH + 'packages') do |filename| Find.find(CREW_CONFIG_PATH + 'meta/') do |packageList| packageName = File.basename filename, '.rb' print '(i) ' if packageList == CREW_CONFIG_PATH + 'meta/' + packageName + '.filelist' end puts File.basename filename, '.rb' if File.extname(filename) == '.rb' end end def search (pkgName, silent = false) Find.find (CREW_LIB_PATH + 'packages') do |filename| return setPkg(pkgName, silent) if filename == CREW_LIB_PATH + 'packages/' + pkgName + '.rb' end abort "package #{pkgName} not found :(" end def whatprovides (pkgName) fileArray = [] Find.find (CREW_CONFIG_PATH + 'meta/') do |packageList| if File.file? packageList if packageList[/\.filelist$/] packageName = File.basename packageList, '.filelist' File.readlines(packageList).each do |line| found = line[/#{Regexp.new(pkgName)}/] if found fileLine = packageName + ': ' + line if not fileArray.include? fileLine fileArray.push(fileLine) end end end end end end if not fileArray.empty? fileArray.sort.each do |item| puts item end puts "\nTotal found: #{fileArray.length}" end end def update abort "'crew update' is used to update crew itself. Use 'crew upgrade to upgrade a specific package." if @pkgName #update package lists Dir.chdir CREW_LIB_PATH do system "git fetch origin master" system "git reset --hard origin/master" end puts "Package lists, crew, and library updated." #check for outdated installed packages puts "Checking for package updates..." puts "" canBeUpdated = 0 @device[:installed_packages].each do |package| search package[:name], true if package[:version] != @pkg.version canBeUpdated += 1 puts @pkg.name + " could be updated from " + package[:version] + " to " + @pkg.version end end if canBeUpdated > 0 puts "" puts "Run 'crew upgrade' to upgrade everything or 'crew upgrade ' to upgrade a specific package." else puts "Your software is up to date." end end def upgrade if @pkgName search @pkgName currentVersion = nil @device[:installed_packages].each do |package| if package[:name] == @pkg.name currentVersion = package[:version] end end if currentVersion != @pkg.version search @pkg.name puts "Updating #{@pkg.name}..." remove @pkg.name resolveDependenciesAndInstall else puts "#{@pkg.name} is already up to date." end else toBeUpdated = [] @device[:installed_packages].each do |package| search package[:name], true if package[:version] != @pkg.version toBeUpdated.push(package[:name]) end end if toBeUpdated.length > 0 puts "Updating packages..." toBeUpdated.each do |package| search package remove @pkg.name resolveDependenciesAndInstall end puts "Packages have been updated." else puts "Your software is already up to date." end end end def download if @pkg.binary_url && @pkg.binary_url.has_key?(@device[:architecture]) url = @pkg.binary_url[@device[:architecture]] source = false puts "Precompiled binary available, downloading..." else url = @pkg.source_url source = true puts "No precompiled binary available for your platform, downloading source..." end uri = URI.parse url filename = File.basename(uri.path) if source sha1sum = @pkg.source_sha1 else sha1sum = @pkg.binary_sha1[@device[:architecture]] end Dir.chdir CREW_BREW_DIR do system('wget', '--continue', '--no-check-certificate', url, '-O', filename) abort 'Checksum mismatch :/ try again' unless Digest::SHA1.hexdigest( File.read("./#{filename}") ) == sha1sum end puts "Archive downloaded" return {source: source, filename: filename} end def resolveDependenciesAndInstall begin origin = @pkg.name resolveDependencies search origin install rescue InstallError => e puts "#{@pkg.name} failed to install: #{e.to_s}" ensure #cleanup unless ARGV[2] == 'keep' Dir.chdir CREW_BREW_DIR do system "rm -rf *" system "mkdir dest" #this is a little ugly, feel free to find a better way end end end end def resolveDependencies @dependencies = [] def pushDeps if @pkg.dependencies @dependencies.unshift @pkg.dependencies @pkg.dependencies.each do |dep| search dep, true pushDeps end end end pushDeps return if @dependencies.empty? puts "Following packages also need to be installed: " @dependencies.flatten!.uniq! @dependencies.each do |dep| print dep + " " end puts "" puts "Do you agree? [Y/n]" response = STDIN.getc case response when "n" abort "No changes made." when "\n", "y", "Y" puts "Proceeding..." proceed = true else puts "I don't understand '#{response}' :(" abort "No changes made." end if proceed @dependencies.each do |dep| search dep install end end end def install if @device[:installed_packages].any? { |pkg| pkg[:name] == @pkg.name } puts "Package #{@pkg.name} already installed, skipping..." return end unless @pkg.is_fake? meta = download topdir = "" Dir.chdir CREW_BREW_DIR do puts "Unpacking archive, this may take a while..." if meta[:filename][-4,4] == ".zip" system "unzip", "-qq", "-d", "tmpzip", meta[:filename] else system "tar", "xf", meta[:filename] end if meta[:source] == true abort "You don't have a working C compiler. Run 'crew install buildessential' to get one and try again." unless system("gcc", "--version") if meta[:filename][-4,4] == ".zip" Dir.chdir "tmpzip" do entries=Dir["*"] if entries.length == 0 abort "empty zip archive: #{meta[:filename]}" elsif entries.length == 1 topdir = "tmpzip/" + entries.first else topdir = "tmpzip/" end end else topdir = `tar -tf #{meta[:filename]} | sed -e 's@/.*@@' | uniq`.chomp! end Dir.chdir CREW_BREW_DIR + topdir do puts "Building from source, this may take a while..." @pkg.build system "rm -rf", CREW_DEST_DIR + "/*" #wipe crew destdir puts "Preconfiguring package..." @pkg.install end Dir.chdir CREW_DEST_DIR do #create directory list system "find . -type f > ../filelist" system "find . -type l >> ../filelist" system "cut -c2- ../filelist > filelist" #create file list system "find . -type d > ../dlist" system "cut -c2- ../dlist > dlistcut" system "tail -n +2 dlistcut > dlist" #remove temporary files system "rm dlistcut ../dlist ../filelist" #create resulting folders list directories = [] Dir.glob('*').select do |fn| if File.directory?(fn) directories.push(fn) end end #wipe directories with the same name in brew dir to avoid conflicts directories.each do |dir| system "rm -rf ../" + dir.chomp end #move result files and directories to brew dir system "mv * ../" end end puts "Installing..." FileUtils.mv 'dlist', CREW_CONFIG_PATH + "meta/#{@pkg.name}.directorylist" FileUtils.mv 'filelist', CREW_CONFIG_PATH + "meta/#{@pkg.name}.filelist" File.open(CREW_CONFIG_PATH + "meta/#{@pkg.name}.directorylist").each_line do |line| system "sudo", "mkdir", "-p", line.chomp end File.open(CREW_CONFIG_PATH + "meta/#{@pkg.name}.filelist").each_line do |line| system "sudo", "mv", CREW_BREW_DIR + line.chomp, line.chomp end end end #add to installed packages @device[:installed_packages].push(name: @pkg.name, version: @pkg.version) File.open(CREW_CONFIG_PATH + 'device.json', 'w') do |file| output = JSON.parse @device.to_json file.write JSON.pretty_generate(output) end puts "#{@pkg.name.capitalize} installed!" end def remove (pkgName) #make sure the package is actually installed unless @device[:installed_packages].any? { |pkg| pkg[:name] == pkgName } puts "Package #{pkgName} isn't installed." return end #if the filelist exists, remove the files and directories installed by the package if File.file?("meta/#{pkgName}.filelist") Dir.chdir CREW_CONFIG_PATH do #remove all files installed by the package File.open("meta/#{pkgName}.filelist").each_line do |line| begin File.unlink line.chomp rescue => exception #swallow exception end end #remove all directories installed by the package File.readlines("meta/#{pkgName}.directorylist").reverse.each do |line| begin Dir.rmdir line.chomp rescue => exception #swallow exception end end #remove the file and directory list File.unlink "meta/#{pkgName}.filelist" File.unlink "meta/#{pkgName}.directorylist" end end #remove from installed packages @device[:installed_packages].each do |elem| @device[:installed_packages].delete elem if elem[:name] == pkgName end #update the device manifest File.open(CREW_CONFIG_PATH + 'device.json', 'w') do |file| out = JSON.parse @device.to_json file.write JSON.pretty_generate(out) end puts "#{pkgName.capitalize} removed!" end case @command when "search" if @pkgName search @pkgName else list_packages end when "whatprovides" if @pkgName whatprovides @pkgName else puts "Usage: crew whatprovides [pattern]" end when "download" search @pkgName download when "update" update when "upgrade" upgrade when "install" search @pkgName resolveDependenciesAndInstall when "remove" abort 'Removing actions must be ran with sudo.' unless USER == 'root' remove @pkgName when nil puts "Chromebrew, version 0.2.2" puts "Usage: crew [command] [package]" puts "Available commands: search, download, install, remove, whatprovides" else puts "I have no idea how to do #{@command} :(" puts "Available commands: search, download, install, remove, whatprovides" end