code_review_notifier 0.1.2 → 0.2.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e426f5dccd98b195d1c7293883395783661f4d2f7415bbef49dec86366c359ed
4
- data.tar.gz: 1bc90b0595fef06e71915280ae60abccb4b4f0210e5f5989b108f00c531f1d09
3
+ metadata.gz: 5760e7d91fa308531d0ab4c0da163525a0b4cf2ed169822c01ad5fedd5c6e28a
4
+ data.tar.gz: 86a8c7f51968a37bca25eaa0406a6c3b7af9f33cf9702b4c03a349ba4f0cc577
5
5
  SHA512:
6
- metadata.gz: 3d04f2041ece20693f1111669c23d995173a7f463bbe40663de23542285c3610243aa099ad65a8f1dd9d14b45e930f332a74de89e25ec5dbb85a0cd6d644f05a
7
- data.tar.gz: 4b7f3b4e1193931f975f350d51f18b14e57094ca82991a9ebd1f759901d6fc1cfd23b3d46b4f86e6402b3183c6aaaef737673f7c7c3f4827829b65b54bc7133c
6
+ metadata.gz: b0f8c25880aa149870bc5c39e13d081b35d9f95b5fcb3efea47ed272b2e7acadb860c7283d3efa9b29ec114b552ae92f9910bedaf5b57c003af0017cdb2fb3b1
7
+ data.tar.gz: b0ed3ddba99bff4fe392847c7f8a5430f68fde44e32f02fdcbfb8deb35edc24526691b3150dc8b4102896667ebf56aa530c7b0647844025522050fba4156f7c1
data/Gemfile CHANGED
@@ -1,6 +1,5 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem "sqlite3"
6
- gem "httparty"
5
+ gem "rubiclifier", "1.0.0"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'code_review_notifier'
3
+ require "code_review_notifier"
4
4
 
5
- CodeReviewNotifier.call(ARGV)
5
+ CodeReviewNotifier.new(ARGV).call
@@ -0,0 +1,28 @@
1
+ require "rubiclifier"
2
+ require_relative "./api.rb"
3
+
4
+ class CodeChangeNotification
5
+ attr_reader :code_change_activity
6
+ private :code_change_activity
7
+
8
+ def initialize(code_change_activity)
9
+ @code_change_activity = code_change_activity
10
+ end
11
+
12
+ def send
13
+ code_change = code_change_activity.code_change
14
+ id = code_change.id
15
+ owner = code_change.owner
16
+ subject = code_change.subject
17
+
18
+ message = code_change_activity.message
19
+ author = code_change_activity.author
20
+ Rubiclifier::Notification.new(
21
+ author,
22
+ message,
23
+ "#{owner}: #{subject}",
24
+ Api.current_api.favicon,
25
+ Api.current_api.code_change_url(code_change)
26
+ ).send
27
+ end
28
+ end
@@ -1,36 +1,24 @@
1
- require "io/console"
1
+ require "rubiclifier"
2
2
  require_relative "./api.rb"
3
- require_relative "./notifier.rb"
3
+ require_relative "./code_change_notification.rb"
4
4
 
5
- SECONDS_BETWEEN_RUNS = 120
5
+ SECONDS_BETWEEN_RUNS = 90
6
6
  SECONDS_BETWEEN_NOTIFICATIONS = 5
7
7
 
8
- class CodeReviewNotifier
9
- def self.setup(args)
10
- system("mkdir -p ~/.code_review_notifier")
11
- system("brew bundle --file #{File.expand_path(File.dirname(__FILE__) + "/..")}/Brewfile")
12
-
13
- if args[0] == "--setup" || !Api.current_api.is_setup?
14
- print("What's the base URL? (i.e. https://gerrit.google.com) ")
15
- DB.save_setting("base_api_url", STDIN.gets.chomp, is_secret: false)
16
-
17
- print("What's the account username? ")
18
- DB.save_setting("username", STDIN.gets.chomp, is_secret: false)
19
-
20
- print("What's the account password? (hiding input) ")
21
- DB.save_setting("password", STDIN.noecho(&:gets).chomp, is_secret: true)
22
- puts
23
-
24
- print("What's the account id? (check #{Api.current_api.base_api_url}/settings/) ")
25
- DB.save_setting("account_id", STDIN.gets.chomp, is_secret: false)
26
-
27
- puts("All setup!")
28
- end
8
+ class CodeReviewNotifier < Rubiclifier::BaseApplication
9
+ def show_help
10
+ puts
11
+ puts("This polls for updates to patch sets/pull requests and notifies you about any relevant changes.")
12
+ puts
13
+ puts("Usage:")
14
+ puts(" code_review_notifier --help | Shows this help menu")
15
+ puts(" code_review_notifier --setup | Runs setup")
16
+ puts(" code_review_notifier | Start listening for changes (should be run in background)")
17
+ puts
18
+ exit
29
19
  end
30
20
 
31
- def self.call(args)
32
- setup(args)
33
-
21
+ def run_application
34
22
  while true
35
23
  is_first_run = is_first_run?
36
24
  puts
@@ -48,15 +36,51 @@ class CodeReviewNotifier
48
36
  code_change_activity.notified
49
37
  unless is_first_run
50
38
  puts("Notifying of change!")
51
- Notifier.notify(code_change_activity)
52
- sleep SECONDS_BETWEEN_NOTIFICATIONS
39
+ CodeChangeNotification.new(code_change_activity).send
40
+ sleep(SECONDS_BETWEEN_NOTIFICATIONS)
53
41
  end
54
42
  end
55
- sleep SECONDS_BETWEEN_RUNS
43
+ sleep(SECONDS_BETWEEN_RUNS)
56
44
  end
57
45
  end
58
46
 
59
- def self.is_first_run?
60
- DB.query_single_row("SELECT id FROM code_change_activity_notified;").nil?
47
+ def not_setup
48
+ Rubiclifier::Notification.new(
49
+ "Missing Setup Info",
50
+ "Run `code_review_notifier --setup` to setup."
51
+ ).send
52
+ end
53
+
54
+ def features
55
+ [
56
+ Rubiclifier::Feature::BACKGROUND,
57
+ Rubiclifier::Feature::DATABASE,
58
+ Rubiclifier::Feature::NOTIFICATIONS
59
+ ]
60
+ end
61
+
62
+ def settings
63
+ @settings ||= [
64
+ Rubiclifier::Setting.new("base_api_url", "base URL", explanation: "e.g. https://gerrit.google.com"),
65
+ Rubiclifier::Setting.new("username", "account username"),
66
+ Rubiclifier::Setting.new("password", "account password", explanation: "input hidden", is_secret: true),
67
+ Rubiclifier::Setting.new("account_id", "account ID", explanation: -> {"check #{Api.current_api.base_api_url}/settings/"})
68
+ ]
69
+ end
70
+
71
+ def executable_name
72
+ "code_review_notifier"
73
+ end
74
+
75
+ def data_directory
76
+ "~/.code_review_notifier"
77
+ end
78
+
79
+ def migrations_location
80
+ "#{File.expand_path(File.dirname(__FILE__) + "/..")}/migrations.rb"
81
+ end
82
+
83
+ def is_first_run?
84
+ Rubiclifier::DB.query_single_row("SELECT id FROM code_change_activity_notified;").nil?
61
85
  end
62
86
  end
@@ -1,9 +1,15 @@
1
- require_relative "./base_api.rb"
1
+ require "rubiclifier"
2
2
  require_relative "./models/code_change.rb"
3
3
  require_relative "./models/code_change_activity.rb"
4
4
 
5
- class GerritApi < BaseApi
6
- def self.authenticate
5
+ class HTTParty::Parser
6
+ def json
7
+ JSON.parse(body.gsub(")]}'", ""))
8
+ end
9
+ end
10
+
11
+ class GerritApi < Rubiclifier::BaseApi
12
+ def self.login_and_get_api_token
7
13
  res = post("/login/", {
8
14
  body: "username=#{username}&password=#{URI.escape(password)}&rememberme=1",
9
15
  headers: {
@@ -11,15 +17,24 @@ class GerritApi < BaseApi
11
17
  },
12
18
  follow_redirects: false
13
19
  })
14
- @token = res.headers["set-cookie"].match("GerritAccount=(.*?);")[1]
15
- raise "Your username or password is incorrect. Trying running `code_review_notifier --setup` again." if @token.nil?
20
+ set_cookie_header = res.headers["set-cookie"] || ""
21
+ set_cookie_header.match("GerritAccount=(.*?);")&.to_a&.fetch(1)
22
+ end
23
+
24
+ def self.invalid_credentials_error
25
+ Rubiclifier::Notification.new(
26
+ "Incorrect Credentials",
27
+ "Trying running `code_review_notifier --setup` again."
28
+ ).send
29
+ sleep(120)
30
+ exit
16
31
  end
17
32
 
18
33
  def self.all_code_changes
19
34
  wrap_with_authentication do
20
35
  get("/changes/?S=0&q=is%3Aopen%20owner%3Aself%20-is%3Awip%20-is%3Aignored%20limit%3A25&q=is%3Aopen%20owner%3Aself%20is%3Awip%20limit%3A25&q=is%3Aopen%20-owner%3Aself%20-is%3Awip%20-is%3Aignored%20reviewer%3Aself%20limit%3A25&q=is%3Aclosed%20-is%3Aignored%20%28-is%3Awip%20OR%20owner%3Aself%29%20%28owner%3Aself%20OR%20reviewer%3Aself%29%20-age%3A4w%20limit%3A10&o=DETAILED_ACCOUNTS&o=MESSAGES", {
21
36
  headers: {
22
- "Cookie" => "GerritAccount=#{@token};"
37
+ "Cookie" => "GerritAccount=#{api_token};"
23
38
  }
24
39
  })
25
40
  end.parsed_response.flat_map { |js| js.map { |j| code_change_from_json(j) } }
@@ -42,4 +57,24 @@ class GerritApi < BaseApi
42
57
  def self.code_change_url(code_change)
43
58
  "#{base_api_url}/c/#{code_change.id}"
44
59
  end
60
+
61
+ def self.api_token_db_key
62
+ "api_token"
63
+ end
64
+
65
+ def self.base_api_url_db_key
66
+ "base_api_url"
67
+ end
68
+
69
+ def self.username
70
+ @username ||= Rubiclifier::DB.get_setting("username")
71
+ end
72
+
73
+ def self.password
74
+ @password ||= Rubiclifier::DB.get_setting("password")
75
+ end
76
+
77
+ def self.account_id
78
+ @account_id ||= Rubiclifier::DB.get_setting("account_id")
79
+ end
45
80
  end
@@ -1,5 +1,4 @@
1
1
  require "date"
2
- require_relative "../database.rb"
3
2
 
4
3
  class CodeChange
5
4
  attr_accessor :code_change_activity
@@ -10,7 +9,7 @@ class CodeChange
10
9
  @id = id
11
10
  @owner = owner
12
11
  @project = project
13
- @subject = subject.gsub("'", "")
12
+ @subject = subject.gsub("'", "")
14
13
  @updated_at = updated_at
15
14
  end
16
15
 
@@ -1,3 +1,5 @@
1
+ require "rubiclifier"
2
+
1
3
  MESSAGES_TO_IGNORE = [/Uploaded patch set 1/, /Build Started/, /owns \d+% of/]
2
4
  AUTHOR_TRANSLATIONS = {
3
5
  "Service Cloud Jenkins" => "Jenkins",
@@ -17,14 +19,14 @@ class CodeChangeActivity
17
19
  end
18
20
 
19
21
  def notified
20
- DB.execute("INSERT INTO code_change_activity_notified (id, notified_at) VALUES('#{id}', CURRENT_TIMESTAMP);")
22
+ Rubiclifier::DB.execute("INSERT INTO code_change_activity_notified (id, notified_at) VALUES('#{id}', CURRENT_TIMESTAMP);")
21
23
  end
22
24
 
23
25
  def should_notify?
24
26
  !is_self &&
25
27
  code_change.activity_from_self_at && created_at > code_change.activity_from_self_at &&
26
28
  MESSAGES_TO_IGNORE.none? { |m| message.match(m) } &&
27
- !DB.query_single_row("SELECT id FROM code_change_activity_notified WHERE id = '#{id}'")
29
+ !Rubiclifier::DB.query_single_row("SELECT id FROM code_change_activity_notified WHERE id = '#{id}'")
28
30
  end
29
31
 
30
32
  def self.translate_author(author)
@@ -0,0 +1,8 @@
1
+ migrations = []
2
+ migrations << <<SQL
3
+ CREATE TABLE IF NOT EXISTS code_change_activity_notified (
4
+ id VARCHAR(50) PRIMARY KEY,
5
+ notified_at TIMESTAMP
6
+ );
7
+ SQL
8
+ migrations
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code_review_notifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Grinstead
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-07 00:00:00.000000000 Z
11
+ date: 2020-07-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: kyleag@hey.com
@@ -17,24 +17,22 @@ executables:
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
- - Brewfile
21
20
  - Gemfile
22
21
  - bin/code_review_notifier
23
22
  - lib/api.rb
24
- - lib/base_api.rb
25
- - lib/cipher.rb
23
+ - lib/code_change_notification.rb
26
24
  - lib/code_review_notifier.rb
27
- - lib/database.rb
28
25
  - lib/gerrit_api.rb
29
26
  - lib/models/code_change.rb
30
27
  - lib/models/code_change_activity.rb
31
- - lib/notifier.rb
28
+ - migrations.rb
32
29
  homepage: https://rubygems.org/gems/code_review_notifier
33
30
  licenses:
34
31
  - MIT
35
32
  metadata:
36
33
  source_code_uri: https://github.com/MrGrinst/code_review_notifier
37
- post_install_message:
34
+ post_install_message: "\n\e[32mThanks for installing code_review_notifier!\e[0m\n\e[32mSet
35
+ it up by running `\e[0mcode_review_notifier --setup\e[32m`\e[0m\n\n"
38
36
  rdoc_options: []
39
37
  require_paths:
40
38
  - lib
data/Brewfile DELETED
@@ -1,2 +0,0 @@
1
- brew 'sqlite'
2
- brew 'terminal-notifier'
@@ -1,62 +0,0 @@
1
- require "httparty"
2
- require_relative "./gerrit_api.rb"
3
-
4
- class HTTParty::Parser
5
- def json
6
- JSON.parse(body.gsub(")]}'", ""))
7
- end
8
- end
9
-
10
- class BaseApi
11
- include HTTParty
12
-
13
- def self.authenticate
14
- raise NotImplementedError
15
- end
16
-
17
- def self.all_code_changes
18
- raise NotImplementedError
19
- end
20
-
21
- def self.favicon
22
- raise NotImplementedError
23
- end
24
-
25
- def self.code_change_url(code_change)
26
- raise NotImplementedError
27
- end
28
-
29
- def self.wrap_with_authentication(&block)
30
- res = block.call
31
- if res.code == 401 || res.code == 403
32
- self.authenticate
33
- block.call
34
- else
35
- res
36
- end
37
- end
38
-
39
- def self.is_setup?
40
- base_api_url && username && password && account_id
41
- end
42
-
43
- def self.base_api_url
44
- @base_api_url ||= begin
45
- url = DB.get_setting("base_api_url")
46
- base_uri(url)
47
- url.chomp("/")
48
- end
49
- end
50
-
51
- def self.username
52
- @username ||= DB.get_setting("username")
53
- end
54
-
55
- def self.password
56
- @password ||= DB.get_setting("password")
57
- end
58
-
59
- def self.account_id
60
- @account_id ||= DB.get_setting("account_id")
61
- end
62
- end
@@ -1,31 +0,0 @@
1
- require "base64"
2
- require "openssl"
3
- require "digest/sha1"
4
-
5
- KEY = ""
6
-
7
- class Cipher
8
- def self.encrypt(text)
9
- cipher = OpenSSL::Cipher.new("aes-256-cbc")
10
- cipher.encrypt
11
- key = Digest::SHA1.hexdigest("yourpass")[0..31]
12
- iv = cipher.random_iv
13
- cipher.key = key
14
- cipher.iv = iv
15
- encrypted = cipher.update(text)
16
- encrypted << cipher.final
17
- ["#{key}:#{Base64.encode64(iv).encode('utf-8')}", Base64.encode64(encrypted).encode('utf-8')]
18
- end
19
-
20
- def self.decrypt(encrypted, salt)
21
- key, iv = salt.split(":")
22
- iv = Base64.decode64(iv.encode('ascii-8bit'))
23
- cipher = OpenSSL::Cipher.new("aes-256-cbc")
24
- cipher.decrypt
25
- cipher.key = key
26
- cipher.iv = iv
27
- decrypted = cipher.update(Base64.decode64(encrypted.encode('ascii-8bit')))
28
- decrypted << cipher.final
29
- decrypted
30
- end
31
- end
@@ -1,48 +0,0 @@
1
- require "sqlite3"
2
- require_relative "./cipher.rb"
3
-
4
- class DB
5
- def self.db
6
- @db ||= begin
7
- db = SQLite3::Database.new(File.expand_path("~/.code_review_notifier/data.db"))
8
- migrate_if_needed(db)
9
- db
10
- end
11
- end
12
-
13
- def self.migrate_if_needed(db)
14
- db.execute("CREATE TABLE IF NOT EXISTS code_change_activity_notified (id VARCHAR(50) PRIMARY KEY, notified_at TIMESTAMP);")
15
- db.execute("CREATE TABLE IF NOT EXISTS settings (key VARCHAR(50) PRIMARY KEY, value TEXT, salt TEXT);")
16
- end
17
-
18
- def self.execute(sql)
19
- db.execute(sql)
20
- end
21
-
22
- def self.query_single_row(sql)
23
- db.execute(sql) do |row|
24
- return row
25
- end
26
- return nil
27
- end
28
-
29
- def self.save_setting(key, value, is_secret:)
30
- salt = "NULL"
31
- if is_secret
32
- salt, encrypted = Cipher.encrypt(value)
33
- salt = "'#{salt}'"
34
- value = encrypted
35
- end
36
- db.execute("DELETE FROM settings WHERE key = '#{key}';")
37
- db.execute("INSERT INTO settings (key, value, salt) VALUES('#{key}', '#{value}', #{salt});")
38
- end
39
-
40
- def self.get_setting(key)
41
- row = query_single_row("SELECT value, salt FROM settings WHERE key = '#{key}'")
42
- if row && row[1]
43
- Cipher.decrypt(row[0], row[1])
44
- elsif row
45
- row[0]
46
- end
47
- end
48
- end
@@ -1,14 +0,0 @@
1
- require_relative './api.rb'
2
-
3
- class Notifier
4
- def self.notify(code_change_activity)
5
- code_change = code_change_activity.code_change
6
- id = code_change.id
7
- owner = code_change.owner
8
- subject = code_change.subject
9
-
10
- message = code_change_activity.message
11
- author = code_change_activity.author
12
- system("/usr/local/bin/terminal-notifier -title '#{author}' -subtitle '#{owner}: #{subject}' -message '#{message}' -appIcon #{Api.current_api.favicon} -open '#{Api.current_api.code_change_url(code_change)}'")
13
- end
14
- end