code_review_notifier 0.1.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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