Class: Harrison::Package

Inherits:
Base
  • Object
show all
Defined in:
lib/harrison/package.rb

Instance Attribute Summary

Attributes inherited from Base

#options

Instance Method Summary collapse

Methods inherited from Base

#close, #download, #exec, option_helper, #parse, #upload

Constructor Details

#initialize(opts = {}) ⇒ Package

Returns a new instance of Package.



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/harrison/package.rb', line 3

def initialize(opts={})
  # Config helpers for Harrisonfile.
  self.class.option_helper(:via)
  self.class.option_helper(:dockerfiles)
  self.class.option_helper(:host)
  self.class.option_helper(:commit)
  self.class.option_helper(:purge)
  self.class.option_helper(:destination)
  self.class.option_helper(:remote_dir)
  self.class.option_helper(:exclude)

  # Command line opts for this action. Will be merged with common opts.
  arg_opts = [
    [ :commit, "Specific commit to be packaged. Accepts anything that `git rev-parse` understands.", :type => :string, :default => "HEAD" ],
    [ :purge, "Remove all previously packaged commits and working copies from the build host when finished.", :type => :boolean, :default => false ],
    [ :destination, "Local or remote folder to save package to. Remote syntax is: (user@)host:/path", :type => :string, :default => "pkg" ],
    [ :remote_dir, "Remote working folder.", :type => :string, :default => "~/.harrison" ],
  ]

  super(arg_opts, opts)
end

Instance Method Details

#remote_exec(cmd) ⇒ Object



25
26
27
28
29
30
31
32
33
# File 'lib/harrison/package.rb', line 25

def remote_exec(cmd)
  ensure_remote_dir("#{remote_project_dir}/package")

  if @_remote_context
    super("cd #{@_remote_context} && #{cmd}")
  else
    super("cd #{remote_project_dir}/package && #{cmd}")
  end
end

#run(&block) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/harrison/package.rb', line 35

def run(&block)
  return super if block_given?

  # Find the URL of the remote in case it differs from git_src.
  remote_url = find_remote(self.commit)

  # Resolve commit ref to an actual short SHA.
  resolve_commit!

  return run_docker if self.via == :docker

  if self.host.respond_to?(:call)
    resolved_host = self.host.call(self)
    self.host = resolved_host
  end

  # Require at least one host.
  if !self.host || self.host.empty?
    abort("ERROR: Unable to resolve build host.")
  end

  puts "Packaging #{commit} from #{remote_url} for \"#{project}\" on #{host}..."

  # Make sure the folder to save the artifact to locally exists.
  ensure_destination(destination)

  # To avoid collisions, we use a version of the full URL as remote name.
  remote_cache_name = remote_url.gsub(/[^a-z0-9_]/i, '_')

  # Fetch/clone git repo on remote host.
  remote_exec <<~ENDCMD
    if [ -d cached ]
    then
      cd cached
      if [ -d .git/refs/remotes/#{remote_cache_name} ]
      then
        git fetch #{remote_cache_name} -p
      else
        git remote add -f #{remote_cache_name} #{remote_url}
      fi
    else
      git clone -o #{remote_cache_name} #{remote_url} cached
    fi
  ENDCMD

  build_dir = remote_cache_name + '-' + artifact_name(commit)

  # Clean up any stale build folder of the target remote/commit.
  remote_exec("rm -rf #{build_dir} && mkdir -p #{build_dir}")

  # Check out target commit into the build_dir.
  checkout_failure = catch :failure do
    remote_exec("cd cached && GIT_WORK_TREE=../#{build_dir} git checkout --detach -f #{commit} && git checkout -f -") # TODO: When git is upgraded: --ignore-other-worktrees

    # We want "checkout_failure" to be false if nothing was caught.
    false
  end

  if checkout_failure
    abort("ERROR: Unable to checkout requested git reference '#{commit}' on build server, ensure you have pushed the requested branch or tag to the remote repo.")
  end

  # Run user supplied build code in the context of the checked out code.
  begin
    @_remote_context = "#{remote_project_dir}/package/#{build_dir}"
    super
  ensure
    @_remote_context = nil
  end

  # Package build folder into tgz.
  remote_exec("rm -f #{artifact_name(commit)}.tar.gz && cd #{build_dir} && tar #{excludes_for_tar} -czf ../#{artifact_name(commit)}.tar.gz .")

  if match = remote_regex.match(destination)
    # Copy artifact to remote destination.
    dest_user, dest_host, dest_path = match.captures
    dest_user ||= self.user

    remote_exec("scp #{artifact_name(commit)}.tar.gz #{dest_user}@#{dest_host}:#{dest_path}")
  else
    # Download (Expand remote path since Net::SCP doesn't expand ~)
    download(remote_exec("readlink -m #{artifact_name(commit)}.tar.gz"), "#{destination}/#{artifact_name(commit)}.tar.gz")
  end

  if purge
    remote_exec("rm -rf #{build_dir}")
    remote_exec("rm #{artifact_name(commit)}.tar.gz")
  end

  puts "Sucessfully packaged #{commit} to #{destination}/#{artifact_name(commit)}.tar.gz"
end