xot 0.4.0 → 0.5.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.
@@ -0,0 +1,86 @@
1
+ require_relative 'helper'
2
+ require 'tmpdir'
3
+ require 'xot/rake/changelog'
4
+
5
+
6
+ class TestRakeChangeLog < Test::Unit::TestCase
7
+
8
+ TEXT = <<~END
9
+ # app ChangeLog
10
+
11
+
12
+ ## [v1.2.3] - 2026-10-12
13
+
14
+ - Add a "quoted" thing
15
+ - Fix another
16
+
17
+
18
+ ## [ v1.2 ] - 2026-10-01
19
+
20
+ - First
21
+ END
22
+
23
+ def changelog(text = TEXT, &block)
24
+ Dir.mktmpdir do |dir|
25
+ path = File.join dir, 'ChangeLog.md'
26
+ File.write path, text
27
+ block.call Xot::ChangeLog.new(path)
28
+ end
29
+ end
30
+
31
+ def test_add()
32
+ changelog do |log|
33
+ log.add '1.2.4', ['Add more', 'Fix it'], date: Time.new(2026, 10, 13)
34
+ assert_equal <<~END, File.read(log.path)
35
+ # app ChangeLog
36
+
37
+
38
+ ## [v1.2.4] - 2026-10-13
39
+
40
+ - Add more
41
+ - Fix it
42
+
43
+
44
+ ## [v1.2.3] - 2026-10-12
45
+
46
+ - Add a "quoted" thing
47
+ - Fix another
48
+
49
+
50
+ ## [ v1.2 ] - 2026-10-01
51
+
52
+ - First
53
+ END
54
+ assert_equal %w[1.2.4 1.2.3 1.2], log.versions
55
+ end
56
+
57
+ changelog "# app ChangeLog\n" do |log|
58
+ log.add 'v0.1.0', ['First'], date: Time.new(2026, 10, 13)
59
+ assert_equal(
60
+ "# app ChangeLog\n\n\n## [v0.1.0] - 2026-10-13\n\n- First\n",
61
+ File.read(log.path))
62
+ assert_equal %w[0.1.0], log.versions
63
+ end
64
+
65
+ # before the section of a version, below one of none
66
+ changelog "# app ChangeLog\n\n\n## Unreleased\n\n- Next\n\n\n## [v0.1.0] - 2026-10-01\n\n- First\n" do |log|
67
+ log.add '0.1.1', ['Add a'], date: Time.new(2026, 10, 13)
68
+ assert_equal(
69
+ "# app ChangeLog\n\n\n## Unreleased\n\n- Next\n\n\n" +
70
+ "## [v0.1.1] - 2026-10-13\n\n- Add a\n\n\n## [v0.1.0] - 2026-10-01\n\n- First\n",
71
+ File.read(log.path))
72
+ end
73
+ end
74
+
75
+ def test_sections()
76
+ changelog do |log|
77
+ assert_equal %w[1.2.3 1.2], log.versions
78
+ assert_equal "- Add a \"quoted\" thing\n- Fix another", log.changes('1.2.3')
79
+ assert_equal "- Add a \"quoted\" thing\n- Fix another", log.changes('v1.2.3')
80
+ assert_equal '- First', log.changes('1.2')
81
+ assert_nil log.changes('9.9')
82
+ end
83
+ changelog("# app ChangeLog\n") {|log| assert_empty log.versions}
84
+ end
85
+
86
+ end# TestRakeChangeLog
@@ -0,0 +1,79 @@
1
+ require_relative 'helper'
2
+ require 'xot/rake'
3
+
4
+
5
+ class TestRakeGitHub < Test::Unit::TestCase
6
+
7
+ include Rake::DSL
8
+ include Xot::Rake
9
+
10
+ def test_dependency_names()
11
+ Dir.mktmpdir do |dir|
12
+ Dir.chdir dir do
13
+ File.write 'Rakefile', <<~END
14
+ require 'xot/rake'
15
+
16
+ require 'xot/extension'
17
+ require "rucy/extension"
18
+ require 'reflex/extension'
19
+ # require 'rays/extension'
20
+ END
21
+ FileUtils.mkdir_p 'lib/reflex'
22
+ FileUtils.touch 'lib/reflex/extension.rb'
23
+ assert_equal %w[xot rucy], dependency_names # but the one of this
24
+ end
25
+ end
26
+ end
27
+
28
+ def test_setup_monorepo()
29
+ Dir.mktmpdir do |dir|
30
+ FileUtils.mkdir_p %W[#{dir}/app #{dir}/all]
31
+ Dir.chdir "#{dir}/app" do
32
+ commands = []
33
+ win32 = false
34
+ define_singleton_method(:sh) {|*args| commands << args}
35
+ define_singleton_method(:rm_rf) {|*args| commands << [:rm_rf, *args]}
36
+ define_singleton_method(:cp_r) {|*args| commands << [:cp_r, *args]}
37
+ define_singleton_method(:win32?) {win32} # whatever the platform running this
38
+
39
+ setup_monorepo 'v1', 'xot', 'rays'
40
+ assert_include commands,
41
+ ['git', 'fetch', '-q', '--depth', '1', 'https://github.com/xord/all.git', 'v1']
42
+ assert_include commands, ['ln', '-snf', 'all/rays', '../rays']
43
+ assert_equal ['cd ../all/xot && rake ext', 'cd ../all/rays && rake ext'],
44
+ commands.map {_1.first.strip}.grep(/rake ext$/)
45
+
46
+ commands.clear
47
+ win32 = true
48
+ setup_monorepo 'v1', 'rays'
49
+ assert_equal [
50
+ 'cd ../all/rays && rake ext',
51
+ [:rm_rf, '../rays'],
52
+ [:cp_r, '../all/rays', '../rays']
53
+ ], commands.last(3).map {_1.first.is_a?(String) ? _1.first.strip : _1}
54
+
55
+ assert_raise(RuntimeError) {setup_monorepo 'master'}
56
+ end
57
+ end
58
+ end
59
+
60
+ def test_setup_mesa()
61
+ Dir.mktmpdir do |dir|
62
+ commands = []
63
+ define_singleton_method(:sh) {|*args| commands << args}
64
+ env, ENV['GITHUB_ENV'] = ENV['GITHUB_ENV'], File.join(dir, 'env')
65
+
66
+ setup_mesa '1.2.3'
67
+ assert_equal [
68
+ ['curl', '-sSfL', '-o', 'mesa.7z',
69
+ 'https://github.com/pal1000/mesa-dist-win/releases/download/1.2.3/mesa3d-1.2.3-release-msvc.7z'],
70
+ ['7z', 'x', 'mesa.7z'],
71
+ ['powershell.exe', '.\\systemwidedeploy.cmd', '1']
72
+ ], commands
73
+ assert_equal "GALLIUM_DRIVER=llvmpipe\n", File.read(ENV['GITHUB_ENV'])
74
+ ensure
75
+ ENV['GITHUB_ENV'] = env
76
+ end
77
+ end
78
+
79
+ end# TestRakeGitHub
@@ -0,0 +1,119 @@
1
+ require_relative 'helper'
2
+ require 'xot/rake'
3
+
4
+
5
+ class TestRakeUtil < Test::Unit::TestCase
6
+
7
+ include Rake::DSL
8
+ include Xot::Rake
9
+ include GitRepo
10
+
11
+ ChangeLog = Xot::ChangeLog
12
+
13
+ def test_install_hooks()
14
+ repo do
15
+ install_hooks
16
+ assert File.file?( '.git/hooks/pre-commit')
17
+ assert File.executable?('.git/hooks/pre-commit') unless win32?
18
+
19
+ log = ChangeLog.new
20
+ log.add '0.2.0', ['Add a'], date: Time.new(2026, 10, 12)
21
+ git! 'add', 'ChangeLog.md'
22
+ git! 'commit', '-q', '-m', 'Update ChangeLog'
23
+ assert_equal '0.2.0', File.read('VERSION')
24
+ assert_equal '0.2.0', IO.popen(%w[git show HEAD:VERSION], &:read)
25
+ assert_empty IO.popen(%w[git status --porcelain], &:read)
26
+
27
+ log.add '1', ['Add c'], date: Time.new(2026, 10, 12)
28
+ git! 'add', 'ChangeLog.md'
29
+ git! 'commit', '-q', '-m', 'Update ChangeLog'
30
+ assert_equal '1', File.read('VERSION')
31
+
32
+ log.add '0.3.0', ['Add b'], date: Time.new(2026, 10, 13)
33
+ File.write 'VERSION', '9.9.9'
34
+ git! 'add', 'ChangeLog.md'
35
+ assert !system('git', 'commit', '-q', '-m', 'Update ChangeLog', out: File::NULL, err: File::NULL)
36
+ assert_equal '9.9.9', File.read('VERSION')
37
+ end
38
+ end
39
+
40
+ def test_version_changes()
41
+ repo do
42
+ assert_empty version_changes
43
+
44
+ commit "Add a\n\nwith the body", 'a.rb' => ''
45
+ commit 'Fix b', 'sub/b.rb' => ''
46
+ assert_equal ['Add a', 'Fix b'], version_changes
47
+ assert_equal ['Fix b'], version_changes('sub')
48
+
49
+ commit 'Bump the version', 'VERSION' => '0.2.9'
50
+ assert_empty version_changes
51
+
52
+ # a branch merged, by the merge commit only, the line after 'Merge
53
+ # branch ...' of which tells the changes of the branch
54
+ git! 'checkout', '-q', '-b', 'topic'
55
+ commit 'Add c', 'c.rb' => ''
56
+ commit 'Add d', 'sub/d.rb' => ''
57
+ git! 'checkout', '-q', '-'
58
+ commit 'Fix e', 'e.rb' => ''
59
+ git! 'merge', '-q', '--no-ff',
60
+ '-m', "Merge branch 'topic'\n\nAdd c and d\n\nwith the body",
61
+ 'topic'
62
+ assert_equal ['Fix e', 'Add c and d'], version_changes
63
+ assert_equal ['Fix b', 'Add c and d'], version_changes('sub') # with no VERSION
64
+
65
+ # the title of one with nothing after 'Merge branch ...', or of another
66
+ git! 'checkout', '-q', '-b', 'topic2'
67
+ commit 'Add f', 'f.rb' => ''
68
+ git! 'checkout', '-q', '-'
69
+ git! 'merge', '-q', '--no-ff', '-m', "Merge branch 'topic2'", 'topic2'
70
+ git! 'checkout', '-q', '-b', 'topic3'
71
+ commit 'Add g', 'g.rb' => ''
72
+ git! 'checkout', '-q', '-'
73
+ git! 'merge', '-q', '--no-ff', '-m', "Add g\n\nwith the body", 'topic3'
74
+ assert_equal ["Merge branch 'topic2'", 'Add g'], version_changes.last(2)
75
+ end
76
+ end
77
+
78
+ def test_next_version()
79
+ repo do
80
+ # the third number, of three numbers
81
+ {
82
+ '0.1.0' => '0.1.1',
83
+ '0.2.9' => '0.2.10',
84
+ '1.2' => '1.2.1',
85
+ '1' => '1.0.1',
86
+ '1.2.3.4' => '1.2.4'
87
+ }.each do |version, next_|
88
+ File.write 'VERSION', version
89
+ assert_equal next_, next_version, version
90
+ end
91
+ end
92
+ end
93
+
94
+ def test_tag_versions()
95
+ repo do
96
+ commit 'Add a', 'a.rb' => ''
97
+ ChangeLog.new.add '0.1.1', ['Add a'], date: Time.new(2026, 10, 12)
98
+ commit 'Version 0.1.1', 'VERSION' => '0.1.1'
99
+ commit 'Add b', 'b.rb' => ''
100
+
101
+ verbose(false) {tag_versions}
102
+ assert_equal %w[v0.1.0 v0.1.1], IO.popen(%w[git tag], &:read).lines(chomp: true)
103
+ assert_equal(
104
+ "- Add a\n",
105
+ IO.popen(%w[git tag -l --format=%(contents) v0.1.1], &:read).sub(/\n+\z/, "\n"))
106
+ assert_equal(
107
+ IO.popen(%w[git rev-parse HEAD~1], &:read),
108
+ IO.popen(%w[git rev-list -n 1 v0.1.1], &:read))
109
+
110
+ # the versions back to the one tagged already, which is not tagged again
111
+ commit 'Version 0.1.2', 'VERSION' => '0.1.2'
112
+ verbose(false) {tag_versions}
113
+ assert_equal(
114
+ %w[v0.1.0 v0.1.1 v0.1.2],
115
+ IO.popen(%w[git tag], &:read).lines(chomp: true))
116
+ end
117
+ end
118
+
119
+ end# TestRakeUtil
data/xot.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
 
12
12
  ext = Xot::Extension
13
13
  name = ext.name true
14
- rdocs = glob.call *%w[README]
14
+ rdocs = glob.call(*%w[README])
15
15
 
16
16
  s.name = name
17
17
  s.version = ext.version
@@ -25,6 +25,9 @@ Gem::Specification.new do |s|
25
25
  s.platform = Gem::Platform::RUBY
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
+ s.add_dependency 'rake'
29
+ s.add_dependency 'ostruct'
30
+
28
31
  s.files = `git ls-files`.split $/
29
32
  s.executables = s.files.grep(%r{^bin/}) {|f| File.basename f}
30
33
  s.test_files = s.files.grep %r{^(test|spec|features)/}
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - xordog
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-04 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2026-11-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ostruct
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  description: This library include some useful utility classes and functions for development
14
42
  with C++.
15
43
  email: xordog@gmail.com
@@ -19,10 +47,10 @@ extensions:
19
47
  extra_rdoc_files: []
20
48
  files:
21
49
  - ".github/PULL_REQUEST_TEMPLATE.md"
50
+ - ".github/actions/setup-monorepo/action.yml"
22
51
  - ".github/workflows/release-gem.yml"
23
52
  - ".github/workflows/tag.yml"
24
53
  - ".github/workflows/test.yml"
25
- - ".github/workflows/utils.rb"
26
54
  - CONTRIBUTING.md
27
55
  - ChangeLog.md
28
56
  - Gemfile
@@ -57,7 +85,10 @@ files:
57
85
  - lib/xot/invoker.rb
58
86
  - lib/xot/rake.rb
59
87
  - lib/xot/rake/alias_task.rb
88
+ - lib/xot/rake/changelog.rb
60
89
  - lib/xot/rake/escalation.rb
90
+ - lib/xot/rake/github.rb
91
+ - lib/xot/rake/hooks/pre-commit
61
92
  - lib/xot/rake/util.rb
62
93
  - lib/xot/setter.rb
63
94
  - lib/xot/test.rb
@@ -78,6 +109,9 @@ files:
78
109
  - test/test_inspectable.rb
79
110
  - test/test_invoker.rb
80
111
  - test/test_rake.rb
112
+ - test/test_rake_changelog.rb
113
+ - test/test_rake_github.rb
114
+ - test/test_rake_util.rb
81
115
  - test/test_setter.rb
82
116
  - test/test_tester.rb
83
117
  - test/test_universal_accessor.rb
@@ -102,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
136
  - !ruby/object:Gem::Version
103
137
  version: '0'
104
138
  requirements: []
105
- rubygems_version: 3.4.19
139
+ rubygems_version: 3.5.22
106
140
  signing_key:
107
141
  specification_version: 4
108
142
  summary: A Utility library for C++ developemt.
@@ -117,6 +151,9 @@ test_files:
117
151
  - test/test_inspectable.rb
118
152
  - test/test_invoker.rb
119
153
  - test/test_rake.rb
154
+ - test/test_rake_changelog.rb
155
+ - test/test_rake_github.rb
156
+ - test/test_rake_util.rb
120
157
  - test/test_setter.rb
121
158
  - test/test_tester.rb
122
159
  - test/test_universal_accessor.rb
@@ -1,127 +0,0 @@
1
- require 'shellwords'
2
-
3
- ALL_REPO = 'xord/all'
4
- ALL_DIR = '../all'
5
- ALL_FETCH_DEPTH = 100
6
-
7
- RENAMES = {reflex: 'reflexion'}
8
-
9
- def sh(cmd)
10
- puts cmd
11
- system cmd
12
- end
13
-
14
- def setup_dependencies(only: nil)
15
- gemspec_path = `git ls-files`.lines(chomp: true).find {|l| l =~ /\.gemspec$/}
16
- return unless gemspec_path
17
-
18
- gemspec = File.read gemspec_path
19
- name = File.basename gemspec_path, '.gemspec'
20
-
21
- exts = File.readlines('Rakefile')
22
- .map {|l| l[%r|^\s*require\W+([\w\-\_]+)/extension\W+$|, 1]}
23
- .compact
24
- .reject {|ext| ext == name}
25
- exts = exts & [only].flatten.map(&:to_s) if only
26
- return if exts.empty?
27
-
28
- unless setup_dependencies_via_monorepo(exts)
29
- setup_dependencies_via_each_repo_by_version(gemspec, exts)
30
- end
31
-
32
- exts.each {|ext| sh %( cd ../#{ext} && rake ext )}
33
- end
34
-
35
- def setup_dependencies_via_monorepo(exts)
36
- return false unless checkout_monorepo
37
- exts.each {|ext| sh %( ln -snf all/#{ext} ../#{ext} )}
38
- true
39
- end
40
-
41
- def checkout_monorepo()
42
- uuid = `git log -1 --format=%B`[/^\[\[([0-9a-fA-F-]+)\]\]$/, 1]
43
- return false unless uuid
44
-
45
- commit = setup_monorepo uuid
46
- return false unless commit
47
-
48
- Dir.chdir(ALL_DIR) {sh %( git checkout -q #{commit} )}
49
- true
50
- end
51
-
52
- def setup_monorepo(uuid)
53
- unless File.directory? ALL_DIR
54
- url = "https://github.com/#{ALL_REPO}.git"
55
- sh %( git clone --no-tags --depth #{ALL_FETCH_DEPTH} #{url} #{ALL_DIR} )
56
- end
57
- loop do
58
- commit = find_monorepo_commit uuid
59
- return commit if commit
60
-
61
- deepened = Dir.chdir ALL_DIR do
62
- before = `git rev-list --count HEAD`.to_i
63
- sh %( git fetch --deepen #{ALL_FETCH_DEPTH} )
64
- `git rev-list --count HEAD`.to_i > before
65
- end
66
- return nil unless deepened
67
- end
68
- end
69
-
70
- def find_monorepo_commit(uuid)
71
- Dir.chdir ALL_DIR do
72
- out = `git log origin/HEAD -F --grep="[[#{uuid}]]" --format=%H -1`.strip
73
- out.empty? ? nil : out
74
- end
75
- end
76
-
77
- def setup_dependencies_via_each_repo_by_version(gemspec, exts)
78
- exts.each do |ext|
79
- gem = RENAMES[ext.to_sym].then {|s| s || ext}
80
- ver = gemspec[/add_dependency.*['"]#{gem}['"].*['"]\s*~>\s*([\d\.]+)\s*['"]/, 1]
81
- opts = '-c advice.detachedHead=false --depth 1'
82
- clone = "git clone #{opts} https://github.com/xord/#{ext}.git ../#{ext}"
83
-
84
- # 'rake subtree:push' pushes all subrepos, so cloning by new tag
85
- # often fails before tagging each new tag
86
- sh %( #{clone} --branch v#{ver} || #{clone} )
87
- end
88
- end
89
-
90
- def tag_versions()
91
- changes = changelogs
92
- tags = `git tag`.lines chomp: true
93
- vers = `git log --oneline ./VERSION`
94
- .lines(chomp: true)
95
- .map {|line| line.split.first[/^\w+$/]}
96
- .map {|sha| [`git cat-file -p #{sha}:./VERSION 2>/dev/null`[/[\d\.]+/], sha]}
97
- .select {|ver, sha| ver && sha}
98
- .reverse
99
- .to_h
100
-
101
- vers.to_a.reverse.each do |ver, sha|
102
- tag = "v#{ver}"
103
- break if tags.include?(tag)
104
- sh %( git tag -a -m \"#{changes[tag]&.gsub '"', '\\"'}\" #{tag} #{sha} )
105
- end
106
- end
107
-
108
- def release(*paths)
109
- tag = ENV['GITHUB_REF']&.sub(%r|^refs/tags/|, '') || raise('GITHUB_REF tag not set')
110
- notes = (changelogs[tag] || '').shellescape
111
- paths = paths.flatten.join ' '
112
-
113
- sh(%( gh release create #{tag} #{paths} --notes #{notes} )) ||
114
- sh(%( gh release upload #{tag} #{paths} --clobber )) ||
115
- raise('failed to upload to releases')
116
- end
117
-
118
- def changelogs()
119
- File.read('ChangeLog.md')
120
- .split(/^\s*##\s*\[\s*(v[\d\.]+)\s*\].*$/)
121
- .slice(1..)
122
- .each_slice(2)
123
- .to_h
124
- .transform_values(&:strip!)
125
- rescue Errno::ENOENT
126
- raise 'failed to get changelogs'
127
- end