aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/cmd/update.rb
blob: 7eac0b389618799fc3ea2c1ceab6ecec95b92497 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
module Homebrew extend self
  def update
    abort "Please `brew install git' first." unless system "/usr/bin/which -s git"

    updater = RefreshBrew.new
    if updater.update_from_masterbrew!
      updater.report
    else
      puts "Already up-to-date."
    end
  end
end

class RefreshBrew
  REPOSITORY_URL   = "http://github.com/mxcl/homebrew.git"
  CHECKOUT_COMMAND = "git checkout -q master"
  UPDATE_COMMAND   = "git pull #{REPOSITORY_URL} master"
  REVISION_COMMAND = "git rev-parse HEAD"
  DIFF_COMMAND     = "git diff-tree -r --name-status -z %s %s"

  FORMULA_DIR = 'Library/Formula/'
  EXAMPLE_DIR = 'Library/Contributions/examples/'

  attr_reader :added_formulae, :updated_formulae, :deleted_formulae, :installed_formulae
  attr_reader :added_examples, :updated_examples, :deleted_examples
  attr_reader :initial_revision, :current_revision

  def initialize
    @added_formulae, @updated_formulae, @deleted_formulae, @installed_formulae = [], [], [], []
    @added_examples, @updated_examples, @deleted_examples = [], [], []
    @initial_revision, @current_revision = nil
  end

  # Performs an update of the homebrew source. Returns +true+ if a newer
  # version was available, +false+ if already up-to-date.
  def update_from_masterbrew!
    HOMEBREW_REPOSITORY.cd do
      if git_repo?
        safe_system CHECKOUT_COMMAND
        @initial_revision = read_revision
      else
        begin
          safe_system "git init"
          safe_system "git fetch #{REPOSITORY_URL}"
          safe_system "git reset FETCH_HEAD"
        rescue Exception
          safe_system "rm -rf .git"
          raise
        end
      end
      execute(UPDATE_COMMAND)
      @current_revision = read_revision
    end

    if initial_revision && initial_revision != current_revision
      # hash with status characters for keys:
      # Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R)
      @changes_map = Hash.new {|h,k| h[k] = [] }

      changes = HOMEBREW_REPOSITORY.cd do
        execute(DIFF_COMMAND % [initial_revision, current_revision]).split("\0")
      end

      while status = changes.shift
        file = changes.shift
        @changes_map[status] << file
      end

      if @changes_map.any?
        @added_formulae   = changed_items('A', FORMULA_DIR)
        @deleted_formulae = changed_items('D', FORMULA_DIR)
        @updated_formulae = changed_items('M', FORMULA_DIR)
        @added_examples   = changed_items('A', EXAMPLE_DIR)
        @deleted_examples = changed_items('D', EXAMPLE_DIR)
        @updated_examples = changed_items('M', EXAMPLE_DIR)

        @installed_formulae = HOMEBREW_CELLAR.children.
          select{ |pn| pn.directory? }.
          map{ |pn| pn.basename.to_s }.sort if HOMEBREW_CELLAR.directory?

        return true
      end
    end
    # assume nothing was updated
    return false
  end

  def git_repo?
    File.directory? '.git'
  end

  def pending_formulae_changes?
    !@updated_formulae.empty?
  end

  def pending_new_formulae?
    !@added_formulae.empty?
  end

  def deleted_formulae?
    !@deleted_formulae.empty?
  end

  def pending_examples_changes?
    !@updated_examples.empty?
  end

  def pending_new_examples?
    !@added_examples.empty?
  end

  def deleted_examples?
    !@deleted_examples.empty?
  end

  def report
    puts "Updated Homebrew from #{initial_revision[0,8]} to #{current_revision[0,8]}."
    ## New Formulae
    if pending_new_formulae?
      ohai "The following formulae are new:"
      puts_columns added_formulae
    end
    ## Deleted Formulae
    if deleted_formulae?
      ohai "The following formulae were removed:"
      puts_columns deleted_formulae, installed_formulae
    end
    ## Updated Formulae
    if pending_formulae_changes?
      ohai "The following formulae were updated:"
      puts_columns updated_formulae, installed_formulae
    end
    ## New examples
    if pending_new_examples?
      ohai "The following external commands are new:"
      puts_columns added_examples
    end
    ## Deleted examples
    if deleted_examples?
      ohai "The following external commands were removed:"
      puts_columns deleted_examples
    end
    ## Updated Formulae
    if pending_examples_changes?
      ohai "The following external commands were updated:"
      puts_columns updated_examples
    end
  end

  private

  def read_revision
    execute(REVISION_COMMAND).chomp
  end

  def filter_by_directory(files, dir)
    files.select { |f| f.index(dir) == 0 }
  end

  def basenames(files)
    files.map { |f| File.basename(f, '.rb') }
  end

  # extracts items by status from @changes_map
  def changed_items(status, dir)
    basenames(filter_by_directory(@changes_map[status], dir)).sort
  end

  def execute(cmd)
    out = `#{cmd}`
    if $? && !$?.success?
      $stderr.puts out
      raise "Failed while executing #{cmd}"
    end
    ohai(cmd, out) if ARGV.verbose?
    out
  end
end