git-restart 0.0.1.pre.dev
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.
- checksums.yaml +7 -0
- data/bin/git-restart +14 -0
- data/lib/git-restart/runner.rb +165 -0
- data/lib/git-restart/task.rb +165 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d328a8601625a761e52bce62d042e047403c2eef
|
4
|
+
data.tar.gz: e5fe83b3019317b259f91ddb31e6841b6b30e050
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 229b49c059c88712e46f35f79466a3442992045b334a1f6fe5f03b620bee9cba408436548170982947e39484e27e3f5e757c1fa1082692231dfd4f586403f054
|
7
|
+
data.tar.gz: b4938804dc08b11107088aba4b7ab57f5e429431c580c9368bdef86918e024ed44fb0bae264aad225ab56bca666bc9ec94346cbddba07221bd7506c7e8db151b
|
data/bin/git-restart
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'mqtt/sub_handler.rb'
|
4
|
+
require 'git-restart/runner.rb'
|
5
|
+
|
6
|
+
puts "Starting runner ..."
|
7
|
+
|
8
|
+
target = ARGV[0] | ".gitrun"
|
9
|
+
|
10
|
+
raise ArgumentError, "No valid task file specified!" unless File.exist? target
|
11
|
+
|
12
|
+
load target
|
13
|
+
|
14
|
+
GitRestart::Task.runner.mqtt.lock_and_listen
|
@@ -0,0 +1,165 @@
|
|
1
|
+
|
2
|
+
require 'mqtt/sub_handler'
|
3
|
+
require 'git'
|
4
|
+
|
5
|
+
require_relative "task.rb"
|
6
|
+
|
7
|
+
module GitRestart
|
8
|
+
class Runner
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
attr_accessor :repo, :branches, :exclude_branches, :start_on
|
12
|
+
|
13
|
+
attr_reader :next_tasks
|
14
|
+
attr_reader :current_task_file
|
15
|
+
|
16
|
+
def current_commit()
|
17
|
+
@git.object("HEAD^").sha;
|
18
|
+
end
|
19
|
+
def current_branch()
|
20
|
+
@git.current_branch();
|
21
|
+
end
|
22
|
+
def current_modified()
|
23
|
+
@current_modified;
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize()
|
27
|
+
GitRestart::Task.runner = self;
|
28
|
+
|
29
|
+
@current_tasks = Hash.new();
|
30
|
+
@next_tasks = Hash.new();
|
31
|
+
|
32
|
+
@branches = Array.new();
|
33
|
+
@exclude_branches = Array.new();
|
34
|
+
|
35
|
+
@branchQueue = Queue.new();
|
36
|
+
|
37
|
+
@git = Git.open(".");
|
38
|
+
|
39
|
+
yield(self);
|
40
|
+
|
41
|
+
@listenedSub = @mqtt.subscribe_to "GitHub/#{@repo}" do |data|
|
42
|
+
puts "Received data: #{data}"
|
43
|
+
begin
|
44
|
+
data = JSON.parse(data, symbolize_names: true);
|
45
|
+
rescue
|
46
|
+
next;
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "Processing data #{data}"
|
50
|
+
|
51
|
+
next unless data[:branch];
|
52
|
+
if(not @branches.empty?)
|
53
|
+
next unless @branches.include? data[:branch];
|
54
|
+
elsif(not @exclude_branches.empty?)
|
55
|
+
next if @exclude_branches.include? data[:branch];
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "Queueing data!"
|
59
|
+
|
60
|
+
@branchQueue << data;
|
61
|
+
end
|
62
|
+
|
63
|
+
autostart();
|
64
|
+
_start_task_thread();
|
65
|
+
end
|
66
|
+
|
67
|
+
def update_status(name, newStatus, message = nil)
|
68
|
+
end
|
69
|
+
|
70
|
+
def _start_task_thread()
|
71
|
+
@taskThread = Thread.new do
|
72
|
+
loop do
|
73
|
+
newData = @branchQueue.pop;
|
74
|
+
|
75
|
+
puts "Popped data: #{newData}"
|
76
|
+
|
77
|
+
@current_modified = newData[:touched];
|
78
|
+
_switch_to(newData[:branch], newData[:head_commit]);
|
79
|
+
end
|
80
|
+
end.abort_on_exception = true;
|
81
|
+
end
|
82
|
+
|
83
|
+
def _stop_tasks(taskList)
|
84
|
+
taskList.each do |name, t|
|
85
|
+
t.stop();
|
86
|
+
end
|
87
|
+
taskList.each do |name, t|
|
88
|
+
t.join();
|
89
|
+
@current_tasks.delete(name);
|
90
|
+
end
|
91
|
+
end
|
92
|
+
def _stop_all_tasks()
|
93
|
+
_stop_tasks(@current_tasks);
|
94
|
+
end
|
95
|
+
def _stop_triggered_tasks()
|
96
|
+
_stop_tasks(@current_tasks.select {|k,v| v.triggered?});
|
97
|
+
end
|
98
|
+
|
99
|
+
def _generate_next_tasks()
|
100
|
+
puts "Generating new tasks..."
|
101
|
+
@next_tasks = Hash.new();
|
102
|
+
|
103
|
+
taskFiles = `find ./ -nowarn -iname "*.gittask"`
|
104
|
+
taskFiles.split("\n").each do |t|
|
105
|
+
puts "Looking at: #{t}"
|
106
|
+
t.gsub!(/^\.\//,"");
|
107
|
+
@current_task_file = t;
|
108
|
+
|
109
|
+
# TODO Add proper error reporting
|
110
|
+
begin
|
111
|
+
load(t);
|
112
|
+
rescue ScriptError, StandardError
|
113
|
+
puts("File #{t} could not be loaded!");
|
114
|
+
rescue GitRestart::TaskValidityError
|
115
|
+
puts("Task-File #{t} is not configured properly!");
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
puts "Finished loading! Next tasks are: #{@next_tasks}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def _start_next_tasks()
|
123
|
+
_generate_next_tasks();
|
124
|
+
|
125
|
+
puts "Starting next tasks!"
|
126
|
+
@next_tasks.each do |name, t|
|
127
|
+
next unless t.active;
|
128
|
+
next unless t.triggered?
|
129
|
+
|
130
|
+
t.start();
|
131
|
+
@current_tasks[name] = t;
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def _switch_to(branch, commit = nil)
|
136
|
+
puts "Switching to branch: #{branch}, commit: #{commit}"
|
137
|
+
@git.fetch();
|
138
|
+
|
139
|
+
if(branch != current_branch())
|
140
|
+
_stop_all_tasks();
|
141
|
+
else
|
142
|
+
_stop_triggered_tasks();
|
143
|
+
end
|
144
|
+
@git.checkout(branch);
|
145
|
+
@git.reset_hard(commit);
|
146
|
+
|
147
|
+
@git.merge("origin/#{branch}");
|
148
|
+
|
149
|
+
_start_next_tasks();
|
150
|
+
end
|
151
|
+
|
152
|
+
def autostart()
|
153
|
+
return unless @start_on;
|
154
|
+
@branchQueue << {branch: @start_on};
|
155
|
+
end
|
156
|
+
|
157
|
+
def mqtt=(mqtt)
|
158
|
+
if(mqtt.is_a? String)
|
159
|
+
@mqtt = MQTT::SubHandler.new(mqtt);
|
160
|
+
else
|
161
|
+
@mqtt = mqtt;
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
|
2
|
+
module GitRestart
|
3
|
+
class TaskValidityError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class Task
|
7
|
+
attr_reader :targets
|
8
|
+
|
9
|
+
attr_accessor :signal
|
10
|
+
attr_accessor :expect_clean_exit
|
11
|
+
attr_accessor :report_status
|
12
|
+
attr_accessor :ci_task
|
13
|
+
attr_accessor :name, :status_file
|
14
|
+
|
15
|
+
attr_accessor :active
|
16
|
+
|
17
|
+
attr_reader :lastStatus
|
18
|
+
attr_reader :status_message
|
19
|
+
|
20
|
+
attr_reader :mqtt
|
21
|
+
|
22
|
+
def self.runner=(runner)
|
23
|
+
@runner = runner;
|
24
|
+
end
|
25
|
+
def self.runner()
|
26
|
+
return @runner;
|
27
|
+
end
|
28
|
+
def runner()
|
29
|
+
return self.class.runner();
|
30
|
+
end
|
31
|
+
|
32
|
+
def branch()
|
33
|
+
runner().current_branch();
|
34
|
+
end
|
35
|
+
def modified()
|
36
|
+
runner().current_modified();
|
37
|
+
end
|
38
|
+
|
39
|
+
def watch(regEx)
|
40
|
+
if(regEx.is_a? String)
|
41
|
+
regEx = Regexp.quote(regEx);
|
42
|
+
end
|
43
|
+
|
44
|
+
@watched << Regexp.new(regEx);
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_branches(branches)
|
48
|
+
[branches].flatten.each do |b|
|
49
|
+
@active |= (b == branch());
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize()
|
54
|
+
@statuschange_mutex = Mutex.new();
|
55
|
+
|
56
|
+
@targets = Array.new();
|
57
|
+
@watched = Array.new();
|
58
|
+
|
59
|
+
@signal = "INT"
|
60
|
+
@expect_clean_exit = true;
|
61
|
+
@exiting = false;
|
62
|
+
|
63
|
+
@lastStatus = 0;
|
64
|
+
@chdir = File.dirname(runner().current_task_file);
|
65
|
+
|
66
|
+
watch(File.basename(runner().current_task_file));
|
67
|
+
|
68
|
+
yield(self);
|
69
|
+
|
70
|
+
valid?
|
71
|
+
|
72
|
+
if(runner().next_tasks[@name])
|
73
|
+
raise TaskValidityError, "A task of name #{@name} already exists!"
|
74
|
+
else
|
75
|
+
runner().next_tasks[@name] = self;
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def triggered?
|
80
|
+
return true if modified().nil?
|
81
|
+
return true if @ci_task
|
82
|
+
|
83
|
+
@watched.each do |regEx|
|
84
|
+
modified().each do |f|
|
85
|
+
next unless f =~ /#{Regexp.quote(@chdir)}(.*)/
|
86
|
+
return true if $1 =~ regEx;
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
return false;
|
91
|
+
end
|
92
|
+
|
93
|
+
def valid?()
|
94
|
+
unless Signal.list[@signal] or @signal.nil?
|
95
|
+
raise TaskValidityError, "The specified kill-signal is not valid!"
|
96
|
+
end
|
97
|
+
|
98
|
+
unless @name
|
99
|
+
raise TaskValidityError, "A name needs to be set for identification!"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def _report_status(status, message = nil)
|
104
|
+
@status_message = ""
|
105
|
+
|
106
|
+
return unless @report_status
|
107
|
+
|
108
|
+
runner().update_status(@name, status, message);
|
109
|
+
puts "Task #{@name} assumed a new status: #{status}#{message ? " MSG:#{message}" : ""}"
|
110
|
+
end
|
111
|
+
private :_report_status
|
112
|
+
|
113
|
+
def start()
|
114
|
+
puts "Starting Task: #{@name}"
|
115
|
+
|
116
|
+
return if @targets.empty?
|
117
|
+
sleep 0.01
|
118
|
+
|
119
|
+
@executionThread = Thread.new do
|
120
|
+
_report_status(:pending);
|
121
|
+
|
122
|
+
@targets.each do |target|
|
123
|
+
@statuschange_mutex.synchronize {
|
124
|
+
break if @exiting
|
125
|
+
options = {
|
126
|
+
[:in, :out, :err] => "/dev/null"
|
127
|
+
}
|
128
|
+
options[:chdir] = @chdir if @chdir
|
129
|
+
|
130
|
+
@currentPID = Process.spawn(target, options);
|
131
|
+
}
|
132
|
+
|
133
|
+
status = Process.wait2(@currentPID)[1];
|
134
|
+
@currentPID = nil;
|
135
|
+
@lastStatus = status.exitstatus();
|
136
|
+
|
137
|
+
break unless @lastStatus == 0;
|
138
|
+
end
|
139
|
+
|
140
|
+
if(@lastStatus == 0)
|
141
|
+
_report_status(:success);
|
142
|
+
elsif(!@exiting || @expect_clean_exit)
|
143
|
+
_report_status(:failure);
|
144
|
+
end
|
145
|
+
end
|
146
|
+
@executionThread.abort_on_exception = true;
|
147
|
+
end
|
148
|
+
|
149
|
+
def stop()
|
150
|
+
puts "Stopping Task: #{@name}"
|
151
|
+
return if @signal.nil?
|
152
|
+
|
153
|
+
@statuschange_mutex.synchronize {
|
154
|
+
@exiting = true;
|
155
|
+
if(p = @currentPID)
|
156
|
+
Process.kill(@signal, p);
|
157
|
+
end
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
def join()
|
162
|
+
@executionThread.join();
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: git-restart
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre.dev
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Xasin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-08-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mqtt-sub_handler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: git
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: |-
|
84
|
+
This gem can be used to (re)start scripts whenever a GitHub push event is recorded.
|
85
|
+
The exit status of scripts can be monitored, and a failure can be sent back, making this capable of running simple tests too!
|
86
|
+
email:
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- bin/git-restart
|
92
|
+
- lib/git-restart/runner.rb
|
93
|
+
- lib/git-restart/task.rb
|
94
|
+
homepage: https://github.com/XasWorks/XasCode/tree/master/Ruby/GitRestart
|
95
|
+
licenses:
|
96
|
+
- GPL-3.0
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 1.3.1
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.6.14.1
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: "(Re)start scripts and monitor them on a GitHub push"
|
118
|
+
test_files: []
|