Module: Fbe
- Defined in:
- lib/fbe.rb,
lib/fbe/unmask_repos.rb
Overview
Unmask.
- Author
-
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
-
Copyright © 2024 Zerocracy
- License
-
MIT
Defined Under Namespace
Modules: Middleware Classes: Award, Conclude, FakeOctokit, Graph, Iterate
Constant Summary collapse
- VERSION =
Current version of the gem (changed by .rultor.yml on every release)
'0.0.71'
Class Method Summary collapse
-
.bylaws(anger: 2, love: 2, paranoia: 2) ⇒ Object
A generator of policies/bylaws.
-
.conclude(fb: Fbe.fb, judge: $judge, loog: $loog, options: $options, global: $global) ⇒ Object
Create a conclude code block.
-
.copy(source, target, except: []) ⇒ Object
Make a copy of a fact, moving all properties to a new fact.
-
.fb(fb: $fb, global: $global, options: $options, loog: $loog) ⇒ Object
Returns an instance of
Factbase
(cached). -
.github_graph(options: $options, global: $global, loog: $loog) ⇒ Object
Interface to GitHub GraphQL API.
-
.if_absent(fb: Fbe.fb) {|f| ... } ⇒ Object
Injects a fact if it’s absent in the factbase, otherwise (it is already there) returns NIL.
-
.issue(fact, options: $options, global: $global, loog: $loog) ⇒ Object
Converts an ID of GitHub issue into a nicely formatting string.
-
.iterate(fb: Fbe.fb, loog: $loog, options: $options, global: $global) ⇒ Object
Create a conclude code block.
-
.just_one(fb: Fbe.fb) {|f| ... } ⇒ Object
Injects a fact if it’s absent in the factbase, otherwise (it is already there) returns the existing one.
- .mask_to_regex(mask) ⇒ Object
-
.octo(options: $options, global: $global, loog: $loog) ⇒ Object
Interface to GitHub API.
-
.overwrite(fact, property, value, fb: Fbe.fb) ⇒ Object
Overwrite a property in the fact.
-
.pmp(fb: Fbe.fb, global: $global, options: $options, loog: $loog) ⇒ Object
Get configuration parameter from the “PMP” fact.
-
.regularly(area, p_every_days, p_since_days = nil, fb: Fbe.fb, judge: $judge, loog: $loog) {|f| ... } ⇒ Object
Run the block provided every X days.
-
.repeatedly(area, p_every_hours, fb: Fbe.fb, judge: $judge, loog: $loog) {|fb.query("(and (eq what '#{judge}'))").each.to_a.first| ... } ⇒ Object
Run the block provided every X hours.
-
.sec(fact, prop = :seconds) ⇒ Object
Converts number of seconds into text.
- .unmask_repos(options: $options, global: $global, loog: $loog) ⇒ Object
-
.who(fact, prop = :who, options: $options, global: $global, loog: $loog) ⇒ Object
Converts an ID of GitHub user into a nicely formatting string with his name.
Class Method Details
.bylaws(anger: 2, love: 2, paranoia: 2) ⇒ Object
A generator of policies/bylaws.
- Author
-
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
-
Copyright © 2024 Yegor Bugayenko
- License
-
MIT
33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/fbe/bylaws.rb', line 33 def Fbe.bylaws(anger: 2, love: 2, paranoia: 2) raise 'The "anger" must be in the [0..4] interval' unless !anger.negative? && anger < 5 raise 'The "lover" must be in the [0..4] interval' unless !love.negative? && love < 5 raise 'The "paranoia" must be in the [1..4] interval' unless paranoia.positive? && paranoia < 5 home = File.join(__dir__, '../../assets/bylaws') raise "The directory with templates is absent '#{home}'" unless File.exist?(home) Dir[File.join(home, '*.liquid')].to_h do |f| formula = Liquid::Template.parse(File.read(f)).render( 'anger' => anger, 'love' => love, 'paranoia' => paranoia ) [File.basename(f).gsub(/\.liquid$/, ''), formula] end end |
.conclude(fb: Fbe.fb, judge: $judge, loog: $loog, options: $options, global: $global) ⇒ Object
Create a conclude code block.
37 38 39 40 |
# File 'lib/fbe/conclude.rb', line 37 def Fbe.conclude(fb: Fbe.fb, judge: $judge, loog: $loog, options: $options, global: $global, &) c = Fbe::Conclude.new(fb:, judge:, loog:, options:, global:) c.instance_eval(&) end |
.copy(source, target, except: []) ⇒ Object
Make a copy of a fact, moving all properties to a new fact.
33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/fbe/copy.rb', line 33 def Fbe.copy(source, target, except: []) raise 'The source is nil' if source.nil? raise 'The target is nil' if target.nil? raise 'The except is nil' if except.nil? source.all_properties.each do |k| next unless target[k].nil? next if except.include?(k) source[k].each do |v| target.send(:"#{k}=", v) end end end |
.fb(fb: $fb, global: $global, options: $options, loog: $loog) ⇒ Object
Returns an instance of Factbase
(cached).
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/fbe/fb.rb', line 39 def Fbe.fb(fb: $fb, global: $global, options: $options, loog: $loog) global[:fb] ||= begin rules = Dir.glob(File.join('rules', '*.fe')).map { |f| File.read(f) } fbe = Factbase::Rules.new( fb, "(and \n#{rules.join("\n")}\n)", uid: '_id' ) fbe = Factbase::Pre.new(fbe) do |f, fbt| max = fbt.query('(eq _id (max _id))').each.to_a.first f._id = (max.nil? ? 0 : max._id) + 1 f._time = Time.now f._version = "#{Factbase::VERSION}/#{Judges::VERSION}/#{.action_version}" f._job = .job_id unless .job_id.nil? end Factbase::Looged.new(fbe, loog) end end |
.github_graph(options: $options, global: $global, loog: $loog) ⇒ Object
Interface to GitHub GraphQL API.
34 35 36 37 38 39 40 41 42 |
# File 'lib/fbe/github_graph.rb', line 34 def Fbe.github_graph(options: $options, global: $global, loog: $loog) global[:github_graph] ||= if .testing.nil? Fbe::Graph.new(token: .github_token || ENV.fetch('GITHUB_TOKEN', nil)) else loog.debug('The connection to GitHub GraphQL API is mocked') Fbe::Graph::Fake.new end end |
.if_absent(fb: Fbe.fb) {|f| ... } ⇒ Object
Injects a fact if it’s absent in the factbase, otherwise (it is already there) returns NIL.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/fbe/if_absent.rb', line 32 def Fbe.if_absent(fb: Fbe.fb) attrs = {} f = others(map: attrs) do |*args| k = args[0] if k.end_with?('=') @map[k[0..-2].to_sym] = args[1] else @map[k.to_sym] end end yield f q = attrs.except('_id', '_time', '_version').map do |k, v| vv = v.to_s if v.is_a?(String) vv = "'#{vv.gsub('"', '\\\\"').gsub("'", "\\\\'")}'" elsif v.is_a?(Time) vv = v.utc.iso8601 end "(eq #{k} #{vv})" end.join(' ') q = "(and #{q})" return nil unless fb.query(q).each.to_a.empty? n = fb.insert attrs.each { |k, v| n.send("#{k}=", v) } n end |
.issue(fact, options: $options, global: $global, loog: $loog) ⇒ Object
Converts an ID of GitHub issue into a nicely formatting string.
33 34 35 36 37 38 39 40 41 |
# File 'lib/fbe/issue.rb', line 33 def Fbe.issue(fact, options: $options, global: $global, loog: $loog) rid = fact['repository'] raise "There is no 'repository' property" if rid.nil? rid = rid.first.to_i issue = fact['issue'] raise "There is no 'issue' property" if issue.nil? issue = issue.first.to_i "#{Fbe.octo(global:, options:, loog:).repo_name_by_id(rid)}##{issue}" end |
.iterate(fb: Fbe.fb, loog: $loog, options: $options, global: $global) ⇒ Object
Create a conclude code block.
31 32 33 34 |
# File 'lib/fbe/iterate.rb', line 31 def Fbe.iterate(fb: Fbe.fb, loog: $loog, options: $options, global: $global, &) c = Fbe::Iterate.new(fb:, loog:, options:, global:) c.instance_eval(&) end |
.just_one(fb: Fbe.fb) {|f| ... } ⇒ Object
Injects a fact if it’s absent in the factbase, otherwise (it is already there) returns the existing one.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/fbe/just_one.rb', line 32 def Fbe.just_one(fb: Fbe.fb) attrs = {} f = others(map: attrs) do |*args| k = args[0] if k.end_with?('=') @map[k[0..-2].to_sym] = args[1] else @map[k.to_sym] end end yield f q = attrs.except('_id', '_time', '_version').map do |k, v| vv = v.to_s if v.is_a?(String) vv = "'#{vv.gsub('"', '\\\\"').gsub("'", "\\\\'")}'" elsif v.is_a?(Time) vv = v.utc.iso8601 end "(eq #{k} #{vv})" end.join(' ') q = "(and #{q})" before = fb.query(q).each.to_a.first return before unless before.nil? n = fb.insert attrs.each { |k, v| n.send("#{k}=", v) } n end |
.mask_to_regex(mask) ⇒ Object
32 33 34 35 36 |
# File 'lib/fbe/unmask_repos.rb', line 32 def self.mask_to_regex(mask) org, repo = mask.split('/') raise "Org '#{org}' can't have an asterisk" if org.include?('*') Regexp.compile("#{org}/#{repo.gsub('*', '.*')}") end |
.octo(options: $options, global: $global, loog: $loog) ⇒ Object
Interface to GitHub API.
It is supposed to be used instead of Octokit client, because it is pre-configured and enables additional fearues, such as retrying, logging, and caching.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/fbe/octo.rb', line 46 def Fbe.octo(options: $options, global: $global, loog: $loog) raise 'The $global is not set' if global.nil? global[:octo] ||= begin if .testing.nil? o = Octokit::Client.new token = .github_token if token.nil? loog.debug("The 'github_token' option is not provided") token = ENV.fetch('GITHUB_TOKEN', nil) if token.nil? loog.debug("The 'GITHUB_TOKEN' environment variable is not set") else loog.debug("The 'GITHUB_TOKEN' environment was provided") end else loog.debug("The 'github_token' option was provided") end if token.nil? loog.warn('Accessing GitHub API without a token!') elsif token.empty? loog.warn('The GitHub API token is an empty string, won\'t use it') else o = Octokit::Client.new(access_token: token) loog.info("Accessing GitHub API with a token (#{token.length} chars, ending by #{token[-4..]})") end o.auto_paginate = true o.per_page = 100 o. = { request: { open_timeout: 15, timeout: 15 } } stack = Faraday::RackBuilder.new do |builder| builder.use( Faraday::Retry::Middleware, exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [ Octokit::TooManyRequests, Octokit::ServiceUnavailable ], max: 4, interval: ENV['RACK_ENV'] == 'test' ? 0.01 : 4, methods: [:get], backoff_factor: 2 ) builder.use(Fbe::Middleware::Quota, loog:, pause: .github_api_pause || 60) builder.use(Faraday::HttpCache, serializer: Marshal, shared_cache: false, logger: Loog::NULL) builder.use(Octokit::Response::RaiseError) builder.use( Faraday::Response::Logger, loog, { formatter: Fbe::Middleware::LoggingFormatter, log_only_errors: true, headers: true, bodies: true, errors: false } ) builder.adapter(Faraday.default_adapter) end o.middleware = stack o = Verbose.new(o, log: loog) else loog.debug('The connection to GitHub API is mocked') o = Fbe::FakeOctokit.new end decoor(o, loog:) do def off_quota left = @origin.rate_limit.remaining if left < 5 @loog.info("To much GitHub API quota consumed already (remaining=#{left}), stopping") true else false end end def user_name_by_id(id) json = @origin.user(id) name = json[:login] @loog.debug("GitHub user ##{id} has a name: @#{name}") name end def repo_id_by_name(name) json = @origin.repository(name) id = json[:id] @loog.debug("GitHub repository #{name} has an ID: ##{id}") id end def repo_name_by_id(id) json = @origin.repository(id) name = json[:full_name] @loog.debug("GitHub repository ##{id} has a name: #{name}") name end end end end |
.overwrite(fact, property, value, fb: Fbe.fb) ⇒ Object
Overwrite a property in the fact.
If the property doesn’t exist in the fact, it will be added. If it does exist, it will be removed (the entire fact will be destroyed, new fact created, and property set).
It is important that the fact has _id
property. If it doesn’t, an exception will be raised.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/fbe/overwrite.rb', line 40 def Fbe.overwrite(fact, property, value, fb: Fbe.fb) raise 'The fact is nil' if fact.nil? raise "The property is not a String but #{property.class} (#{property})" unless property.is_a?(String) return if !fact[property].nil? && fact[property].size == 1 && fact[property].first == value before = {} fact.all_properties.each do |prop| before[prop.to_s] = fact[prop] end id = fact['_id']&.first raise 'There is no _id in the fact, cannot use Fbe.overwrite' if id.nil? raise "No facts by _id = #{id}" if fb.query("(eq _id #{id})").delete!.zero? n = fb.insert before[property.to_s] = [value] before.each do |k, vv| next unless n[k].nil? vv.each do |v| n.send("#{k}=", v) end end end |
.pmp(fb: Fbe.fb, global: $global, options: $options, loog: $loog) ⇒ Object
Get configuration parameter from the “PMP” fact.
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/fbe/pmp.rb', line 35 def Fbe.pmp(fb: Fbe.fb, global: $global, options: $options, loog: $loog) others do |*args1| area = args1.first others do |*args2| param = args2.first f = Fbe.fb(global:, fb:, options:, loog:).query("(and (eq what 'pmp') (eq area '#{area}'))").each.to_a.first raise "Unknown area '#{area}'" if f.nil? r = f[param] raise "Unknown property '#{param}' in the '#{area}' area" if r.nil? r.first end end end |
.regularly(area, p_every_days, p_since_days = nil, fb: Fbe.fb, judge: $judge, loog: $loog) {|f| ... } ⇒ Object
Run the block provided every X days.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/fbe/regularly.rb', line 36 def Fbe.regularly(area, p_every_days, p_since_days = nil, fb: Fbe.fb, judge: $judge, loog: $loog, &) pmp = fb.query("(and (eq what 'pmp') (eq area '#{area}') (exists #{p_every_days}))").each.to_a.first interval = pmp.nil? ? 7 : pmp[p_every_days].first unless fb.query( "(and (eq what '#{judge}') (gt when (minus (to_time (env 'TODAY' '#{Time.now.utc.iso8601}')) '#{interval} days')))" ).each.to_a.empty? loog.debug("#{$judge} statistics have recently been collected, skipping now") return end f = fb.insert f.what = judge f.when = Time.now unless p_since_days.nil? days = pmp.nil? ? 28 : pmp[p_since_days].first since = Time.now - (days * 24 * 60 * 60) f.since = since end yield f end |
.repeatedly(area, p_every_hours, fb: Fbe.fb, judge: $judge, loog: $loog) {|fb.query("(and (eq what '#{judge}'))").each.to_a.first| ... } ⇒ Object
Run the block provided every X hours.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/fbe/repeatedly.rb', line 36 def Fbe.repeatedly(area, p_every_hours, fb: Fbe.fb, judge: $judge, loog: $loog, &) pmp = fb.query("(and (eq what 'pmp') (eq area '#{area}') (exists #{p_every_hours}))").each.to_a.first hours = pmp.nil? ? 24 : pmp[p_every_hours].first unless fb.query( "(and (eq what '#{judge}') (gt when (minus (to_time (env 'TODAY' '#{Time.now.utc.iso8601}')) '#{hours} hours')))" ).each.to_a.empty? loog.debug("#{$judge} have recently been executed, skipping now") return end f = fb.query("(and (eq what '#{judge}'))").each.to_a.first if f.nil? f = fb.insert f.what = judge end Fbe.overwrite(f, 'when', Time.now) yield fb.query("(and (eq what '#{judge}'))").each.to_a.first end |
.sec(fact, prop = :seconds) ⇒ Object
Converts number of seconds into text.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/fbe/sec.rb', line 31 def Fbe.sec(fact, prop = :seconds) s = fact[prop.to_s] raise "There is no #{prop} property" if s.nil? s = s.first.to_i if s < 60 format('%d seconds', s) elsif s < 60 * 60 format('%d minutes', s / 60) elsif s < 60 * 60 * 24 format('%d hours', s / (60 * 60)) else format('%d days', s / (60 * 60 * 24)) end end |
.unmask_repos(options: $options, global: $global, loog: $loog) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/fbe/unmask_repos.rb', line 38 def self.unmask_repos(options: $options, global: $global, loog: $loog) repos = [] octo = Fbe.octo(loog:, global:, options:) masks = (.repositories || '').split(',') masks.reject { |m| m.start_with?('-') }.each do |mask| unless mask.include?('*') repos << mask next end re = Fbe.mask_to_regex(mask) octo.repositories(mask.split('/')[0]).each do |r| repos << r[:full_name] if re.match?(r[:full_name]) end end masks.select { |m| m.start_with?('-') }.each do |mask| re = Fbe.mask_to_regex(mask[1..]) repos.reject! { |r| re.match?(r) } end repos.reject! { |repo| octo.repository(repo)[:archived] } raise "No repos found matching: #{.repositories}" if repos.empty? loog.debug("Scanning #{repos.size} repositories: #{repos.join(', ')}...") repos end |
.who(fact, prop = :who, options: $options, global: $global, loog: $loog) ⇒ Object
Converts an ID of GitHub user into a nicely formatting string with his name.
34 35 36 37 38 39 |
# File 'lib/fbe/who.rb', line 34 def Fbe.who(fact, prop = :who, options: $options, global: $global, loog: $loog) id = fact[prop.to_s] raise "There is no #{prop} property" if id.nil? id = id.first.to_i "@#{Fbe.octo(options:, global:, loog:).user_name_by_id(id)}" end |