sr-integrity 0.1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/README.markdown +73 -0
  2. data/Rakefile +91 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/integrity +4 -0
  5. data/config/config.sample.ru +21 -0
  6. data/config/config.sample.yml +41 -0
  7. data/config/thin.sample.yml +13 -0
  8. data/integrity.gemspec +55 -0
  9. data/lib/integrity/app.rb +137 -0
  10. data/lib/integrity/author.rb +39 -0
  11. data/lib/integrity/build.rb +84 -0
  12. data/lib/integrity/commit.rb +71 -0
  13. data/lib/integrity/core_ext/object.rb +6 -0
  14. data/lib/integrity/helpers/authorization.rb +33 -0
  15. data/lib/integrity/helpers/breadcrumbs.rb +20 -0
  16. data/lib/integrity/helpers/forms.rb +28 -0
  17. data/lib/integrity/helpers/pretty_output.rb +45 -0
  18. data/lib/integrity/helpers/rendering.rb +14 -0
  19. data/lib/integrity/helpers/resources.rb +13 -0
  20. data/lib/integrity/helpers/urls.rb +49 -0
  21. data/lib/integrity/helpers.rb +16 -0
  22. data/lib/integrity/installer.rb +121 -0
  23. data/lib/integrity/migrations.rb +140 -0
  24. data/lib/integrity/notifier/base.rb +65 -0
  25. data/lib/integrity/notifier.rb +50 -0
  26. data/lib/integrity/project.rb +142 -0
  27. data/lib/integrity/project_builder.rb +56 -0
  28. data/lib/integrity/scm/git/uri.rb +57 -0
  29. data/lib/integrity/scm/git.rb +84 -0
  30. data/lib/integrity/scm.rb +19 -0
  31. data/lib/integrity.rb +80 -0
  32. data/public/buttons.css +82 -0
  33. data/public/reset.css +7 -0
  34. data/public/spinner.gif +0 -0
  35. data/test/helpers/acceptance/git_helper.rb +99 -0
  36. data/test/helpers/acceptance/textfile_notifier.rb +26 -0
  37. data/test/helpers/acceptance.rb +80 -0
  38. data/test/helpers/expectations/be_a.rb +23 -0
  39. data/test/helpers/expectations/change.rb +90 -0
  40. data/test/helpers/expectations/have.rb +105 -0
  41. data/test/helpers/expectations/have_tag.rb +128 -0
  42. data/test/helpers/expectations/predicates.rb +37 -0
  43. data/test/helpers/expectations.rb +5 -0
  44. data/test/helpers/fixtures.rb +107 -0
  45. data/test/helpers/initial_migration_fixture.sql +44 -0
  46. data/test/helpers.rb +70 -0
  47. data/views/_commit_info.haml +24 -0
  48. data/views/build.haml +2 -0
  49. data/views/error.haml +37 -0
  50. data/views/home.haml +21 -0
  51. data/views/integrity.sass +400 -0
  52. data/views/layout.haml +28 -0
  53. data/views/new.haml +51 -0
  54. data/views/not_found.haml +31 -0
  55. data/views/notifier.haml +7 -0
  56. data/views/project.builder +21 -0
  57. data/views/project.haml +30 -0
  58. data/views/unauthorized.haml +38 -0
  59. metadata +190 -0
@@ -0,0 +1,142 @@
1
+ module Integrity
2
+ class Project
3
+ include DataMapper::Resource
4
+
5
+ property :id, Integer, :serial => true
6
+ property :name, String, :nullable => false
7
+ property :permalink, String
8
+ property :uri, URI, :nullable => false, :length => 255
9
+ property :branch, String, :nullable => false, :default => "master"
10
+ property :command, String, :nullable => false, :length => 255, :default => "rake"
11
+ property :public, Boolean, :default => true
12
+ property :building, Boolean, :default => false
13
+ property :created_at, DateTime
14
+ property :updated_at, DateTime
15
+
16
+ has n, :commits, :class_name => "Integrity::Commit"
17
+ has n, :notifiers, :class_name => "Integrity::Notifier"
18
+
19
+ before :save, :set_permalink
20
+ before :destroy, :delete_code
21
+
22
+ validates_is_unique :name
23
+
24
+ def self.only_public_unless(condition)
25
+ if condition
26
+ all
27
+ else
28
+ all(:public => true)
29
+ end
30
+ end
31
+
32
+ def build(commit_identifier="HEAD")
33
+ commit_identifier = head_of_remote_repo if commit_identifier == "HEAD"
34
+ commit = find_or_create_commit_with_identifier(commit_identifier)
35
+ commit.queue_build
36
+ end
37
+
38
+ def push(payload)
39
+ payload = JSON.parse(payload || "")
40
+ return unless payload["ref"] =~ /#{branch}/
41
+ return if payload["commits"].nil?
42
+ return if payload["commits"].empty?
43
+
44
+ commits = if Integrity.config[:build_all_commits]
45
+ payload["commits"]
46
+ else
47
+ [payload["commits"].first]
48
+ end
49
+
50
+ commits.each do |commit_data|
51
+ create_commit_from(commit_data)
52
+ build(commit_data['id'])
53
+ end
54
+ end
55
+
56
+ def last_commit
57
+ commits.first(:project_id => id, :order => [:committed_at.desc])
58
+ end
59
+
60
+ def last_build
61
+ warn "Project#last_build is deprecated, use Project#last_commit"
62
+ last_commit
63
+ end
64
+
65
+ def previous_commits
66
+ commits.all(:project_id => id, :order => [:committed_at.desc]).tap {|commits| commits.shift }
67
+ end
68
+
69
+ def previous_builds
70
+ warn "Project#previous_builds is deprecated, use Project#previous_commits"
71
+ previous_commits
72
+ end
73
+
74
+ def status
75
+ last_commit && last_commit.status
76
+ end
77
+
78
+ def human_readable_status
79
+ last_commit && last_commit.human_readable_status
80
+ end
81
+
82
+ def public=(flag)
83
+ attribute_set(:public, case flag
84
+ when "1", "0" then flag == "1"
85
+ else !!flag
86
+ end)
87
+ end
88
+
89
+ def config_for(notifier)
90
+ notifier = notifiers.first(:name => notifier.to_s.split(/::/).last, :project_id => id)
91
+ notifier.blank? ? {} : notifier.config
92
+ end
93
+
94
+ def notifies?(notifier)
95
+ !notifiers.first(:name => notifier.to_s.split(/::/).last, :project_id => id).blank?
96
+ end
97
+
98
+ def enable_notifiers(*args)
99
+ Notifier.enable_notifiers(id, *args)
100
+ end
101
+
102
+ private
103
+ def find_or_create_commit_with_identifier(commit_identifier)
104
+ # We abuse +committed_at+ here setting it to Time.now because we use it
105
+ # to sort (for last_commit and previous_commits). I don't like this
106
+ # very much, but for now it's the only solution I can find.
107
+ #
108
+ # This also creates a dependency, as now we *always* have to update the
109
+ # +committed_at+ field after building to ensure the date is correct :(
110
+ #
111
+ # This might also make your commit listings a little jumpy, if some
112
+ # commits change place every time a build finishes =\
113
+ commits.first_or_create({ :identifier => commit_identifier, :project_id => id }, :committed_at => Time.now)
114
+ end
115
+
116
+ def head_of_remote_repo
117
+ SCM.new(uri, branch).head
118
+ end
119
+
120
+ def create_commit_from(data)
121
+ commits.create(:identifier => data["id"],
122
+ :author => data["author"],
123
+ :message => data["message"],
124
+ :committed_at => data["timestamp"])
125
+ end
126
+
127
+ def set_permalink
128
+ self.permalink = (name || "").downcase.
129
+ gsub(/'s/, "s").
130
+ gsub(/&/, "and").
131
+ gsub(/[^a-z0-9]+/, "-").
132
+ gsub(/-*$/, "")
133
+ end
134
+
135
+ def delete_code
136
+ commits.all(:project_id => id).destroy!
137
+ ProjectBuilder.new(self).delete_code
138
+ rescue SCM::SCMUnknownError => error
139
+ Integrity.log "Problem while trying to deleting code: #{error}"
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,56 @@
1
+ module Integrity
2
+ class ProjectBuilder
3
+ def initialize(project)
4
+ @project = project
5
+ @uri = project.uri
6
+ @build_script = project.command
7
+ @branch = project.branch
8
+ @scm = SCM.new(@uri, @branch, export_directory)
9
+ end
10
+
11
+ def build(commit)
12
+ @commit = commit
13
+ @build = commit.build
14
+ @build.start!
15
+ Integrity.log "Building #{commit.identifier} (#{@branch}) of #{@project.name} in #{export_directory} using #{@scm.name}"
16
+ @scm.with_revision(commit.identifier) { run_build_script }
17
+ @build
18
+ ensure
19
+ @build.complete!
20
+ @commit.update_attributes(@scm.info(commit.identifier))
21
+ send_notifications
22
+ end
23
+
24
+ def delete_code
25
+ FileUtils.rm_r export_directory
26
+ rescue Errno::ENOENT
27
+ nil
28
+ end
29
+
30
+ private
31
+ def send_notifications
32
+ @project.notifiers.each do |notifier|
33
+ begin
34
+ Integrity.log "Notifying of build #{@commit.short_identifier} using the #{notifier.name} notifier"
35
+ notifier.notify_of_build @commit
36
+ rescue Timeout::Error
37
+ Integrity.log "#{notifier.name} notifier timed out"
38
+ next
39
+ end
40
+ end
41
+ end
42
+
43
+ def export_directory
44
+ Integrity.config[:export_directory] / "#{SCM.working_tree_path(@uri)}-#{@branch}"
45
+ end
46
+
47
+ def run_build_script
48
+ Integrity.log "Running `#{@build_script}` in #{@scm.working_directory}"
49
+
50
+ IO.popen "(cd #{@scm.working_directory} && #{@build_script}) 2>&1", "r" do |pipe|
51
+ @build.output = pipe.read
52
+ end
53
+ @build.successful = $?.success?
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,57 @@
1
+ module Integrity
2
+ module SCM
3
+ class Git
4
+ # From the git-pull man page:
5
+ #
6
+ # GIT URLS
7
+ # One of the following notations can be used to name the remote repository:
8
+ #
9
+ # rsync://host.xz/path/to/repo.git/
10
+ # http://host.xz/path/to/repo.git/
11
+ # git://host.xz/~user/path/to/repo.git/
12
+ # ssh://[user@]host.xz[:port]/path/to/repo.git/
13
+ # ssh://[user@]host.xz/path/to/repo.git/
14
+ # ssh://[user@]host.xz/~user/path/to/repo.git/
15
+ # ssh://[user@]host.xz/~/path/to/repo.git
16
+ #
17
+ # SSH is the default transport protocol over the network. You can optionally
18
+ # specify which user to log-in as, and an alternate, scp-like syntax is also
19
+ # supported
20
+ #
21
+ # Both syntaxes support username expansion, as does the native git protocol,
22
+ # but only the former supports port specification. The following three are
23
+ # identical to the last three above, respectively:
24
+ #
25
+ # [user@]host.xz:/path/to/repo.git/
26
+ # [user@]host.xz:~user/path/to/repo.git/
27
+ # [user@]host.xz:path/to/repo.git
28
+ #
29
+ class URI
30
+ def initialize(uri_string)
31
+ @uri = Addressable::URI.parse(uri_string)
32
+ end
33
+
34
+ def working_tree_path
35
+ strip_extension(path).gsub("/", "-")
36
+ end
37
+
38
+ private
39
+
40
+ def strip_extension(string)
41
+ uri = Pathname.new(string)
42
+ if uri.extname.any?
43
+ uri = Pathname.new(string)
44
+ string.gsub(Regexp.new("#{uri.extname}\/?"), "")
45
+ else
46
+ string
47
+ end
48
+ end
49
+
50
+ def path
51
+ path = @uri.path
52
+ path.gsub(/\~[a-zA-Z0-9]*\//, "").gsub(/^\//, "")
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,84 @@
1
+ module Integrity
2
+ module SCM
3
+ class Git
4
+ require File.dirname(__FILE__) / "git/uri"
5
+
6
+ attr_reader :uri, :branch, :working_directory
7
+
8
+ def self.working_tree_path(uri)
9
+ Git::URI.new(uri).working_tree_path
10
+ end
11
+
12
+ def initialize(uri, branch, working_directory=nil)
13
+ @uri = uri.to_s
14
+ @branch = branch.to_s
15
+ @working_directory = working_directory
16
+ end
17
+
18
+ def with_revision(revision)
19
+ fetch_code
20
+ checkout(revision)
21
+ yield
22
+ end
23
+
24
+ def name
25
+ self.class.name.split("::").last
26
+ end
27
+
28
+ def head
29
+ log "Getting the HEAD of '#{uri}' at '#{branch}'"
30
+ `git ls-remote --heads #{uri} #{branch} | awk '{print $1}'`.chomp
31
+ end
32
+
33
+ def info(revision)
34
+ format = %Q(---%n:author: %an <%ae>%n:message: >-%n %s%n:committed_at: %ci%n)
35
+ YAML.load(`cd #{working_directory} && git show -s --pretty=format:"#{format}" #{revision}`)
36
+ end
37
+
38
+ private
39
+
40
+ def fetch_code
41
+ clone unless cloned?
42
+ checkout unless on_branch?
43
+ pull
44
+ end
45
+
46
+ def clone
47
+ log "Cloning #{uri} to #{working_directory}"
48
+ `git clone #{uri} #{working_directory} &>/dev/null`
49
+ end
50
+
51
+ def checkout(treeish=nil)
52
+ strategy = case
53
+ when treeish then treeish
54
+ when local_branches.include?(branch) then branch
55
+ else "origin/#{branch}"
56
+ end
57
+
58
+ log "Checking-out #{strategy}"
59
+ `cd #{working_directory} && git reset --hard #{strategy} &>/dev/null`
60
+ end
61
+
62
+ def pull
63
+ log "Pull-ing in #{working_directory}"
64
+ `cd #{working_directory} && git pull &>/dev/null`
65
+ end
66
+
67
+ def local_branches
68
+ `cd #{working_directory} && git branch`.split("\n").map {|b| b.delete("*").strip }
69
+ end
70
+
71
+ def cloned?
72
+ File.directory?(working_directory / ".git")
73
+ end
74
+
75
+ def on_branch?
76
+ File.basename(`cd #{working_directory} && git symbolic-ref HEAD &>/dev/null`).chomp == branch
77
+ end
78
+
79
+ def log(message)
80
+ Integrity.log("Git") { message }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,19 @@
1
+ module Integrity
2
+ module SCM
3
+ class SCMUnknownError < StandardError; end
4
+
5
+ def self.new(uri, *args)
6
+ scm_class_for(uri).new(uri, *args)
7
+ end
8
+
9
+ def self.working_tree_path(uri)
10
+ scm_class_for(uri).working_tree_path(uri)
11
+ end
12
+
13
+ private
14
+ def self.scm_class_for(uri)
15
+ return Git if uri.scheme == "git" || uri.path =~ /\.git\/?/
16
+ raise SCMUnknownError, "could not find any SCM based on URI '#{uri.to_s}'"
17
+ end
18
+ end
19
+ end
data/lib/integrity.rb ADDED
@@ -0,0 +1,80 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require "json"
4
+ require "dm-core"
5
+ require "dm-validations"
6
+ require "dm-types"
7
+ require "dm-timestamps"
8
+ require "dm-aggregates"
9
+ require "sinatra/base"
10
+
11
+ require "yaml"
12
+ require "logger"
13
+ require "digest/sha1"
14
+ require "timeout"
15
+ require "ostruct"
16
+ require "pathname"
17
+
18
+ require "integrity/core_ext/object"
19
+
20
+ require "integrity/project"
21
+ require "integrity/author"
22
+ require "integrity/commit"
23
+ require "integrity/build"
24
+ require "integrity/project_builder"
25
+ require "integrity/scm"
26
+ require "integrity/scm/git"
27
+ require "integrity/notifier"
28
+ require "integrity/helpers"
29
+ require "integrity/app"
30
+
31
+ module Integrity
32
+ def self.new(config_file = nil)
33
+ self.config = YAML.load_file(config_file) unless config_file.nil?
34
+ DataMapper.setup(:default, config[:database_uri])
35
+ end
36
+
37
+ def self.root
38
+ Pathname.new(File.dirname(__FILE__)).join("..").expand_path
39
+ end
40
+
41
+ def self.default_configuration
42
+ @defaults ||= { :database_uri => "sqlite3::memory:",
43
+ :export_directory => root / "exports",
44
+ :log => STDOUT,
45
+ :base_uri => "http://localhost:8910",
46
+ :use_basic_auth => false,
47
+ :build_all_commits => true,
48
+ :log_debug_info => false }.dup
49
+ end
50
+
51
+ def self.config
52
+ @config ||= default_configuration
53
+ end
54
+
55
+ def self.config=(options)
56
+ @config = default_configuration.merge(options)
57
+ end
58
+
59
+ def self.log(message, &block)
60
+ logger.info(message, &block)
61
+ end
62
+
63
+ def self.logger
64
+ @logger ||= Logger.new(config[:log], "daily").tap do |logger|
65
+ logger.formatter = LogFormatter.new
66
+ end
67
+ end
68
+
69
+ def self.version
70
+ YAML.load_file(File.dirname(__FILE__) + "/../VERSION.yml").
71
+ values.join(".")
72
+ end
73
+
74
+ private
75
+ class LogFormatter < Logger::Formatter
76
+ def call(severity, time, progname, msg)
77
+ time.strftime("[%H:%M:%S] ") + msg2str(msg) + "\n"
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,82 @@
1
+ /* --------------------------------------------------------------
2
+
3
+ buttons.css
4
+ * Gives you some great CSS-only buttons.
5
+
6
+ Created by Kevin Hale [particletree.com]
7
+ * particletree.com/features/rediscovering-the-button-element
8
+
9
+ See Readme.txt in this folder for instructions.
10
+
11
+ -------------------------------------------------------------- */
12
+
13
+ button {
14
+ display:block;
15
+ float:left;
16
+ margin:0 0.583em 0.667em 0;
17
+ padding:5px 10px 5px 7px; /* Links */
18
+
19
+ border:1px solid #dedede;
20
+ border-top:1px solid #eee;
21
+ border-left:1px solid #eee;
22
+
23
+ background-color:#f5f5f5;
24
+ font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
25
+ font-size:100%;
26
+ line-height:130%;
27
+ text-decoration:none;
28
+ font-weight:bold;
29
+ color:#565656;
30
+ cursor:pointer;
31
+ }
32
+ button {
33
+ width:auto;
34
+ overflow:visible;
35
+ padding:4px 10px 3px 7px; /* IE6 */
36
+ }
37
+ button[type] {
38
+ padding:4px 10px 4px 7px; /* Firefox */
39
+ line-height:17px; /* Safari */
40
+ }
41
+ *:first-child+html button[type] {
42
+ padding:4px 10px 3px 7px; /* IE7 */
43
+ }
44
+ button img {
45
+ margin:0 3px -3px 0 !important;
46
+ padding:0;
47
+ border:none;
48
+ width:16px;
49
+ height:16px;
50
+ float:none;
51
+ }
52
+
53
+
54
+ /* Button colors
55
+ -------------------------------------------------------------- */
56
+
57
+ /* Standard */
58
+ button:hover {
59
+ background-color:#dff4ff;
60
+ border:1px solid #c2e1ef;
61
+ color:#336699;
62
+ }
63
+
64
+ /* Positive */
65
+ body .positive {
66
+ color:#529214;
67
+ }
68
+ button.positive:hover {
69
+ background-color:#E6EFC2;
70
+ border:1px solid #C6D880;
71
+ color:#529214;
72
+ }
73
+
74
+ /* Negative */
75
+ body .negative {
76
+ color:#d12f19;
77
+ }
78
+ button.negative:hover {
79
+ background:#fbe3e4;
80
+ border:1px solid #fbc2c4;
81
+ color:#d12f19;
82
+ }
data/public/reset.css ADDED
@@ -0,0 +1,7 @@
1
+ /*
2
+ Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3
+ Code licensed under the BSD License:
4
+ http://developer.yahoo.net/yui/license.txt
5
+ version: 2.5.2
6
+ */
7
+ html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;font-variant:normal;}sup {vertical-align:text-top;}sub {vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
Binary file
@@ -0,0 +1,99 @@
1
+ module GitHelper
2
+ @@_git_repositories = Hash.new {|h,k| h[k] = Repo.new(k) }
3
+
4
+ def git_repo(name)
5
+ @@_git_repositories[name]
6
+ end
7
+
8
+ def destroy_all_git_repos
9
+ @@_git_repositories.each {|n,r| r.destroy }
10
+ @@_git_repositories.clear
11
+ end
12
+
13
+ class Repo
14
+ attr_reader :path
15
+
16
+ def initialize(name)
17
+ @name = name
18
+ @path = "/tmp" / @name.to_s
19
+ create
20
+ end
21
+
22
+ def path
23
+ @path / ".git"
24
+ end
25
+
26
+ def create
27
+ destroy
28
+ FileUtils.mkdir_p @path
29
+
30
+ Dir.chdir(@path) do
31
+ system 'git init &>/dev/null'
32
+ system 'git config user.name "John Doe"'
33
+ system 'git config user.email "johndoe@example.org"'
34
+ system 'echo "just a test repo" >> README'
35
+ system 'git add README &>/dev/null'
36
+ system 'git commit -m "First commit" &>/dev/null'
37
+ end
38
+
39
+ add_successful_commit
40
+ end
41
+
42
+ def commits
43
+ Dir.chdir(@path) do
44
+ commits = `git log --pretty=oneline`.collect { |line| line.split(" ").first }
45
+ commits.inject([]) do |commits, sha1|
46
+ format = %Q(---%n:message: >-%n %s%n:timestamp: %ci%n:id: %H%n:author: %an <%ae>)
47
+ commits << YAML.load(`git show -s --pretty=format:"#{format}" #{sha1}`)
48
+ end
49
+ end
50
+ end
51
+
52
+ def add_commit(message, &action)
53
+ Dir.chdir(@path) do
54
+ yield action
55
+ system %Q(git commit -m "#{message}" &>/dev/null)
56
+ end
57
+ end
58
+
59
+ def add_failing_commit
60
+ add_commit "This commit will fail" do
61
+ system %Q(echo '#{build_script(false)}' > test)
62
+ system %Q(chmod +x test)
63
+ system %Q(git add test &>/dev/null)
64
+ end
65
+ end
66
+
67
+ def add_successful_commit
68
+ add_commit "This commit will work" do
69
+ system %Q(echo '#{build_script(true)}' > test)
70
+ system %Q(chmod +x test)
71
+ system %Q(git add test &>/dev/null)
72
+ end
73
+ end
74
+
75
+ def head
76
+ Dir.chdir(@path) do
77
+ `git log --pretty=format:%H | head -1`.chomp
78
+ end
79
+ end
80
+
81
+ def short_head
82
+ head[0..6]
83
+ end
84
+
85
+ def destroy
86
+ FileUtils.rm_rf @path if File.directory?(@path)
87
+ end
88
+
89
+ protected
90
+
91
+ def build_script(successful=true)
92
+ <<-script
93
+ #!/bin/sh
94
+ echo "Running tests..."
95
+ exit #{successful ? 0 : 1}
96
+ script
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,26 @@
1
+ module Integrity
2
+ class Notifier
3
+ class Textfile < Notifier::Base
4
+ def self.to_haml
5
+ <<-haml
6
+ %p.normal
7
+ %label{ :for => "textfile_notifier_file" } File
8
+ %input.text#textfile_notifier_file{ :name => "notifiers[Textfile][file]", :type => "text", :value => config["file"] }
9
+ haml
10
+ end
11
+
12
+ def initialize(build, config={})
13
+ super
14
+ @file = @config["file"]
15
+ end
16
+
17
+ def deliver!
18
+ File.open(@file, "a") do |f|
19
+ f.puts "=== #{short_message} ==="
20
+ f.puts
21
+ f.puts full_message
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end