vcpkg_pipeline 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'claide'
4
+
5
+ module VPL
6
+ # VPL::Command
7
+ class Command < CLAide::Command
8
+ require 'vcpkg_pipeline/command/new'
9
+ require 'vcpkg_pipeline/command/scan'
10
+ require 'vcpkg_pipeline/command/update'
11
+ require 'vcpkg_pipeline/command/publish'
12
+
13
+ self.abstract_command = true
14
+ self.command = 'vpl'
15
+ self.description = 'vcpkg-Pipeline 是 vcpkg 的流水线工具'
16
+
17
+ def self.options
18
+ [
19
+ ['--help', '展示改命令的介绍面板']
20
+ ]
21
+ end
22
+
23
+ def self.options_extension
24
+ options = []
25
+ options_extension_hash.each do |key, options_extension|
26
+ options_extension.each do |option_extension|
27
+ options << [option_extension.first.gsub(/(--)(.*)/, "\\1#{key}-\\2"), option_extension.last]
28
+ end
29
+ end
30
+ options
31
+ end
32
+
33
+ def self.options_extension_hash
34
+ Hash[]
35
+ end
36
+
37
+ def self.run(argv)
38
+ ensure_not_root_or_allowed! argv
39
+ verify_minimum_git_version!
40
+ verify_xcode_license_approved!
41
+
42
+ super(argv)
43
+ end
44
+
45
+ #
46
+ # 确保root用户
47
+ #
48
+ # @return [void]
49
+ #
50
+ def self.ensure_not_root_or_allowed!(argv, uid = Process.uid, is_windows = Gem.win_platform?)
51
+ root_allowed = argv.include?('--allow-root')
52
+ help! 'You cannot run vcpkg-Pipeline as root' unless root_allowed || uid != 0 || is_windows
53
+ end
54
+
55
+ # 读取Git版本号, 返回一个新的 {Gem::Version} 实例
56
+ #
57
+ # @return [Gem::Version]
58
+ #
59
+ def self.git_version
60
+ raw_version = `git version`
61
+ unless (match = raw_version.scan(/\d+\.\d+\.\d+/).first)
62
+ raise "Failed to extract git version from `git --version` (#{raw_version.inspect})"
63
+ end
64
+
65
+ Gem::Version.new(match)
66
+ end
67
+
68
+ # 检查Git版本号是否低于 1.8.5
69
+ #
70
+ # @raise Git版本号低于 1.8.5
71
+ #
72
+ # @return [void]
73
+ #
74
+ def self.verify_minimum_git_version!
75
+ return unless git_version < Gem::Version.new('1.8.5')
76
+
77
+ raise 'You need at least git version 1.8.5 to use vcpkg-Pipeline'
78
+ end
79
+
80
+ #
81
+ # 检查xcode许可是否被批准
82
+ #
83
+ # @return [void]
84
+ #
85
+ def self.verify_xcode_license_approved!
86
+ return unless `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$?.success?
87
+
88
+ raise 'You have not agreed to the Xcode license, which ' \
89
+ 'you must do to use vcpkg. Agree to the license by running: ' \
90
+ '`xcodebuild -license`'
91
+ end
92
+
93
+ def initialize(argv)
94
+ @argv_extension = {}
95
+ self.class.options_extension_hash.each_key do |key|
96
+ @argv_extension[key] = []
97
+ self.class.options.each do |option|
98
+ name = option.first
99
+ next unless name.include?(key)
100
+
101
+ is_option = name.include? '='
102
+ if is_option
103
+ option = name.gsub(/(--)(.*)(=.*)/, '\\2')
104
+ value = argv.option(option, '')
105
+ unless value.empty?
106
+ @argv_extension[key] << name.gsub(/(--.*=)(.*)/, "\\1#{value}").gsub("#{key}-",
107
+ '')
108
+ end
109
+ else
110
+ flag = name.gsub(/(--)(.*)/, '\\2')
111
+ value = argv.flag?(flag)
112
+ @argv_extension[key] << name.gsub("#{key}-", '') if value
113
+ end
114
+ end
115
+ end
116
+
117
+ super
118
+ end
119
+
120
+ attr_reader :argv_extension
121
+ end
122
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # VPL
4
+ module VPL
5
+ def self.attribute_str(msg, type)
6
+ "\n\033[#{type}m#{msg}\033[0m\n"
7
+ end
8
+
9
+ def self.title(msg)
10
+ puts attribute_str("-- #{msg} --", 44)
11
+ end
12
+
13
+ def self.info(msg)
14
+ puts attribute_str(msg, 46)
15
+ end
16
+
17
+ def self.error(msg)
18
+ raise attribute_str("Error: #{msg}!", 41)
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git'
4
+
5
+ require 'vcpkg_pipeline/extension/cmake_vpl'
6
+ require 'vcpkg_pipeline/extension/vcport_vpl'
7
+ require 'vcpkg_pipeline/extension/vcpkg_vpl'
8
+ require 'vcpkg_pipeline/extension/git_vpl'
9
+
10
+ require 'vcpkg_pipeline/core/spec'
11
+
12
+ module VPL
13
+ # VPL::Scanner
14
+ class Scanner
15
+ attr_accessor :path, :spec, :git, :cmake, :vcport
16
+
17
+ def initialize(path)
18
+ @path = path
19
+ @spec = Spec.load(path)
20
+ @cmake = CMake.open(path)
21
+ @vcport = VCPort.open(path)
22
+ @git = Git.open(path)
23
+ end
24
+
25
+ def scan_spec
26
+ @spec = Spec.load(path)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vcpkg_pipeline/extension/vcpkg_vpl'
4
+
5
+ require 'vcpkg_pipeline/core/log'
6
+
7
+ module VPL
8
+ # VPL::Spec
9
+ class Spec
10
+ # spec file
11
+ attr_accessor :path, :content_path, :content
12
+
13
+ # port info
14
+ attr_accessor :name, :version, :user, :homepage, :description, :dependencies
15
+
16
+ # dist info
17
+ attr_accessor :sources, :disturl
18
+
19
+ def self.eval_vplspec(content, content_path)
20
+ instance_eval(content)
21
+ rescue RescueException => e
22
+ VPL.error("Invalid `#{content_path.basename}` file: #{e.message}")
23
+ end
24
+
25
+ def self.load(path)
26
+ content_path_list = Dir["#{path}/*.vplspec"]
27
+ VPL.error('未找到或存在多个 *.vplspec 文件') if content_path_list.empty? || content_path_list.count > 1
28
+ content_path = content_path_list.first
29
+ content = File.read(content_path)
30
+
31
+ spec = eval_vplspec(content, content_path)
32
+ spec.path = path
33
+ spec.content_path = content_path
34
+ spec.content = content
35
+ spec
36
+ end
37
+
38
+ def initialize
39
+ yield self if block_given?
40
+ end
41
+
42
+ def distfile_name
43
+ "#{name}-#{version}.zip"
44
+ end
45
+
46
+ def distfile_package(output_path = nil)
47
+ output_path ||= '.'
48
+ distfile_path = "#{output_path}/#{distfile_name}"
49
+ `zip #{distfile_path} #{sources.join(' ')}`
50
+ if File.exist? distfile_path
51
+ VPL.info("Dist包 打包成功: #{distfile_path}")
52
+ else
53
+ VPL.error("Dist包 打包失败: #{distfile_path}")
54
+ end
55
+ VCPkg.hash(distfile_path)
56
+ end
57
+
58
+ def write(property, value)
59
+ VPL.info("写入 #{File.basename(@content_path)} : #{property} = #{value}")
60
+
61
+ new_content = @content.gsub!(/(.*.#{property}.*=.*)('.*')/, "\\1'#{value}'")
62
+ File.write(@content_path, new_content)
63
+ end
64
+
65
+ def version_increased
66
+ versions = @version.split('.')
67
+
68
+ new_version_last = (versions.last.to_i + 1).to_s
69
+ versions.pop
70
+ versions.push(new_version_last)
71
+
72
+ versions.join('.')
73
+ end
74
+
75
+ def version_update(new_version = nil)
76
+ new_version ||= version_increased
77
+ write('version', new_version)
78
+ end
79
+
80
+ def to_s
81
+ "#{@name} (#{@version})\
82
+ \nuser: #{@user}\
83
+ \nhomepage: #{@homepage}\
84
+ \ndescription: #{@description}\
85
+ \nsources: #{@sources}\
86
+ \ndependencies: #{@dependencies}"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vcpkg_pipeline/extension/vcpkg_vpl'
4
+
5
+ require 'vcpkg_pipeline/core/scanner'
6
+
7
+ module VPL
8
+ # VPL::Updater
9
+ class Updater
10
+ attr_accessor :path, :scanner
11
+
12
+ def initialize(path)
13
+ @path = path
14
+ @scanner = Scanner.new(path)
15
+ end
16
+
17
+ def update_spec(version = nil)
18
+ @scanner.spec.version_update(version)
19
+ @scanner.scan_spec
20
+ end
21
+
22
+ def update_cmake
23
+ @scanner.cmake.version_update(
24
+ @scanner.spec.version
25
+ )
26
+ end
27
+
28
+ def update_vcport_vcpkg
29
+ vcpkg = @scanner.vcport.vcpkg
30
+ vcpkg.version_update(@scanner.spec.version)
31
+ VCPkg.format(vcpkg.content_path)
32
+ end
33
+
34
+ def update_vcport_portfile(output_path = nil)
35
+ distfile_hash = @scanner.spec.distfile_package(output_path)
36
+ @scanner.vcport.portfile.download_distfile_update(
37
+ @scanner.spec.disturl,
38
+ @scanner.spec.distfile_name,
39
+ distfile_hash
40
+ )
41
+ end
42
+
43
+ def update_vcport(output_path = nil)
44
+ update_vcport_vcpkg
45
+ update_vcport_portfile(output_path)
46
+ end
47
+
48
+ def update_git
49
+ @scanner.git.quick_push(
50
+ @scanner.spec.version
51
+ )
52
+ end
53
+
54
+ def update_all(version = nil, output_path = nil)
55
+ @scanner.git.reset
56
+
57
+ update_spec(version)
58
+ @scanner.git.add('*.vplspec')
59
+ update_cmake
60
+ @scanner.git.add('CMakeLists.txt')
61
+ update_vcport(output_path)
62
+ @scanner.git.add('vcport')
63
+
64
+ @scanner.git.commit(@scanner.cmake.version)
65
+
66
+ update_git
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vcpkg_pipeline/extension/string_vpl'
4
+ require 'vcpkg_pipeline/extension/dir_vpl'
5
+
6
+ require 'vcpkg_pipeline/core/log'
7
+
8
+ # CMake
9
+ module CMake
10
+ def self.open(path)
11
+ CMake::Base.new(path)
12
+ end
13
+
14
+ # CMake::Base
15
+ class Base
16
+ attr_accessor :path, :content_path, :content, :project, :name, :version
17
+
18
+ def initialize(path)
19
+ @path = path
20
+ @content_path = "#{path}/CMakeLists.txt"
21
+
22
+ VPL.error("#{@content_path} Not Found") unless File.exist? @content_path
23
+ @content = File.read(@content_path)
24
+
25
+ parse_content(@content)
26
+ end
27
+
28
+ def parse_content(content)
29
+ content.each_line(')') { |piece| parse_project(piece) }
30
+ end
31
+
32
+ def parse_project(piece)
33
+ return unless piece.include? 'project'
34
+
35
+ @project = piece
36
+
37
+ clean_project = piece.clean
38
+ parse_project_name(clean_project)
39
+ parse_project_version(clean_project)
40
+ end
41
+
42
+ def parse_project_name(clean_project)
43
+ regex_name = /project \( ([^ ]*) /
44
+ regex_name.match(clean_project)
45
+ @name = Regexp.last_match(1)
46
+ end
47
+
48
+ def parse_project_version(clean_project)
49
+ regex_version = /VERSION ([^ ]*) /
50
+ regex_version.match(clean_project)
51
+ @version = Regexp.last_match(1)
52
+ end
53
+
54
+ def version_increased
55
+ regex_version = /([^.]*)\.([^.]*)\.([^.]*)\.*([^.]*)/
56
+ regex_version.match(@version)
57
+
58
+ new_patch = Regexp.last_match(3).to_i + 1
59
+ "#{Regexp.last_match(1)}.#{Regexp.last_match(2)}.#{new_patch}#{Regexp.last_match(4)}"
60
+ end
61
+
62
+ def version_update(new_version = nil)
63
+ new_version ||= version_increased
64
+
65
+ new_project = @project.sub(@version, new_version)
66
+ new_content = @content.sub(@project, new_project)
67
+ File.write(@content_path, new_content)
68
+
69
+ @content = new_content
70
+ @project = new_project
71
+ @version = new_version
72
+ end
73
+
74
+ def to_s
75
+ "#{name}-#{version}"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Dir
4
+ class Dir
5
+ def reset(path)
6
+ return unless File.directory? path
7
+
8
+ `rm -fr #{path}`
9
+ `mkdir #{path}`
10
+ end
11
+
12
+ def self.replace_content(path, replacements)
13
+ content = File.read(path)
14
+ replacements.each do |find, replace|
15
+ content = content.gsub(find, replace)
16
+ end
17
+ File.write(path, content)
18
+ end
19
+
20
+ def self.replace_all(path, replacements)
21
+ Dir["#{path}/*"].each do |subpath|
22
+ subpath_rpl = subpath
23
+ replacements.each { |find, replace| subpath_rpl = subpath_rpl.gsub(find, replace)}
24
+ File.rename(subpath, subpath_rpl)
25
+
26
+ if File.directory? subpath_rpl
27
+ replace_all(subpath_rpl, replacements)
28
+ else
29
+ replace_content(subpath_rpl, replacements)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git'
4
+
5
+ require 'vcpkg_pipeline/core/log'
6
+
7
+ module Git
8
+ # Git::Base
9
+ class Base
10
+ def current_branch
11
+ branches.current.first
12
+ end
13
+
14
+ def quick_push_tag(has_tag)
15
+ push(remote, current_branch, has_tag)
16
+ VPL.info("Git上传 #{remote} #{current_branch} #{new_version}")
17
+ end
18
+
19
+ def quick_push(new_tag = nil)
20
+ return unless ENV['Release']
21
+
22
+ has_tag = !new_tag.empty?
23
+ if has_tag
24
+ tags.each { |tag| VPL.error("当前版本 #{new_tag} 已发布, 请尝试其他版本号") if tag.name.eql? new_tag }
25
+
26
+ add_tag(new_tag)
27
+ VPL.info("Git提交Tag: #{new_tag}")
28
+ end
29
+ quick_push_tag(has_tag)
30
+ end
31
+
32
+ def to_s
33
+ "#{remote}-#{branches.current.first}"
34
+ end
35
+ end
36
+
37
+ # Git::Branches
38
+ class Branches
39
+ def current
40
+ local.select { :current }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # String
4
+ class String
5
+ def clean
6
+ gsub(/\#.*/, '').gsub(/\(/, ' ( ').gsub(/\)/, ' ) ').gsub(/\n/, ' ').gsub(/\ +/, ' ')
7
+ end
8
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vcpkg_pipeline/extension/vcport_vpl'
4
+
5
+ require 'vcpkg_pipeline/core/log'
6
+
7
+ # VCPkg
8
+ module VCPkg
9
+ def self.root
10
+ `echo $VCPKG_ROOT`
11
+ end
12
+
13
+ def self.ports
14
+ "#{root}/ports"
15
+ end
16
+
17
+ def self.hash(zip_path)
18
+ `vcpkg hash #{zip_path}`.sub(/\n/, '')
19
+ end
20
+
21
+ def self.format(vcpkg_json_path)
22
+ `vcpkg format-manifest #{vcpkg_json_path}`
23
+ end
24
+
25
+ def self.publish(vcport)
26
+ name = vcport.vcpkg.name
27
+ version = vcport.vcpkg.version
28
+
29
+ git_vcpkg = Git.open(root)
30
+ git_vcpkg.stashes.all
31
+
32
+ port_exist = File.directory? "#{VCPkg.root}/ports/#{name}"
33
+
34
+ `cp -fr #{vcport.port_path} #{ports}`
35
+ `vcpkg x-add-version #{name}`
36
+
37
+ git_vcpkg.add('.')
38
+ git_vcpkg.commit("[#{name}] #{port_exist ? 'Update' : 'Add'} #{version}")
39
+ git_vcpkg.quick_push
40
+ end
41
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require 'vcpkg_pipeline/extension/string_vpl'
6
+
7
+ require 'vcpkg_pipeline/core/log'
8
+
9
+ # VCPort
10
+ module VCPort
11
+ def self.open(path)
12
+ VCPort::Base.new(path)
13
+ end
14
+
15
+ # VCPort::Base
16
+ class Base
17
+ attr_accessor :path, :port_path, :vcpkg, :portfile
18
+
19
+ def initialize(path)
20
+ @path = path
21
+ @port_path = "#{path}/vcport"
22
+ VPL.error("#{@port_path} Not Found") unless File.directory? @port_path
23
+
24
+ @vcpkg = VCPkg.new(@port_path)
25
+ @portfile = Portfile.new(@port_path)
26
+ end
27
+
28
+ def to_s
29
+ "#{vcpkg}\n#{portfile}"
30
+ end
31
+
32
+ # VCPort::Base::VCPkg
33
+ class VCPkg
34
+ attr_accessor :path, :content_path, :content, :json, :name, :version
35
+
36
+ def initialize(path)
37
+ @path = path
38
+ @content_path = "#{path}/vcpkg.json"
39
+
40
+ VPL.error("#{@content_path} Not Found") unless File.exist? @content_path
41
+ @content = File.read(@content_path)
42
+
43
+ parse_content(@content)
44
+ end
45
+
46
+ def parse_content(content)
47
+ @json = JSON.parse(content)
48
+ @name = @json['name']
49
+ @version = @json['version']
50
+ end
51
+
52
+ def version_update(new_version)
53
+ @json['version'] = new_version
54
+ File.write(@content_path, JSON.dump(@json))
55
+ VPL.info("Port 版本号更新: #{@version} -> #{new_version}")
56
+ end
57
+
58
+ def to_s
59
+ "#{json['name']}-#{json['version']}"
60
+ end
61
+ end
62
+
63
+ # VCPort::Base::Portfile
64
+ class Portfile
65
+ attr_accessor :path, :content_path, :content, :download_distfile, :url, :filename, :hash
66
+
67
+ def initialize(path)
68
+ @path = path
69
+ @content_path = "#{path}/portfile.cmake"
70
+
71
+ VPL.error("#{@content_path} Not Found") unless File.exist? @content_path
72
+ @content = File.read(@content_path)
73
+
74
+ parse_content(@content)
75
+ end
76
+
77
+ def parse_content(content)
78
+ content.each_line(')') { |piece| parse_download_distfile(piece) }
79
+ end
80
+
81
+ def parse_download_distfile(piece)
82
+ return unless piece.include? 'vcpkg_download_distfile'
83
+
84
+ @download_distfile = piece
85
+
86
+ clean_download_distfile = piece.clean
87
+ parse_download_distfile_url(clean_download_distfile)
88
+ parse_download_distfile_filename(clean_download_distfile)
89
+ parse_download_distfile_hash(clean_download_distfile)
90
+ end
91
+
92
+ def parse_download_distfile_url(clean_download_distfile)
93
+ regex_url = /URLS "([^ ]*)" /
94
+ regex_url.match(clean_download_distfile)
95
+ @url = Regexp.last_match(1)
96
+ end
97
+
98
+ def parse_download_distfile_filename(clean_download_distfile)
99
+ regex_filename = /FILENAME "([^ ]*)" /
100
+ regex_filename.match(clean_download_distfile)
101
+ @filename = Regexp.last_match(1)
102
+ end
103
+
104
+ def parse_download_distfile_hash(clean_download_distfile)
105
+ regex_hash = /SHA512 ([^ ]*) /
106
+ regex_hash.match(clean_download_distfile)
107
+ @hash = Regexp.last_match(1)
108
+ end
109
+
110
+ def download_distfile_update(new_url, new_filename, new_hash)
111
+ new_download_distfile = @download_distfile
112
+ new_download_distfile = new_download_distfile.sub("URLS \"#{@url}\"", "URLS \"#{new_url}\"")
113
+ new_download_distfile = new_download_distfile.sub("FILENAME \"#{@filename}\"", "FILENAME \"#{new_filename}\"")
114
+ new_download_distfile = new_download_distfile.sub("SHA512 #{@hash}", "SHA512 #{new_hash}")
115
+ File.write(@content_path, @content.sub(@download_distfile, new_download_distfile))
116
+ VPL.info("Port 下载文件信息更新: \
117
+ \n URLS #{@url} -> #{new_url}\
118
+ \n FILENAME #{@filename} -> #{new_filename}\
119
+ \n SHA512 #{@hash} -> #{new_hash}\
120
+ ")
121
+ end
122
+
123
+ def to_s
124
+ "#{@url} -> #{@filename}"
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # VPL
4
+ module VPL
5
+ autoload :Command, 'vcpkg_pipeline/command'
6
+ end