1+ require_relative 'common'
2+ require_relative 'constants'
3+
14require 'digest'
25require 'fileutils'
36require 'json'
@@ -53,51 +56,49 @@ def self.wait(condition = nil, timeout: 20, sleep_interval: 5, &block)
5356
5457 # System
5558
56- def self . arch ( ctx )
57- case ctx [ 'kernel' ] [ 'machine' ] . to_s
58- when /arm64|aarch64/
59- 'arm64'
60- when /armv6|armv7l/
61- 'armv7'
62- else
63- 'amd64'
64- end
59+ def self . arch
60+ { 'x86_64' => 'amd64' , 'aarch64' => 'arm64' , 'arm64' => 'arm64' , 'armv7l' => 'armv7' } . fetch ( `uname -m` . strip , 'amd64' )
6561 end
6662
67- def self . snapshot ( ctx , dir , snapshot_dir : '/share/snapshots' , name : ctx . cookbook_name , restore : false , user : Default . user ( ctx ) , group : Default . group ( ctx ) , mode : 0o755 )
68- timestamp = Time . now . strftime ( '%H%M-%d%m%y' )
69- snapshot = File . join ( snapshot_dir , name , "#{ name } -#{ timestamp } .tar.gz" )
63+ def self . snapshot ( ctx , data_dir , name : ctx . cookbook_name , restore : false , user : Default . user ( ctx ) , group : Default . group ( ctx ) , snapshot_dir : Default . snapshot_dir ( ctx ) , mode : 0o755 )
64+
65+ snapshot_dir = "#{ snapshot_dir } /#{ name } "
66+ snapshot = File . join ( snapshot_dir , "#{ name } -#{ Time . now . strftime ( '%H%M-%d%m%y' ) } .tar.gz" )
67+
7068 md5_dir = -> ( path ) {
7169 entries = Dir . glob ( "#{ path } /**/*" , File ::FNM_DOTMATCH )
7270 files = entries . reject { |f | File . directory? ( f ) || File . symlink? ( f ) || [ '.' , '..' ] . include? ( File . basename ( f ) ) || File . basename ( f ) . start_with? ( '._' ) }
7371 Digest ::MD5 . new . tap { |md5 | files . sort . each { |f | File . open ( f , 'rb' ) { |io | md5 . update ( io . read ) } } } . hexdigest }
72+
7473 verify = -> ( archive , compare_dir ) {
7574 Dir . mktmpdir do |tmp |
7675 Logs . try! ( "snapshot extraction" , [ :archive , archive , :tmp , tmp ] , raise : true ) do
7776 system ( "tar -xzf #{ Shellwords . escape ( archive ) } -C #{ Shellwords . escape ( tmp ) } " ) or raise ( "snapshot verification failed" )
7877 end
7978 raise ( "verify snapshot failed" ) unless md5_dir . ( tmp ) == ( Dir . exist? ( compare_dir ) ? md5_dir . ( compare_dir ) : '' )
80- end
81- true
82- }
79+ end ; true }
80+
8381 if restore
84- latest = Dir [ File . join ( snapshot_dir , name , "#{ name } -*.tar.gz" ) ] . max_by { |f | [ File . mtime ( f ) , File . basename ( f ) ] }
82+ latest = Dir [ File . join ( snapshot_dir , "#{ name } -*.tar.gz" ) ] . max_by { |f | [ File . mtime ( f ) , File . basename ( f ) ] }
8583 if latest && ::File . exist? ( latest )
86- FileUtils . rm_rf ( dir )
87- FileUtils . mkdir_p ( dir )
88- Logs . try! ( "snapshot restore" , [ :dir , dir , :archive , latest ] , raise : true ) do
89- system ( "tar -xzf #{ Shellwords . escape ( latest ) } -C #{ Shellwords . escape ( dir ) } " ) or raise ( "tar extract failed" )
84+ FileUtils . rm_rf ( data_dir )
85+ FileUtils . mkdir_p ( data_dir )
86+ Logs . try! ( "snapshot restore" , [ :app_dir , data_dir , :archive , latest ] , raise : true ) do
87+ system ( "tar -xzf #{ Shellwords . escape ( latest ) } -C #{ Shellwords . escape ( data_dir ) } " ) or raise ( "tar extract failed" )
9088 end
91- FileUtils . chown_R ( user , group , dir )
92- FileUtils . chmod_R ( mode , dir )
89+ FileUtils . chown_R ( user , group , data_dir )
90+ FileUtils . chmod_R ( mode , data_dir )
9391 end
92+ return true
9493 end
95- return true unless Dir . exist? ( dir ) # true to be idempotent integrable before installation
94+ return true unless Dir . exist? ( data_dir ) && !Dir . glob ( "#{ data_dir } /*" ) . empty? # true to be idempotent integrable before installation
95+
9696 FileUtils . mkdir_p ( File . dirname ( snapshot ) )
97- Logs . try! ( "snapshot creation" , [ :dir , dir , :snapshot , snapshot ] , raise : true ) do
98- system ( "tar -czf #{ Shellwords . escape ( snapshot ) } -C #{ Shellwords . escape ( dir ) } ." ) or raise ( "tar compress failed" )
97+ Logs . try! ( "snapshot creation" , [ :data_dir , data_dir , :snapshot , snapshot ] , raise : true ) do
98+ system ( "tar -czf #{ Shellwords . escape ( snapshot ) } -C #{ Shellwords . escape ( data_dir ) } ." ) or raise ( "tar compress failed" )
9999 end
100- return verify . ( snapshot , dir )
100+
101+ return verify . ( snapshot , data_dir )
101102 end
102103
103104 # Remote
@@ -140,51 +141,85 @@ def self.proxmox(ctx, path)
140141 request ( url , headers : headers ) . json [ 'data' ]
141142 end
142143
143- def self . install ( ctx , uri , app_dir , data_dir , version_dir : "/app" , snapshot_dir : '/share/snapshots' )
144- version_file = File . join ( version_dir , '.version' )
144+ def self . install ( ctx , owner : , repo : , app_dir : , name : nil , version : 'latest' , user : Default . user ( ctx ) , group : Default . group ( ctx ) , extract : true )
145+ version_file = File . join ( app_dir , '.version' )
145146 version_installed = ::File . exist? ( version_file ) ? ::File . read ( version_file ) . strip : nil
146- version = latest ( uri , version_installed )
147- Common . directories ( ctx , app_dir , recreate : version )
148- snapshot ( ctx , data_dir , snapshot_dir : snapshot_dir ) if version
149- return false unless version
147+ release = nil
148+
149+ FileUtils . mkdir_p ( app_dir )
150+ assets = -> ( a ) { a [ :name ] . match? ( /linux[-_]#{ Utils . arch } /i ) && !a [ :name ] . end_with? ( '.asc' , '.sha265' , '.pem' ) }
151+
152+ if version == 'latest'
153+ release = Logs . raise_if_blank ( "check latest" , latest ( owner , repo ) )
154+ release = release . first if release . is_a? ( Array )
155+ return false unless release
156+ version = release [ :tag_name ] . to_s . gsub ( /^v/ , '' )
157+ end
150158
151- ctx . file version_file do
159+ if version_installed . nil?
160+ Logs . info ( "initial installation (version '#{ version } ')" )
161+ elsif Gem ::Version . new ( version ) > Gem ::Version . new ( version_installed )
162+ Logs . info ( "update from '#{ version_installed } ' to '#{ version } '" )
163+ else
164+ return Logs . info? ( "no update required from '#{ version_installed } ' to '#{ version } '" , result : false )
165+ end
166+
167+ unless release
168+ uri = Constants ::URI_GITHUB_TAG . call ( owner , repo , version )
169+ release = Logs . try! ( "get release by tag" , [ :uri , uri ] ) do
170+ response = request ( uri , headers : { 'Accept' => 'application/vnd.github+json' } , log : false )
171+ response . is_a? ( Net ::HTTPSuccess ) ? response . json ( symbolize_names : true ) : nil
172+ end
173+ return Logs . returns ( "no release for '#{ version } '" , false ) unless release
174+ end
175+
176+ download_url , filename = ( if ( asset = release [ :assets ] . find ( &assets ) )
177+ [ asset [ :browser_download_url ] , File . basename ( URI . parse ( asset [ :browser_download_url ] ) . path ) ]
178+ else [ release [ :tarball_url ] , "#{ repo } -#{ version } .tar.gz" ]
179+ end ) ; Logs . raise_if_blank ( download_url , "missing asset for '#{ version } '" )
180+
181+ Dir . mktmpdir do |tmpdir |
182+ archive_path = File . join ( tmpdir , filename )
183+ Logs . try! ( "download asset #{ download_url } " , [ :to , archive_path ] ) { download ( ctx , archive_path , url : download_url ) }
184+
185+ if extract && archive_path . end_with? ( '.tar.gz' , '.tgz' , '.zip' )
186+ ( system ( "tar -xzf #{ Shellwords . escape ( archive_path ) } --strip-components=1 -C #{ Shellwords . escape ( app_dir ) } " ) or
187+ raise "tar extract failed for #{ archive_path } " ) if extract
188+ else # Binary
189+ FileUtils . mv ( archive_path , File . join ( app_dir , name || repo ) )
190+ end
191+
192+ FileUtils . chown_R ( user , group , app_dir )
193+ FileUtils . chmod_R ( 0755 , app_dir )
194+ end
195+
196+ Ctx . dsl ( ctx ) . file version_file do
152197 content version . to_s
153198 owner Default . user ( ctx )
154199 group Default . group ( ctx )
155- mode 775
200+ mode '0755'
156201 action :create
157202 end
158-
159203 return version
204+
160205 end
161206
162207 def self . download ( ctx , path , url :, owner : Default . user ( ctx ) , group : Default . group ( ctx ) , mode : '0754' , action : :create )
163- ctx . remote_file path do
164- source url . respond_to? ( :call ) ? lazy { url . call } : url
208+ Common . directories ( Ctx . dsl ( ctx ) , File . dirname ( path ) , owner : owner , group : group , mode : mode )
209+ Ctx . dsl ( ctx ) . remote_file path do
210+ source url . respond_to? ( :call ) ? lazy { url . call } : url
165211 owner owner
166212 group group
167213 mode mode
168214 action action
169- end
215+ end . run_action ( action )
170216 end
171217
172- def self . latest ( url , installed_version = nil )
173- latest_version = ( request ( url ) . body [ /title>.*?v?([0-9]+\. [0-9]+(?:\. [0-9]+)?)/ , 1 ] || "latest" ) . to_s
174- Logs . info ( "latest version: '#{ latest_version } ' (#{ url } )" )
175-
176- if installed_version . nil?
177- Logs . info ( "initial installation (version '#{ latest_version } ')" )
178- return latest_version
179- end
180-
181- if Gem ::Version . new ( latest_version ) > Gem ::Version . new ( installed_version )
182- Logs . info ( "update from '#{ installed_version } ' to '#{ latest_version } '" )
183- return latest_version
184- else
185- Logs . info ( "no update required from version '#{ installed_version } ' to '#{ latest_version } '" )
186- return false
187- end
218+ def self . latest ( owner , repo )
219+ api_url = Constants ::URI_GITHUB_LATEST . call ( owner , repo )
220+ response = request ( api_url , headers : { 'Accept' => 'application/vnd.github+json' } , log : false )
221+ return false unless response . is_a? ( Net ::HTTPSuccess )
222+ response . json ( symbolize_names : true )
188223 end
189224
190- end
225+ end
0 commit comments