subtrigger 0.2.7 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.md +158 -0
- data/Rakefile +3 -9
- data/VERSION +1 -1
- data/lib/subtrigger.rb +83 -118
- data/lib/subtrigger/dsl.rb +42 -0
- data/lib/subtrigger/path.rb +82 -0
- data/lib/subtrigger/revision.rb +95 -0
- data/lib/subtrigger/rule.rb +165 -0
- data/lib/subtrigger/template.rb +96 -0
- data/subtrigger.gemspec +24 -24
- data/test/test_helper.rb +3 -0
- data/test/test_path.rb +50 -0
- data/test/test_revision.rb +43 -0
- data/test/test_rule.rb +66 -0
- data/test/test_template.rb +42 -0
- metadata +48 -26
- data/LICENSE +0 -20
- data/README.rdoc +0 -56
- data/bin/subtrigger +0 -9
- data/lib/subtrigger/email.rb +0 -56
- data/lib/subtrigger/repository.rb +0 -140
- data/lib/subtrigger/trigger.rb +0 -60
- data/test/helper.rb +0 -11
- data/test/test_email.rb +0 -44
- data/test/test_repository.rb +0 -75
- data/test/test_subtrigger.rb +0 -44
- data/test/test_trigger.rb +0 -37
data/.gitignore
CHANGED
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# Subtrigger
|
2
|
+
|
3
|
+
A simple DSL for defining callbacks to be fired as Subversion hooks with
|
4
|
+
built-in support for inspecting the repository and sending out e-mails.
|
5
|
+
|
6
|
+
## Example Usage
|
7
|
+
|
8
|
+
This library is intended for use as a Subversion post-commit hook. It allows
|
9
|
+
you to define callbacks that fire when certain conditions on a revision are
|
10
|
+
met. Simply require Subtrigger and define your rules:
|
11
|
+
|
12
|
+
require 'subtrigger'
|
13
|
+
|
14
|
+
on /deploy to (\w+)/ do |revision, matches|
|
15
|
+
puts "Should deploy to #{matches[:message].first}"
|
16
|
+
end
|
17
|
+
|
18
|
+
Save this as a file in your Subversion repository, like
|
19
|
+
`/path/to/repo/hooks/deploy.rb`. Then in your Subversion commit hook
|
20
|
+
file (`/path/to/repo/hooks/post-commit`) simply call the file using
|
21
|
+
Ruby:
|
22
|
+
|
23
|
+
/usr/bin/ruby -rubygems /path/to/repo/hooks/deploy.rb "$1" "$2"
|
24
|
+
|
25
|
+
You could define triggers to…
|
26
|
+
|
27
|
+
* Send out confirmation e-mails to specific developers
|
28
|
+
* Auto-update a working copy on a production server
|
29
|
+
* Create a back-up archive
|
30
|
+
* Whatever else you can do with Ruby…
|
31
|
+
|
32
|
+
## Detailed usage
|
33
|
+
|
34
|
+
### Matchers
|
35
|
+
|
36
|
+
The default usage in the example above uses a regular expression which by
|
37
|
+
default will be matched against the log message of the revision that
|
38
|
+
triggers the hook. But you can test both other attributes and with other
|
39
|
+
objects (basically anything that responds to `#===`).
|
40
|
+
|
41
|
+
# Test on author name
|
42
|
+
# You can use `:author`, `:message`, `:date`,
|
43
|
+
# `:number`
|
44
|
+
on :author => /john|graham|michael|terry/ do
|
45
|
+
puts 'Always look on the bright side of life!'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Test using a matcher object
|
49
|
+
class EvenNumberMatcher
|
50
|
+
def ===(revision)
|
51
|
+
revision.number % 2 == 0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
on :number => EvenNumberMatcher.new do
|
55
|
+
puts 'The revision number is an even number'
|
56
|
+
end
|
57
|
+
|
58
|
+
### Sending e-mails
|
59
|
+
|
60
|
+
Subtrigger uses Pony to enable the sending of e-mails straigt from your
|
61
|
+
triggers. This means you can send an e-mail when a branch is created, just
|
62
|
+
to name an example.
|
63
|
+
|
64
|
+
on /confirm via email/ do
|
65
|
+
mail :to => 'me@example.com',
|
66
|
+
:from => 'svn@example.com',
|
67
|
+
:subject => 'E-mail confirmation of commit',
|
68
|
+
:body => 'Your commit has been saved.'
|
69
|
+
end
|
70
|
+
|
71
|
+
### Inline templates
|
72
|
+
|
73
|
+
To remove long strings from your templates you can define templates right
|
74
|
+
in your rules file.
|
75
|
+
|
76
|
+
on /confirm via email/ do |r|
|
77
|
+
mail :to => 'me@example.com',
|
78
|
+
:from => 'svn@example.com',
|
79
|
+
:subject => 'E-mail confirmation of commit',
|
80
|
+
:body => template('E-mail confirmation', r.number)
|
81
|
+
end
|
82
|
+
__END__
|
83
|
+
@@ E-mail confirmation
|
84
|
+
Your commit (%s) has been saved
|
85
|
+
@@ Other template
|
86
|
+
...
|
87
|
+
|
88
|
+
This will result in an e-mail like `Your commit (5299) has been
|
89
|
+
saved`.
|
90
|
+
|
91
|
+
### Running the triggers
|
92
|
+
|
93
|
+
Note that all triggers are run when your rules file ends (using the global
|
94
|
+
`at_exit` callback). There's no need to explicitly start this process
|
95
|
+
yourself.
|
96
|
+
|
97
|
+
## Warnings
|
98
|
+
|
99
|
+
Note that subversion calls its hooks in an empty environment, so there's
|
100
|
+
no `$PATH` or anything. Always use full absolute paths. Also, hooks are
|
101
|
+
notoriously hard to debug, so make sure to write some debugging information
|
102
|
+
somewhere so you know what is going on.
|
103
|
+
|
104
|
+
## Changes
|
105
|
+
|
106
|
+
Note that Subtrigger is still in early stages of development.
|
107
|
+
Until it hits 1.0 there are bound to be major changes.
|
108
|
+
|
109
|
+
### 0.3.0
|
110
|
+
|
111
|
+
* Complete rewrite of the system
|
112
|
+
* Improved documentation
|
113
|
+
* Improved test coverage
|
114
|
+
* Added matching on more revision attributes
|
115
|
+
* Added custom matcher objects
|
116
|
+
* Use Pony for e-mail
|
117
|
+
* Cleaner rules files
|
118
|
+
|
119
|
+
## Credits
|
120
|
+
|
121
|
+
* **Author**: Arjan van der Gaag
|
122
|
+
* **E-mail**: arjan@arjanvandergaag.nl
|
123
|
+
* **URL**: http://arjanvandergaag.nl
|
124
|
+
* **Source**: http://github.com/avdgaag/subtrigger
|
125
|
+
|
126
|
+
## Note on Patches/Pull Requests
|
127
|
+
|
128
|
+
* Fork the project.
|
129
|
+
* Make your feature addition or bug fix.
|
130
|
+
* Add tests for it. This is important so I don't break it in a
|
131
|
+
future version unintentionally.
|
132
|
+
* Commit, do not mess with rakefile, version, or history.
|
133
|
+
(if you want to have your own version, that is fine but bump version in a
|
134
|
+
commit by itself I can ignore when I pull)
|
135
|
+
* Send me a pull request. Bonus points for topic branches.
|
136
|
+
|
137
|
+
## License
|
138
|
+
|
139
|
+
Copyright (c) 2010 Arjan van der Gaag
|
140
|
+
|
141
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
142
|
+
a copy of this software and associated documentation files (the
|
143
|
+
"Software"), to deal in the Software without restriction, including
|
144
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
145
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
146
|
+
permit persons to whom the Software is furnished to do so, subject to
|
147
|
+
the following conditions:
|
148
|
+
|
149
|
+
The above copyright notice and this permission notice shall be
|
150
|
+
included in all copies or substantial portions of the Software.
|
151
|
+
|
152
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
153
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
154
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
155
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
156
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
157
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
158
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -12,6 +12,7 @@ begin
|
|
12
12
|
gem.authors = ["Arjan van der Gaag"]
|
13
13
|
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
14
|
gem.add_development_dependency "mocha", ">= 0"
|
15
|
+
gem.add_dependency 'pony'
|
15
16
|
end
|
16
17
|
Jeweler::GemcutterTasks.new
|
17
18
|
rescue LoadError
|
@@ -42,12 +43,5 @@ task :test => :check_dependencies
|
|
42
43
|
|
43
44
|
task :default => :test
|
44
45
|
|
45
|
-
require '
|
46
|
-
Rake::
|
47
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
-
|
49
|
-
rdoc.rdoc_dir = 'rdoc'
|
50
|
-
rdoc.title = "subtrigger #{version}"
|
51
|
-
rdoc.rdoc_files.include('README*')
|
52
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
-
end
|
46
|
+
require 'yard'
|
47
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/subtrigger.rb
CHANGED
@@ -1,133 +1,98 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
# Subtrigger.on(/\[deploy\]/) do |matches, repo|
|
18
|
-
# # do some smart stuff here
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# Your trigger has access to the captured groups in its regular expression
|
22
|
-
# matcher, and to all the <tt>svnlook</tt> information from the repository
|
23
|
-
# at the revision that fired the hook. This gives you access to changed paths,
|
24
|
-
# its author, date, etc.
|
25
|
-
#
|
26
|
-
# == E-mail notifications
|
27
|
-
#
|
28
|
-
# Subtrigger allows you to send notification e-mails to developers:
|
29
|
-
#
|
30
|
-
# # in your trigger:
|
31
|
-
# Subtrigger::Email.new(:to => "#{repo.author}@company.tld",
|
32
|
-
# :from => 'svn@company.tld',
|
33
|
-
# :subject => 'Trigger notification',
|
34
|
-
# :body => "Dear #{repo.author}, ...")
|
35
|
-
#
|
36
|
-
# == Usage
|
37
|
-
#
|
38
|
-
# This library is intended to be used as a Subversion post-commit hook.
|
39
|
-
# The best way to use it to create a post-commit hook file that requires this
|
40
|
-
# library, sets up one or more triggers and than fires the processing.
|
41
|
-
#
|
42
|
-
# Here's an example:
|
43
|
-
#
|
44
|
-
# #!/usr/local/bin/ruby
|
45
|
-
# require 'rubygems'
|
46
|
-
# require 'subtrigger'
|
47
|
-
# Subtrigger.configure { |c|
|
48
|
-
# c.svn = '/usr/bin/svn'
|
49
|
-
# }.on(/foo/) { |matches, repo|
|
50
|
-
# puts "#{repo.author} comitted foo!"
|
51
|
-
# }.on(/bar/) { |matches, repo|
|
52
|
-
# puts "#{repo.author} comitted bar!"
|
53
|
-
# }.run(*ARGV)
|
54
|
-
#
|
55
|
-
# === Command Line Usage
|
56
|
-
#
|
57
|
-
# There is a command-line tool available for running Subtrigger. It simply
|
58
|
-
# requires the Subtrigger library and calls +run+. Most of the time, you'll
|
59
|
-
# want to write your own script and <tt>require 'subtrigger'</tt> yourself.
|
60
|
-
#
|
61
|
-
# You can run <tt>subtrigger -v</tt> to see the currently installed version
|
62
|
-
# of Subtrigger.
|
63
|
-
#
|
64
|
-
# === Configuration
|
65
|
-
#
|
66
|
-
# Since subversion usually calls its hooks in an empty environment (even
|
67
|
-
# without a $PATH) you might need to make some settings manually:
|
68
|
-
#
|
69
|
-
# Subtrigger.svn = '/path/to/svn'
|
70
|
-
# Subtrigger.sendmail = '/path/to/sendmail'
|
71
|
-
# Subtrigger.svn_args = ''
|
72
|
-
#
|
73
|
-
# You can alternatively use a block:
|
74
|
-
#
|
75
|
-
# Subtrigger.configure do |c|
|
76
|
-
# c.svn = '/path/to/svn'
|
77
|
-
# end
|
78
|
-
#
|
79
|
-
# This will return Subtrigger itself, so you can chain further commands.
|
80
|
-
#
|
81
|
-
# The <tt>svn_args</tt> setting is a string appended to every +svn+ command.
|
82
|
-
# This allows you to, for example, set a custom username and password. You
|
83
|
-
# might also want to apply the <tt>--non-interactive</tt> argument. For
|
84
|
-
# example:
|
85
|
-
#
|
86
|
-
# Subtrigger.svn_args = '--username my_name --password secret --non-interactive'
|
87
|
-
#
|
88
|
-
# Make sure your gems are installed and the correct permissions are set. Note
|
89
|
-
# that Subversion hooks generate no output, so run your hooks manually for
|
90
|
-
# testing purposes.
|
1
|
+
# Libraries
|
2
|
+
begin
|
3
|
+
require 'pony'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'WARNING: Subtrigger requires Pony to send e-mails.'
|
6
|
+
end
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
# Load internals
|
10
|
+
require 'subtrigger/dsl'
|
11
|
+
require 'subtrigger/revision'
|
12
|
+
require 'subtrigger/rule'
|
13
|
+
require 'subtrigger/template'
|
14
|
+
require 'subtrigger/path'
|
15
|
+
|
91
16
|
module Subtrigger
|
92
|
-
attr_accessor :svn, :sendmail, :svn_args
|
93
17
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
18
|
+
# Standard exception for all Subtrigger exceptions
|
19
|
+
Exception = Class.new(Exception)
|
20
|
+
|
21
|
+
# Return the current version number as defined in ./VERSION
|
22
|
+
# @return [String] version number (e.g. '0.3.1')
|
23
|
+
def version
|
24
|
+
File.read(File.join(File.dirname(__FILE__), '..', 'VERSION'))
|
98
25
|
end
|
99
26
|
|
100
|
-
|
101
|
-
|
27
|
+
# Run the current file of rules.
|
28
|
+
#
|
29
|
+
# This method is called after all the rules have been defined. It takes
|
30
|
+
# the command line arguments that come from subversion.
|
31
|
+
#
|
32
|
+
# @param [String] repository_path is the path to the repository to query
|
33
|
+
# @param [String] revision_number is the revision number that triggered the
|
34
|
+
# hook.
|
35
|
+
# @return nil
|
36
|
+
def run(repository_path, revision_number)
|
37
|
+
Template.parse(DATA.read)
|
38
|
+
rev = Revision.new(
|
39
|
+
revision_number,
|
40
|
+
svnlook('info', repository_path),
|
41
|
+
svnlook('dirs-changed', repository_path)
|
42
|
+
)
|
43
|
+
Rule.matching(rev).each { |r| r.run(rev) }
|
102
44
|
end
|
103
45
|
|
104
|
-
#
|
105
|
-
|
106
|
-
|
46
|
+
# Make a system call to <tt>svn</tt> with the given arguments. The
|
47
|
+
# executable that used is the first found by {Path#to}.
|
48
|
+
#
|
49
|
+
# @example Using multiple arguments
|
50
|
+
# svn 'update', 'foo', '--ignore-externals'
|
51
|
+
# # => '/usr/bin/svn update foo --ignore-externals'
|
52
|
+
# @return [String] output from the command
|
53
|
+
def svn(*args)
|
54
|
+
`svn #{[*args].join(' ')}`
|
107
55
|
end
|
108
56
|
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
# number.
|
57
|
+
# Make a system call to <tt>svnlook</tt> with the given arguments. The
|
58
|
+
# executable # that used is the first found in
|
59
|
+
# <tt>POSSIBLE_PATHS</tt>.
|
113
60
|
#
|
114
|
-
#
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
61
|
+
# @example Using multiple arguments
|
62
|
+
# svnlook 'youngest', '/path/to/repo'
|
63
|
+
# # => '/usr/bin/svnlook youngest /path/to/repo
|
64
|
+
# @return [String] output from the command
|
65
|
+
def svnlook(*args)
|
66
|
+
`svnlook #{[*args].join(' ')}`
|
67
|
+
end
|
68
|
+
|
69
|
+
# @see Path#initialize
|
70
|
+
# @return [Path] The 'global' Path object
|
71
|
+
def path
|
72
|
+
@path ||= Path.new
|
119
73
|
end
|
120
74
|
|
121
|
-
|
122
|
-
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
|
75
|
+
private
|
76
|
+
|
77
|
+
# Override Kernel#` to prefix our commands with the path to subversion
|
78
|
+
# @param [String] arg is the command to run
|
79
|
+
# @return [String] the command's output
|
80
|
+
# @todo maybe build a check to only prefix the path when actually calling
|
81
|
+
# svn or svnlook or something.
|
82
|
+
def `(arg)
|
83
|
+
super(path_to('svn') + '/' + arg)
|
127
84
|
end
|
85
|
+
|
128
86
|
extend self
|
129
87
|
end
|
130
88
|
|
131
|
-
|
132
|
-
|
133
|
-
|
89
|
+
# Make the DSL available in the top-level domain
|
90
|
+
include Subtrigger::Dsl
|
91
|
+
|
92
|
+
# At the end of the rules file perfrom the actual {Subtrigger#run}
|
93
|
+
at_exit do
|
94
|
+
unless $prevent_subtrigger_run
|
95
|
+
raise ArgumentError unless ARGV.size == 2
|
96
|
+
Subtrigger.run(*ARGV)
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Subtrigger
|
2
|
+
# The Dsl module provides some nice-looking methods that can be used to
|
3
|
+
# perform the most important Subtrigger operations.
|
4
|
+
#
|
5
|
+
# This is intended to be included in the top-level namespace, so a script
|
6
|
+
# can call these functions directly.
|
7
|
+
#
|
8
|
+
# @author Arjan van der Gaag
|
9
|
+
# @since 0.3.0
|
10
|
+
module Dsl
|
11
|
+
# Define a new trigger on incoming Revision.
|
12
|
+
#
|
13
|
+
# @see Rule#initialize
|
14
|
+
# @return [nil]
|
15
|
+
def on(*args, &block)
|
16
|
+
Rule.new(*args, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create and deliver a new Mail object using Pony. See its documentation
|
20
|
+
# for more information.
|
21
|
+
# @return [nil]
|
22
|
+
def mail(*args)
|
23
|
+
::Pony.mail(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Call Subversion commands using the configured svn executable.
|
27
|
+
#
|
28
|
+
# @see Subtrigger#svn
|
29
|
+
# @return [String] the command's output
|
30
|
+
def svn(*args)
|
31
|
+
Subtrigger.svn(*args)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get a template defined inline and format it using the given arguments.
|
35
|
+
#
|
36
|
+
# @see Template#format
|
37
|
+
# @return [String] the formatted template
|
38
|
+
def template(name, *format_arguments)
|
39
|
+
Template.find(name).format [*format_arguments]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|