Class: Vcs

Inherits:
Object
  • Object
show all
Defined in:
lib/vcs/add.rb,
lib/vcs/url.rb,
lib/vcs/back.rb,
lib/vcs/diff.rb,
lib/vcs/edit.rb,
lib/vcs/form.rb,
lib/vcs/junk.rb,
lib/vcs/mail.rb,
lib/vcs/news.rb,
lib/vcs/delete.rb,
lib/vcs/script.rb,
lib/vcs/message.rb,
lib/vcs/conflict.rb,
lib/vcs/diffstat.rb,
lib/vcs/changelog.rb,
lib/vcs/opt_parse.rb,
lib/vcs/environment.rb,
lib/vcs/common_commit.rb,
lib/vcs/last_changed_date.rb

Overview

Author

Nicolas Pouillard <[email protected]>.

Copyright

Copyright © 2004, 2005 LRDE. All rights reserved.

License

GNU General Public License (GPL).

Revision

$Id$

Direct Known Subclasses

Cvs, Prcs, Svn

Defined Under Namespace

Classes: OptParse

Constant Summary collapse

IForm =
',iform'.to_path
Form =
',form'.to_path
MAIL =
Sendmail::MAIL_FILE
NEWS =
',news'.to_path
Message =
',message'.to_path
LogEntry =
',log'.to_path
@@diffstat =
'diffstat'.to_cmd

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.editorObject



14
15
16
# File 'lib/vcs/environment.rb', line 14

def editor
  env('EDITOR').to_cmd
end

.env(name, &block) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/vcs/environment.rb', line 33

def env ( name, &block )
  return @@vars[name] if @@vars.has_key? name
  @@vars[name] =
    if var = ENV[name]
      var
    elsif block.nil?
      logger.error "Need #{name} in the environement"
      @@env_status = false
      "!!! #{name} not set !!!"
    else
      default = block[]
      logger.warn "Need #{name} in the environement (default: #{default})"
      ENV[name] = default
      default
    end
end

.full_emailObject



22
23
24
# File 'lib/vcs/environment.rb', line 22

def full_email
  "#{fullname}  <#{email}>"
end

.fullnameObject



10
11
12
# File 'lib/vcs/environment.rb', line 10

def fullname
  env('FULLNAME') { Etc.getpwnam(user).gecos }
end

.method_missing(meth) ⇒ Object



26
27
28
# File 'lib/vcs/environment.rb', line 26

def method_missing ( meth )
  env meth.to_s.upcase
end

.pagerObject



18
19
20
# File 'lib/vcs/environment.rb', line 18

def pager
  env('PAGER').to_cmd
end

Instance Method Details

#add!(files = [], options = {}) ⇒ Object



10
11
12
13
14
15
16
17
18
19
# File 'lib/vcs/add.rb', line 10

def add! ( files=[], options={} )
  if options[:auto]
    options.delete(:auto)
    list!(files, :unrecognize => true) do |path_list|
      add_!(path_list.stringify, options) unless path_list.empty?
    end
  else
    add_!(files, options)
  end
end

#back!(files = [], options = {}) ⇒ Object

This command take a command as argument and search the last revision where this command success.

Example:

* this dummy example success only if the revision is equal to 0 modulo X
* replace X by the success revision and Y by a greater revision

vcs-svn back -r Y vcs-svn script ‘exit(rev.read.to_i % X)’

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/vcs/back.rb', line 14

def back! ( files=[], options={} )
  sub_vcs = sub_vcs_with_name('checkout-4-vcs-back')
  dir = TempPath.new('checkout-dir-4-vcs-back')
  cmd = files.to_cmd
  cmd.dir = dir
  rev = options[:revision] || revision.read.chomp
  raise ArgumentError, "Just integers are supported for revisions" if rev !~ /^\d+$/
  rev = rev.to_i
  target_url = url.read.chomp
  while not rev.zero?
    sub_vcs.checkout([target_url, dir], :quiet => true, :revision => rev)
    data = cmd.system
    data.display if options[:verbose]
    if data.status.success?
      logger.info { "Your command success on revision #{rev}"}
      break
    else
      logger.warn { "Your command fail on revision #{rev}" }
    end
    rev -= 1
  end
end

#check_diffstatObject



19
20
21
22
23
# File 'lib/vcs/diffstat.rb', line 19

def check_diffstat
  unless `diffstat -V` =~ /diffstat version/
    raise ArgumentError, 'diffstat: diffstat is required'
  end
end

#check_envObject

class << self



52
53
54
# File 'lib/vcs/environment.rb', line 52

def check_env
  %w[ email fullname editor pager ].each { |m| Vcs.send(m) }
end

#check_gnu_diffObject



12
13
14
15
16
# File 'lib/vcs/diff.rb', line 12

def check_gnu_diff
  unless `diff --version` =~ /GNU/
    raise ArgumentError, 'diffw: Gnu diff is required'
  end
end

#commit_failed(ex = nil) ⇒ Object



99
100
101
102
103
# File 'lib/vcs/common_commit.rb', line 99

def commit_failed ( ex=nil )
  logger.error "Aborting #{ex}"
  logger.info 'You can rerun the same command to resume the commit'
  raise 'Commit failed'
end

#commited?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/vcs/common_commit.rb', line 106

def commited?
  Vcs.commited
end

#concat_changelog!(*args) ⇒ Object

Same switches as mk_changelog_entry



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/vcs/changelog.rb', line 82

def concat_changelog! ( *args )
  error_handling :concat_changelog_failed

  unless TMP_CL.exist?
    logger.info "Backup your `#{CL}' to `#{TMP_CL}' ..."
    CL.rename(TMP_CL)
  end

  CL.open('w') do |file|
    logger.info "#{CL}: Writing your new entry ..."
    with(file).mk_changelog_entry!(*args)
    file.puts
    logger.info "#{CL}: Writing the others ..."
    file.print TMP_CL.read
  end
end

#concat_changelog_failedObject



100
101
102
103
104
105
# File 'lib/vcs/changelog.rb', line 100

def concat_changelog_failed
  if TMP_CL.exist?
    logger.info "Restoring `#{CL}' from `#{TMP_CL}' ..."
    TMP_CL.rename(CL)
  end
end

#delete!(files = [], options = {}) ⇒ Object



10
11
12
13
14
15
16
17
18
19
# File 'lib/vcs/delete.rb', line 10

def delete! ( files=[], options={} )
  if options[:auto]
    options.delete(:auto)
    list!(files, :missing => true) do |path_list|
      delete_!(path_list.stringify, options) unless path_list.empty?
    end
  else
    delete_!(files, options)
  end
end

#diffstat!(*a) ⇒ Object

Use the diffstat command to display statitics on your patch.



13
14
15
16
# File 'lib/vcs/diffstat.rb', line 13

def diffstat! ( *a )
  check_diffstat
  (diffw(*a) | @@diffstat).run(@runner)
end

#diffw!(*args) ⇒ Object



8
9
10
# File 'lib/vcs/diff.rb', line 8

def diffw! ( *args )
  diff_!(*args)
end

#edit!(files = [], options = {}) ⇒ Object



10
11
12
13
# File 'lib/vcs/edit.rb', line 10

def edit! ( files=[], options={} )
  cmd = Vcs.editor + files > [STDOUT, STDERR]
  cmd.run(@runner)
end

#edit_conflicts!(files, options = {}) ⇒ Object



15
16
17
# File 'lib/vcs/conflict.rb', line 15

def edit_conflicts! ( files, options={} )
  edit! mk_conflicts_list
end

#edit_form!(*args) ⇒ Object



8
9
10
11
12
13
14
15
16
17
# File 'lib/vcs/form.rb', line 8

def edit_form! ( *args )
  mk_form(*args)
  edit! Form if Form.read =~ /\A---/
  if Form.read =~ /\A---/
    raise Failure, "You must fill this file: `#{Form}' (and remove the first line)"
  else
    mk_iform(*args)
    return YAML.load(IForm.read)['commited']
  end
end

#junk!(files = [], options = {}) ⇒ Object

This command removes all junk files (by default all files begining with ‘,’). Warning: this command removes some files that may contains some important information. For example during a commit the information that you type is stored in one of these ‘,files’. So be careful using this command. See the user configuration to customize what is considered a junk file.



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/vcs/junk.rb', line 14

def junk! ( files=[], options={} )
  list!(files, options.merge(:junk => true)) do |path_list|
    path_list.each { |path| logger.info { "Remove #{path}" } }
    if @h.agree('Are you sure? (y/n)', true)
      path_list.each do |path|
        logger.info { "Removing #{path}..." }
        path.rm_f
      end
    end
  end
end

#last_changed_date!(*args) ⇒ Object



8
9
10
# File 'lib/vcs/last_changed_date.rb', line 8

def last_changed_date! ( *args )
  puts info(*args).read[/^Last Changed Date:.*?\(([^)]*)\).*$/, 1]
end

#mail!(files = [], options = {}) ⇒ Object

Mail.

FIXME handle options properly. Delegate the option parsing to Sendmail.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/vcs/mail.rb', line 21

def mail! ( files=[], options={} )

  # Backward compatiblity
  files, options = [], files if files.is_a? Hash

  error_handling :mail_failed
  unless MAIL.exist?
    options = @@default_options.merge(options)
    options[:signed] = Vcs.user_conf.sign
    print_body(MAIL, @@mailer.parse_mail_options(options), files)
  end
  @@mailer.sendmail
  puts 'Mail: Sent.'
end

#mail_conf_checkerObject



43
44
45
46
47
48
49
50
51
52
# File 'lib/vcs/mail.rb', line 43

def mail_conf_checker
  if Vcs.user_conf.sign
    unless `gpg --version` =~ /^gpg \(GnuPG\)/
      logger.error 'mail: gunpg is required'
    end
    unless File.exist?("#{ENV['HOME']}/.gnupg/secring.gpg")
      logger.error 'no private key: in your ~/.gnupg'
    end
  end
end

#mail_failedObject



36
37
38
39
40
41
# File 'lib/vcs/mail.rb', line 36

def mail_failed
  if defined? @@mail and @@mail.exist?
    logger.info { "#{MAIL}: Contains the generated mail" }
    logger.info { "       AND information to send it (smtpserver, port...)" }
  end
end

#mk_changelog_entry!(*args) ⇒ Object

Same switches as mk_log_entry



62
63
64
65
66
# File 'lib/vcs/changelog.rb', line 62

def mk_changelog_entry! ( *args )
    puts Time.now.strftime("%Y-%m-%d  #{Vcs.full_email}")
    puts
    mk_log_entry(*args).each_line(&method(:log_to_changelog))
end

#mk_form!(files = [], options = {}) ⇒ Object



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
# File 'lib/vcs/form.rb', line 49

def mk_form! ( files=[], options={} )
  with_cache! Form, 'complete form (title, subject, ChangeLog entry, diff)' do

    puts "
    |--- | ########## Fill this file correctly and remove this line ########## | ---
    |title: 
    |subject: #{@@subject_format}
    |
    |--- | ###################### Your ChangeLog entry ###############  80c| # | ---
    |<%= title %>
    |
    |".head_cut!

    mk_log_entry!(files)

    puts "|
    |--- | ########### This line, and those below, will be ignored #### 80c| # | ---
    |Instructions:
    |- The first line must be removed when this file is filled.
    |- After you must specify a title, for the news/mail subject.
    |  The line containing <%= title %> will be replaced by your title,
    |  <%= subject %> by the subject line, <%= rev %> by the revision...
    |- Everywhere in the document you can get/compute some values with
    |  these tags <%= aRubyExpression %> even some vcs specific call.
    |  For example <%= status.read %> will include the 'svn status' output.
    |- Tabulations and stars ('*') will be added in the ChangeLog before each line.
    |- The '80c|' on the fourth line is here to show you where will be the
    |  limit. The '|' is the 79th char.
    |
    |".head_cut!

    diffw!(files)
  end
end

#mk_iform!(*args) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/vcs/form.rb', line 20

def mk_iform! ( *args )
  with_cache! IForm, 'instanciated form (title, subject, ChangeLog entry, diff)' do
    cl = mk_form(*args).read
    ls = []
    YAML.each_document("--- |\n" + cl) { |x| (ls.size == 2)? break : ls << x }
    title_subject, input = ls
    header = { 'title'   => title_subject[/^title: (.*)$/, 1],
               'subject' => title_subject[/^subject: (.*)$/, 1] }
    rev = revision.read.to_i + 1
    header.merge!('revision' => rev, 'commited' => false)
    if header['title'].nil? or header['title'].blank?
      raise Failure, "No title found. Reopen `#{Form}' and add it"
    end
    header['title'] += '.' unless header['title'] =~ /[.?!]$/
    b = getBinding(header.merge(:rev => rev))
    input = ERB.new(input, nil, '<-%->', '$erbout_').result(b)
    LogEntry.open('w') { |f| f.print input }
    header.each_value do |v|
      next unless v.is_a? String
      v.replace(ERB.new(v, nil, '<-%->', '$erbout_').result(b))
    end
    puts header.to_yaml
  end
end

#mk_log_entry!(*args) ⇒ Object

Same switches as status



40
41
42
43
44
45
46
# File 'lib/vcs/changelog.rb', line 40

def mk_log_entry! ( *args )
  with_cache! LogEntry, 'Log entry' do
    mk_log_entry_contents(*args).each do |se|
      puts "- #{se.file}: #{se.comment}."
    end
  end
end

#mk_message!(files = [], options = {}) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/vcs/message.rb', line 21

def mk_message! ( files=[], options={} )
  with_cache! Message, 'generated message (ChangeLog, diffstat, diff)' do
    url!
    if defined? COLLABOA
      puts
      puts 'You can also view this changeset here:'
      puts
      next_rev = rev.output.read.to_i
      next_rev += 1 unless commited?
      puts "http://#{COLLABOA}/repository/changesets/#{next_rev}"
    end
    puts
    flush
    mk_message_entry!(files)
    puts
    flush
    diffstat!(files)
    puts
    flush
    diffw(files).each_line do |line|
      print line if line !~ /^=+$/
    end
  end
end

#mk_message_entry!(*args) ⇒ Object

Same switches as mk_log_entry



71
72
73
74
75
76
# File 'lib/vcs/changelog.rb', line 71

def mk_message_entry! ( *args )
    puts 'Index: ChangeLog'
    puts "from  #{Vcs.full_email}"
    puts
    mk_log_entry(*args).each_line(&method(:log_to_changelog))
end

#news!(*args) ⇒ Object

Post the news.



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
# File 'lib/vcs/news.rb', line 61

def news! ( *args )
  error_handling :news_failed

  print_body(NEWS, parse_news_options(*args)) unless NEWS.exist?

  @news_status = 'Sent.'
  NEWS.open('r') do |file|
    opt = YAML::chop_header(file)
    server, port = opt[:server].split(/:/)
    port ||= 119
    logger.info('news') { "Nntp Server: #{server}:#{port}" }
    unless @h.agree("Post a news, with this subject: #{opt[:subject]}\n" +
           "  to #{opt[:groups].join(', ')}\n  from #{opt[:from]}\n" +
           'Are you sure? (y/n)', true)
      logger.error('news') { 'Aborting' }
      exit
    end
    require 'socket'
    TCPSocket.open(server, port) do |f|
      check_line(f, /^200/)
	f.puts 'post'
      check_line(f, /^340/)
	f.puts "Newsgroups: #{opt[:groups].join(', ')}"
	f.puts "From: #{opt[:from]}"
	f.puts "Subject: #{opt[:subject]}"
	f.puts
	file.each do |line|
 f.print line.gsub(/^\./, ' .')
	end
	f.puts '.'
      check_line(f, /^240/)
	f.puts 'quit'
      check_line(f, /^205/)
    end
  end
  NEWS.delete
  puts @news_status
end

#news_conf_checkerObject



107
108
109
110
111
112
113
# File 'lib/vcs/news.rb', line 107

def news_conf_checker
  %w[ NNTPSERVER ].each do |var|
    if ENV[var].nil? or ENV[var].empty?
      logger.error "environment variable `#{var}' not set"
    end
  end
end

#news_failedObject



100
101
102
103
104
105
# File 'lib/vcs/news.rb', line 100

def news_failed
  if defined? NEWS and NEWS.exist?
    logger.info "#{NEWS}: Contains the generated news" +
             "(generated from #{@@message})"
  end
end

#resolve_conflicts!(files, options = {}) ⇒ Object



19
20
21
22
23
24
25
# File 'lib/vcs/conflict.rb', line 19

def resolve_conflicts! ( files, options={} )
  conflicts = mk_conflicts_list
  question = "Resolve these conflicts?: \n  - #{conflicts.join("\n  - ")}\n(y/n)"
  if @h.agree question, true
    return resolved(conflicts)
  end
end

#script(files = [], options = {}) ⇒ Object



10
11
12
# File 'lib/vcs/script.rb', line 10

def script ( files=[], options={} )
  puts script!(files, options)
end

#script!(files = [], options = {}) ⇒ Object



14
15
16
17
18
19
20
21
22
23
# File 'lib/vcs/script.rb', line 14

def script! ( files=[], options={} )
  begin
    eval(files.join(' '))
  rescue SystemExit => ex
    raise ex
  rescue Exception => ex
    logger.error { 'Vcs#script: during the client execution' }
    logger.error { ex.long_pp }
  end
end

#url!(*args) ⇒ Object



8
9
10
# File 'lib/vcs/url.rb', line 8

def url! ( *args )
  puts info(*args).read[/^URL:\s+(.*)$/, 1]
end