schema-evolution-manager 0.9.57 → 0.9.58
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/README.md +1 -1
- data/bin/sem-apply +10 -0
- data/bin/sem-dist +2 -0
- data/bin/sem-info +9 -0
- data/lib/schema-evolution-manager/migration_file.rb +13 -12
- data/lib/schema-evolution-manager/scripts.rb +10 -0
- data/lib/schema-evolution-manager/sem_info.rb +26 -0
- data/lib/schema-evolution-manager/versions.rb +62 -0
- data/lib/schema-evolution-manager.rb +1 -0
- data/scripts/20260717-000000.sql +27 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a59b34cee82db218b0008110f365a64567ec8dcbaa9f59a0fcd30acce9586f0
|
|
4
|
+
data.tar.gz: 5a6066de334be67b4915debfc0670e27c56d40eeb008c60bbc2348da8be37603
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 21fe48093f5386013d3eed04dec8a9ebd512a670608a6b5a663b2c68c0b9af3fedb910577a58c3e3abe8098d3e66ac76cc25019cad0ebaa47e22014aa061c10c
|
|
7
|
+
data.tar.gz: 447e2949614cb4f2c332f317596195b796ff811016d8d6259b42a351e59b8a04c324b8bbb8bae3e8248b9a521bb3ba719db4d35676ea2ad1ebbc011ce6984caf
|
data/README.md
CHANGED
|
@@ -133,7 +133,7 @@ There are three ways to install schema evolution manager:
|
|
|
133
133
|
|
|
134
134
|
git clone git@github.com:mbryzek/schema-evolution-manager.git
|
|
135
135
|
cd schema-evolution-manager
|
|
136
|
-
git checkout 0.9.
|
|
136
|
+
git checkout 0.9.58
|
|
137
137
|
ruby ./configure.rb
|
|
138
138
|
sudo ./install.rb
|
|
139
139
|
|
data/bin/sem-apply
CHANGED
|
@@ -34,6 +34,16 @@ begin
|
|
|
34
34
|
if count == 0
|
|
35
35
|
puts " All scripts have been previously applied"
|
|
36
36
|
end
|
|
37
|
+
|
|
38
|
+
version_file = "./VERSION"
|
|
39
|
+
if !util.dry_run? && File.exist?(version_file)
|
|
40
|
+
version = IO.read(version_file).strip
|
|
41
|
+
if !version.empty?
|
|
42
|
+
if SchemaEvolutionManager::Versions.new(db).record!(version)
|
|
43
|
+
puts " Recorded schema version #{version}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
37
47
|
rescue SchemaEvolutionManager::ScriptError => e
|
|
38
48
|
puts ""
|
|
39
49
|
puts "ERROR applying script: %s" % e.path
|
data/bin/sem-dist
CHANGED
|
@@ -55,6 +55,7 @@ else
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
SchemaEvolutionManager::Library.git_assert_tag_exists(tag)
|
|
58
|
+
SchemaEvolutionManager::Versions.validate_version!(tag)
|
|
58
59
|
|
|
59
60
|
changes = SchemaEvolutionManager::Library.git_changes(:tag => tag)
|
|
60
61
|
repo_path = SchemaEvolutionManager::Library.normalize_path(`pwd`.strip)
|
|
@@ -77,6 +78,7 @@ SchemaEvolutionManager::Library.with_temp_file do |tmp|
|
|
|
77
78
|
|
|
78
79
|
SchemaEvolutionManager::Library.system_or_error("cp -R %s %s" % [File.join(repo_path, "scripts"), File.join(tmpdir, "scripts")])
|
|
79
80
|
File.open(File.join(tmpdir, "CHANGES"), "w") { |out| out << changes }
|
|
81
|
+
File.open(File.join(tmpdir, "VERSION"), "w") { |out| out << tag }
|
|
80
82
|
|
|
81
83
|
# Clear extended attributes to avoid tar warnings on Linux
|
|
82
84
|
SchemaEvolutionManager::Library.system_or_error("xattr -cr #{tmpdir} 2>/dev/null || true")
|
data/bin/sem-info
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
# Output information on the next tag. Defaults to micro
|
|
18
18
|
# version increment from latest tag
|
|
19
19
|
#
|
|
20
|
+
# sem-info db version --url <database url>
|
|
21
|
+
# Display the latest recorded deployed schema version
|
|
22
|
+
#
|
|
23
|
+
# sem-info db scripts --url <database url>
|
|
24
|
+
# Display the applied script filenames
|
|
25
|
+
#
|
|
20
26
|
# Description
|
|
21
27
|
#
|
|
22
28
|
# Utility script to pull information from the schema-evolution-manager
|
|
@@ -31,6 +37,9 @@ command = ARGV.shift.to_s.strip
|
|
|
31
37
|
if command == "tag"
|
|
32
38
|
puts SchemaEvolutionManager::SemInfo.tag(ARGV)
|
|
33
39
|
|
|
40
|
+
elsif command == "db"
|
|
41
|
+
SchemaEvolutionManager::SemInfo.db(ARGV)
|
|
42
|
+
|
|
34
43
|
elsif command == "version"
|
|
35
44
|
puts SchemaEvolutionManager::SemInfo.version(ARGV)
|
|
36
45
|
|
|
@@ -77,21 +77,22 @@ module SchemaEvolutionManager
|
|
|
77
77
|
# -- sem.attribute.name = value
|
|
78
78
|
#
|
|
79
79
|
# and yields each matching row with |name, value|
|
|
80
|
+
#
|
|
81
|
+
# Read in binary mode so the shell locale (e.g. LANG=C in a minimal
|
|
82
|
+
# Docker image) cannot tag UTF-8 bytes as US-ASCII and cause String#strip
|
|
83
|
+
# to raise Encoding::CompatibilityError. Attribute lines are strict ASCII;
|
|
84
|
+
# scrubbing invalid bytes on non-attribute lines is harmless.
|
|
80
85
|
def each_property
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
rescue Encoding::InvalidByteSequenceError
|
|
90
|
-
# Ignore - attributes must be in ascii
|
|
86
|
+
File.foreach(path, mode: "rb") do |raw|
|
|
87
|
+
line = raw.force_encoding("UTF-8")
|
|
88
|
+
line = line.scrub unless line.valid_encoding?
|
|
89
|
+
stripped = line.strip
|
|
90
|
+
if stripped.match(/^\-\-\s+sem\.attribute\./)
|
|
91
|
+
stripped.sub!(/^\-\-\s+sem\.attribute\./, '')
|
|
92
|
+
name, value = stripped.split(/\=/, 2).map(&:strip)
|
|
93
|
+
yield name, value
|
|
91
94
|
end
|
|
92
95
|
end
|
|
93
|
-
rescue Encoding::InvalidByteSequenceError
|
|
94
|
-
# Ignore - file must be in ascii in order to parse attributes
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
end
|
|
@@ -79,6 +79,16 @@ module SchemaEvolutionManager
|
|
|
79
79
|
@db.psql_command(command)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
# Returns the sorted list of filenames already applied to this database.
|
|
83
|
+
def applied
|
|
84
|
+
if @db.schema_schema_evolution_manager_exists?
|
|
85
|
+
sql = "select filename from %s.%s order by filename" % [Db.schema_name, @table_name]
|
|
86
|
+
@db.psql_command(sql).strip.split
|
|
87
|
+
else
|
|
88
|
+
[]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
82
92
|
private
|
|
83
93
|
# Fetch the list of scripts that have already been applied to this
|
|
84
94
|
# database.
|
|
@@ -6,6 +6,32 @@ module SchemaEvolutionManager
|
|
|
6
6
|
SchemaEvolutionManager::SemVersion::VERSION.dup
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
def SemInfo.db(args)
|
|
10
|
+
valid = ['version', 'scripts']
|
|
11
|
+
subcommand = args.shift.to_s.strip
|
|
12
|
+
|
|
13
|
+
if !valid.include?(subcommand)
|
|
14
|
+
if subcommand.empty?
|
|
15
|
+
puts "ERROR: Missing db subcommand. Must be one of: %s" % valid.join(", ")
|
|
16
|
+
else
|
|
17
|
+
puts "ERROR: Invalid db subcommand[%s]. Must be one of: %s" % [subcommand, valid.join(", ")]
|
|
18
|
+
end
|
|
19
|
+
exit(4)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
db_args = SchemaEvolutionManager::Args.new(args.join(" "), :optional => ['url', 'host', 'user', 'name', 'port', 'set'])
|
|
23
|
+
db = SchemaEvolutionManager::Db.from_args(db_args)
|
|
24
|
+
|
|
25
|
+
if subcommand == "version"
|
|
26
|
+
version = SchemaEvolutionManager::Versions.new(db).latest
|
|
27
|
+
puts version unless version.nil?
|
|
28
|
+
elsif subcommand == "scripts"
|
|
29
|
+
SchemaEvolutionManager::Scripts.new(db, SchemaEvolutionManager::Scripts::SCRIPTS).applied.each do |filename|
|
|
30
|
+
puts filename
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
9
35
|
def SemInfo.tag(args)
|
|
10
36
|
valid = ['exists', 'latest', 'next']
|
|
11
37
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module SchemaEvolutionManager
|
|
2
|
+
|
|
3
|
+
# Records and reads the released repository version applied to a database.
|
|
4
|
+
# The version is written at apply time and the latest row (max id) is the
|
|
5
|
+
# deployed schema version.
|
|
6
|
+
class Versions
|
|
7
|
+
|
|
8
|
+
if !defined?(MAX_VERSION_LENGTH)
|
|
9
|
+
MAX_VERSION_LENGTH = 100
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Validates a version string. Raises RuntimeError with an actionable
|
|
13
|
+
# message if invalid; returns the version if valid.
|
|
14
|
+
def Versions.validate_version!(version)
|
|
15
|
+
Preconditions.assert_class(version, String)
|
|
16
|
+
stripped = version.strip
|
|
17
|
+
if stripped.empty?
|
|
18
|
+
raise "Version cannot be blank"
|
|
19
|
+
end
|
|
20
|
+
if stripped.length > MAX_VERSION_LENGTH
|
|
21
|
+
raise "Version '%s' is %d chars; max %d" % [stripped, stripped.length, MAX_VERSION_LENGTH]
|
|
22
|
+
end
|
|
23
|
+
# Strict whitelist: letters, digits, and . _ + - only. Covers semver
|
|
24
|
+
# (including pre-release/build metadata) and rejects anything unsafe
|
|
25
|
+
# (whitespace, quotes, SQL metacharacters, etc.).
|
|
26
|
+
unless stripped.match(/\A[A-Za-z0-9._+-]+\z/)
|
|
27
|
+
raise "Version '%s' is invalid; only letters, digits, and . _ + - are allowed" % stripped
|
|
28
|
+
end
|
|
29
|
+
stripped
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(db)
|
|
33
|
+
@db = Preconditions.assert_class(db, Db)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Inserts the version only if it differs from the latest recorded value.
|
|
37
|
+
# Returns true if a row was inserted, false if unchanged.
|
|
38
|
+
def record!(version)
|
|
39
|
+
clean = Versions.validate_version!(version)
|
|
40
|
+
return false if latest == clean
|
|
41
|
+
@db.psql_command("insert into %s.versions (version) values ('%s')" % [Db.schema_name, clean])
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Latest recorded version, or nil if none recorded / table absent.
|
|
46
|
+
def latest
|
|
47
|
+
return nil unless versions_table_exists?
|
|
48
|
+
value = @db.psql_command("select version from %s.versions order by id desc limit 1" % Db.schema_name).strip
|
|
49
|
+
value.empty? ? nil : value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def versions_table_exists?
|
|
55
|
+
return false unless @db.schema_schema_evolution_manager_exists?
|
|
56
|
+
sql = "select count(*) from information_schema.tables where table_schema = '%s' and table_name = 'versions'" % Db.schema_name
|
|
57
|
+
@db.psql_command(sql).to_i > 0
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
@@ -18,6 +18,7 @@ load File.join(lib_dir, 'ask.rb')
|
|
|
18
18
|
load File.join(lib_dir, 'version.rb')
|
|
19
19
|
load File.join(lib_dir, 'args.rb')
|
|
20
20
|
load File.join(lib_dir, 'scripts.rb')
|
|
21
|
+
load File.join(lib_dir, 'versions.rb')
|
|
21
22
|
load File.join(lib_dir, 'connection_data.rb')
|
|
22
23
|
load File.join(lib_dir, 'db.rb')
|
|
23
24
|
load File.join(lib_dir, 'apply_util.rb')
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- Records the released repository version applied to this database. The latest
|
|
2
|
+
-- row (max id) is the deployed schema version. Written at apply time by sem-apply
|
|
3
|
+
-- from the VERSION file baked into the distribution by sem-dist.
|
|
4
|
+
--
|
|
5
|
+
-- Existence-guarded via to_regclass so the script is idempotent. The original
|
|
6
|
+
-- table_exists() helper cannot be used here: it is dropped at the end of the
|
|
7
|
+
-- first bootstrap script (scripts/20130318-105434.sql).
|
|
8
|
+
|
|
9
|
+
set search_path to schema_evolution_manager;
|
|
10
|
+
|
|
11
|
+
do $$
|
|
12
|
+
begin
|
|
13
|
+
if to_regclass('schema_evolution_manager.versions') is null then
|
|
14
|
+
|
|
15
|
+
create table schema_evolution_manager.versions (
|
|
16
|
+
id bigserial,
|
|
17
|
+
version varchar(100) not null,
|
|
18
|
+
created_at timestamp with time zone default now() not null
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
alter table schema_evolution_manager.versions add constraint versions_id_pk primary key(id);
|
|
22
|
+
|
|
23
|
+
comment on table schema_evolution_manager.versions is
|
|
24
|
+
'Records the released repository version applied to this database. Latest row (max id) is the deployed schema version.';
|
|
25
|
+
|
|
26
|
+
end if;
|
|
27
|
+
end$$;
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: schema-evolution-manager
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.58
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Bryzek
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-07-17 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: '["Michael Bryzek"]'
|
|
13
13
|
email: mbryzek@alum.mit.edu
|
|
@@ -48,8 +48,10 @@ files:
|
|
|
48
48
|
- lib/schema-evolution-manager/sem_version.rb
|
|
49
49
|
- lib/schema-evolution-manager/template.rb
|
|
50
50
|
- lib/schema-evolution-manager/version.rb
|
|
51
|
+
- lib/schema-evolution-manager/versions.rb
|
|
51
52
|
- scripts/20130318-105434.sql
|
|
52
53
|
- scripts/20130318-105456.sql
|
|
54
|
+
- scripts/20260717-000000.sql
|
|
53
55
|
- template/README.md
|
|
54
56
|
- template/dev.rb
|
|
55
57
|
homepage: https://github.com/gilt/schema-evolution-manager
|