Add CI to run linter and also comply with the linter

Stephen Paul Weber created

The metrics changes in .rubocop.yml definitely need to be reset to defaults, but
the required refactors are too big for this change alone so for now they were
increased to make it pass.

Other disabled rules may want to be revisited in the future also.

At least this passes its own ruleset now, and can run on sourcehut CI for every
commit.

Change summary

.builds/debian-stable.yml |  18 ++++++
.rubocop.yml              |  44 ++++++++++------
Gemfile                   |   5 -
em_promise.rb             |   2 
r2s-bwmsgsv2.rb           |  10 +-
sgx-bwmsgsv2.rb           | 106 ++++++++++++++++++++--------------------
6 files changed, 106 insertions(+), 79 deletions(-)

Detailed changes

.builds/debian-stable.yml 🔗

@@ -0,0 +1,18 @@
+image: debian/stable
+sources:
+- https://gitlab.com/soprani.ca/sgx-bwmsgsv2
+packages:
+- ruby
+- ruby-dev
+- bundler
+- libxml2-dev
+- rubocop
+environment:
+  LANG: C.UTF-8
+tasks:
+- dependencies: |
+    cd sgx-bwmsgsv2
+    bundle install --path=.gems
+- lint: |
+    cd sgx-bwmsgsv2
+    rubocop .

.rubocop.yml 🔗

@@ -1,10 +1,10 @@
 Metrics/LineLength:
   Max: 80
 
-Style/Tab:
+Layout/Tab:
   Enabled: false
 
-Style/IndentationWidth:
+Layout/IndentationWidth:
   Width: 1 # one tab
 
 Lint/EndAlignment:
@@ -14,7 +14,7 @@ Lint/RescueException:
   Enabled: false
 
 Metrics/AbcSize:
-  Max: 134
+  Max: 190
 
 Metrics/BlockLength:
   Max: 200
@@ -26,7 +26,7 @@ Metrics/ClassLength:
   Max: 200
 
 Metrics/CyclomaticComplexity:
-  Max: 22
+  Max: 30
 
 Metrics/MethodLength:
   Max: 200
@@ -35,21 +35,21 @@ Metrics/ModuleLength:
   Max: 1000
 
 Metrics/ParameterLists:
-  Max: 7
+  Max: 8
 
 Metrics/PerceivedComplexity:
-  Max: 22
+  Max: 30
 
 Style/AndOr:
   Enabled: false
 
-Style/AlignParameters:
+Layout/AlignParameters:
   Enabled: false
 
 Style/BlockDelimiters:
   Enabled: false
 
-Style/CaseIndentation:
+Layout/CaseIndentation:
   EnforcedStyle: end
 
 Style/Documentation:
@@ -63,13 +63,13 @@ Style/IfInsideElse:
   Exclude:
     - 'sgx-bwmsgsv2.rb'
 
-Style/LeadingCommentSpace:
+Layout/LeadingCommentSpace:
   Enabled: false
 
-Style/MultilineMethodCallBraceLayout:
+Layout/MultilineMethodCallBraceLayout:
   Enabled: false
 
-Style/MultilineOperationIndentation:
+Layout/MultilineOperationIndentation:
   Enabled: false
 
 Style/MultilineTernaryOperator:
@@ -88,10 +88,10 @@ Style/NumericLiterals:
 Style/NumericPredicate:
   Enabled: false
 
-Style/SpaceAroundOperators:
+Layout/SpaceAroundOperators:
   Enabled: false
 
-Style/SpaceInsideHashLiteralBraces:
+Layout/SpaceInsideHashLiteralBraces:
   EnforcedStyle: no_space
 
 Style/StringLiterals:
@@ -107,14 +107,26 @@ Style/RedundantReturn:
 Style/MultilineBlockChain:
   Enabled: false
 
-Style/SpaceAroundEqualsInParameterDefault:
+Layout/SpaceAroundEqualsInParameterDefault:
   EnforcedStyle: no_space
 
-Style/IndentArray:
+Layout/IndentArray:
   EnforcedStyle: consistent
 
 Style/SymbolArray:
   EnforcedStyle: brackets
 
-Style/FirstParameterIndentation:
+Layout/FirstParameterIndentation:
   EnforcedStyle: consistent
+
+Style/Lambda:
+  EnforcedStyle: lambda
+
+Layout/AccessModifierIndentation:
+  EnforcedStyle: outdent
+
+Style/FormatStringToken:
+  Enabled: false
+
+Style/WordArray:
+  EnforcedStyle: brackets

Gemfile 🔗

@@ -2,14 +2,13 @@ source 'https://rubygems.org'
 
 gem 'activesupport', '<5.0.0'
 gem 'blather'
-gem 'redis'
 gem 'em-hiredis'
 gem 'em-http-request'
 gem 'eventmachine'
-gem 'promise.rb'
-
 gem 'goliath'
 gem 'log4r'
+gem 'promise.rb'
+gem 'redis'
 
 group :development do
 	gem 'rubocop', require: false

em_promise.rb 🔗

@@ -1,5 +1,3 @@
-#!/usr/bin/env ruby
-#
 # Copyright (C) 2017  Stephen Paul Weber <singpolyma@singpolyma.net>
 #
 # This file is part of sgx-bwmsgsv2.

r2s-bwmsgsv2.rb 🔗

@@ -44,7 +44,7 @@ end
 t = Time.now
 puts "LOG %d.%09d: starting...\n\n" % [t.to_i, t.nsec]
 
-redis = Redis.new(:driver => :hiredis)
+redis = Redis.new(driver: :hiredis)
 
 pending_len = redis.llen('pending_messages-' + ARGV[0])
 if pending_len != 0
@@ -53,7 +53,7 @@ if pending_len != 0
 	exit 1
 end
 
-while true
+loop do
 	timestamps_plus_json_blob = redis.brpoplpush('incoming_messages-' +
 		ARGV[0], 'pending_messages-' + ARGV[0])
 
@@ -92,15 +92,15 @@ while true
 
 	uri = URI("http://#{ARGV[1]}:#{ARGV[2]}/")
 	req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
-	req.body = new_json_blob.split('G', 2)[1][2..-1]  # only use MSG part
+	req.body = new_json_blob.split('G', 2)[1][2..-1] # only use MSG part
 	begin
 		res = Net::HTTP.start(uri.hostname, uri.port) do |http|
 			http.request(req)
 		end
 	# list of exceptions is from https://stackoverflow.com/a/5370726
 	rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
-		Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
-		Net::ProtocolError => e
+	       Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
+	       Net::ProtocolError => e
 
 		puts "TODO problem '#{e}' - now we should try again..."
 		# TODO: actually try again

sgx-bwmsgsv2.rb 🔗

@@ -43,12 +43,11 @@ end
 
 def extract_shortcode(dest)
 	num, context = dest.split(';', 2)
-	num if context && context == 'phone-context=ca-us.phone-context.soprani.ca'
+	num if context == 'phone-context=ca-us.phone-context.soprani.ca'
 end
 
-def is_anonymous_tel?(dest)
-	num, context = dest.split(';', 2)
-	context && context == 'phone-context=anonymous.phone-context.soprani.ca'
+def anonymous_tel?(dest)
+	dest.split(';', 2)[1] == 'phone-context=anonymous.phone-context.soprani.ca'
 end
 
 class SGXClient < Blather::Client
@@ -66,8 +65,8 @@ class SGXClient < Blather::Client
 
 protected
 
-	def wrap_handler(*args, &block)
-		v = block.call(*args)
+	def wrap_handler(*args)
+		v = yield(*args)
 		v.catch(&method(:panic)) if v.is_a?(Promise)
 		true # Do not run other handlers unless throw :pass
 	rescue Exception => e
@@ -169,7 +168,7 @@ module SGXbwmsgsv2
 		panic(e)
 	end
 
-	def self.error_msg(orig, query_node, type, name, text=nil)
+	def self.error_msg(orig, query_node, type, name, _text=nil)
 		orig.type = :error
 
 		error = Nokogiri::XML::Node.new 'error', orig.document
@@ -236,8 +235,7 @@ module SGXbwmsgsv2
 			# TODO: set token and secret to vals provided at startup
 			# TODO: replace pth's user_id with user_id from ARGV[7]
 			#  -> pth = "v1/users/#{new_id}/" + pth.split('/', 4)[3]
-		else
-			# TODO: error
+			# TODO: else error
 		end
 
 		EM::HttpRequest.new(
@@ -269,29 +267,25 @@ module SGXbwmsgsv2
 
 	def self.to_catapult_possible_oob(s, num_dest, user_id, token, secret,
 		usern)
+		un = s.at("oob|x > oob|url", oob: "jabber:x:oob")
+		unless un
+			puts "MMSOOB: no url node found so process as normal"
+			return to_catapult(s, nil, num_dest, user_id, token,
+				secret, usern)
+		end
+		puts "MMSOOB: found a url node - checking if to make MMS..."
 
-			# TODO: fix indentation
-
-			un = s.at("oob|x > oob|url", oob: "jabber:x:oob")
-			if not un
-				puts "MMSOOB: no url node found so process as normal"
-				return to_catapult(s, nil, num_dest, user_id, token,
-					secret, usern)
-			end
-			puts "MMSOOB: found a url node - checking if to make MMS..."
-
-			# TODO: check size of file at un.text and shrink if need
+		# TODO: check size of file at un.text and shrink if need
 
-			body = s.respond_to?(:body) ? s.body : ''
-			# some clients send URI in both body & <url/> so delete
-			s.body = body.sub(/\s*#{Regexp::escape(un.text)}\s*$/, '')
+		body = s.respond_to?(:body) ? s.body : ''
+		# some clients send URI in both body & <url/> so delete
+		s.body = body.sub(/\s*#{Regexp.escape(un.text)}\s*$/, '')
 
-			puts "MMSOOB: url text is '#{un.text}'"
-			puts "MMSOOB: the body is '#{body.to_s.strip}'"
+		puts "MMSOOB: url text is '#{un.text}'"
+		puts "MMSOOB: the body is '#{body.to_s.strip}'"
 
-			puts "MMSOOB: sending MMS since found OOB & user asked"
-			to_catapult(s, un.text, num_dest, user_id, token,
-				secret, usern)
+		puts "MMSOOB: sending MMS since found OOB & user asked"
+		to_catapult(s, un.text, num_dest, user_id, token, secret, usern)
 	end
 
 	def self.to_catapult(s, murl, num_dest, user_id, token, secret, usern)
@@ -303,9 +297,7 @@ module SGXbwmsgsv2
 		end
 
 		extra = {}
-		if murl
-			extra = { media: murl }
-		end
+		extra[:media] = murl if murl
 
 		call_catapult(
 			token,
@@ -352,11 +344,11 @@ module SGXbwmsgsv2
 	def self.validate_num(m)
 		# if sent to SGX domain use https://wiki.soprani.ca/SGX/GroupMMS
 		if m.to == ARGV[0]
-			an = m.children.find { |v| v.element_name == "addresses"
-				}
+			an = m.children.find { |v| v.element_name == "addresses" }
 			if not an
-				return EMPromise.reject([:cancel,
-					'item-not-found'])
+				return EMPromise.reject(
+					[:cancel, 'item-not-found']
+				)
 			end
 			puts "ADRXEP: found an addresses node - iterate addrs.."
 
@@ -376,8 +368,7 @@ module SGXbwmsgsv2
 						end
 						num = c[1].to_s[4..-1]
 						# TODO: confirm num validates
-					else
-						# TODO: error - unexpected name
+						# TODO: else, error - unexpected name
 					end
 				end
 				if num.empty? or type.empty?
@@ -396,7 +387,7 @@ module SGXbwmsgsv2
 				next shortcode if shortcode
 			end
 
-			if is_anonymous_tel?(num_dest)
+			if anonymous_tel?(num_dest)
 				EMPromise.reject([:cancel, 'gone'])
 			else
 				# TODO: text re num not (yet) supportd/implmentd
@@ -464,9 +455,7 @@ module SGXbwmsgsv2
 
 	# TODO: must re-add stuff so can do ad-hoc commands
 	def self.user_cap_features
-		[
-			"urn:xmpp:receipts",
-		]
+		["urn:xmpp:receipts"]
 	end
 
 	def self.add_gateway_feature(feature)
@@ -670,8 +659,8 @@ module SGXbwmsgsv2
 				:get,
 				"api/v2/users/#{user_id}/media"
 			).then { |response|
-				params = JSON.parse(response)
-				# TODO: confirm params is array - could be empty
+				JSON.parse(response)
+				# TODO: confirm response is array - could be empty
 
 				puts "register got str #{response.to_s[0..999]}"
 
@@ -797,11 +786,21 @@ module SGXbwmsgsv2
 	end
 
 	iq :get? do |i|
-		write_to_stream error_msg(i.reply, i.children, 'cancel', 'feature-not-implemented')
+		write_to_stream(error_msg(
+			i.reply,
+			i.children,
+			'cancel',
+			'feature-not-implemented'
+		))
 	end
 
 	iq :set? do |i|
-		write_to_stream error_msg(i.reply, i.children, 'cancel', 'feature-not-implemented')
+		write_to_stream(error_msg(
+			i.reply,
+			i.children,
+			'cancel',
+			'feature-not-implemented'
+		))
 	end
 end
 
@@ -819,7 +818,7 @@ class WebhookHandler < Goliath::API
 	def response(env)
 		# TODO: add timestamp grab here, and MUST include ./tai version
 
-		puts 'ENV: ' + env.reject{ |k| k == 'params' }.to_s
+		puts 'ENV: ' + env.reject { |k| k == 'params' }.to_s
 
 		if params.empty?
 			puts 'PARAMS empty!'
@@ -935,12 +934,15 @@ class WebhookHandler < Goliath::API
 				text = jparams['text']
 
 				if jparams['to'].length > 1
+					# TODO
 					msg = Blather::Stanza::Message.new(
-						'cheogram.com', text)  # TODO
+						'cheogram.com',
+						text
+					)
 
 					addrs = Nokogiri::XML::Node.new(
 						'addresses', msg.document)
-					addrs['xmlns'] = 'http://jabber.org/' +
+					addrs['xmlns'] = 'http://jabber.org/' \
 						'protocol/address'
 
 					addr1 = Nokogiri::XML::Node.new(
@@ -969,11 +971,10 @@ class WebhookHandler < Goliath::API
 					puts "RESPONSE9: #{msg.inspect}"
 				end
 
-				jparams['media'].each do |media_url|
-					if not media_url.end_with?(
+				Array(jparams['media']).each do |media_url|
+					unless media_url.end_with?(
 						'.smil', '.txt', '.xml'
 					)
-
 						has_media = true
 						SGXbwmsgsv2.send_media(
 							others_num + '@' +
@@ -982,7 +983,7 @@ class WebhookHandler < Goliath::API
 							nil, nil, msg
 						)
 					end
-				end unless not jparams['media']
+				end
 			else
 				text = "unknown type (#{type})"\
 					" with text: " + jparams['text']
@@ -1056,7 +1057,6 @@ class WebhookHandler < Goliath::API
 		SGXbwmsgsv2.write(msg)
 
 		[200, {}, "OK"]
-
 	rescue Exception => e
 		puts 'Shutting down gateway due to exception 013: ' + e.message
 		SGXbwmsgsv2.shutdown