tinyci 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.tinyci.yml +8 -0
- data/CHANGELOG.md +3 -0
- data/Dockerfile +38 -0
- data/Gemfile.lock +1 -1
- data/README.md +15 -80
- data/lib/tinyci/cli.rb +15 -0
- data/lib/tinyci/compactor.rb +99 -0
- data/lib/tinyci/runner.rb +5 -3
- data/lib/tinyci/scheduler.rb +18 -10
- data/lib/tinyci/subprocesses.rb +2 -2
- data/lib/tinyci/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39e61578a53b2ca58444388af0f0b1b3653c2896
|
4
|
+
data.tar.gz: 86be8e7dbf6d742a8814323ccf75d5c1ce645c22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 864c7040005a7a6f778bb5392c580847e35024e160150063148d52a0ddcf059b5ab0f5f56f756efc8907eb16a74c0aea071585b1a399413dde3611a83dca92e0
|
7
|
+
data.tar.gz: b84f48399d21f174c485c2b3626fb35ca3b05d89f0b5547e06388848caaa5b2672e1c55ac8bdd4adf73ea291e710af44f3bfb03894289a651b002e34839a956d
|
data/.tinyci.yml
ADDED
data/CHANGELOG.md
CHANGED
data/Dockerfile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
FROM alpine:3.8
|
2
|
+
|
3
|
+
RUN mkdir -p /etc \
|
4
|
+
&& { \
|
5
|
+
echo 'install: --no-document'; \
|
6
|
+
echo 'update: --no-document'; \
|
7
|
+
} >> /etc/gemrc
|
8
|
+
|
9
|
+
RUN apk add --no-cache -u \
|
10
|
+
ruby \
|
11
|
+
ruby-dev \
|
12
|
+
ruby-irb \
|
13
|
+
ruby-etc \
|
14
|
+
libffi-dev \
|
15
|
+
build-base \
|
16
|
+
git
|
17
|
+
|
18
|
+
RUN git config --global user.email "you@example.com"
|
19
|
+
RUN git config --global user.name "Your Name"
|
20
|
+
|
21
|
+
# RUN ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime
|
22
|
+
|
23
|
+
RUN gem install bundler
|
24
|
+
RUN bundle config --global silence_root_warning 1
|
25
|
+
|
26
|
+
WORKDIR /tmp
|
27
|
+
ADD Gemfile Gemfile
|
28
|
+
ADD Gemfile.lock Gemfile.lock
|
29
|
+
ADD tinyci.gemspec tinyci.gemspec
|
30
|
+
ADD lib/tinyci/version.rb lib/tinyci/version.rb
|
31
|
+
ADD lib/tinyci/logo.txt lib/tinyci/logo.txt
|
32
|
+
RUN bundle install
|
33
|
+
|
34
|
+
ADD . /tinyci
|
35
|
+
|
36
|
+
WORKDIR /tinyci
|
37
|
+
|
38
|
+
CMD bin/tinyci
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -107,6 +107,19 @@ hooker:
|
|
107
107
|
after_build: ./after_build.sh
|
108
108
|
```
|
109
109
|
|
110
|
+
#### Compactor
|
111
|
+
|
112
|
+
With continued use, the `builds` directory will grow ever larger. TinyCI provides the `compact` command to deal with this. It compresses old builds into `.tar.gz` files.
|
113
|
+
|
114
|
+
"Old" in this context is defined using two options to the `tinyci compact` command:
|
115
|
+
|
116
|
+
* `--num-builds-to-leave` - How many build directories to leave in place, starting from the newest. Defaults to `1`.
|
117
|
+
* `--builds-to-leave` - A comma-separated list of specific build directories to leave in place.
|
118
|
+
|
119
|
+
The latter option is intended for use in an automated deployment system, to allow the script to run without removing builds that are being used somewhere else in the system. For a demonstration of this, see the [example project](#Example_Project).
|
120
|
+
|
121
|
+
To use it, simply run `tinyci compact` from the root of the repo you wish to compact.
|
122
|
+
|
110
123
|
#### Logging/Output
|
111
124
|
|
112
125
|
TinyCI is executed in a `post-update` git hook. As such, the output is shown to the user as part of the local call to `git push`. Once the TinyCI hook is running, the local git process can be freely killed if the user does not wish to watch the output - this will not affect the remote execution of TinyCI.
|
@@ -123,8 +136,6 @@ Instead, this second TinyCI execution should begin tailing the output of the fir
|
|
123
136
|
|
124
137
|
* It would be desirable to add a command to the TinyCI binary to SSH into the remote server and tail the the log output of a currently running TinyCI process, enabling functionality similar to that described in above, but without making a new commit.
|
125
138
|
|
126
|
-
* The exported copies of each build are left on the disk forever. This will obviously eat disk space. One could handle deleting/compressing them with a cronjob, however it would be desirable to integrate this functionality into TinyCI and to automate it.
|
127
|
-
|
128
139
|
* More configuration options should be added, eg. specification of the export path.
|
129
140
|
|
130
141
|
* In general, more local functionality for the TinyCI binary would be desirable, some way to retrieve the results of tests and query them from the local clone for example, or some way to show them in `git log` output.
|
@@ -160,85 +171,9 @@ Now, commit the configuration file and push it to your remote repository. As dis
|
|
160
171
|
|
161
172
|
#### Example Project
|
162
173
|
|
163
|
-
|
164
|
-
|
165
|
-
The example project has very simple build and test scripts written in bash.
|
166
|
-
|
167
|
-
First, create a directory to store both clones:
|
168
|
-
|
169
|
-
$ mkdir tinyci-test
|
170
|
-
$ cd tinyci-test
|
171
|
-
|
172
|
-
Clone the example project from github:
|
173
|
-
|
174
|
-
$ git clone https://github.com/JonnieCache/tinyci-example.git
|
175
|
-
|
176
|
-
Cloning into 'tinyci-example'...
|
177
|
-
remote: Counting objects: 8, done.
|
178
|
-
remote: Compressing objects: 100% (6/6), done.
|
179
|
-
remote: Total 8 (delta 0), reused 8 (delta 0), pack-reused 0
|
180
|
-
Unpacking objects: 100% (8/8), done.
|
181
|
-
|
182
|
-
Clone it again into a bare repository:
|
183
|
-
|
184
|
-
$ git clone --bare tinyci-example tinyci-example-bare
|
185
|
-
|
186
|
-
Cloning into bare repository 'tinyci-example-bare'...
|
187
|
-
done.
|
188
|
-
|
189
|
-
Install tinyci:
|
190
|
-
|
191
|
-
$ gem install tinyci
|
192
|
-
|
193
|
-
Install the tinyci hook into our bare clone:
|
194
|
-
|
195
|
-
$ cd tinyci-example-bare
|
196
|
-
$ tinyci install
|
197
|
-
|
198
|
-
[09:48:42] tinyci post-update hook installed sucessfully
|
199
|
-
|
200
|
-
Go into our initial clone and make a change:
|
201
|
-
|
202
|
-
$ cd ../tinyci-example
|
203
|
-
$ echo "foo" > bar
|
204
|
-
$ git add bar
|
205
|
-
$ git commit -m 'foobar'
|
206
|
-
|
207
|
-
[master cebb4ec] foobar
|
208
|
-
1 file changed, 1 insertion(+)
|
209
|
-
create mode 100644 bar
|
210
|
-
|
211
|
-
Add our bare repo as a remote:
|
212
|
-
|
213
|
-
$ git remote add test ../tinyci-example-bare
|
214
|
-
|
215
|
-
Push the commit and watch tinyci in action:
|
216
|
-
|
217
|
-
$ git push test master
|
218
|
-
|
219
|
-
Counting objects: 3, done.
|
220
|
-
Delta compression using up to 4 threads.
|
221
|
-
Compressing objects: 100% (2/2), done.
|
222
|
-
Writing objects: 100% (3/3), 268 bytes | 0 bytes/s, done.
|
223
|
-
Total 3 (delta 1), reused 0 (delta 0)
|
224
|
-
remote: [09:49:53] Commit: 22c36d0c1213361d06b385d2b55648985347e1f4
|
225
|
-
remote: [09:49:53] Cleaning...
|
226
|
-
remote: [09:49:53] Exporting...
|
227
|
-
remote: [09:49:53] Building...
|
228
|
-
remote: [09:49:53] Testing...
|
229
|
-
remote: [09:49:53] foo bar
|
230
|
-
remote: [09:49:53] Finished 22c36d0c1213361d06b385d2b55648985347e1f4
|
231
|
-
remote: [09:49:53] Commit: cebb4ec18b89f093c7cdeb909c2c340708052575
|
232
|
-
remote: [09:49:53] Cleaning...
|
233
|
-
remote: [09:49:53] Exporting...
|
234
|
-
remote: [09:49:53] Building...
|
235
|
-
remote: [09:49:53] Testing...
|
236
|
-
remote: [09:49:53] foo bar
|
237
|
-
remote: [09:49:53] Finished cebb4ec18b89f093c7cdeb909c2c340708052575
|
238
|
-
To ../tinyci-example-bare
|
239
|
-
22c36d0..cebb4ec master -> master
|
174
|
+
There is an example project available at https://github.com/JonnieCache/tinyci-example.
|
240
175
|
|
241
|
-
|
176
|
+
See that repo for a walkthrough demonstrating various TinyCI features.
|
242
177
|
|
243
178
|
### Contributing
|
244
179
|
|
data/lib/tinyci/cli.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'tinyci/version'
|
2
2
|
require 'tinyci/scheduler'
|
3
3
|
require 'tinyci/installer'
|
4
|
+
require 'tinyci/compactor'
|
4
5
|
require 'tinyci/git_utils'
|
5
6
|
require 'optparse'
|
7
|
+
require 'pidfile'
|
6
8
|
|
7
9
|
module TinyCI
|
8
10
|
# Defines the CLI interface. Uses OptionParser.
|
@@ -29,6 +31,12 @@ module TinyCI
|
|
29
31
|
'install' => OptionParser.new do |o|
|
30
32
|
o.banner = "Usage: install [options]"
|
31
33
|
o.on("-q", "--[no-]quiet", "quietly run") {|v| opts[:quiet] = v}
|
34
|
+
end,
|
35
|
+
'compact' => OptionParser.new do |o|
|
36
|
+
o.banner = "Usage: compact [options]"
|
37
|
+
o.on("-n", "--num-builds-to-leave <NUM>", "number of builds to leave in place, starting from the most recent") {|n| opts[:num_builds_to_leave] = n}
|
38
|
+
o.on("-b", "--builds-to-leave <BUILDS>", "specific build directories to leave in place, comma-separated") {|b| opts[:builds_to_leave] = b.split(',')}
|
39
|
+
o.on("-q", "--[no-]quiet", "quietly run") {|v| opts[:quiet] = v}
|
32
40
|
end
|
33
41
|
}
|
34
42
|
|
@@ -40,6 +48,7 @@ Global options:
|
|
40
48
|
Available commands:
|
41
49
|
run build and test the repo
|
42
50
|
install install the git hook into the current repository
|
51
|
+
compact compress old build artifacts
|
43
52
|
version print the TinyCI version number
|
44
53
|
TXT
|
45
54
|
if argv[0] == '--help'
|
@@ -95,6 +104,12 @@ TXT
|
|
95
104
|
TinyCI::Installer.new(logger: logger, working_dir: opts[:dir]).write!
|
96
105
|
end
|
97
106
|
|
107
|
+
def self.do_compact(opts)
|
108
|
+
logger = MultiLogger.new(quiet: opts[:quiet])
|
109
|
+
|
110
|
+
TinyCI::Compactor.new(logger: logger, working_dir: opts[:dir], num_builds_to_leave: opts[:num_builds_to_leave], builds_to_leave: opts[:builds_to_leave]).compact!
|
111
|
+
end
|
112
|
+
|
98
113
|
def do_version(opts)
|
99
114
|
puts TinyCI::VERSION
|
100
115
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tinyci/git_utils'
|
3
|
+
require 'zlib'
|
4
|
+
require 'rubygems/package'
|
5
|
+
require 'find'
|
6
|
+
|
7
|
+
module TinyCI
|
8
|
+
|
9
|
+
# Tool for compressing old builds into .tar.gz files
|
10
|
+
class Compactor
|
11
|
+
include GitUtils
|
12
|
+
include Subprocesses
|
13
|
+
|
14
|
+
BLOCKSIZE_TO_READ = 1024 * 1000
|
15
|
+
|
16
|
+
# Constructor
|
17
|
+
#
|
18
|
+
# @param [String] working_dir The directory from which to run.
|
19
|
+
# @param [Integer] num_builds_to_leave How many builds not to compact, starting from the newest
|
20
|
+
# @param [String] builds_to_leave Comma-separated list of build directory names not to compact
|
21
|
+
# @param [Logger] logger Logger object
|
22
|
+
def initialize(working_dir: nil, num_builds_to_leave: nil, builds_to_leave: nil, logger: nil)
|
23
|
+
@logger = logger
|
24
|
+
@working_dir = working_dir || repo_root
|
25
|
+
@num_builds_to_leave = (num_builds_to_leave || 1).to_i
|
26
|
+
@builds_to_leave = builds_to_leave || []
|
27
|
+
end
|
28
|
+
|
29
|
+
# Compress and delete the build directories
|
30
|
+
def compact!
|
31
|
+
unless inside_repository?
|
32
|
+
log_error "not currently in a git repository"
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
directories_to_compact.each do |dir|
|
37
|
+
compress_directory dir
|
38
|
+
FileUtils.rm_rf builds_dir(dir)
|
39
|
+
|
40
|
+
log_info "Compacted #{archive_path(dir)}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Build the list of directories to compact according to the options
|
47
|
+
def directories_to_compact
|
48
|
+
builds = Dir.entries builds_dir
|
49
|
+
builds.select! {|e| File.directory? builds_dir(e) }
|
50
|
+
builds.reject! {|e| %w{. ..}.include? e }
|
51
|
+
builds.sort!
|
52
|
+
|
53
|
+
builds = builds[0..-(@num_builds_to_leave+1)]
|
54
|
+
builds.reject! {|e| @builds_to_leave.include?(e) || @builds_to_leave.include?(builds_dir(e, 'export'))}
|
55
|
+
|
56
|
+
builds
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get the location of the builds directory
|
60
|
+
def builds_dir(*path_segments)
|
61
|
+
File.join @working_dir, 'builds/', *path_segments
|
62
|
+
end
|
63
|
+
|
64
|
+
# Build the path for a compressed archive
|
65
|
+
def archive_path(dir)
|
66
|
+
File.join(builds_dir, dir+".tar.gz")
|
67
|
+
end
|
68
|
+
|
69
|
+
# Create a .tar.gz file from a directory
|
70
|
+
# Done in pure ruby to ensure portability
|
71
|
+
def compress_directory(dir)
|
72
|
+
File.open archive_path(dir), 'wb' do |oarchive_path|
|
73
|
+
Zlib::GzipWriter.wrap oarchive_path do |gz|
|
74
|
+
Gem::Package::TarWriter.new gz do |tar|
|
75
|
+
Find.find "#{builds_dir}/"+dir do |f|
|
76
|
+
relative_path = f.sub "#{builds_dir}/", ""
|
77
|
+
mode = File.stat(f).mode
|
78
|
+
size = File.stat(f).size
|
79
|
+
|
80
|
+
if File.directory? f
|
81
|
+
tar.mkdir relative_path, mode
|
82
|
+
else
|
83
|
+
tar.add_file_simple relative_path, mode, size do |tio|
|
84
|
+
File.open f, 'rb' do |rio|
|
85
|
+
while buffer = rio.read(BLOCKSIZE_TO_READ)
|
86
|
+
tio.write buffer
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/tinyci/runner.rb
CHANGED
@@ -83,7 +83,7 @@ module TinyCI
|
|
83
83
|
raise e if ENV['TINYCI_ENV'] == 'test'
|
84
84
|
|
85
85
|
log_error e
|
86
|
-
|
86
|
+
log_debug e.backtrace
|
87
87
|
|
88
88
|
return false
|
89
89
|
ensure
|
@@ -101,7 +101,7 @@ module TinyCI
|
|
101
101
|
raise e if ENV['TINYCI_ENV'] == 'test'
|
102
102
|
|
103
103
|
log_error e
|
104
|
-
|
104
|
+
log_debug e.backtrace
|
105
105
|
|
106
106
|
return false
|
107
107
|
ensure
|
@@ -116,7 +116,7 @@ module TinyCI
|
|
116
116
|
raise e if ENV['TINYCI_ENV'] == 'test'
|
117
117
|
|
118
118
|
log_error e
|
119
|
-
|
119
|
+
log_debug e.backtrace
|
120
120
|
return false
|
121
121
|
end
|
122
122
|
|
@@ -182,10 +182,12 @@ module TinyCI
|
|
182
182
|
Time.at execute(git_cmd('show', '-s', '--format=%ct', @commit)).to_i
|
183
183
|
end
|
184
184
|
|
185
|
+
# Ensure a path exists
|
185
186
|
def ensure_path(path)
|
186
187
|
execute 'mkdir', '-p', path
|
187
188
|
end
|
188
189
|
|
190
|
+
# Delete the export path
|
189
191
|
def clean
|
190
192
|
FileUtils.rm_rf export_path
|
191
193
|
end
|
data/lib/tinyci/scheduler.rb
CHANGED
@@ -43,7 +43,7 @@ module TinyCI
|
|
43
43
|
pid = PidFile.new(pidfile: 'tinyci.pid', piddir: @working_dir)
|
44
44
|
|
45
45
|
result = if @commit
|
46
|
-
run_commit @commit
|
46
|
+
run_commit get_commit @commit
|
47
47
|
else
|
48
48
|
run_all_commits
|
49
49
|
end
|
@@ -60,16 +60,15 @@ module TinyCI
|
|
60
60
|
# @return [Array<String>] the sha1 hashes in reverse order of creation time
|
61
61
|
def get_commits
|
62
62
|
log = execute(git_cmd('log', '--notes=tinyci*', '--format=%H %ct %N§§§', '--reverse'))
|
63
|
-
|
64
63
|
lines = log.split("§§§")
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
64
|
+
|
65
|
+
lines.map {|l| format_commit_data(l)}.select {|c| c[:result].nil?}
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_commit(sha)
|
69
|
+
data = execute(git_cmd('show', '--quiet', '--notes=tinyci*', "--format=%H %ct", sha))
|
70
|
+
|
71
|
+
format_commit_data(data)
|
73
72
|
end
|
74
73
|
|
75
74
|
# Instantiates {Runner} for a given git object, runs it, and stores the result
|
@@ -84,6 +83,15 @@ module TinyCI
|
|
84
83
|
set_result(commit, result)
|
85
84
|
end
|
86
85
|
|
86
|
+
def format_commit_data(data)
|
87
|
+
parts = data.split(' ')
|
88
|
+
{
|
89
|
+
sha: parts[0],
|
90
|
+
time: parts[1],
|
91
|
+
result: parts[2]
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
87
95
|
# Repeatedly gets the list of eligable commits and runs TinyCI against them until there are no more remaining
|
88
96
|
def run_all_commits
|
89
97
|
commits = get_commits
|
data/lib/tinyci/subprocesses.rb
CHANGED
@@ -21,7 +21,7 @@ module TinyCI
|
|
21
21
|
|
22
22
|
unless status.success?
|
23
23
|
log_error output
|
24
|
-
raise SubprocessError.new(label, command.join(' '), status
|
24
|
+
raise SubprocessError.new(label, command.join(' '), status)
|
25
25
|
end
|
26
26
|
|
27
27
|
output.chomp
|
@@ -43,7 +43,7 @@ module TinyCI
|
|
43
43
|
status = waiter.value
|
44
44
|
unless status.success?
|
45
45
|
log_error output
|
46
|
-
raise SubprocessError.new(label, commands[i].join(' '), status
|
46
|
+
raise SubprocessError.new(label, commands[i].join(' '), status)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
data/lib/tinyci/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tinyci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Davies
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -233,8 +233,10 @@ files:
|
|
233
233
|
- ".rspec"
|
234
234
|
- ".rubocop.yml"
|
235
235
|
- ".ruby-version"
|
236
|
+
- ".tinyci.yml"
|
236
237
|
- ".yardopts"
|
237
238
|
- CHANGELOG.md
|
239
|
+
- Dockerfile
|
238
240
|
- Gemfile
|
239
241
|
- Gemfile.lock
|
240
242
|
- Guardfile
|
@@ -247,6 +249,7 @@ files:
|
|
247
249
|
- lib/tinyci/builders/script_builder.rb
|
248
250
|
- lib/tinyci/builders/test_builder.rb
|
249
251
|
- lib/tinyci/cli.rb
|
252
|
+
- lib/tinyci/compactor.rb
|
250
253
|
- lib/tinyci/config.rb
|
251
254
|
- lib/tinyci/executor.rb
|
252
255
|
- lib/tinyci/git_utils.rb
|
@@ -271,7 +274,7 @@ metadata:
|
|
271
274
|
allowed_push_host: https://rubygems.org
|
272
275
|
post_install_message: " _____ _ _____ _____\n/__ (_)_ __ _ _ /
|
273
276
|
___/ /_ _/\n | || | '_ \\| | | |/ / / /\n | || | | | | |_| / /___/\\/ /_
|
274
|
-
\ \n |_||_|_| |_|\\__, \\____/\\____/ 0.
|
277
|
+
\ \n |_||_|_| |_|\\__, \\____/\\____/ 0.3.0\n |___/\n\n"
|
275
278
|
rdoc_options: []
|
276
279
|
require_paths:
|
277
280
|
- lib
|