prick 0.2.0 → 0.7.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.
- checksums.yaml +4 -4
- data/.gitignore +6 -5
- data/Gemfile +4 -1
- data/TODO +10 -0
- data/doc/prick.txt +114 -0
- data/exe/prick +328 -402
- data/lib/ext/fileutils.rb +18 -0
- data/lib/ext/forward_method.rb +18 -0
- data/lib/ext/shortest_path.rb +44 -0
- data/lib/prick.rb +20 -10
- data/lib/prick/branch.rb +254 -0
- data/lib/prick/builder.rb +164 -0
- data/lib/prick/cache.rb +34 -0
- data/lib/prick/command.rb +19 -11
- data/lib/prick/constants.rb +122 -48
- data/lib/prick/database.rb +28 -20
- data/lib/prick/diff.rb +125 -0
- data/lib/prick/exceptions.rb +15 -3
- data/lib/prick/git.rb +77 -30
- data/lib/prick/head.rb +183 -0
- data/lib/prick/migration.rb +40 -200
- data/lib/prick/program.rb +493 -0
- data/lib/prick/project.rb +523 -351
- data/lib/prick/rdbms.rb +4 -13
- data/lib/prick/schema.rb +16 -90
- data/lib/prick/share.rb +64 -0
- data/lib/prick/state.rb +192 -0
- data/lib/prick/version.rb +62 -29
- data/libexec/strip-comments +33 -0
- data/make_releases +48 -345
- data/make_schema +10 -0
- data/prick.gemspec +14 -23
- data/share/diff/diff.after-tables.sql +4 -0
- data/share/diff/diff.before-tables.sql +4 -0
- data/share/diff/diff.tables.sql +8 -0
- data/share/migration/diff.tables.sql +8 -0
- data/share/migration/features.yml +6 -0
- data/share/migration/migrate.sql +3 -0
- data/share/migration/migrate.yml +8 -0
- data/share/migration/tables.sql +3 -0
- data/share/schema/build.yml +14 -0
- data/share/schema/schema.sql +5 -0
- data/share/schema/schema/build.yml +3 -0
- data/share/schema/schema/prick/build.yml +14 -0
- data/share/schema/schema/prick/data.sql +7 -0
- data/share/schema/schema/prick/schema.sql +5 -0
- data/share/{schemas/prick/schema.sql → schema/schema/prick/tables.sql} +2 -5
- data/{file → share/schema/schema/public/.keep} +0 -0
- data/share/schema/schema/public/build.yml +14 -0
- data/share/schema/schema/public/schema.sql +3 -0
- data/test_assorted +192 -0
- data/test_feature +112 -0
- data/test_refactor +34 -0
- data/test_single_dev +83 -0
- metadata +43 -68
- data/lib/prick/build.rb +0 -376
- data/lib/prick/migra.rb +0 -22
- data/share/schemas/prick/data.sql +0 -8
data/lib/prick/exceptions.rb
CHANGED
@@ -2,12 +2,24 @@
|
|
2
2
|
module Prick
|
3
3
|
class Error < RuntimeError; end
|
4
4
|
class Fail < Error; end
|
5
|
+
|
5
6
|
class NotYet < NotImplementedError
|
6
|
-
def initialize() super("
|
7
|
+
def initialize() super("Internal error: Not yet implemented") end
|
8
|
+
end
|
9
|
+
|
10
|
+
class NotThis < ScriptError
|
11
|
+
def initialize() super("Internal error: Abstract method called") end
|
7
12
|
end
|
8
|
-
|
9
|
-
|
13
|
+
|
14
|
+
class Abstract < ScriptError
|
15
|
+
def initialize() super("Internal error: Abstract method called") end
|
10
16
|
end
|
17
|
+
|
18
|
+
AbstractMethod = Abstract
|
19
|
+
|
11
20
|
class Internal < ScriptError; end
|
21
|
+
class Oops < Internal
|
22
|
+
def initialize() super("Oops, this shouldn't happen") end
|
23
|
+
end
|
12
24
|
end
|
13
25
|
|
data/lib/prick/git.rb
CHANGED
@@ -9,34 +9,41 @@ module Prick
|
|
9
9
|
Command.command "git init"
|
10
10
|
end
|
11
11
|
|
12
|
-
# Returns true if the repository has no modified files
|
13
|
-
# repository to have at least one commit. Creating
|
14
|
-
# Project::initialize_directory guarantees that
|
12
|
+
# Returns true if the repository has no modified files or unresolved
|
13
|
+
# conflicts. Requires the repository to have at least one commit. Creating
|
14
|
+
# the repository using Project::initialize_directory guarantees that
|
15
15
|
def self.clean?()
|
16
16
|
Command.command("git status").find { |l|
|
17
|
-
l =~
|
17
|
+
(l =~ /^Changes to be committed:/) ||
|
18
|
+
(l =~ /^Unmerged paths:/) ||
|
19
|
+
(l =~ /^Changes not staged for commit:/)
|
18
20
|
}.nil?
|
19
21
|
end
|
20
22
|
|
21
23
|
# Returns true if the repository is on a detached branch
|
22
24
|
def self.detached?
|
23
|
-
|
24
|
-
line =~ /^ref:/ ? false : true
|
25
|
+
Command.command? "git symbolic-ref -q HEAD", expect: 1
|
25
26
|
end
|
26
27
|
|
27
28
|
# The current tag. This is only defined if the repository is in "detached
|
28
29
|
# head" mode
|
29
|
-
def self.current_tag()
|
30
|
-
|
30
|
+
def self.current_tag()
|
31
|
+
self.detached? ? Command.command("git describe --tags").first.sub(/^v/, "") : nil
|
32
|
+
end
|
33
|
+
|
31
34
|
# Return true if `version` has an associated tag
|
32
35
|
def self.tag?(version)
|
33
|
-
!list_tags.grep(version).empty?
|
36
|
+
!list_tags.grep(version.to_s).empty?
|
34
37
|
end
|
35
38
|
|
36
|
-
# List tag versions
|
37
39
|
# Create version tag
|
38
|
-
def self.create_tag(version)
|
39
|
-
Command.command "git tag -a 'v#{version}' -m '
|
40
|
+
def self.create_tag(version, message: "Release #{version}", commit_id: nil)
|
41
|
+
Command.command "git tag -a 'v#{version}' -m '#{message}' #{commit_id}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create a cancel-version tag
|
45
|
+
def self.cancel_tag(version)
|
46
|
+
create_tag("#{version}_cancelled", message: "Cancel #{version}", commit_id: tag_id(version))
|
40
47
|
end
|
41
48
|
|
42
49
|
def self.delete_tag(version, remote: false)
|
@@ -44,18 +51,30 @@ module Prick
|
|
44
51
|
Command.command("git push --delete origin 'v#{version}'", fail: false) if remote
|
45
52
|
end
|
46
53
|
|
54
|
+
def self.tag_id(version)
|
55
|
+
Command.command("git rev-parse 'v#{version}^{}'").first
|
56
|
+
end
|
57
|
+
|
47
58
|
# Checkout a version tag as a detached head
|
48
59
|
def self.checkout_tag(version)
|
49
60
|
Command.command "git checkout 'v#{version}'"
|
50
61
|
end
|
51
62
|
|
52
|
-
def self.list_tags
|
53
|
-
Command.command("git tag")
|
63
|
+
def self.list_tags(include_cancelled: false)
|
64
|
+
tags = Command.command("git tag")
|
65
|
+
if !include_cancelled
|
66
|
+
cancelled = tags.select { |tag| tag =~ /_cancelled$/ }
|
67
|
+
for cancel_tag in cancelled
|
68
|
+
tags.delete(cancel_tag)
|
69
|
+
tags.delete(cancel_tag.sub(/_cancelled$/, ""))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
tags.map { |tag| tag = tag[1..-1] }
|
54
73
|
end
|
55
74
|
|
56
|
-
# Name of the current branch
|
75
|
+
# Name of the current branch. This is nil if on a tag ("detached HEAD")
|
57
76
|
def self.current_branch()
|
58
|
-
Command.command("git rev-parse --abbrev-ref HEAD").first
|
77
|
+
self.detached? ? nil : Command.command("git rev-parse --abbrev-ref HEAD").first
|
59
78
|
end
|
60
79
|
|
61
80
|
# Check if branch exist
|
@@ -69,6 +88,11 @@ module Prick
|
|
69
88
|
Command.command "git branch #{name}"
|
70
89
|
end
|
71
90
|
|
91
|
+
# Rename a branch
|
92
|
+
def self.rename_branch(from, to)
|
93
|
+
Command.command "git branch -m #{from} #{to}"
|
94
|
+
end
|
95
|
+
|
72
96
|
# Destroy branch
|
73
97
|
def self.delete_branch(name)
|
74
98
|
Command.command "git branch -D #{name}", fail: false
|
@@ -94,28 +118,46 @@ module Prick
|
|
94
118
|
|
95
119
|
Command.command "git merge --no-commit #{name}", fail: false
|
96
120
|
|
97
|
-
#
|
98
|
-
files.each { |path, content|
|
121
|
+
# Restore excluded files
|
122
|
+
files.each { |path, content|
|
99
123
|
File.open(path, "w") { |file| file.puts(content) }
|
100
124
|
# Resolve git unmerged status
|
101
125
|
Git.add(path)
|
102
126
|
}
|
127
|
+
|
128
|
+
# TODO Detect outstanding merges
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.merge_tag(name, exclude_files: [], fail: false)
|
132
|
+
merge_branch(name, exclude_files: exclude_files, fail: fail)
|
103
133
|
end
|
104
134
|
|
105
|
-
# List branches
|
106
|
-
def self.list_branches
|
107
|
-
|
135
|
+
# List branches. Detached head "branches" are ignored unless :detached_head is true
|
136
|
+
def self.list_branches(detached_head: false)
|
137
|
+
if detached_head
|
138
|
+
Command.command "git branch --format='%(refname:short)'"
|
139
|
+
else
|
140
|
+
Command.command "git for-each-ref --format='%(refname:short)' refs/heads/*"
|
141
|
+
end
|
108
142
|
end
|
109
143
|
|
110
144
|
# Add a file to the index of the current branch
|
111
145
|
def self.add(*files)
|
112
|
-
Array(files).each { |file|
|
146
|
+
Array(files).flatten.each { |file|
|
113
147
|
Dir.chdir(File.dirname(file)) {
|
114
148
|
Command.command "git add '#{File.basename(file)}'"
|
115
149
|
}
|
116
150
|
}
|
117
151
|
end
|
118
152
|
|
153
|
+
def self.changed?(file)
|
154
|
+
Command.command("git status --porcelain").any? { |l| l =~ /^.M #{file}$/ }
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.added?(file)
|
158
|
+
Command.command("git status --porcelain").any? { |l| l =~ /^A. #{file}$/ }
|
159
|
+
end
|
160
|
+
|
119
161
|
# Return content of file in the given tag or branch. Defaults to HEAD
|
120
162
|
def self.readlines(file, tag: nil, branch: nil)
|
121
163
|
!(tag && branch) or raise Internal, "Can't use both tag: and branch: options"
|
@@ -127,7 +169,7 @@ module Prick
|
|
127
169
|
end.map { |l| "#{l}\n" }
|
128
170
|
end
|
129
171
|
|
130
|
-
# Return content of file
|
172
|
+
# Return content of file as a String
|
131
173
|
def self.read(file, tag: nil, branch: nil)
|
132
174
|
!(tag && branch) or raise Internal, "Can't use both tag: and branch: options"
|
133
175
|
if tag
|
@@ -135,18 +177,23 @@ module Prick
|
|
135
177
|
else
|
136
178
|
branch ||= "HEAD"
|
137
179
|
Command.command "git show #{branch}:#{file}"
|
138
|
-
end.join("\n")
|
180
|
+
end.join("\n") + "\n"
|
139
181
|
end
|
140
182
|
|
141
|
-
def self.rm(
|
142
|
-
|
143
|
-
|
183
|
+
def self.rm(*files)
|
184
|
+
Array(files).flatten.each { |file|
|
185
|
+
Dir.chdir(File.dirname(file)) {
|
186
|
+
Command.command "git rm -f '#{File.basename(file)}'", fail: false
|
187
|
+
}
|
144
188
|
}
|
145
189
|
end
|
146
190
|
|
147
|
-
def self.rm_rf(
|
148
|
-
|
149
|
-
|
191
|
+
def self.rm_rf(*files)
|
192
|
+
Array(files).flatten.each { |file|
|
193
|
+
Dir.chdir(File.dirname(file)) {
|
194
|
+
next if file == ".keep"
|
195
|
+
Command.command "git rm -rf '#{File.basename(file)}'", fail: false
|
196
|
+
}
|
150
197
|
}
|
151
198
|
end
|
152
199
|
|
data/lib/prick/head.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
|
2
|
+
module Prick
|
3
|
+
class Head
|
4
|
+
def project_name() Prick.project.name end
|
5
|
+
|
6
|
+
# Usually equal to #version.to_s
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# Version. This should be equal to schema.version
|
10
|
+
attr_reader :version
|
11
|
+
|
12
|
+
def base_version() @migration.base_version end
|
13
|
+
|
14
|
+
attr_reader :schema
|
15
|
+
|
16
|
+
attr_reader :migration
|
17
|
+
|
18
|
+
def clean?() Git::clean? end
|
19
|
+
|
20
|
+
def tag?() false end
|
21
|
+
def release_tag?() false end
|
22
|
+
def migration_tag?() false end
|
23
|
+
|
24
|
+
def branch?() false end
|
25
|
+
def release_branch?() false end
|
26
|
+
def prerelease_branch?() false end
|
27
|
+
def feature_branch?() false end
|
28
|
+
def migration_branch?() false end
|
29
|
+
|
30
|
+
def initialize(name, version, migration)
|
31
|
+
@name = name
|
32
|
+
@version = version
|
33
|
+
@schema = Schema.new
|
34
|
+
@migration = migration
|
35
|
+
end
|
36
|
+
|
37
|
+
# TODO: Handle migrations
|
38
|
+
def self.load(name)
|
39
|
+
version, tag = Version.parse(name)
|
40
|
+
if tag
|
41
|
+
ReleaseTag.load
|
42
|
+
else
|
43
|
+
if version.release?
|
44
|
+
ReleaseBranch.load
|
45
|
+
elsif version.prerelease?
|
46
|
+
PrereleaseBranch.load(version)
|
47
|
+
elsif version.feature?
|
48
|
+
FeatureBranch.load(version)
|
49
|
+
else
|
50
|
+
raise NotHere
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def create()
|
56
|
+
raise NotThis
|
57
|
+
end
|
58
|
+
|
59
|
+
def build(database)
|
60
|
+
schema.build(database)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.database_name(version)
|
64
|
+
version.truncate(:pre).to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Tag < Head
|
69
|
+
def tag?() true end
|
70
|
+
|
71
|
+
def initialize(version, base_version, migration = nil)
|
72
|
+
migration ||= Migration.new(version, base_version)
|
73
|
+
super(version.to_s, version, migration)
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.load
|
77
|
+
state = Migration.load
|
78
|
+
self.new(state.version, state.base_version)
|
79
|
+
end
|
80
|
+
|
81
|
+
def create
|
82
|
+
initial_branch_name = "#{version}_initial"
|
83
|
+
clean? or raise Internal, "Repository is not clean"
|
84
|
+
!Git.detached? or raise Internal, "Not on a branch"
|
85
|
+
Git.create_branch(initial_branch_name)
|
86
|
+
Git.checkout_branch(initial_branch_name)
|
87
|
+
migration.update(version)
|
88
|
+
schema.version = version
|
89
|
+
Git.commit "Created release v#{version}"
|
90
|
+
Git.create_tag(version)
|
91
|
+
Git.checkout_tag(version)
|
92
|
+
self
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ReleaseTag < Tag
|
97
|
+
def release_tag?() true end
|
98
|
+
end
|
99
|
+
|
100
|
+
# TODO
|
101
|
+
class PrereleaseTag < Tag
|
102
|
+
end
|
103
|
+
|
104
|
+
class MigrationTag < Tag
|
105
|
+
def migration_tag?() true end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Branch < Head
|
109
|
+
def branch?() true end
|
110
|
+
|
111
|
+
def initialize(name, version, migration)
|
112
|
+
super(name, version, migration)
|
113
|
+
end
|
114
|
+
|
115
|
+
def create()
|
116
|
+
Git.create_branch(version)
|
117
|
+
Git.checkout_branch(version)
|
118
|
+
migration.create
|
119
|
+
self
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class ReleaseBranch < Branch
|
124
|
+
def release_branch?() true end
|
125
|
+
|
126
|
+
def initialize(fork = nil, base_version)
|
127
|
+
if fork
|
128
|
+
version = Version.new(base_version, fork: fork)
|
129
|
+
else
|
130
|
+
version = base_version
|
131
|
+
end
|
132
|
+
super(version.to_s, version, Migration.new(nil, base_version))
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.load
|
136
|
+
state = MigrationState.load
|
137
|
+
ReleaseBranch.new(nil, state.base_version)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class PrereleaseBranch < Branch
|
142
|
+
def prerelease_branch?() true end
|
143
|
+
|
144
|
+
def initialize(version, base_version)
|
145
|
+
target_version = version.truncate(:pre)
|
146
|
+
super(version.to_s, target_version, Migration.new(target_version, base_version))
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.load
|
150
|
+
state = MigrationState.load
|
151
|
+
PrereleaseBranch.new(state.version, state.base_version)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class FeatureBranch < Branch
|
156
|
+
def feature_branch?() true end
|
157
|
+
|
158
|
+
def initialize(feature_name, base_version)
|
159
|
+
name = Version.new(base_version, feature: feature_name).to_s
|
160
|
+
super(name, base_version, FeatureMigration.new(feature_name, base_version))
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.load
|
164
|
+
state = MigrationState.load
|
165
|
+
FeatureBranch.new(state.version.feature)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# TODO: Versioned migrations (or maybe not)
|
170
|
+
class MigrationBranch < Branch
|
171
|
+
def migrationn_branch?() true end
|
172
|
+
|
173
|
+
def initialize(version, base_version)
|
174
|
+
if (version.fork || "") == (base_version.fork || "")
|
175
|
+
name = version.to_s + "-" + base_version.semver.to_s
|
176
|
+
else
|
177
|
+
name = version.to_s + "-" + base_version.to_s
|
178
|
+
end
|
179
|
+
super(name, version, Migration.new(version, base_version))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
data/lib/prick/migration.rb
CHANGED
@@ -1,230 +1,70 @@
|
|
1
1
|
|
2
|
-
require 'yaml'
|
3
|
-
|
4
2
|
module Prick
|
5
3
|
class Migration
|
6
|
-
|
7
|
-
|
8
|
-
attr_reader :path
|
9
|
-
|
10
|
-
def files() raise AbstractMethod end
|
11
|
-
def release_files() files end
|
12
|
-
|
13
|
-
# Return versions of the features in this migration
|
14
|
-
def feature_versions() raise AbstractMethod end
|
15
|
-
|
16
|
-
def initialize(path, template_dir)
|
17
|
-
@path = path
|
18
|
-
@template_dir = template_dir
|
19
|
-
end
|
20
|
-
|
21
|
-
def exist?() File.exist?(keep_file) end
|
22
|
-
def create() FileUtils.touch_p(keep_file); Git.add(keep_file) end
|
23
|
-
def destroy() Git.rm_rf(path) end
|
24
|
-
|
25
|
-
def present?() exist? && files.all? { |f| File.exist?(f) } end
|
26
|
-
def prepare() files.each { |f| FileUtils.cp(template_file(f), path) }; Git.add(path) end
|
27
|
-
|
28
|
-
def include_feature(feature) raise AbstractMethod end
|
29
|
-
|
30
|
-
def migrate() raise AbstractMethod end
|
31
|
-
|
32
|
-
def to_s() path end
|
33
|
-
def <=>(other) path <=> other.path end
|
34
|
-
|
35
|
-
def self.path(version)
|
36
|
-
if version.feature?
|
37
|
-
File.join(FEATURE_DIR, version.truncate(:feature).to_s, version.feature)
|
38
|
-
else
|
39
|
-
File.join(FEATURE_DIR, version)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.version(path)
|
44
|
-
Version.new(path.delete_prefix("#{FEATURE_DIR}/").sub("/", "_"))
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.feature?(path)
|
48
|
-
Version.new(path.delete_prefix("#{FEATURE_DIR}/").sub("/", "_")).feature?
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.files(path) raise AbstractMethod end
|
52
|
-
def self.release_files(path) files(path) end
|
53
|
-
|
54
|
-
def dump(&block)
|
55
|
-
# puts self.class
|
56
|
-
# indent {
|
57
|
-
# puts "path : #{path}"
|
58
|
-
# puts "files: #{files.inspect}"
|
59
|
-
# puts "release_files: #{release_files.inspect}"
|
60
|
-
# puts "feature_versions: #{feature_versions.map(&:to_s).inspect}"
|
61
|
-
# puts "keep_file: #{keep_file}"
|
62
|
-
# yield if block_given?
|
63
|
-
# }
|
64
|
-
end
|
65
|
-
|
66
|
-
protected
|
67
|
-
def template_file(path) File.join(SHARE_PATH, @template_dir, File.basename(path)) end
|
68
|
-
def keep_file() File.join(path, KEEP_FILE) end
|
69
|
-
end
|
70
|
-
|
71
|
-
class ReleaseMigration < Migration
|
72
|
-
FEATURES_TMPL_DIR = "features"
|
73
|
-
FILES = [
|
74
|
-
FEATURES_SQL = "features.sql",
|
75
|
-
FEATURES_YML = "features.yml",
|
76
|
-
MIGRATIONS_SQL = "migrations.sql",
|
77
|
-
DIFF_SQL = "diff.sql"
|
78
|
-
]
|
79
|
-
|
80
|
-
attr_reader :features_yml
|
81
|
-
attr_reader :features_sql
|
82
|
-
attr_reader :migrations_sql
|
83
|
-
attr_reader :diff_sql
|
84
|
-
|
85
|
-
def files() [features_yml, features_sql, migrations_sql, diff_sql] end
|
4
|
+
attr_reader :version
|
5
|
+
attr_reader :base_version
|
86
6
|
|
87
|
-
def initialize(
|
88
|
-
|
89
|
-
@
|
90
|
-
@features_sql = File.join(path, FEATURES_SQL)
|
91
|
-
@migrations_sql = File.join(path, MIGRATIONS_SQL)
|
92
|
-
@diff_sql = File.join(path, DIFF_SQL)
|
7
|
+
def initialize(version, base_version)
|
8
|
+
@version = version
|
9
|
+
@base_version = base_version
|
93
10
|
end
|
94
11
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
98
|
-
# `feature` is a Feature object
|
99
|
-
def include_feature(migration, append: true)
|
100
|
-
migration.is_a?(FeatureMigration) or raise "Expected FeatureMigration object, got #{migration.class}"
|
101
|
-
!feature_paths.include?(migration.path) or raise Error, "Feature #{migration.version} is already included"
|
102
|
-
exclude_files = [Schema.data_file] +
|
103
|
-
migration.release_files +
|
104
|
-
migration.feature_paths.map { |path| FeatureMigration.release_files(path) }.flatten
|
105
|
-
|
106
|
-
Git.merge_branch(migration.version, exclude_files: exclude_files)
|
107
|
-
|
108
|
-
feature_paths = YAML.load(Git.read(migration.features_yml, branch: migration.version))
|
109
|
-
append_features_yml(feature_paths, append: append)
|
110
|
-
|
111
|
-
feature_paths.each { |feature_path|
|
112
|
-
if path != File.dirname(feature_path)
|
113
|
-
FileUtils.ln_s(File.join("../..", feature_path), path)
|
114
|
-
Git.add(File.join(path, File.basename(feature_path)))
|
115
|
-
end
|
116
|
-
}
|
12
|
+
def self.load
|
13
|
+
state = MigrationState.load
|
14
|
+
Migration.new(state.version, state.base_version)
|
117
15
|
end
|
118
16
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
Dir.chdir(path) {
|
123
|
-
puts " cd #{path}"
|
124
|
-
puts " psql -d #{database_name} < #{MIGRATIONS_SQL}"
|
125
|
-
Command.command "psql -d #{database_name} < #{MIGRATIONS_SQL}"
|
126
|
-
}
|
127
|
-
end
|
17
|
+
# Remove content of the migration/ directory
|
18
|
+
def self.clear
|
19
|
+
FileUtils.empty!(MIGRATION_DIR)
|
128
20
|
end
|
129
21
|
|
22
|
+
def exist?() MigrationState.exist? end
|
130
23
|
|
131
|
-
def
|
132
|
-
|
24
|
+
def create()
|
25
|
+
files = Share.cp "migration/*", MIGRATION_DIR
|
26
|
+
state = MigrationState.new.write(version: version, base_version: base_version)
|
27
|
+
Git.add files, state.path
|
28
|
+
self
|
133
29
|
end
|
134
30
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
31
|
+
def update(version)
|
32
|
+
state = MigrationState.new.read
|
33
|
+
@version = state.version = version
|
34
|
+
state.write
|
35
|
+
Git.add state.path
|
36
|
+
self
|
139
37
|
end
|
140
38
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
File.open(features_sql, "a") { |f|
|
146
|
-
paths.map { |path|
|
147
|
-
f.puts "\\cd #{File.basename(path)}"
|
148
|
-
f.puts "\\i migrate.sql" }
|
149
|
-
f.puts "\\cd .."
|
150
|
-
f.puts
|
151
|
-
}
|
152
|
-
Git.add(features_yml)
|
153
|
-
Git.add(features_sql)
|
39
|
+
def migrate(database)
|
40
|
+
base_version or raise Internal, "Can't migrate from nil to #{version}"
|
41
|
+
version or raise Internal, "Can't migrate from #{base_version} to nil"
|
42
|
+
MigrationBuilder.new(database, MIGRATION_DIR).build
|
154
43
|
end
|
155
44
|
end
|
156
45
|
|
157
46
|
class FeatureMigration < Migration
|
158
|
-
|
159
|
-
FILES = [
|
160
|
-
MIGRATE_SQL = "migrate.sql",
|
161
|
-
DIFF_SQL = "diff.sql"
|
162
|
-
]
|
47
|
+
attr_reader :feature
|
163
48
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
attr_reader :migrate_sql
|
169
|
-
attr_reader :diff_sql
|
170
|
-
|
171
|
-
def features_yml() release_migration.features_yml end
|
172
|
-
def features_sql() release_migration.features_sql end
|
173
|
-
def migrations_sql() release_migration.migrations_sql end
|
174
|
-
def release_diff_sql() release_migration.diff_sql end
|
175
|
-
|
176
|
-
def files() [migrate_sql, diff_sql] end
|
177
|
-
def release_files() release_migration.files end
|
178
|
-
|
179
|
-
def feature_versions() release_migration.feature_versions end
|
180
|
-
def feature_paths() release_migration.feature_paths end
|
181
|
-
|
182
|
-
def initialize(path, name: File.basename(path))
|
183
|
-
Migration.feature?(path) or raise "Expected a feature path, got #{path}"
|
184
|
-
super(path, FEATURE_TMPL_DIR)
|
185
|
-
@name = name
|
186
|
-
@version = Migration.version(path)
|
187
|
-
@release_migration = ReleaseMigration.new(File.dirname(path))
|
188
|
-
@migrate_sql = File.join(path, MIGRATE_SQL)
|
189
|
-
@diff_sql = File.join(path, DIFF_SQL)
|
49
|
+
def initialize(feature, base_version)
|
50
|
+
super(base_version, base_version)
|
51
|
+
@feature = feature
|
190
52
|
end
|
191
53
|
|
192
|
-
def
|
193
|
-
|
194
|
-
|
195
|
-
|
54
|
+
def self.load
|
55
|
+
migration_state = MigrationState.load
|
56
|
+
feature_state = FeatureState.load
|
57
|
+
FeatureMigration.new(feature_state.feature, migration_state.base_version)
|
196
58
|
end
|
197
59
|
|
198
|
-
def
|
199
|
-
def prepare()
|
200
|
-
release_migration.present? || release_migration.prepare
|
60
|
+
def create()
|
201
61
|
super
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
release_migration.include_feature(migration, append: false)
|
207
|
-
end
|
208
|
-
|
209
|
-
def self.files(path)
|
210
|
-
release_files + FILES.map { |file| File.join(path, file) }
|
211
|
-
end
|
212
|
-
|
213
|
-
def self.release_files(path)
|
214
|
-
ReleaseMigration.files(path)
|
215
|
-
end
|
216
|
-
|
217
|
-
def dump
|
218
|
-
super {
|
219
|
-
puts "name: #{name}"
|
220
|
-
puts "release_migration: #{path}"
|
221
|
-
}
|
62
|
+
files = Share.cp "migration", File.join(MIGRATION_DIR, feature)
|
63
|
+
state = FeatureState.write(feature: feature)
|
64
|
+
Git.add files, state.path
|
65
|
+
self
|
222
66
|
end
|
223
67
|
end
|
224
68
|
end
|
225
69
|
|
226
70
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|