subtrigger 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|