Index: ChangeLog
from Nicolas Pouillard <ertai(a)lrde.epita.fr>
* rcs-wrapper/src, rcs-wrapper, rcs-wrapper/src/news.rb,
* rcs-wrapper/bin/rcsw, rcs-wrapper/bin, rcs-wrapper/src/cvs.rb,
* rcs-wrapper/src/changelog.rb, rcs-wrapper/src/revision.rb,
* rcs-wrapper/src/rcs.rb, rcs-wrapper/src/prcs.rb,
* rcs-wrapper/src/svn.rb, rcs-wrapper/src/message.rb,
* rcs-wrapper/src/mycommit.rb, rcs-wrapper/src/mail.rb,
* rcs-wrapper/src/tools.rb: New.
---
Intro: >
Bon voilà je n'ai pas trop le temps en ce moment de m'occuper de cet outil.
Il est bien commencé et à mon goût déjà utilisable.
Je vais continuer à le debugger, mais en attendant vous pouvez vous amuser
avec.
C'est un wrapper pour les systèmes de versions comme Svn, Cvs, Prcs, Arch...
Il permet donc au minimum de faire tout ce que le système sous-jacent propose,
mais l'avantage est de pouvoir l'étendre facilement.
Exemples d'utilisation:
- Pour plus de transparence on va faire des alias:
genre: alias svn=...../rcsw, idem pour cvs, prcs...
pour cela taper simplement: >
`le_chemin_vers_rcsw/bin/rcsw --mk-alias`
- Les commandes de svn fonctionnent toujours: >
placez vous dans un répertoire svn.
ex: svn status
- Les commandes de base peuvent être surchargées: >
par exemple `svn commit' a un comportement non interactif par défaut.
Mais l'on peut tout de même appeler le véritable `svn commit'.
Pour cela il y a une convention: toutes les méthodes terminant par `_'
sont directement celles du système.
Avec cela on peut vérifier que l'outil est bien devant le vrai svn:
svn status_
- Quelques fonctionnalités:
- Un des principaux avantages est la gestion des erreurs: >
Par exemple on utilise une commande qui commit puis poste une news.
Si le commit réussit et que la news ne passe pas, il suffit de
relancer l'envoi de la news: svn news
En effet chaque méthode peut conserver des fichiers, ces fichiers
commencent par `,'.
- Les alias de méthodes: >
Tout comme svn et cvs il existe des raccourcis pour certaines méthodes
et bien sûr lorsque l'on surcharge une méthode, le raccourci pointe
vers la nouvelle méthode.
- Quelques nouvelles méthodes:
- svn revision: Renvoie le numéro de révision courante
alias: rev
- svn mkchangelog: Génère une entrée de ChangeLog à partir de `status'
alias: mkcl
- svn changelog: Concatène la nouvelle entrée au véritable ChangeLog
alias: cl
- svn message: Crée le contenu du message du mail ou de la news
alias: msg
- svn mail: Envoie un mail.
- svn news: Poste une news.
- Plus facile d'écrire des scripts: >
Lorsque l'on veut automatiser un traitement fait avec un Rcs,
il est généralement plus simple de le faire en script shell
lorsque cela n'excède pas une dixaine de lignes. Après cela certains
avantages des langages de script évolués nous manquent.
Pourtant certaines choses très faciles peuvent devenir plus lourdes:
svn commit => system("svn commit")
Heureusement avec le modèle objet déjà posé cela devient plus
raisonnable, comparez par exemple:
svn commit => svn.commit
svn info | grep '^Revision' | sed s/^Revision: //
=>
svn.info.readlines.grep(/^Revision/)[0].sub(/^Revision: /, '').to_i
- Exemple final: commit complet pour les lrde tools.
commande: svn lrdetools_commit 'votre sujet pour la news'
ensuite: laissez vous guider.
- S'il y a des questions il y aura des réponses.
Index: rcs-wrapper/src/revision.rb
--- rcs-wrapper/src/revision.rb (revision 0)
+++ rcs-wrapper/src/revision.rb (revision 0)
@@ -0,0 +1,19 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'svn'
+require 'stringio'
+
+class Svn
+
+ def revision ( *args )
+ StringIO.new(info.readlines.grep(/^Revision:/)[0].sub(/^Revision: /, ''))
+ end
+
+ alias_command :rev, :revision
+
+end # class Svn
Index: rcs-wrapper/src/mycommit.rb
--- rcs-wrapper/src/mycommit.rb (revision 0)
+++ rcs-wrapper/src/mycommit.rb (revision 0)
@@ -0,0 +1,69 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'rcs'
+
+class Rcs
+
+ def edit! ( *files )
+ system(ENV['EDITOR'], *files)
+ end
+
+ def common_commit! ( *args )
+
+ begin
+ changelog!(*args)
+ rescue MustBeFilled => ex
+ edit! ex.file
+ end
+
+ message!(*args)
+
+ edit! @@message
+
+ changelog!(*args)
+
+ #pager! diff
+ #pager! status
+
+ args << 'ChangeLog' unless args.grep(/^[^-]/).empty?
+
+ commit!(*args)
+
+ return self.revision.readline.to_i
+
+ end
+ protected :common_commit!
+
+
+ def lrdetools_commit! ( s, *args )
+
+ rev = common_commit!(*args)
+
+ s = "LT #{rev}: #{s}"
+
+ news(:groups => ['lrde.proj'], :subject => s)
+
+ @@message.delete
+
+ end
+ alias_command :ltci, :lrdetools_commit
+
+ def test_commit! ( s, *args )
+
+ rev = common_commit!(*args)
+
+ s = "Test #{rev}: #{s}"
+
+# news(:groups => ['local.test'], :subject => s)
+ mail(:to => ['ertai(a)lrde.epita.fr']fr'], :subject => s)
+
+ @@message.delete
+
+ end
+
+end # class Rcs
Index: rcs-wrapper/src/tools.rb
--- rcs-wrapper/src/tools.rb (revision 0)
+++ rcs-wrapper/src/tools.rb (revision 0)
@@ -0,0 +1,51 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+
+module WarnAndError
+
+ def warn ( aString )
+ print "#{RCSW}: warning: ", aString, "\n"
+ end
+
+ def error ( aString )
+ print "#{RCSW}: error: ", aString, "\n"
+ end
+
+ def debug ( aString )
+ print "#{RCSW}: debug: ", aString, "\n"
+ end
+
+end # module WarnAndError
+
+
+class IO
+ include WarnAndError
+end # class IO
+
+
+require 'stringio'
+class StringIO
+ include WarnAndError
+end # class StringIO
+
+
+require 'yaml'
+module YAML
+
+ def self.chop_header ( io )
+ aStr = io.gets
+ raise Exception, "First line is not valid: `#{aLine}'" unless aStr =~
/^---/
+ io.each do |aLine|
+ break if aLine =~ /^---/
+ aStr += aLine
+ end
+ YAML::load(aStr)
+ end
+
+end # module YAML
+
Index: rcs-wrapper/src/mail.rb
--- rcs-wrapper/src/mail.rb (revision 0)
+++ rcs-wrapper/src/mail.rb (revision 0)
@@ -0,0 +1,77 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'rcs'
+require 'tools'
+
+class Rcs
+
+ @@mail = Pathname.new(',mail')
+
+ def parse_mail_options ( *args )
+ require 'optparse'
+ result =
+ {
+ :from => EMAIL, #FULL_EMAIL,
+ :to => [],
+ :server => ENV['SMTPSERVER'] || 'localhost',
+ }
+ if !args.nil? and !args.empty? and args[0].is_a?(Hash)
+ args[0].each do |k,v|
+ result[k] = v
+ end
+ return result
+ end
+ OptionParser.new do |opts|
+ opts.separator ''
+ opts.on('-t', '--mail-to NAME', 'Choose a recipient') do
|aString|
+ result[:to] << aString
+ end
+ opts.on('-s', '--server NAME', 'Choose a mail server') do
|aString|
+ result[:server] = aString
+ end
+ opts.on('-S', '--subject NAME', 'Choose your mail subject')
do |aString|
+ result[:subject] = aString.sub(/\.?$/, '.')
+ end
+ opts.on_tail('-h', '--help', 'Show this message') do
+ puts opts
+ exit
+ end
+ end.parse!(args)
+ raise Failure, 'No recipents' if result[:to].empty?
+ raise Failure, 'No mail server' if result[:server].nil?
+ raise Failure, 'No mail subject' if result[:subject].nil?
+ result
+ end
+ protected :parse_mail_options
+
+ #
+ # Mail.
+ #
+ def mail ( *args )
+
+ print_body(@@mail, parse_mail_options(*args)) unless @@mail.exist?
+
+ @@mail.open('r') do |file|
+ opt = YAML::chop_header(file)
+ STDERR.puts "Smtp Server: #{opt[:server]}"
+ require 'net/smtp'
+ Net::SMTP.start(opt[:server], 25) do |smtp|
+ smtp.open_message_stream(EMAIL, opt[:to]) do |f|
+ f.print "From: #{opt[:from]}\n"
+ f.print "Subject: #{opt[:subject]}\n"
+ f.print "To: #{opt[:to].join(', ')}\n"
+ f.print "\n"
+ file.each { |line| f.print line }
+ end
+ end
+ end
+ @@mail.delete
+ ['Mail: Sent.']
+ end
+
+end # class Rcs
Index: rcs-wrapper/src/message.rb
--- rcs-wrapper/src/message.rb (revision 0)
+++ rcs-wrapper/src/message.rb (revision 0)
@@ -0,0 +1,44 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'rcs'
+require 'changelog'
+
+class Rcs
+
+ @@message = Pathname.new(',message')
+
+ def print_body ( file, options )
+ STDERR.warn "Creating a new `#{file}' file"
+ file.open('w') do |f|
+ f.puts options.to_yaml
+ f.puts '---'
+ f.puts
+ message.each do |line|
+ f.print line
+ end
+ end
+ end
+ private :print_body
+
+ def message ( *args )
+ unless @@message.exist?
+ cl = mkchangelog
+ @@message.open('w') do |f|
+ f.puts 'Index: ChangeLog'
+ cl.each { |line| f.print line.sub(/^\d+-\d+-\d+/, 'from') }
+ f.puts
+ # FIXME: need to remove the ChangeLog entry in diff.
+ diff.each { |line| f.print line unless line =~ /^=+$/ }
+ end
+ end
+ @@message.open('r')
+ end
+
+ alias_command :msg, :message
+
+end # class Rcs
Index: rcs-wrapper/src/svn.rb
--- rcs-wrapper/src/svn.rb (revision 0)
+++ rcs-wrapper/src/svn.rb (revision 0)
@@ -0,0 +1,41 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'rcs'
+require 'tools'
+
+class Svn < Rcs
+
+ class Failure < Rcs::Failure
+ end
+
+ def initialize ( aCmd='svn' )
+ super
+ end
+
+ %w[ blame copy list move switch revert info
+ propdel propget proplist propset mkdir ].each do |m|
+ add_basic_method(m)
+ end
+
+ add_basic_method!(:propedit)
+
+ def commit! ( *args )
+
+ changelog!
+
+ if run!('commit', '--non-interactive', '-F', @@add_cl,
*args)
+ STDERR.puts "Deleting junk files..."
+ @@add_cl.delete
+ @@tmp_cl.delete if @@tmp_cl.exist?
+ else
+ raise Failure, 'commit failed'
+ end
+
+ end
+
+end # class Svn
Index: rcs-wrapper/src/changelog.rb
--- rcs-wrapper/src/changelog.rb (revision 0)
+++ rcs-wrapper/src/changelog.rb (revision 0)
@@ -0,0 +1,108 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'rcs'
+require 'svn'
+
+class Svn
+
+ @@file_st =
+ {
+ 'A' => 'New',
+ 'D' => 'Remove',
+ 'M' => ''
+ }
+
+ @@prop_st =
+ {
+ 'M' => 'Changed property'
+ }
+
+ def mkchangelog_from_status ( *args )
+ result = {}
+ status(*args).each do |line|
+ line =~ /^(.)(.).(.).\s*(.*)$/
+ if $1 != '?'
+ file_st, prop_st, copy_st, file = @@file_st[$1], @@prop_st[$2], $3, $4
+ str = ''
+ str += file_st if file_st
+ str += (file_st.nil?) ? prop_st : (', ' + prop_st.downcase) if prop_st
+ result[file] = str
+ end
+ end
+ raise Failure, 'No changes, so no ChangeLog entry.' if result.empty?
+ result
+ end
+ private :mkchangelog_from_status
+
+end # class Svn
+
+class Rcs
+
+ @@cl = Pathname.new('ChangeLog')
+ @@add_cl = Pathname.new(',ChangeLog')
+ @@tmp_cl = Pathname.new(',,ChangeLog')
+
+ class MustBeFilled < Exception
+ attr_reader :file
+ def initialize ( file )
+ @file = file
+ super("You must fill this file: `#{file.to_s}'")
+ end
+ end
+
+ def mkchangelog ( *args )
+ cl = @@add_cl
+
+ if cl.exist?
+ f = cl.open('r')
+ if f.readline !~ /^===/
+ f.rewind
+ return f
+ end
+ f.close
+ else
+ cl_add = mkchangelog_from_status(*args)
+ STDERR.warn "Creating a new `#{cl}' file"
+ cl.open('w') do |f|
+ f.puts '=== Fill this file correctly and remove this line ==='
+ f.puts `date +"%Y-%m-%d #{FULL_EMAIL}"`
+ f.puts
+ cl_add.each do |file, str|
+ f.puts "\t* #{file}: #{str}."
+ end
+ end
+ end
+
+ raise MustBeFilled, cl
+ end
+
+ def changelog! ( *args )
+ unless @@cl.exist?
+ raise Failure, 'No ChangeLog, you are probably not in a valid directory.'
+ end
+
+ if cl = mkchangelog(*args)
+
+ unless @@tmp_cl.exist?
+ STDERR.warn "Move ChangeLog to `#{@@tmp_cl}'"
+ @@cl.rename(@@tmp_cl)
+ end
+
+ @@cl.open('w') do |file|
+ cl.each { |line| file.print line }
+ file.puts
+ IO.foreach(@@tmp_cl) { |line| file.print line }
+ end
+
+ end
+ end
+
+ alias_command :mkcl, :mkchangelog
+ alias_command :cl, :changelog
+
+end # class Rcs
Index: rcs-wrapper/src/prcs.rb
--- rcs-wrapper/src/prcs.rb (revision 0)
+++ rcs-wrapper/src/prcs.rb (revision 0)
@@ -0,0 +1,21 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'rcs'
+
+class Prcs < Rcs
+
+ def initialize ( aCmd='prcs' )
+ super
+ end
+
+ %w[ admin ].each do |m|
+ add_basic_method(m)
+ end
+
+end # class Prcs
+
Index: rcs-wrapper/src/rcs.rb
--- rcs-wrapper/src/rcs.rb (revision 0)
+++ rcs-wrapper/src/rcs.rb (revision 0)
@@ -0,0 +1,155 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+class Class # :nodoc:
+
+ def add_basic_method ( meth )
+ class_eval <<-end_eval
+ def #{meth} ( *args )
+ run("#{meth}", *args)
+ end
+ def #{meth}_ ( *args )
+ run("#{meth}", *args)
+ end
+ end_eval
+ end
+
+ def add_basic_method! ( meth )
+ class_eval <<-end_eval
+ def #{meth}! ( *args )
+ run!("#{meth}", *args)
+ end
+ def #{meth}_! ( *args )
+ run!("#{meth}", *args)
+ end
+ end_eval
+ end
+
+ def alias_command ( m1, m2 )
+ class_eval <<-end_eval
+ def #{m1} ( *args )
+ #{m2}(*args)
+ end
+ def #{m1}_ ( *args )
+ #{m2}_(*args)
+ end
+ def #{m1}! ( *args )
+ #{m2}!(*args)
+ end
+ def #{m1}_! ( *args )
+ #{m2}_!(*args)
+ end
+ end_eval
+ end
+
+end # class Class
+
+# The abstract class for a Rcs wrapper.
+# Conventions:
+# example:
+# svn checkout
http://foo.bar/proj # normal command
+# xrcs checkout
http://foo.bar/proj # normal
+#
+# checkout
+# checkout_
+# checkout!
+# checkout_!
+#
+class Rcs
+
+ class Failure < Exception
+ end
+
+ def initialize ( aCmd )
+ @cmd = aCmd
+ end
+
+ def common_run ( args )
+ cmd = @cmd + ' ' + args.join(' ')
+ STDERR.debug "running: #{cmd}" if $debug > 1
+ cmd
+ end
+ private :common_run
+
+ def run ( *args )
+ IO.popen(common_run(args))
+ end
+
+ def run! ( *args )
+ system(common_run(args))
+ end
+
+ %w[ checkout delete diff help status log add update ].each do |m|
+ add_basic_method(m)
+ end
+
+ add_basic_method!(:commit)
+
+ def method_missing ( meth, *args )
+ meth = meth.to_s
+ if meth =~ /^(.*)!$/
+ if respond_to? $1
+ send($1, *args).each do |line|
+ puts line
+ end
+ else
+ STDERR.warn "unknown method #{meth}"
+ run!($1.sub(/_$/, ''), *args)
+ end
+ else
+ STDERR.warn "unknown method #{meth}"
+ run(meth.sub(/_$/, ''), *args)
+ end
+ end
+
+ alias_command :ann, :blame
+ alias_command :annotate, :blame
+ alias_command :praise, :blame
+ alias_command :co, :checkout
+ alias_command :ci, :commit
+ alias_command :cp, :copy
+ alias_command :del, :delete
+ alias_command :remove, :delete
+ alias_command :rm, :delete
+ alias_command :di, :diff
+ alias_command :h, :help
+ alias_command :ls, :list
+ alias_command :mv, :move
+ alias_command :rename, :move
+ alias_command :ren, :move
+ alias_command :pdel, :propdel
+ alias_command :pd, :propdel
+ alias_command :pedit, :propedit
+ alias_command :pe, :propedit
+ alias_command :pget, :propget
+ alias_command :pg, :propget
+ alias_command :plist, :proplist
+ alias_command :pl, :proplist
+ alias_command :pset, :propset
+ alias_command :ps, :propset
+ alias_command :st, :status
+ alias_command :stat, :status
+ alias_command :sw, :switch
+ alias_command :up, :update
+
+ # Cvs Alias
+ alias_command :new, :add
+ alias_command :rcs, :admin
+ alias_command :get, :checkout
+ alias_command :rlog, :log
+ alias_command :patch, :rdiff
+ alias_command :remove, :delete
+ alias_command :rm, :delete
+ alias_command :rfreeze, :rtag
+ alias_command :freeze, :tag
+
+ # Prcs Alias
+ alias_command :checkin, :commit
+ alias_command :populate, :add
+
+end # class Rcs
+
Index: rcs-wrapper/src/cvs.rb
--- rcs-wrapper/src/cvs.rb (revision 0)
+++ rcs-wrapper/src/cvs.rb (revision 0)
@@ -0,0 +1,21 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'rcs'
+
+class Cvs < Rcs
+
+ def initialize ( aCmd='cvs' )
+ super
+ end
+
+ %w[ rdiff rtag tag ].each do |m|
+ add_basic_method(m)
+ end
+
+end # class Cvs
+
Index: rcs-wrapper/src/news.rb
--- rcs-wrapper/src/news.rb (revision 0)
+++ rcs-wrapper/src/news.rb (revision 0)
@@ -0,0 +1,79 @@
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+require 'tools'
+require 'message'
+
+class Rcs
+
+ @@news = Pathname.new(',news')
+
+ def parse_news_options ( *args )
+ require 'optparse'
+ result =
+ {
+ :from => FULL_EMAIL,
+ :groups => [],
+ :server => ENV['NNTPSERVER'],
+ }
+ if !args.nil? and !args.empty? and args[0].is_a?(Hash)
+ args[0].each do |k,v|
+ result[k] = v
+ end
+ return result
+ end
+ OptionParser.new do |opts|
+ opts.separator ''
+ opts.on('-g', '--group NAME', 'Choose a news group') do
|aString|
+ result[:groups] << aString
+ end
+ opts.on('-s', '--server NAME', 'Choose a news server') do
|aString|
+ result[:server] = aString
+ end
+ opts.on('-S', '--subject NAME', 'Choose your news subject')
do |aString|
+ result[:subject] = aString
+ end
+ opts.on_tail('-h', '--help', 'Show this message') do
+ puts opts
+ exit
+ end
+ end.parse!(args)
+ raise Failure, 'No news group' if result[:groups].empty?
+ raise Failure, 'No news server' if result[:server].nil?
+ raise Failure, 'No news subject' if result[:subject].nil?
+ result
+ end
+ protected :parse_news_options
+
+ #
+ # Post the news.
+ #
+ def news ( *args )
+ print_body(@@news, parse_news_options(*args)) unless @@news.exist?
+
+ @@news.open('r') do |file|
+ opt = YAML::chop_header(file)
+ STDERR.puts "Nntp Server: #{opt[:server]}"
+ require 'socket'
+ TCPSocket.open(opt[:server], 119) do |f|
+ f.puts 'post'
+ 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 '.'
+ f.puts 'quit'
+ end
+ end
+ @@news.delete
+ StringIO.new('News: Sent.')
+ end
+
+end # class Rcs
Index: rcs-wrapper/bin/rcsw
--- rcs-wrapper/bin/rcsw (revision 0)
+++ rcs-wrapper/bin/rcsw (revision 0)
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+# Author:: Nicolas Pouillard <ertai(a)lrde.epita.fr>fr>.
+# Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
+# License:: GNU General Public License (GPL).
+
+# $LastChangedBy: ertai $
+# $Id: header 98 2004-09-29 12:07:43Z ertai $
+
+
+# rcsw : The rcs wrapper.
+#
+# rcsw is a wrapper over any revision control system.
+
+require 'pathname'
+
+RCSW_PATH = Pathname.new(__FILE__).expand_path
+RCSW_DIR, RCSW = RCSW_PATH.split
+SRC = RCSW_DIR + '..' + 'src'
+$: << SRC
+
+
+if ARGV == ['--mk-alias']
+ %w[ svn cvs prcs ].each { |x| puts "alias #{x}=#{RCSW_PATH}" }
+ exit
+end
+
+$debug = ENV['RCSW_DEBUG'].to_i
+
+FULLNAME = ENV['FULLNAME'] || (Etc.getpwnam ENV['USER']).gecos
+EMAIL = ENV['EMAIL']
+FULL_EMAIL = "#{FULLNAME} <#{EMAIL}>"
+if FULLNAME.nil? or EMAIL.nil?
+ STDERR.error 'Need FULLNAME and EMAIL in the environement'
+ exit
+end
+
+Pathname.glob(SRC + '*.rb') do |file|
+ puts file.basename if $debug > 4
+ require file
+end
+
+def guess_rcs
+ if Pathname.new('CVS').directory?
+ Cvs
+ elsif Pathname.new('.svn').directory?
+ Svn
+ elsif Pathname.new('prj').exist?
+ Prcs
+ else
+ raise ArgumentError, 'Can\'t guess your RCS system'
+ end
+end
+
+def main
+ rcs = guess_rcs().new
+
+ if ARGV.empty?
+ meth = :help!
+ else
+ meth = ARGV.shift.sub(/([^!])$/, '\1!')
+ end
+
+ begin
+ rcs.send(meth, *ARGV)
+ rescue Exception
+ raise if $debug > 0
+ STDERR.error $!.to_s.sub(/\.$/, '') unless $!.to_s == 'exit'
+ end
+end
+
+if __FILE__ == $0
+
+ main()
+
+end
Property changes on: rcs-wrapper/bin/rcsw
___________________________________________________________________
Name: svn:executable
+ *
Index: rcs-wrapper/README
--- rcs-wrapper/README (revision 0)
+++ rcs-wrapper/README (revision 0)
@@ -0,0 +1,100 @@
+---
+
+Intro: >
+ Bon voilà je n'ai pas trop le temps en ce moment de m'occuper de cet outil.
+ Il est bien commencé et à mon goût déjà utilisable.
+
+ Je vais continuer à le debugger, mais en attendant vous pouvez vous amuser
+ avec.
+
+ C'est un wrapper pour les systèmes de versions comme Svn, Cvs, Prcs, Arch...
+
+ Il permet donc au minimum de faire tout ce que le système sous-jacent propose,
+ mais l'avantage est de pouvoir l'étendre facilement.
+
+Exemples d'utilisation:
+
+ - Pour plus de transparence on va faire des alias:
+ genre: alias svn=...../rcsw, idem pour cvs, prcs...
+ pour cela taper simplement: >
+ `le_chemin_vers_rcsw/bin/rcsw --mk-alias`
+
+ - Les commandes de svn fonctionnent toujours: >
+ placez vous dans un répertoire svn.
+
+ ex: svn status
+
+ - Les commandes de base peuvent être surchargées: >
+ par exemple `svn commit' a un comportement non interactif par défaut.
+
+ Mais l'on peut tout de même appeler le véritable `svn commit'.
+
+ Pour cela il y a une convention: toutes les méthodes terminant par `_'
+ sont directement celles du système.
+
+ Avec cela on peut vérifier que l'outil est bien devant le vrai svn:
+ svn status_
+
+ - Quelques fonctionnalités:
+
+ - Un des principaux avantages est la gestion des erreurs: >
+
+ Par exemple on utilise une commande qui commit puis poste une news.
+ Si le commit réussit et que la news ne passe pas, il suffit de
+ relancer l'envoi de la news: svn news
+
+ En effet chaque méthode peut conserver des fichiers, ces fichiers
+ commencent par `,'.
+
+ - Les alias de méthodes: >
+
+ Tout comme svn et cvs il existe des raccourcis pour certaines méthodes
+ et bien sûr lorsque l'on surcharge une méthode, le raccourci pointe
+ vers la nouvelle méthode.
+
+ - Quelques nouvelles méthodes:
+
+ - svn revision: Renvoie le numéro de révision courante
+ alias: rev
+
+ - svn mkchangelog: Génère une entrée de ChangeLog à partir de `status'
+ alias: mkcl
+
+ - svn changelog: Concatène la nouvelle entrée au véritable ChangeLog
+ alias: cl
+
+ - svn message: Crée le contenu du message du mail ou de la news
+ alias: msg
+
+ - svn mail: Envoie un mail.
+
+ - svn news: Poste une news.
+
+ - Plus facile d'écrire des scripts: >
+
+ Lorsque l'on veut automatiser un traitement fait avec un Rcs,
+ il est généralement plus simple de le faire en script shell
+ lorsque cela n'excède pas une dixaine de lignes. Après cela certains
+ avantages des langages de script évolués nous manquent.
+ Pourtant certaines choses très faciles peuvent devenir plus lourdes:
+ svn commit => system("svn commit")
+
+ Heureusement avec le modèle objet déjà posé cela devient plus
+ raisonnable, comparez par exemple:
+
+ svn commit => svn.commit
+
+ svn info | grep '^Revision' | sed s/^Revision: //
+
+ =>
+
+ svn.info.readlines.grep(/^Revision/)[0].sub(/^Revision: /, '').to_i
+
+ - Exemple final: commit complet pour les lrde tools.
+
+ commande: svn lrdetools_commit 'votre sujet pour la news'
+
+ ensuite: laissez vous guider.
+
+ - S'il y a des questions il y aura des réponses.
+