prick 0.19.0 → 0.20.1
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 +4 -4
- data/Gemfile +10 -4
- data/README.md +7 -7
- data/Rakefile +3 -1
- data/TODO +13 -11
- data/bin/console +2 -1
- data/doc/build-yml.txt +14 -0
- data/exe/prick +237 -19
- data/lib/builder/batch.rb +147 -0
- data/lib/builder/builder.rb +122 -0
- data/lib/builder/node.rb +189 -0
- data/lib/builder/node_pool.rb +105 -0
- data/lib/builder/parser.rb +120 -0
- data/lib/local/command.rb +193 -0
- data/lib/{prick → local}/git.rb +148 -22
- data/lib/local/timer.rb +98 -0
- data/lib/prick/constants.rb +54 -66
- data/lib/prick/diff.rb +28 -18
- data/lib/prick/prick_version.rb +161 -0
- data/lib/prick/state.rb +80 -165
- data/lib/prick/version.rb +2 -163
- data/lib/prick.rb +38 -24
- data/lib/share/init/.gitignore +10 -0
- data/lib/share/init/.prick-context +2 -0
- data/lib/share/init/.rspec +3 -0
- data/{share/schema/schema/public → lib/share/init/migration}/.keep +0 -0
- data/lib/share/init/prick.yml +6 -0
- data/lib/share/init/schema/.keep +0 -0
- data/lib/share/init/schema/build.yml +2 -0
- data/lib/share/init/schema/prick/.keep +0 -0
- data/lib/share/init/schema/prick/build.yml +5 -0
- data/lib/share/init/schema/prick/data.sql +6 -0
- data/{share/schema → lib/share/init}/schema/prick/tables.sql +2 -3
- data/lib/share/init/schema/public/.keep +0 -0
- data/lib/share/init/spec/prick_helper.rb +1 -0
- data/lib/share/init/spec/prick_spec.rb +6 -0
- data/lib/share/init/spec/spec_helper.rb +50 -0
- data/lib/share/migrate/migration/build.yml +4 -0
- data/lib/share/migrate/migration/diff.after-tables.sql +0 -0
- data/lib/share/migrate/migration/diff.before-tables.sql +0 -0
- data/lib/share/migrate/migration/diff.tables.sql +0 -0
- data/lib/subcommand/prick-build.rb +55 -0
- data/lib/subcommand/prick-create.rb +78 -0
- data/lib/subcommand/prick-drop.rb +25 -0
- data/lib/subcommand/prick-fox.rb +62 -0
- data/lib/subcommand/prick-init.rb +46 -0
- data/lib/subcommand/prick-make.rb +202 -0
- data/lib/subcommand/prick-migrate.rb +37 -0
- data/lib/subcommand/prick-release.rb +23 -0
- data/lib/subcommand/prick-setup.rb +20 -0
- data/lib/subcommand/prick-teardown.rb +18 -0
- data/prick.gemspec +32 -21
- metadata +95 -76
- data/.gitignore +0 -29
- data/.travis.yml +0 -7
- data/doc/create_release.txt +0 -17
- data/doc/flow.txt +0 -98
- data/doc/migra +0 -1
- data/doc/migrations.txt +0 -172
- data/doc/notes.txt +0 -116
- data/doc/prick.txt +0 -114
- data/doc/sh.prick +0 -316
- data/lib/ext/algorithm.rb +0 -14
- data/lib/ext/fileutils.rb +0 -26
- data/lib/ext/forward_method.rb +0 -18
- data/lib/ext/pg.rb +0 -18
- data/lib/ext/shortest_path.rb +0 -44
- data/lib/prick/archive.rb +0 -124
- data/lib/prick/branch.rb +0 -265
- data/lib/prick/builder.rb +0 -246
- data/lib/prick/cache.rb +0 -34
- data/lib/prick/command.rb +0 -104
- data/lib/prick/database.rb +0 -82
- data/lib/prick/dsort.rb +0 -151
- data/lib/prick/ensure.rb +0 -119
- data/lib/prick/exceptions.rb +0 -25
- data/lib/prick/head.rb +0 -189
- data/lib/prick/migration.rb +0 -70
- data/lib/prick/program.rb +0 -287
- data/lib/prick/project.rb +0 -626
- data/lib/prick/rdbms.rb +0 -137
- data/lib/prick/schema.rb +0 -27
- data/lib/prick/share.rb +0 -64
- data/libexec/strip-comments +0 -33
- data/make_releases +0 -72
- data/make_schema +0 -10
- data/share/diff/diff.after-tables.sql +0 -4
- data/share/diff/diff.before-tables.sql +0 -4
- data/share/diff/diff.tables.sql +0 -8
- data/share/features/diff.sql +0 -2
- data/share/features/feature/diff.sql +0 -2
- data/share/features/feature/migrate.sql +0 -2
- data/share/features/features.sql +0 -2
- data/share/features/features.yml +0 -2
- data/share/features/migrations.sql +0 -4
- data/share/gitignore +0 -2
- data/share/migration/diff.tables.sql +0 -8
- data/share/migration/features.yml +0 -6
- data/share/migration/migrate.sql +0 -3
- data/share/migration/migrate.yml +0 -8
- data/share/migration/tables.sql +0 -3
- data/share/schema/build.yml +0 -14
- data/share/schema/schema/build.yml +0 -3
- data/share/schema/schema/prick/build.yml +0 -14
- data/share/schema/schema/prick/data.sql +0 -7
- data/share/schema/schema/prick/schema.sql +0 -3
- data/share/schema/schema/public/build.yml +0 -13
- data/share/schema/schema.sql +0 -3
- data/test_assorted +0 -192
- data/test_feature +0 -112
- data/test_refactor +0 -34
- data/test_single_dev +0 -83
data/lib/{prick → local}/git.rb
RENAMED
|
@@ -1,35 +1,161 @@
|
|
|
1
1
|
|
|
2
|
-
require "prick/command.rb"
|
|
3
|
-
|
|
4
2
|
module Prick
|
|
5
|
-
# Tags have a 'v' prefixed the version in the git repository but this is made
|
|
6
|
-
# transparent to the application
|
|
7
3
|
module Git
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
# Return the origin of the repository
|
|
5
|
+
def self.origin()
|
|
6
|
+
Command.command("git remote get-url origin").first
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Clone a repository
|
|
10
|
+
def self.clone(url, directory = nil, branch: nil)
|
|
11
|
+
branch_arg = branch ? "--branch #{branch}" : ""
|
|
12
|
+
Command.command("git clone --quiet #{branch_arg} '#{url}' #{directory}")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Return the current commit id
|
|
16
|
+
def self.id() Command.command("git rev-parse HEAD").first end
|
|
17
|
+
|
|
18
|
+
# Return true if the repository has no modified files or unresolved
|
|
19
|
+
# conflicts. Requires the repository to have at least one commit
|
|
20
|
+
def self.clean?(file = nil)
|
|
21
|
+
re = file ? /^ #{file}(?: .*)?$/ : /^\?\?|!!/
|
|
22
|
+
!Command.command("git status --porcelain").any? { |l| l !~ re }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Return true if the repo is synchronized with the remote
|
|
26
|
+
def self.synchronized?()
|
|
27
|
+
out = Command.command "git rev-list --count --left-right 'HEAD...@{upstream}'"
|
|
28
|
+
!(out.first =~ /^0\s+0$/).nil?
|
|
10
29
|
end
|
|
11
30
|
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
31
|
+
# Add files to the index
|
|
32
|
+
def self.add(*files)
|
|
33
|
+
files = Array(files).flatten
|
|
34
|
+
Command.command "git add #{files.join(" ")}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# True if the file was added to the index
|
|
38
|
+
def self.added?(file = nil)
|
|
39
|
+
re = file ? /^[ACDMR]. #{file}$/ : /^[ACDMR]/
|
|
40
|
+
Command.command("git status --porcelain").any? { |l| l =~ re }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Commit changes on the current branch"
|
|
44
|
+
def self.commit(msg)
|
|
45
|
+
out = Command.command "git commit -m '#{msg}'", fail: false
|
|
46
|
+
Command.status == 0 or raise Command.exception.exception(out.join("\n"))
|
|
21
47
|
end
|
|
22
48
|
|
|
23
|
-
#
|
|
24
|
-
def self.
|
|
25
|
-
Command.command
|
|
49
|
+
# Pull changes from repository
|
|
50
|
+
def self.pull
|
|
51
|
+
Command.command "git pull"
|
|
26
52
|
end
|
|
27
53
|
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
self.detached? ? Command.command("git describe --tags").first.sub(/^v/, "") : nil
|
|
54
|
+
# Push change to repository
|
|
55
|
+
def self.push
|
|
56
|
+
Command.command "git push --quiet --atomic"
|
|
32
57
|
end
|
|
58
|
+
|
|
59
|
+
# Access to tag methods
|
|
60
|
+
def self.tag() Tag end
|
|
61
|
+
|
|
62
|
+
# Access to branch methods
|
|
63
|
+
def self.branch() Branch end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
module Tag
|
|
67
|
+
# The associated commit ID of a tag
|
|
68
|
+
def self.id(tag)
|
|
69
|
+
tag && Command.command("git rev-list -n 1 #{tag}").first
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# True if tag exists
|
|
73
|
+
def self.exist?(tag)
|
|
74
|
+
tag && Command.command?("git describe --tags #{tag}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Create tag
|
|
78
|
+
def self.create(tag, id: nil)
|
|
79
|
+
Command.command "git tag '#{tag}' #{id}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Drop a tag
|
|
83
|
+
def self.drop(tag)
|
|
84
|
+
Command.command "git tag -d '#{tag}'"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Return list of all tags. Not in any particular order
|
|
88
|
+
def self.list()
|
|
89
|
+
Command.command("git tag")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Return the most recent tag before the given commit (defaults to the
|
|
93
|
+
# last commit)
|
|
94
|
+
def self.current(id = nil)
|
|
95
|
+
describe_tag(id)&.first
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
# Return a [tag, number-of-commits, commit-id] tuple of the most recent
|
|
100
|
+
# tag. Return nil if no tag was found
|
|
101
|
+
def self.describe_tag(id = nil)
|
|
102
|
+
stdout, stderr = Command.command("git describe --tags #{id}", stderr: true, fail: false)
|
|
103
|
+
if Command.status != 0
|
|
104
|
+
return nil if stderr.first =~ /No names found/
|
|
105
|
+
raise Command.exception
|
|
106
|
+
end
|
|
107
|
+
if stdout.first =~ /^(.*)-(\d+)-.([0-9a-f]{7})$/
|
|
108
|
+
[$1, $2, $3]
|
|
109
|
+
else
|
|
110
|
+
[stdout.first, 0, nil]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
module Branch
|
|
116
|
+
def self.exist?(branch)
|
|
117
|
+
Command.command? "git show-ref --verify --quiet refs/heads/#{branch}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.create(branch, id = nil, set_upstream: true)
|
|
121
|
+
if set_upstream
|
|
122
|
+
current = Git.branch.current
|
|
123
|
+
Command.command %(
|
|
124
|
+
git checkout --quiet -b #{branch} #{id}
|
|
125
|
+
git push --quiet --set-upstream origin #{branch}
|
|
126
|
+
git checkout --quiet #{current}
|
|
127
|
+
)
|
|
128
|
+
else
|
|
129
|
+
Command.command "git branch #{branch} #{id}"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.drop(branch)
|
|
134
|
+
Command.command "git branch -D #{branch}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def self.list()
|
|
138
|
+
Command.command "git for-each-ref --format='%(refname:short)' refs/heads/*"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.current()
|
|
142
|
+
Command.command("git branch --show-current").first
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.checkout(branch)
|
|
146
|
+
Command.command "git checkout --quiet #{branch}"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
__END__
|
|
153
|
+
|
|
154
|
+
def self.changed?(file)
|
|
155
|
+
Command.command("git status --porcelain").any? { |l| l =~ /^.M #{file}$/ }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
|
|
33
159
|
|
|
34
160
|
# Return true if `version` has an associated tag
|
|
35
161
|
def self.tag?(version)
|
data/lib/local/timer.rb
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
# TODO: Move class method implementations to Timer::Timer
|
|
3
|
+
module Timer
|
|
4
|
+
def self.on!() @on = true end
|
|
5
|
+
def self.off!() @on = false end
|
|
6
|
+
def self.on?() @on end
|
|
7
|
+
def self.off?() !@on end
|
|
8
|
+
|
|
9
|
+
# Output file used by #emit. Default is STDERR (this is set at load-time)
|
|
10
|
+
def self.file() @file end
|
|
11
|
+
def self.file=(file) @file = file end
|
|
12
|
+
|
|
13
|
+
# Currently only :s, or :ms. Default is :ms
|
|
14
|
+
def self.unit() @unit end
|
|
15
|
+
def self.unit=(unit) @unit = unit end
|
|
16
|
+
|
|
17
|
+
# Number of digits after the decimal point. Default 0
|
|
18
|
+
def self.scale() @scale end
|
|
19
|
+
def self.scale=(scale) @scale = scale end
|
|
20
|
+
|
|
21
|
+
def self.time(label, &block)
|
|
22
|
+
if on?
|
|
23
|
+
timer = Timer.new(label)
|
|
24
|
+
r = yield
|
|
25
|
+
timer.emit
|
|
26
|
+
r
|
|
27
|
+
else
|
|
28
|
+
yield
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def time(label, &block) ::Timer.time(label, &block) end
|
|
33
|
+
|
|
34
|
+
def self.new(*args)
|
|
35
|
+
object = Timer.allocate
|
|
36
|
+
object.send(:initialize, *args)
|
|
37
|
+
object
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Timer
|
|
41
|
+
attr_reader :title
|
|
42
|
+
|
|
43
|
+
def file() ::Timer.file end
|
|
44
|
+
|
|
45
|
+
def self.on!() ::Timer.on! end
|
|
46
|
+
def self.on?() ::Time.on? end
|
|
47
|
+
|
|
48
|
+
def unit() ::Timer.unit end
|
|
49
|
+
def unit=(unit) ::Timer.unit = unit end
|
|
50
|
+
|
|
51
|
+
def scale() ::Timer.scale end
|
|
52
|
+
def scale=(scale) ::Timer.scale scale end
|
|
53
|
+
|
|
54
|
+
def initialize(title = nil)
|
|
55
|
+
@title = title
|
|
56
|
+
start
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def start(title = self.title) @t0 = Time.now; @title = title end
|
|
60
|
+
def stop() @t1 ||= Time.now end
|
|
61
|
+
def time() @t1 - @t0 end
|
|
62
|
+
|
|
63
|
+
def to_s(title = self.title) "#{title}: #{Timer.format(time)}" end
|
|
64
|
+
|
|
65
|
+
def emit(title = self.title)
|
|
66
|
+
stop
|
|
67
|
+
::Timer.file.puts to_s(title) if ::Timer.on?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.factor() { s: 1, ms: 1000 }[::Timer.unit] end
|
|
71
|
+
|
|
72
|
+
def self.format(time)
|
|
73
|
+
time ? sprintf("%.#{::Timer.scale}f#{::Timer.unit}", Timer.factor * time) : time.inspect
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
@on = false
|
|
79
|
+
@file = STDERR
|
|
80
|
+
@unit = :ms
|
|
81
|
+
@scale = 0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
#def time(title, &block)
|
|
91
|
+
# t0 = Time.now
|
|
92
|
+
# result = yield
|
|
93
|
+
# t1 = Time.now
|
|
94
|
+
# dt = (1000 * (t1 - t0)).round(0)
|
|
95
|
+
# puts "#{title}: #{dt}ms" if defined?(TIME) ? TIME : true
|
|
96
|
+
# result
|
|
97
|
+
#end
|
|
98
|
+
|
data/lib/prick/constants.rb
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
module Prick
|
|
3
|
+
### TIME
|
|
4
|
+
|
|
5
|
+
EPOCH = Time.at(0).utc
|
|
6
|
+
|
|
3
7
|
### DIRECTORIES AND FILE NAMES
|
|
4
8
|
|
|
5
9
|
# Shared files (part of the installation)
|
|
6
|
-
SHARE_PATH = "#{File.dirname(File.dirname(__dir__))}/share"
|
|
7
|
-
LIBEXEC_PATH = "#{File.dirname(File.dirname(__dir__))}/libexec"
|
|
10
|
+
SHARE_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/share"
|
|
11
|
+
LIBEXEC_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/libexec"
|
|
8
12
|
|
|
9
13
|
# Project directories
|
|
10
14
|
DIRS = [
|
|
@@ -16,22 +20,52 @@ module Prick
|
|
|
16
20
|
CACHE_DIR = "#{VAR_DIR}/cache",
|
|
17
21
|
SPOOL_DIR = "#{VAR_DIR}/spool",
|
|
18
22
|
TMP_DIR = "tmp",
|
|
19
|
-
CLONE_DIR = "tmp/
|
|
23
|
+
CLONE_DIR = "tmp/clones",
|
|
20
24
|
SPEC_DIR = "spec"
|
|
21
25
|
]
|
|
22
26
|
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
# Project specification file
|
|
28
|
+
PRICK_PROJECT_FILE = "prick.yml"
|
|
29
|
+
PRICK_PROJECT_PATH = PRICK_PROJECT_FILE
|
|
30
|
+
|
|
31
|
+
# Context file
|
|
32
|
+
PRICK_CONTEXT_FILE = ".prick-context"
|
|
33
|
+
PRICK_CONTEXT_PATH = PRICK_CONTEXT_FILE
|
|
26
34
|
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
# Fox state file (contains anchors and table sizes)
|
|
36
|
+
FOX_STATE_FILE = ".fox-state.yml"
|
|
37
|
+
FOX_STATE_PATH = FOX_STATE_FILE
|
|
30
38
|
|
|
31
|
-
#
|
|
39
|
+
# Reflections file
|
|
40
|
+
REFLECTIONS_FILE = "reflections.yml"
|
|
41
|
+
REFLECTIONS_PATH = File.join(SCHEMA_DIR, REFLECTIONS_FILE)
|
|
42
|
+
|
|
43
|
+
# Schema data file
|
|
32
44
|
SCHEMA_VERSION_FILE = "data.sql"
|
|
33
45
|
SCHEMA_VERSION_PATH = File.join(PRICK_DIR, SCHEMA_VERSION_FILE)
|
|
34
46
|
|
|
47
|
+
# Rspec temporary directory
|
|
48
|
+
SPEC_TMP_DIR = "spec"
|
|
49
|
+
SPEC_TMP_PATH = File.join(TMP_DIR, SPEC_TMP_DIR)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Migration diff files
|
|
53
|
+
DIFF_FILE = "diff.sql"
|
|
54
|
+
DIFF_FILES = [
|
|
55
|
+
BEFORE_TABLES_DIFF_FILE = "diff.before-tables.sql",
|
|
56
|
+
TABLES_DIFF_FILE = "diff.tables.sql",
|
|
57
|
+
AFTER_TABLES_DIFF_FILE = "diff.after-tables.sql"
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Not in use:
|
|
64
|
+
|
|
65
|
+
# The project state file
|
|
66
|
+
PROJECT_STATE_FILE = ".prick-project"
|
|
67
|
+
PROJECT_STATE_PATH = PROJECT_STATE_FILE
|
|
68
|
+
|
|
35
69
|
# The the .prick-migration file
|
|
36
70
|
PRICK_MIGRATION_FILE = ".prick-migration"
|
|
37
71
|
PRICK_MIGRATION_PATH = File.join(MIGRATION_DIR, PRICK_MIGRATION_FILE)
|
|
@@ -45,16 +79,16 @@ module Prick
|
|
|
45
79
|
STRIP_COMMENTS_PATH = File.join(LIBEXEC_PATH, "strip-comments")
|
|
46
80
|
|
|
47
81
|
# Diff files
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
82
|
+
# DIFF_FILE = "diff.sql"
|
|
83
|
+
# BEFORE_TABLES_DIFF_NAME = "diff.before-tables.sql"
|
|
84
|
+
# TABLES_DIFF_NAME = "diff.tables.sql"
|
|
85
|
+
# AFTER_TABLES_DIFF_NAME = "diff.after-tables.sql"
|
|
52
86
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
87
|
+
# BEFORE_TABLES_DIFF_PATH = File.join(MIGRATION_DIR, BEFORE_TABLES_DIFF_NAME)
|
|
88
|
+
# TABLES_DIFF_PATH = File.join(MIGRATION_DIR, TABLES_DIFF_NAME)
|
|
89
|
+
# AFTER_TABLES_DIFF_PATH = File.join(MIGRATION_DIR, AFTER_TABLES_DIFF_NAME)
|
|
90
|
+
#
|
|
91
|
+
# TABLES_DIFF_SHARE_PATH = File.join(SHARE_PATH, "diff", TABLES_DIFF_NAME)
|
|
58
92
|
|
|
59
93
|
|
|
60
94
|
# Dump files
|
|
@@ -174,53 +208,6 @@ module Prick
|
|
|
174
208
|
/x
|
|
175
209
|
ABSTRACT_RELEASE_RE = /^#{ABSTRACT_RELEASE_SUB_RE}$/
|
|
176
210
|
|
|
177
|
-
# Matches a (proper) release
|
|
178
|
-
#
|
|
179
|
-
# The RE defines the following captures:
|
|
180
|
-
# $1 - custom name, can be nil
|
|
181
|
-
# $2 - semantic version
|
|
182
|
-
#
|
|
183
|
-
# RELEASE_SUB_RE = /
|
|
184
|
-
# (?:(#{CUSTOM_NAME_SUB_RE})-)?
|
|
185
|
-
# (#{MMP_SEMVER_SUB_RE})
|
|
186
|
-
# /x
|
|
187
|
-
# RELEASE_RE = /^#{RELEASE_SUB_RE}$/
|
|
188
|
-
|
|
189
|
-
# Matches a prerelease branch
|
|
190
|
-
#
|
|
191
|
-
# The RE defines the following captures:
|
|
192
|
-
# $1 - custom name, can be nil
|
|
193
|
-
# $2 - semantic version
|
|
194
|
-
#
|
|
195
|
-
# PRERELEASE_SUB_RE = /
|
|
196
|
-
# (?:(#{CUSTOM_NAME_SUB_RE})-)?
|
|
197
|
-
# (#{MMP_SEMVER_SUB_RE}-#{PRE_SEMVER_SUB_RE})
|
|
198
|
-
# /x
|
|
199
|
-
# PRERELEASE_RE = /^#{PRERELEASE_SUB_RE}$/
|
|
200
|
-
|
|
201
|
-
# Matches a feature branch
|
|
202
|
-
#
|
|
203
|
-
# The RE defines the following captures:
|
|
204
|
-
# $1 - custom name, can be nil
|
|
205
|
-
# $2 - semantic version
|
|
206
|
-
# $3 - feature name
|
|
207
|
-
#
|
|
208
|
-
# FEATURE_SUB_RE = /#{ABSTRACT_RELEASE_SUB_RE}_(#{FEATURE_NAME_SUB_RE})/
|
|
209
|
-
# FEATURE_RE = /^#{FEATURE_SUB_RE}$/
|
|
210
|
-
|
|
211
|
-
# Migration RE. The syntax is <version>_<version>
|
|
212
|
-
#
|
|
213
|
-
# The RE defines the following captures
|
|
214
|
-
# $1 - from version
|
|
215
|
-
# $2 - from custom name, can be nil
|
|
216
|
-
# $3 - from semantic version
|
|
217
|
-
# $4 - to version
|
|
218
|
-
# $5 - to custom name, can be nil
|
|
219
|
-
# $6 - to semantic version
|
|
220
|
-
#
|
|
221
|
-
# MIGRATION_SUB_RE = /(#{RELEASE_SUB_RE})_(#{RELEASE_SUB_RE})/
|
|
222
|
-
# MIGRATION_RE = /^#{MIGRATION_SUB_RE}$/
|
|
223
|
-
|
|
224
211
|
# Project release RE. The general syntax is '<project>-<custom>-<version>'
|
|
225
212
|
#
|
|
226
213
|
# The RE defines the following captures:
|
|
@@ -271,3 +258,4 @@ module Prick
|
|
|
271
258
|
def self.tmp_databases_re(project_name) /^#{tmp_databases_sub_re(project_name)}$/ end
|
|
272
259
|
end
|
|
273
260
|
|
|
261
|
+
|
data/lib/prick/diff.rb
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
module Prick
|
|
2
|
+
# Database diff
|
|
2
3
|
class Diff
|
|
3
4
|
# Diff as a list of lines
|
|
4
5
|
attr_reader :diff
|
|
5
6
|
|
|
6
|
-
#
|
|
7
|
+
# Diff split into before/after table changes
|
|
7
8
|
attr_reader :before_table_changes
|
|
8
9
|
attr_reader :table_changes
|
|
9
10
|
attr_reader :after_table_changes
|
|
10
11
|
|
|
11
12
|
def initialize(db1, db2)
|
|
13
|
+
constrain db1, String
|
|
14
|
+
constrain db2, String
|
|
12
15
|
@db1, @db2 = db1, db2
|
|
13
16
|
do_diff
|
|
14
17
|
split_on_table_changes
|
|
@@ -17,40 +20,35 @@ module Prick
|
|
|
17
20
|
def self.same?(db1, db2) Diff.new(db1, db2).same? end
|
|
18
21
|
|
|
19
22
|
# Return true if the two databases are equal. Named #same? to avoid name
|
|
20
|
-
# collision with the built
|
|
23
|
+
# collision with the built-in #equal? method
|
|
21
24
|
def same?() diff.empty? end
|
|
22
25
|
|
|
26
|
+
# :call-seq:
|
|
27
|
+
# write(file, mark: true)
|
|
28
|
+
# write(file1, file2, file3, mark: false)
|
|
29
|
+
#
|
|
23
30
|
# Write the diff between the databases to the given file(s). Return true if
|
|
24
31
|
# the databases are equal
|
|
25
|
-
def
|
|
32
|
+
def write(file1, file2 = nil, file3 = nil, mark: nil)
|
|
26
33
|
if file2.nil? && file3.nil?
|
|
27
34
|
file2 = file3 = file1
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
mark = mark.nil? ? true : mark
|
|
36
|
+
elsif file2.nil? != file3.nil?
|
|
37
|
+
raise Prick::Fail, "Either none or both of `file2` and `file3` should be nil"
|
|
38
|
+
else
|
|
39
|
+
mark = mark.nil? ? false : mark
|
|
30
40
|
end
|
|
31
41
|
write_segment(file1, :before_table_changes, mark: mark)
|
|
32
42
|
write_segment(file2, :table_changes, mark: mark)
|
|
33
43
|
write_segment(file3, :after_table_changes, mark: mark)
|
|
34
44
|
end
|
|
35
45
|
|
|
36
|
-
def write_segment(file, segment, mark: true)
|
|
37
|
-
lines = self.send(segment)
|
|
38
|
-
if lines.empty?
|
|
39
|
-
FileUtils.rm_f(file) if file != "/dev/stdout"
|
|
40
|
-
else
|
|
41
|
-
File.open(file, "w") { |f|
|
|
42
|
-
f.puts "--" + segment.to_s.gsub("_", " ").upcase if mark
|
|
43
|
-
f.puts lines
|
|
44
|
-
}
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
46
|
private
|
|
49
47
|
def do_diff(file = nil)
|
|
50
48
|
command = "migra --unsafe --with-privileges postgres:///#{@db1} postgres:///#{@db2}"
|
|
51
49
|
@diff = Command.command(command, fail: false)
|
|
52
50
|
[0,2].include?(Command.status) or
|
|
53
|
-
raise
|
|
51
|
+
raise Prick::Fail, "migrate command failed with status #{Command.status}: #{command}"
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
# Initialize table change variables
|
|
@@ -110,6 +108,18 @@ module Prick
|
|
|
110
108
|
end
|
|
111
109
|
}
|
|
112
110
|
end
|
|
111
|
+
|
|
112
|
+
def write_segment(file, segment, mark: true)
|
|
113
|
+
lines = self.send(segment)
|
|
114
|
+
if lines.empty?
|
|
115
|
+
FileUtils.touch(file)
|
|
116
|
+
else
|
|
117
|
+
File.open(file, "w") { |f|
|
|
118
|
+
f.puts "-- " + segment.to_s.gsub("_", " ").upcase if mark
|
|
119
|
+
f.puts lines
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
113
123
|
end
|
|
114
124
|
end
|
|
115
125
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
|
|
2
|
+
require 'semantic' # https://github.com/jlindsey/semantic
|
|
3
|
+
|
|
4
|
+
# Project related code starts here
|
|
5
|
+
module Prick
|
|
6
|
+
class PrickVersion
|
|
7
|
+
class FormatError < RuntimeError; end
|
|
8
|
+
|
|
9
|
+
include Comparable
|
|
10
|
+
|
|
11
|
+
PRE_LABEL = "pre"
|
|
12
|
+
PRE_RE = /^#{PRE_LABEL}\.(\d+)$/
|
|
13
|
+
|
|
14
|
+
def self.zero() PrickVersion.new("0.0.0") end
|
|
15
|
+
def zero?() self == PrickVersion.zero end
|
|
16
|
+
|
|
17
|
+
# Return true if `string` is a version
|
|
18
|
+
def self.version?(string)
|
|
19
|
+
string.is_a?(String) or raise Internal, "String expected"
|
|
20
|
+
!(string =~ VERSION_RE).nil?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
attr_accessor :fork
|
|
24
|
+
attr_accessor :semver
|
|
25
|
+
attr_accessor :feature
|
|
26
|
+
|
|
27
|
+
def major() @semver.major end
|
|
28
|
+
def major=(major) @semver.major = major end
|
|
29
|
+
|
|
30
|
+
def minor() @semver.minor end
|
|
31
|
+
def minor=(minor) @semver.minor = minor end
|
|
32
|
+
|
|
33
|
+
def patch() @semver.patch end
|
|
34
|
+
def patch=(patch) @semver.patch = patch end
|
|
35
|
+
|
|
36
|
+
# Return true if this is a fork release
|
|
37
|
+
def fork?() !@fork.nil? end
|
|
38
|
+
|
|
39
|
+
# Return true if this is a feature release
|
|
40
|
+
def feature?() !@feature.nil? end
|
|
41
|
+
|
|
42
|
+
# Return true if this is a release branch (and not a prerelease)
|
|
43
|
+
def release?() !feature? && !pre? end
|
|
44
|
+
|
|
45
|
+
# Return true if this is a pre-release
|
|
46
|
+
def pre?() !@semver.pre.nil? end
|
|
47
|
+
def prerelease?() pre? end
|
|
48
|
+
|
|
49
|
+
# The releases is stored as a String (eg. 'pre.1') in the semantic version
|
|
50
|
+
# but #pre returns only the Integer number
|
|
51
|
+
def pre() @semver.pre =~ PRE_RE ? $1.to_i : nil end
|
|
52
|
+
def prerelease() pre end
|
|
53
|
+
|
|
54
|
+
# #pre= expects an integer or nil argument
|
|
55
|
+
def pre=(pre) @semver.pre = (pre ? "#{PRE_LABEL}.#{pre}" : nil) end
|
|
56
|
+
def prerelease=(pre) self.pre = pre end
|
|
57
|
+
|
|
58
|
+
def dup() PrickVersion.new(self) end
|
|
59
|
+
def clone() PrickVersion.new(self) end
|
|
60
|
+
|
|
61
|
+
def eql?(other) self == other end
|
|
62
|
+
def hash() @semver.hash end
|
|
63
|
+
|
|
64
|
+
def initialize(version, fork: nil, feature: nil)
|
|
65
|
+
case version
|
|
66
|
+
when String
|
|
67
|
+
version =~ VERSION_RE or raise PrickVersion::FormatError, "Expected a version, got #{version.inspect}"
|
|
68
|
+
@fork = fork || $1
|
|
69
|
+
@semver = Semantic::Version.new($3)
|
|
70
|
+
@feature = feature || $4
|
|
71
|
+
when Semantic::Version
|
|
72
|
+
@fork = fork
|
|
73
|
+
@semver = version.dup
|
|
74
|
+
@feature = feature
|
|
75
|
+
when PrickVersion
|
|
76
|
+
@fork = fork || version.fork
|
|
77
|
+
@semver = version.semver.dup
|
|
78
|
+
@feature = feature || version.feature
|
|
79
|
+
else
|
|
80
|
+
raise Internal, "Expected a String, PrickVersion, or Semantic::Version, got #{version.class}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Try converting the string `version` into a PrickVersion object. Return nil if unsuccessful
|
|
85
|
+
def self.try(version)
|
|
86
|
+
version.is_a?(PrickVersion) ? version : (version?(version) ? new(version) : nil)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Parse a branch or tag name into a PrickVersion object. Return a [version, tag]
|
|
90
|
+
# tuple where tag is true if name was a tag
|
|
91
|
+
def self.parse(name)
|
|
92
|
+
name =~ VERSION_RE or raise PrickVersion::FormatError, "Expected a version, got #{name.inspect}"
|
|
93
|
+
fork, tag, semver, feature = $1, $2, $3, $4
|
|
94
|
+
version = PrickVersion.new(semver, fork: fork, feature: feature)
|
|
95
|
+
[version, tag]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# `part` can be one of :major, :minor, :patch, or :pre. If pre is undefined, it
|
|
99
|
+
# is set to `pre_initial_value`
|
|
100
|
+
def increment(part, pre_initial_value = 1)
|
|
101
|
+
self.dup.increment!(part, pre_initial_value)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def increment!(part, pre_initial_value = 1)
|
|
105
|
+
if part == :pre
|
|
106
|
+
self.pre = (self.pre ? self.pre + 1 : pre_initial_value)
|
|
107
|
+
else
|
|
108
|
+
@semver = semver.increment!(part)
|
|
109
|
+
end
|
|
110
|
+
self
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def truncate(part)
|
|
114
|
+
case part
|
|
115
|
+
when :pre
|
|
116
|
+
v = self.dup
|
|
117
|
+
v.feature = nil
|
|
118
|
+
v.pre = nil
|
|
119
|
+
v
|
|
120
|
+
when :feature
|
|
121
|
+
v = self.dup
|
|
122
|
+
v.feature = nil
|
|
123
|
+
v
|
|
124
|
+
else
|
|
125
|
+
raise NotYet
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# def path
|
|
130
|
+
# parts = [FEATURE_DIR, truncate(:pre), feature].compact
|
|
131
|
+
# File.join(*parts)
|
|
132
|
+
# end
|
|
133
|
+
|
|
134
|
+
# def link
|
|
135
|
+
# !feature? or raise Internal, "PrickVersion #{to_s} is a feature, not a release"
|
|
136
|
+
# File.join(RELEASE_DIR, to_s)
|
|
137
|
+
# end
|
|
138
|
+
|
|
139
|
+
def <=>(other)
|
|
140
|
+
r = (fork || "") <=> (other.fork || "")
|
|
141
|
+
return r if r != 0
|
|
142
|
+
r = semver <=> other.semver
|
|
143
|
+
return r if r != 0
|
|
144
|
+
r = (feature || "") <=> (other.feature || "")
|
|
145
|
+
return r
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Render as branch
|
|
149
|
+
def to_s(tag: false)
|
|
150
|
+
(fork ? "#{fork}-" : "") + (tag ? "v" : "") + semver.to_s + (feature ? "_#{feature}" : "")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Render as a tag
|
|
154
|
+
def to_tag() to_s(tag: true) end
|
|
155
|
+
|
|
156
|
+
# Render as string
|
|
157
|
+
def inspect() to_s end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
|