Install Octobox as a GitHub App (#806)

* Lock pg gem to 1.0.0

1.1.0 and 1.1.1 are broken on alpine linux

* Install Octobox as a GitHub App
This commit is contained in:
Andrew Nesbitt 2018-08-28 12:36:03 +01:00 коммит произвёл GitHub
Родитель 1fcf354d23
Коммит ad92abebb1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
39 изменённых файлов: 1568 добавлений и 38 удалений

Просмотреть файл

@ -3,7 +3,7 @@ class ApplicationController < ActionController::Base
API_HEADER = 'X-Octobox-API' API_HEADER = 'X-Octobox-API'
protect_from_forgery with: :exception, unless: -> { octobox_api_request? } protect_from_forgery with: :exception, unless: -> { octobox_api_request? }
helper_method :current_user, :logged_in?, :initial_sync? helper_method :current_user, :logged_in?, :initial_sync?, :display_subject?
before_action :authenticate_user! before_action :authenticate_user!
before_action :check_access_token_present before_action :check_access_token_present
@ -24,6 +24,11 @@ class ApplicationController < ActionController::Base
private private
def display_subject?
return true if Octobox.config.fetch_subject
Octobox.config.github_app && current_user && current_user.app_token.present?
end
def add_user_info_to_bugsnag(notification) def add_user_info_to_bugsnag(notification)
return unless logged_in? return unless logged_in?
@ -53,7 +58,7 @@ class ApplicationController < ActionController::Base
def check_access_token_present def check_access_token_present
if current_user && current_user.access_token.nil? if current_user && current_user.access_token.nil?
cookies.delete(:user_id) cookies.delete(:user_id)
redirect_to root_url redirect_to login_path
end end
end end

Просмотреть файл

@ -0,0 +1,138 @@
class HooksController < ApplicationController
skip_before_action :verify_authenticity_token
skip_before_action :authenticate_user!
before_action :authenticate_github_request!
def create
case event_header
when 'issues', 'issue_comment'
remote_subject = payload.issue
subject = Subject.find_or_create_by(url: remote_subject.url)
subject.update({
state: remote_subject.state,
author: remote_subject.user.login,
html_url: remote_subject.html_url,
created_at: remote_subject.created_at,
updated_at: remote_subject.updated_at
})
subject.sync_involved_users
when 'pull_request'
remote_subject = payload.pull_request
subject = Subject.find_or_create_by(url: remote_subject.url)
subject.update({
state: remote_subject.merged_at.present? ? 'merged' : remote_subject.state,
author: remote_subject.user.login,
html_url: remote_subject.html_url,
created_at: remote_subject.created_at,
updated_at: remote_subject.updated_at
})
subject.sync_involved_users
when 'label'
if payload.action == 'edited'
repository = Repository.find_by_github_id(payload.repository.id)
return if repository.nil?
subjects = repository.subjects.label(payload.changes.name.from)
subjects.each do |subject|
n = subject.notifications.first
n.try(:send, :update_subject, true)
end
end
when 'installation'
case payload.action
when 'created'
app_installation = AppInstallation.create({
github_id: payload.installation.id,
app_id: payload.installation.app_id,
account_login: payload.installation.account.login,
account_id: payload.installation.account.id,
account_type: payload.installation.account.type,
target_type: payload.installation.target_type,
target_id: payload.installation.target_id,
permission_pull_requests: payload.installation.permissions.pull_requests,
permission_issues: payload.installation.permissions.issues
})
payload.repositories.each do |remote_repository|
repository = Repository.find_or_create_by(github_id: remote_repository.id)
repository.update_attributes({
full_name: remote_repository.full_name,
private: remote_repository.private,
owner: remote_repository.full_name.split('/').first,
github_id: remote_repository.id,
last_synced_at: Time.current,
app_installation_id: app_installation.id
})
repository.notifications.each{|n| n.send :update_subject, true }
end
when 'deleted'
AppInstallation.find_by_github_id(payload.installation.id).try(:destroy)
end
when 'installation_repositories'
app_installation = AppInstallation.find_by_github_id(payload.installation.id)
return unless app_installation.present?
payload.repositories_added.each do |remote_repository|
repository = app_installation.repositories.find_or_create_by(github_id: remote_repository.id)
repository.update_attributes({
full_name: remote_repository.full_name,
private: remote_repository.private,
owner: remote_repository.full_name.split('/').first,
github_id: remote_repository.id,
last_synced_at: Time.current,
app_installation_id: app_installation.id
})
repository.notifications.each{|n| n.send :update_subject, true }
end
payload.repositories_removed.each do |remote_repository|
repository = app_installation.repositories.find_by_github_id(remote_repository.id)
next unless repository.present?
repository.subjects.each(&:destroy)
repository.destroy
end
when 'github_app_authorization'
user = User.find_by_github_id(payload.sender.id)
user.update_attributes(app_token: nil) if user.present?
end
head :no_content
end
private
HMAC_DIGEST = OpenSSL::Digest.new('sha1')
def authenticate_github_request!
secret = Rails.application.secrets.github_webhook_secret
return unless secret.present?
expected_signature = "sha1=#{OpenSSL::HMAC.hexdigest(HMAC_DIGEST, secret, request_body)}"
if signature_header != expected_signature
raise ActiveSupport::MessageVerifier::InvalidSignature
end
end
def request_body
@request_body ||= (
request.body.rewind
request.body.read
)
end
def payload
@payload ||= JSON.parse(request_body, object_class: OpenStruct)
end
def signature_header
request.headers['X-Hub-Signature']
end
def event_header
request.headers['X-GitHub-Event']
end
end

Просмотреть файл

@ -76,7 +76,7 @@ class NotificationsController < ApplicationController
@reasons = scope.reorder(nil).distinct.group(:reason).count @reasons = scope.reorder(nil).distinct.group(:reason).count
@unread_repositories = scope.reorder(nil).distinct.group(:repository_full_name).count @unread_repositories = scope.reorder(nil).distinct.group(:repository_full_name).count
if Octobox.config.fetch_subject if display_subject?
@states = scope.reorder(nil).distinct.joins(:subject).group('subjects.state').count @states = scope.reorder(nil).distinct.joins(:subject).group('subjects.state').count
@unlabelled = scope.reorder(nil).unlabelled.count @unlabelled = scope.reorder(nil).unlabelled.count
@bot_notifications = scope.reorder(nil).bot_author.count @bot_notifications = scope.reorder(nil).bot_author.count
@ -241,7 +241,7 @@ class NotificationsController < ApplicationController
end end
def notifications_for_presentation def notifications_for_presentation
eager_load_relation = Octobox.config.fetch_subject ? [{subject: :labels}, :repository] : nil eager_load_relation = display_subject? ? [{subject: :labels}, :repository] : nil
scope = current_user.notifications.includes(eager_load_relation) scope = current_user.notifications.includes(eager_load_relation)
if params[:q].present? if params[:q].present?

Просмотреть файл

@ -9,7 +9,7 @@ class SessionsController < ApplicationController
def create def create
user = User.find_by_auth_hash(auth_hash) || User.new user = User.find_by_auth_hash(auth_hash) || User.new
user.assign_from_auth_hash(auth_hash) user.assign_from_auth_hash(auth_hash, params[:provider])
cookies.permanent.signed[:user_id] = {value: user.id, httponly: true} cookies.permanent.signed[:user_id] = {value: user.id, httponly: true}

Просмотреть файл

@ -226,8 +226,4 @@ module NotificationsHelper
return true unless param == :repo return true unless param == :repo
params[:owner].blank? params[:owner].blank?
end end
def display_subject?
Octobox.config.fetch_subject
end
end end

Просмотреть файл

@ -0,0 +1,7 @@
class AppInstallation < ApplicationRecord
has_many :repositories, dependent: :destroy
validates :github_id, presence: true, uniqueness: true
validates :account_login, presence: true
validates :account_id, presence: true
end

Просмотреть файл

@ -72,7 +72,7 @@ class Notification < ApplicationRecord
end end
def state def state
return unless Octobox.config.fetch_subject return unless display_subject?
subject.try(:state) subject.try(:state)
end end
@ -115,7 +115,7 @@ class Notification < ApplicationRecord
end end
def expanded_subject_url def expanded_subject_url
return subject_url unless Octobox.config.fetch_subject return subject_url unless Octobox.config.subjects_enabled?
subject.try(:html_url) || subject_url # Use the sync'd HTML URL if possible, else the API one subject.try(:html_url) || subject_url # Use the sync'd HTML URL if possible, else the API one
end end
@ -146,14 +146,22 @@ class Notification < ApplicationRecord
update_repository update_repository
end end
def github_app_installed?
user.app_token.present? && repository.try(:github_app_installed?)
end
def subjectable? def subjectable?
SUBJECTABLE_TYPES.include?(subject_type) SUBJECTABLE_TYPES.include?(subject_type)
end end
def display_subject?
github_app_installed? || Octobox.config.fetch_subject
end
private private
def download_subject def download_subject
user.github_client.get(subject_url) user.subject_client.get(subject_url)
# If permissions changed and the user hasn't accepted, we get a 401 # If permissions changed and the user hasn't accepted, we get a 401
# We may receive a 403 Forbidden or a 403 Not Available # We may receive a 403 Forbidden or a 403 Not Available
@ -169,7 +177,7 @@ class Notification < ApplicationRecord
def update_subject(force = false) def update_subject(force = false)
return unless subjectable? return unless subjectable?
return unless Octobox.config.fetch_subject return unless display_subject?
# skip syncing if the notification was updated around the same time as subject # skip syncing if the notification was updated around the same time as subject
return if !force && subject != nil && updated_at - subject.updated_at < 2.seconds return if !force && subject != nil && updated_at - subject.updated_at < 2.seconds
@ -219,7 +227,7 @@ class Notification < ApplicationRecord
end end
def update_repository def update_repository
return unless Octobox.config.fetch_subject return unless display_subject?
return if repository != nil && updated_at - repository.updated_at < 2.seconds return if repository != nil && updated_at - repository.updated_at < 2.seconds
remote_repository = download_repository remote_repository = download_repository

Просмотреть файл

@ -1,9 +1,14 @@
class Repository < ApplicationRecord class Repository < ApplicationRecord
has_many :notifications, foreign_key: :repository_full_name, primary_key: :full_name has_many :notifications, foreign_key: :repository_full_name, primary_key: :full_name
belongs_to :app_installation
validates :full_name, presence: true, uniqueness: true validates :full_name, presence: true, uniqueness: true
validates :github_id, uniqueness: true validates :github_id, uniqueness: true
def github_app_installed?
app_installation_id.present?
end
def subjects def subjects
Subject.repository(full_name) Subject.repository(full_name)
end end

Просмотреть файл

@ -9,6 +9,8 @@ class Subject < ApplicationRecord
scope :label, ->(label_name) { joins(:labels).where(Label.arel_table[:name].matches(label_name)) } scope :label, ->(label_name) { joins(:labels).where(Label.arel_table[:name].matches(label_name)) }
scope :repository, ->(full_name) { where(arel_table[:url].matches("%/repos/#{full_name}/%")) } scope :repository, ->(full_name) { where(arel_table[:url].matches("%/repos/#{full_name}/%")) }
after_update :sync_involved_users
def author_url def author_url
"#{Octobox.config.github_domain}#{author_url_path}" "#{Octobox.config.github_domain}#{author_url_path}"
end end
@ -37,6 +39,7 @@ class Subject < ApplicationRecord
end end
def sync_involved_users def sync_involved_users
return unless Octobox.github_app?
user_ids.each { |user_id| SyncNotificationsWorker.perform_async(user_id) } user_ids.each { |user_id| SyncNotificationsWorker.perform_async(user_id) }
end end

Просмотреть файл

@ -2,6 +2,7 @@
class User < ApplicationRecord class User < ApplicationRecord
attr_encrypted :access_token, key: Octobox.config.attr_encyrption_key attr_encrypted :access_token, key: Octobox.config.attr_encyrption_key
attr_encrypted :personal_access_token, key: Octobox.config.attr_encyrption_key attr_encrypted :personal_access_token, key: Octobox.config.attr_encyrption_key
attr_encrypted :app_token, key: Octobox.config.attr_encyrption_key
has_secure_token :api_token has_secure_token :api_token
has_many :notifications, dependent: :delete_all has_many :notifications, dependent: :delete_all
@ -15,7 +16,7 @@ class User < ApplicationRecord
}.freeze }.freeze
validates :github_id, presence: true, uniqueness: true validates :github_id, presence: true, uniqueness: true
validates :encrypted_access_token, presence: true, uniqueness: true validates :encrypted_access_token, uniqueness: true
validates :github_login, presence: true validates :github_login, presence: true
validates :refresh_interval, numericality: { validates :refresh_interval, numericality: {
only_integer: true, only_integer: true,
@ -45,11 +46,12 @@ class User < ApplicationRecord
User.find_by(github_id: auth_hash['uid']) User.find_by(github_id: auth_hash['uid'])
end end
def assign_from_auth_hash(auth_hash) def assign_from_auth_hash(auth_hash, app = 'github')
token_field = app == 'github' ? :access_token : :app_token
github_attributes = { github_attributes = {
github_id: auth_hash['uid'], github_id: auth_hash['uid'],
github_login: auth_hash['info']['nickname'], github_login: auth_hash['info']['nickname'],
access_token: auth_hash.dig('credentials', 'token') token_field => auth_hash.dig('credentials', 'token')
} }
update_attributes(github_attributes) update_attributes(github_attributes)
@ -73,6 +75,14 @@ class User < ApplicationRecord
@github_client @github_client
end end
def subject_client
Octokit::Client.new(access_token: subject_token, auto_paginate: true)
end
def subject_token
app_token || effective_access_token
end
def github_avatar_url def github_avatar_url
"#{Octobox.config.github_domain}/#{github_login}.png" "#{Octobox.config.github_domain}/#{github_login}.png"
end end

Просмотреть файл

@ -64,7 +64,7 @@ class DownloadService
def process_notifications(notifications, unarchive: false) def process_notifications(notifications, unarchive: false)
return if notifications.blank? return if notifications.blank?
eager_load_relation = Octobox.config.fetch_subject ? :subject : nil eager_load_relation = Octobox.config.subjects_enabled? ? :subject : nil
existing_notifications = user.notifications.includes(eager_load_relation).where(github_id: notifications.map(&:id)) existing_notifications = user.notifications.includes(eager_load_relation).where(github_id: notifications.map(&:id))
notifications.reject{|n| !unarchive && n.unread }.each do |notification| notifications.reject{|n| !unarchive && n.unread }.each do |notification|
n = existing_notifications.find{|en| en.github_id == notification.id.to_i} n = existing_notifications.find{|en| en.github_id == notification.id.to_i}

Просмотреть файл

@ -21,13 +21,13 @@
</td> </td>
<% end %> <% end %>
<td class='notification-icon <%= notification_icon_color(notification.state) if display_subject? %>'> <td class='notification-icon <%= notification_icon_color(notification.state) if notification.display_subject? %>'>
<%= octicon notification_icon(notification.subject_type, notification.state), :height => 16, title: notification_icon_title(notification.subject_type, notification.state), data: {toggle: 'tooltip'} %> <%= octicon notification_icon(notification.subject_type, notification.state), :height => 16, title: notification_icon_title(notification.subject_type, notification.state), data: {toggle: 'tooltip'} %>
</td> </td>
<td class='notification-subject'> <td class='notification-subject'>
<%= link_to notification.subject_title, notification.web_url, target: '_blank', rel: "noopener", class: 'link', onclick: "markRead(#{notification.id})" %> <%= link_to notification.subject_title, notification.web_url, target: '_blank', rel: "noopener", class: 'link', onclick: "markRead(#{notification.id})" %>
<% if display_subject? %> <% if notification.display_subject? %>
<% if notification.subject %> <% if notification.subject %>
<% notification.subject.labels.each do |label| %> <% notification.subject.labels.each do |label| %>
<%= link_to emojify(label.name), root_path(filtered_params(label: label.name)), class: "badge d-sm", style: "background-color: ##{label.color}; color: #{label.text_color}" %> <%= link_to emojify(label.name), root_path(filtered_params(label: label.name)), class: "badge d-sm", style: "background-color: ##{label.color}; color: #{label.text_color}" %>
@ -42,15 +42,15 @@
</small> </small>
</td> </td>
<% end %> <% end %>
<% if display_subject? %>
<td class='notification-subject-author'> <td class='notification-subject-author'>
<% if notification.display_subject? %>
<% if notification.subject %> <% if notification.subject %>
<small> <small>
<%= link_to notification.subject.author, root_path(filtered_params(author: notification.subject.author)), class: 'text-muted', title: 'Author', data: {toggle: 'tooltip'} %> <%= link_to notification.subject.author, root_path(filtered_params(author: notification.subject.author)), class: 'text-muted', title: 'Author', data: {toggle: 'tooltip'} %>
</small> </small>
<% end %> <% end %>
</td>
<% end %> <% end %>
</td>
<td class='notification-reason'> <td class='notification-reason'>
<%= link_to notification.reason.humanize, root_path(filtered_params(reason: notification.reason)), class: "badge badge-#{reason_label(notification.reason)}" %> <%= link_to notification.reason.humanize, root_path(filtered_params(reason: notification.reason)), class: "badge badge-#{reason_label(notification.reason)}" %>
</td> </td>

Просмотреть файл

@ -9,6 +9,12 @@
<%= octicon 'mark-github', height: 22 %> <%= octicon 'mark-github', height: 22 %>
<span class='ml-1'>Sign in with GitHub</span> <span class='ml-1'>Sign in with GitHub</span>
<% end %> <% end %>
<% if Octobox.github_app? && !Octobox.config.octobox_io %>
<%= link_to Octobox.config.app_install_url, class: 'btn btn-outline-dark btn-lg' do %>
<%= octicon 'briefcase', height: 22 %>
<span class='ml-1'>Install the GitHub App</span>
<% end %>
<% end %>
</p> </p>
<p> <p>
<a class="github-button" href="https://github.com/octobox/octobox" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star octobox/octobox on GitHub">Star</a> <a class="github-button" href="https://github.com/octobox/octobox" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star octobox/octobox on GitHub">Star</a>

Просмотреть файл

@ -108,6 +108,13 @@
<%= octicon 'tools' %>&nbsp; <%= octicon 'tools' %>&nbsp;
Manage Oauth Access on GitHub Manage Oauth Access on GitHub
<% end %> <% end %>
<% if Octobox.github_app? && current_user.app_token.present? %>
<%= link_to "#{Octobox.config.github_domain}/settings/connections/applications/#{Rails.application.secrets.github_app_client_id}", class: 'btn btn-outline-dark' do %>
<%= octicon 'octoface' %>&nbsp;
Manage App Access on GitHub
<% end %>
<% end %>
</div> </div>
</div> </div>

Просмотреть файл

@ -1,4 +1,4 @@
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file. # Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password, :personal_access_token, :access_token] Rails.application.config.filter_parameters += [:password, :personal_access_token, :access_token, :app_token]

Просмотреть файл

@ -1,4 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
module OmniAuth
module Strategies
class GithubApp < GitHub
end
end
end
Rails.application.config.middleware.use OmniAuth::Builder do Rails.application.config.middleware.use OmniAuth::Builder do
site = Octobox.config.github_api_prefix site = Octobox.config.github_api_prefix
authorize_url = "#{Octobox.config.github_domain}/login/oauth/authorize" authorize_url = "#{Octobox.config.github_domain}/login/oauth/authorize"
@ -9,4 +16,11 @@ Rails.application.config.middleware.use OmniAuth::Builder do
Rails.application.secrets.github_client_secret, Rails.application.secrets.github_client_secret,
client_options: { site: site, authorize_url: authorize_url, token_url: token_url }, client_options: { site: site, authorize_url: authorize_url, token_url: token_url },
scope: Octobox.config.scopes scope: Octobox.config.scopes
if Octobox.github_app?
provider :github_app,
Rails.application.secrets.github_app_client_id,
Rails.application.secrets.github_app_client_secret,
client_options: { site: site, authorize_url: authorize_url, token_url: token_url }
end
end end

Просмотреть файл

@ -42,6 +42,8 @@ Rails.application.routes.draw do
end end
end end
post '/hooks/github', to: 'hooks#create'
if Octobox.config.octobox_io if Octobox.config.octobox_io
get '/privacy', to: 'pages#privacy' get '/privacy', to: 'pages#privacy'
get '/terms', to: 'pages#terms' get '/terms', to: 'pages#terms'

Просмотреть файл

@ -16,6 +16,10 @@ default: &default
google_analytics_id: <%= ENV['GA_ANALYTICS_ID'] %> google_analytics_id: <%= ENV['GA_ANALYTICS_ID'] %>
bugsnag_api_key: <%= ENV['BUGSNAG_API_KEY'] %> bugsnag_api_key: <%= ENV['BUGSNAG_API_KEY'] %>
bugsnag_js_api_key: <%= ENV['BUGSNAG_JS_API_KEY'] %> bugsnag_js_api_key: <%= ENV['BUGSNAG_JS_API_KEY'] %>
github_webhook_secret: <%= ENV['GITHUB_WEBHOOK_SECRET'] %>
github_app_id: <%= ENV['GITHUB_APP_ID'] %>
github_app_client_id: <%= ENV['GITHUB_APP_CLIENT_ID'] %>
github_app_client_secret: <%= ENV['GITHUB_APP_CLIENT_SECRET'] %>
development: development:
<<: *default <<: *default

Просмотреть файл

@ -0,0 +1,6 @@
class AddAppTokenToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :encrypted_app_token, :string
add_column :users, :encrypted_app_token_iv, :string
end
end

Просмотреть файл

@ -0,0 +1,17 @@
class CreateAppInstallations < ActiveRecord::Migration[5.2]
def change
create_table :app_installations do |t|
t.integer :github_id
t.integer :app_id
t.string :account_login
t.integer :account_id
t.string :account_type
t.string :target_type
t.integer :target_id
t.string :permission_pull_requests
t.string :permission_issues
t.timestamps
end
end
end

Просмотреть файл

@ -0,0 +1,5 @@
class AddAppInstallationIdToRepositories < ActiveRecord::Migration[5.2]
def change
add_column :repositories, :app_installation_id, :integer
end
end

Просмотреть файл

@ -16,6 +16,20 @@ ActiveRecord::Schema.define(version: 2018_08_17_172203) do
enable_extension "pg_stat_statements" enable_extension "pg_stat_statements"
enable_extension "plpgsql" enable_extension "plpgsql"
create_table "app_installations", force: :cascade do |t|
t.integer "github_id"
t.integer "app_id"
t.string "account_login"
t.integer "account_id"
t.string "account_type"
t.string "target_type"
t.integer "target_id"
t.string "permission_pull_requests"
t.string "permission_issues"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "labels", force: :cascade do |t| create_table "labels", force: :cascade do |t|
t.string "name" t.string "name"
t.string "color" t.string "color"
@ -58,6 +72,7 @@ ActiveRecord::Schema.define(version: 2018_08_17_172203) do
t.datetime "last_synced_at" t.datetime "last_synced_at"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "app_installation_id"
t.index ["full_name"], name: "index_repositories_on_full_name", unique: true t.index ["full_name"], name: "index_repositories_on_full_name", unique: true
end end
@ -85,6 +100,8 @@ ActiveRecord::Schema.define(version: 2018_08_17_172203) do
t.string "encrypted_access_token_iv" t.string "encrypted_access_token_iv"
t.string "encrypted_personal_access_token" t.string "encrypted_personal_access_token"
t.string "encrypted_personal_access_token_iv" t.string "encrypted_personal_access_token_iv"
t.string "encrypted_app_token"
t.string "encrypted_app_token_iv"
t.index ["api_token"], name: "index_users_on_api_token", unique: true t.index ["api_token"], name: "index_users_on_api_token", unique: true
t.index ["github_id"], name: "index_users_on_github_id", unique: true t.index ["github_id"], name: "index_users_on_github_id", unique: true
end end

Просмотреть файл

@ -29,6 +29,7 @@ in your GitHub settings for Octobox to work.
* [Downloading subjects](#downloading-subjects) * [Downloading subjects](#downloading-subjects)
* [API Documentation](#api-documentation) * [API Documentation](#api-documentation)
* [Google Analytics](#google-analytics) * [Google Analytics](#google-analytics)
* [Running Octobox as a GitHub App](#api-documentation)
# Installation # Installation
## Database Selection ## Database Selection
@ -359,3 +360,5 @@ This is included by default in the container build using `Dockerfile`. To includ
To enable Google analytics tracking set the following environment variable: To enable Google analytics tracking set the following environment variable:
GA_ANALYTICS_ID=UA-XXXXXX-XX GA_ANALYTICS_ID=UA-XXXXXX-XX
## Running Octobox as a GitHub App

Просмотреть файл

@ -21,5 +21,9 @@ module Octobox
def restricted_access_enabled? def restricted_access_enabled?
config.restricted_access_enabled config.restricted_access_enabled
end end
def github_app?
config.github_app
end
end end
end end

Просмотреть файл

@ -32,17 +32,26 @@ module Octobox
def scopes def scopes
default_scopes = 'notifications' default_scopes = 'notifications'
default_scopes += ', read:org' if Octobox.restricted_access_enabled? default_scopes += ', read:org' if !github_app && Octobox.restricted_access_enabled?
default_scopes += ', repo' if fetch_subject default_scopes += ', repo' if !github_app && fetch_subject
ENV.fetch('GITHUB_SCOPE', default_scopes) ENV.fetch('GITHUB_SCOPE', default_scopes)
end end
def github_app
@github_app || ENV['GITHUB_APP_ID'].present?
end
attr_writer :github_app
def fetch_subject def fetch_subject
@fetch_subject || (ENV['FETCH_SUBJECT'].try(:downcase) == "true") @fetch_subject || (ENV['FETCH_SUBJECT'].try(:downcase) == "true")
end end
attr_writer :fetch_subject attr_writer :fetch_subject
def subjects_enabled?
github_app || fetch_subject
end
def personal_access_tokens_enabled def personal_access_tokens_enabled
@personal_access_tokens_enabled || ENV['PERSONAL_ACCESS_TOKENS_ENABLED'].present? @personal_access_tokens_enabled || ENV['PERSONAL_ACCESS_TOKENS_ENABLED'].present?
end end
@ -108,6 +117,21 @@ module Octobox
end end
attr_writer :source_repo attr_writer :source_repo
def app_install_url
"#{app_url}/installations/new"
end
attr_writer :app_install_url
def app_url
"#{github_domain}/apps/#{app_slug}"
end
attr_writer :app_url
def app_slug
ENV['GITHUB_APP_SLUG']
end
attr_writer :app_slug
def octobox_io def octobox_io
@octobox_io || ENV['OCTOBOX_IO'].present? @octobox_io || ENV['OCTOBOX_IO'].present?
end end

Просмотреть файл

@ -0,0 +1,53 @@
# frozen_string_literal: true
require 'test_helper'
class HooksControllerTest < ActionController::TestCase
test 'labels webhook payload' do
@request.headers['X-GitHub-Event'] = 'label'
fixture = 'label.json'
post :create, body: File.read("#{Rails.root}/test/fixtures/github_webhooks/#{fixture}")
assert_response :success
end
test 'github_app_authorization webhook payload' do
@request.headers['X-GitHub-Event'] = 'github_app_authorization'
fixture = 'github_app_authorization.json'
post :create, body: File.read("#{Rails.root}/test/fixtures/github_webhooks/#{fixture}")
assert_response :success
end
test 'installation_repositories webhook payload' do
@request.headers['X-GitHub-Event'] = 'installation_repositories'
fixture = 'installation_repositories.json'
post :create, body: File.read("#{Rails.root}/test/fixtures/github_webhooks/#{fixture}")
assert_response :success
end
test 'installation webhook payload' do
@request.headers['X-GitHub-Event'] = 'installation'
fixture = 'installation.json'
post :create, body: File.read("#{Rails.root}/test/fixtures/github_webhooks/#{fixture}")
assert_response :success
end
test 'issues webhook payload' do
@request.headers['X-GitHub-Event'] = 'issues'
fixture = 'issues.json'
post :create, body: File.read("#{Rails.root}/test/fixtures/github_webhooks/#{fixture}")
assert_response :success
end
test 'pull_request webhook payload' do
@request.headers['X-GitHub-Event'] = 'pull_request'
fixture = 'pull_request.json'
post :create, body: File.read("#{Rails.root}/test/fixtures/github_webhooks/#{fixture}")
assert_response :success
end
test 'issue_comment webhook payload' do
@request.headers['X-GitHub-Event'] = 'issue_comment'
fixture = 'issue_comment.json'
post :create, body: File.read("#{Rails.root}/test/fixtures/github_webhooks/#{fixture}")
assert_response :success
end
end

Просмотреть файл

@ -3,6 +3,7 @@ require 'test_helper'
class NotificationsControllerTest < ActionDispatch::IntegrationTest class NotificationsControllerTest < ActionDispatch::IntegrationTest
setup do setup do
Octobox.config.stubs(:github_app).returns(false)
stub_fetch_subject_enabled(value: false) stub_fetch_subject_enabled(value: false)
stub_notifications_request stub_notifications_request
stub_repository_request stub_repository_request
@ -112,6 +113,7 @@ class NotificationsControllerTest < ActionDispatch::IntegrationTest
test 'renders notifications filtered by label' do test 'renders notifications filtered by label' do
stub_fetch_subject_enabled stub_fetch_subject_enabled
Octobox.config.stubs(:github_app).returns(false)
sign_in_as(@user) sign_in_as(@user)
get '/' get '/'

Просмотреть файл

@ -3,6 +3,7 @@ require 'test_helper'
class SessionsControllerTest < ActionDispatch::IntegrationTest class SessionsControllerTest < ActionDispatch::IntegrationTest
setup do setup do
Octobox.config.stubs(:github_app).returns(false)
@notifications_request = stub_notifications_request(body: '[]') @notifications_request = stub_notifications_request(body: '[]')
@user = create(:user) @user = create(:user)
end end
@ -13,7 +14,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
@user.save(validate: false) # Requires access token @user.save(validate: false) # Requires access token
get '/settings' get '/settings'
assert_redirected_to root_path assert_redirected_to login_path
end end
test 'GET #new redirects to /auth/github' do test 'GET #new redirects to /auth/github' do

Просмотреть файл

@ -0,0 +1,7 @@
FactoryBot.define do
factory :app_installation do
sequence(:github_id, 1000000){|n| n}
account_login { 'andrew' }
account_id { 1060 }
end
end

23
test/fixtures/github_webhooks/github_app_authorization.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"action": "revoked",
"sender": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
}
}

81
test/fixtures/github_webhooks/installation.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,81 @@
{
"action": "created",
"installation": {
"id": 293804,
"account": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"repository_selection": "selected",
"access_tokens_url": "https://api.github.com/installations/293804/access_tokens",
"repositories_url": "https://api.github.com/installation/repositories",
"html_url": "https://github.com/settings/installations/293804",
"app_id": 14761,
"target_id": 1060,
"target_type": "User",
"permissions": {
"issues": "read",
"metadata": "read",
"pull_requests": "read"
},
"events": [
"issues",
"issue_comment",
"label",
"milestone",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"repository",
"watch"
],
"created_at": 1534869839,
"updated_at": 1534869839,
"single_file_name": null
},
"repositories": [
{
"id": 139431141,
"name": "no-rabbits",
"full_name": "andrew/no-rabbits",
"private": true
}
],
"sender": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
}
}

85
test/fixtures/github_webhooks/installation_repositories.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,85 @@
{
"action": "added",
"installation": {
"id": 293804,
"account": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"repository_selection": "selected",
"access_tokens_url": "https://api.github.com/installations/293804/access_tokens",
"repositories_url": "https://api.github.com/installation/repositories",
"html_url": "https://github.com/settings/installations/293804",
"app_id": 14761,
"target_id": 1060,
"target_type": "User",
"permissions": {
"pull_requests": "read",
"issues": "read",
"metadata": "read"
},
"events": [
"issues",
"issue_comment",
"label",
"milestone",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"repository",
"watch"
],
"created_at": 1534869839,
"updated_at": 1534869839,
"single_file_name": null
},
"repository_selection": "selected",
"repositories_added": [
{
"id": 96149167,
"name": "chardstorage",
"full_name": "andrew/chardstorage",
"private": true
}
],
"repositories_removed": [
],
"sender": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
}
}

205
test/fixtures/github_webhooks/issue_comment.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,205 @@
{
"action": "created",
"issue": {
"url": "https://api.github.com/repos/andrew/no-rabbits/issues/5",
"repository_url": "https://api.github.com/repos/andrew/no-rabbits",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/issues/5/labels{/name}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/issues/5/comments",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/5/events",
"html_url": "https://github.com/andrew/no-rabbits/issues/5",
"id": 350082489,
"node_id": "MDU6SXNzdWUzNTAwODI0ODk=",
"number": 5,
"title": "I love carrots",
"user": {
"login": "bertie",
"id": 7251939,
"node_id": "MDQ6VXNlcjcyNTE5Mzk=",
"avatar_url": "https://avatars0.githubusercontent.com/u/7251939?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bertie",
"html_url": "https://github.com/bertie",
"followers_url": "https://api.github.com/users/bertie/followers",
"following_url": "https://api.github.com/users/bertie/following{/other_user}",
"gists_url": "https://api.github.com/users/bertie/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bertie/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bertie/subscriptions",
"organizations_url": "https://api.github.com/users/bertie/orgs",
"repos_url": "https://api.github.com/users/bertie/repos",
"events_url": "https://api.github.com/users/bertie/events{/privacy}",
"received_events_url": "https://api.github.com/users/bertie/received_events",
"type": "User",
"site_admin": false
},
"labels": [
{
"id": 1023072455,
"node_id": "MDU6TGFiZWwxMDIzMDcyNDU1",
"url": "https://api.github.com/repos/andrew/no-rabbits/labels/%F0%9F%A5%95%F0%9F%A5%95carrot",
"name": "🥕🥕carrot",
"color": "080bcc",
"default": false
}
],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [
],
"milestone": null,
"comments": 0,
"created_at": "2018-08-13T15:36:47Z",
"updated_at": "2018-08-28T11:06:13Z",
"closed_at": null,
"author_association": "COLLABORATOR",
"body": "🥕 🥕 🥕 🥕 "
},
"comment": {
"url": "https://api.github.com/repos/andrew/no-rabbits/issues/comments/416544845",
"html_url": "https://github.com/andrew/no-rabbits/issues/5#issuecomment-416544845",
"issue_url": "https://api.github.com/repos/andrew/no-rabbits/issues/5",
"id": 416544845,
"node_id": "MDEyOklzc3VlQ29tbWVudDQxNjU0NDg0NQ==",
"user": {
"login": "bertie",
"id": 7251939,
"node_id": "MDQ6VXNlcjcyNTE5Mzk=",
"avatar_url": "https://avatars0.githubusercontent.com/u/7251939?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bertie",
"html_url": "https://github.com/bertie",
"followers_url": "https://api.github.com/users/bertie/followers",
"following_url": "https://api.github.com/users/bertie/following{/other_user}",
"gists_url": "https://api.github.com/users/bertie/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bertie/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bertie/subscriptions",
"organizations_url": "https://api.github.com/users/bertie/orgs",
"repos_url": "https://api.github.com/users/bertie/repos",
"events_url": "https://api.github.com/users/bertie/events{/privacy}",
"received_events_url": "https://api.github.com/users/bertie/received_events",
"type": "User",
"site_admin": false
},
"created_at": "2018-08-28T11:06:13Z",
"updated_at": "2018-08-28T11:06:13Z",
"author_association": "COLLABORATOR",
"body": "issue!"
},
"repository": {
"id": 139431141,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzk0MzExNDE=",
"name": "no-rabbits",
"full_name": "andrew/no-rabbits",
"owner": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"private": true,
"html_url": "https://github.com/andrew/no-rabbits",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/andrew/no-rabbits",
"forks_url": "https://api.github.com/repos/andrew/no-rabbits/forks",
"keys_url": "https://api.github.com/repos/andrew/no-rabbits/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/andrew/no-rabbits/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/andrew/no-rabbits/teams",
"hooks_url": "https://api.github.com/repos/andrew/no-rabbits/hooks",
"issue_events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/events{/number}",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/events",
"assignees_url": "https://api.github.com/repos/andrew/no-rabbits/assignees{/user}",
"branches_url": "https://api.github.com/repos/andrew/no-rabbits/branches{/branch}",
"tags_url": "https://api.github.com/repos/andrew/no-rabbits/tags",
"blobs_url": "https://api.github.com/repos/andrew/no-rabbits/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/andrew/no-rabbits/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/andrew/no-rabbits/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/andrew/no-rabbits/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/andrew/no-rabbits/statuses/{sha}",
"languages_url": "https://api.github.com/repos/andrew/no-rabbits/languages",
"stargazers_url": "https://api.github.com/repos/andrew/no-rabbits/stargazers",
"contributors_url": "https://api.github.com/repos/andrew/no-rabbits/contributors",
"subscribers_url": "https://api.github.com/repos/andrew/no-rabbits/subscribers",
"subscription_url": "https://api.github.com/repos/andrew/no-rabbits/subscription",
"commits_url": "https://api.github.com/repos/andrew/no-rabbits/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/andrew/no-rabbits/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/andrew/no-rabbits/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/andrew/no-rabbits/contents/{+path}",
"compare_url": "https://api.github.com/repos/andrew/no-rabbits/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/andrew/no-rabbits/merges",
"archive_url": "https://api.github.com/repos/andrew/no-rabbits/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/andrew/no-rabbits/downloads",
"issues_url": "https://api.github.com/repos/andrew/no-rabbits/issues{/number}",
"pulls_url": "https://api.github.com/repos/andrew/no-rabbits/pulls{/number}",
"milestones_url": "https://api.github.com/repos/andrew/no-rabbits/milestones{/number}",
"notifications_url": "https://api.github.com/repos/andrew/no-rabbits/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/labels{/name}",
"releases_url": "https://api.github.com/repos/andrew/no-rabbits/releases{/id}",
"deployments_url": "https://api.github.com/repos/andrew/no-rabbits/deployments",
"created_at": "2018-07-02T10:57:41Z",
"updated_at": "2018-08-02T09:03:24Z",
"pushed_at": "2018-08-02T09:03:22Z",
"git_url": "git://github.com/andrew/no-rabbits.git",
"ssh_url": "git@github.com:andrew/no-rabbits.git",
"clone_url": "https://github.com/andrew/no-rabbits.git",
"svn_url": "https://github.com/andrew/no-rabbits",
"homepage": null,
"size": 3,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"open_issues_count": 3,
"license": null,
"forks": 0,
"open_issues": 3,
"watchers": 0,
"default_branch": "master"
},
"sender": {
"login": "bertie",
"id": 7251939,
"node_id": "MDQ6VXNlcjcyNTE5Mzk=",
"avatar_url": "https://avatars0.githubusercontent.com/u/7251939?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bertie",
"html_url": "https://github.com/bertie",
"followers_url": "https://api.github.com/users/bertie/followers",
"following_url": "https://api.github.com/users/bertie/following{/other_user}",
"gists_url": "https://api.github.com/users/bertie/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bertie/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bertie/subscriptions",
"organizations_url": "https://api.github.com/users/bertie/orgs",
"repos_url": "https://api.github.com/users/bertie/repos",
"events_url": "https://api.github.com/users/bertie/events{/privacy}",
"received_events_url": "https://api.github.com/users/bertie/received_events",
"type": "User",
"site_admin": false
},
"installation": {
"id": 293804
}
}

174
test/fixtures/github_webhooks/issues.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,174 @@
{
"action": "reopened",
"issue": {
"url": "https://api.github.com/repos/andrew/no-rabbits/issues/5",
"repository_url": "https://api.github.com/repos/andrew/no-rabbits",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/issues/5/labels{/name}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/issues/5/comments",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/5/events",
"html_url": "https://github.com/andrew/no-rabbits/issues/5",
"id": 350082489,
"node_id": "MDU6SXNzdWUzNTAwODI0ODk=",
"number": 5,
"title": "I love carrots",
"user": {
"login": "bertie",
"id": 7251939,
"node_id": "MDQ6VXNlcjcyNTE5Mzk=",
"avatar_url": "https://avatars0.githubusercontent.com/u/7251939?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bertie",
"html_url": "https://github.com/bertie",
"followers_url": "https://api.github.com/users/bertie/followers",
"following_url": "https://api.github.com/users/bertie/following{/other_user}",
"gists_url": "https://api.github.com/users/bertie/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bertie/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bertie/subscriptions",
"organizations_url": "https://api.github.com/users/bertie/orgs",
"repos_url": "https://api.github.com/users/bertie/repos",
"events_url": "https://api.github.com/users/bertie/events{/privacy}",
"received_events_url": "https://api.github.com/users/bertie/received_events",
"type": "User",
"site_admin": false
},
"labels": [
{
"id": 1023072455,
"node_id": "MDU6TGFiZWwxMDIzMDcyNDU1",
"url": "https://api.github.com/repos/andrew/no-rabbits/labels/%F0%9F%A5%95:carrot:",
"name": "🥕:carrot:",
"color": "080bcc",
"default": false
}
],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [
],
"milestone": null,
"comments": 0,
"created_at": "2018-08-13T15:36:47Z",
"updated_at": "2018-08-22T10:33:07Z",
"closed_at": null,
"author_association": "COLLABORATOR",
"body": "🥕 🥕 🥕 🥕 "
},
"repository": {
"id": 139431141,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzk0MzExNDE=",
"name": "no-rabbits",
"full_name": "andrew/no-rabbits",
"owner": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"private": true,
"html_url": "https://github.com/andrew/no-rabbits",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/andrew/no-rabbits",
"forks_url": "https://api.github.com/repos/andrew/no-rabbits/forks",
"keys_url": "https://api.github.com/repos/andrew/no-rabbits/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/andrew/no-rabbits/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/andrew/no-rabbits/teams",
"hooks_url": "https://api.github.com/repos/andrew/no-rabbits/hooks",
"issue_events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/events{/number}",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/events",
"assignees_url": "https://api.github.com/repos/andrew/no-rabbits/assignees{/user}",
"branches_url": "https://api.github.com/repos/andrew/no-rabbits/branches{/branch}",
"tags_url": "https://api.github.com/repos/andrew/no-rabbits/tags",
"blobs_url": "https://api.github.com/repos/andrew/no-rabbits/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/andrew/no-rabbits/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/andrew/no-rabbits/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/andrew/no-rabbits/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/andrew/no-rabbits/statuses/{sha}",
"languages_url": "https://api.github.com/repos/andrew/no-rabbits/languages",
"stargazers_url": "https://api.github.com/repos/andrew/no-rabbits/stargazers",
"contributors_url": "https://api.github.com/repos/andrew/no-rabbits/contributors",
"subscribers_url": "https://api.github.com/repos/andrew/no-rabbits/subscribers",
"subscription_url": "https://api.github.com/repos/andrew/no-rabbits/subscription",
"commits_url": "https://api.github.com/repos/andrew/no-rabbits/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/andrew/no-rabbits/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/andrew/no-rabbits/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/andrew/no-rabbits/contents/{+path}",
"compare_url": "https://api.github.com/repos/andrew/no-rabbits/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/andrew/no-rabbits/merges",
"archive_url": "https://api.github.com/repos/andrew/no-rabbits/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/andrew/no-rabbits/downloads",
"issues_url": "https://api.github.com/repos/andrew/no-rabbits/issues{/number}",
"pulls_url": "https://api.github.com/repos/andrew/no-rabbits/pulls{/number}",
"milestones_url": "https://api.github.com/repos/andrew/no-rabbits/milestones{/number}",
"notifications_url": "https://api.github.com/repos/andrew/no-rabbits/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/labels{/name}",
"releases_url": "https://api.github.com/repos/andrew/no-rabbits/releases{/id}",
"deployments_url": "https://api.github.com/repos/andrew/no-rabbits/deployments",
"created_at": "2018-07-02T10:57:41Z",
"updated_at": "2018-08-02T09:03:24Z",
"pushed_at": "2018-08-02T09:03:22Z",
"git_url": "git://github.com/andrew/no-rabbits.git",
"ssh_url": "git@github.com:andrew/no-rabbits.git",
"clone_url": "https://github.com/andrew/no-rabbits.git",
"svn_url": "https://github.com/andrew/no-rabbits",
"homepage": null,
"size": 3,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"open_issues_count": 3,
"license": null,
"forks": 0,
"open_issues": 3,
"watchers": 0,
"default_branch": "master"
},
"sender": {
"login": "bertie",
"id": 7251939,
"node_id": "MDQ6VXNlcjcyNTE5Mzk=",
"avatar_url": "https://avatars0.githubusercontent.com/u/7251939?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bertie",
"html_url": "https://github.com/bertie",
"followers_url": "https://api.github.com/users/bertie/followers",
"following_url": "https://api.github.com/users/bertie/following{/other_user}",
"gists_url": "https://api.github.com/users/bertie/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bertie/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bertie/subscriptions",
"organizations_url": "https://api.github.com/users/bertie/orgs",
"repos_url": "https://api.github.com/users/bertie/repos",
"events_url": "https://api.github.com/users/bertie/events{/privacy}",
"received_events_url": "https://api.github.com/users/bertie/received_events",
"type": "User",
"site_admin": false
},
"installation": {
"id": 293804
}
}

132
test/fixtures/github_webhooks/label.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,132 @@
{
"action": "edited",
"label": {
"id": 1023072455,
"node_id": "MDU6TGFiZWwxMDIzMDcyNDU1",
"url": "https://api.github.com/repos/andrew/no-rabbits/labels/%F0%9F%A5%95%F0%9F%A5%95:carrot:",
"name": "🥕🥕:carrot:",
"color": "080bcc",
"default": false
},
"changes": {
"name": {
"from": "🥕:carrot:"
}
},
"repository": {
"id": 139431141,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzk0MzExNDE=",
"name": "no-rabbits",
"full_name": "andrew/no-rabbits",
"owner": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"private": true,
"html_url": "https://github.com/andrew/no-rabbits",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/andrew/no-rabbits",
"forks_url": "https://api.github.com/repos/andrew/no-rabbits/forks",
"keys_url": "https://api.github.com/repos/andrew/no-rabbits/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/andrew/no-rabbits/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/andrew/no-rabbits/teams",
"hooks_url": "https://api.github.com/repos/andrew/no-rabbits/hooks",
"issue_events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/events{/number}",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/events",
"assignees_url": "https://api.github.com/repos/andrew/no-rabbits/assignees{/user}",
"branches_url": "https://api.github.com/repos/andrew/no-rabbits/branches{/branch}",
"tags_url": "https://api.github.com/repos/andrew/no-rabbits/tags",
"blobs_url": "https://api.github.com/repos/andrew/no-rabbits/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/andrew/no-rabbits/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/andrew/no-rabbits/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/andrew/no-rabbits/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/andrew/no-rabbits/statuses/{sha}",
"languages_url": "https://api.github.com/repos/andrew/no-rabbits/languages",
"stargazers_url": "https://api.github.com/repos/andrew/no-rabbits/stargazers",
"contributors_url": "https://api.github.com/repos/andrew/no-rabbits/contributors",
"subscribers_url": "https://api.github.com/repos/andrew/no-rabbits/subscribers",
"subscription_url": "https://api.github.com/repos/andrew/no-rabbits/subscription",
"commits_url": "https://api.github.com/repos/andrew/no-rabbits/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/andrew/no-rabbits/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/andrew/no-rabbits/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/andrew/no-rabbits/contents/{+path}",
"compare_url": "https://api.github.com/repos/andrew/no-rabbits/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/andrew/no-rabbits/merges",
"archive_url": "https://api.github.com/repos/andrew/no-rabbits/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/andrew/no-rabbits/downloads",
"issues_url": "https://api.github.com/repos/andrew/no-rabbits/issues{/number}",
"pulls_url": "https://api.github.com/repos/andrew/no-rabbits/pulls{/number}",
"milestones_url": "https://api.github.com/repos/andrew/no-rabbits/milestones{/number}",
"notifications_url": "https://api.github.com/repos/andrew/no-rabbits/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/labels{/name}",
"releases_url": "https://api.github.com/repos/andrew/no-rabbits/releases{/id}",
"deployments_url": "https://api.github.com/repos/andrew/no-rabbits/deployments",
"created_at": "2018-07-02T10:57:41Z",
"updated_at": "2018-08-02T09:03:24Z",
"pushed_at": "2018-08-02T09:03:22Z",
"git_url": "git://github.com/andrew/no-rabbits.git",
"ssh_url": "git@github.com:andrew/no-rabbits.git",
"clone_url": "https://github.com/andrew/no-rabbits.git",
"svn_url": "https://github.com/andrew/no-rabbits",
"homepage": null,
"size": 3,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"open_issues_count": 3,
"license": null,
"forks": 0,
"open_issues": 3,
"watchers": 0,
"default_branch": "master"
},
"sender": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"installation": {
"id": 293804
}
}

453
test/fixtures/github_webhooks/pull_request.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,453 @@
{
"action": "reopened",
"number": 3,
"pull_request": {
"url": "https://api.github.com/repos/andrew/no-rabbits/pulls/3",
"id": 204166844,
"node_id": "MDExOlB1bGxSZXF1ZXN0MjA0MTY2ODQ0",
"html_url": "https://github.com/andrew/no-rabbits/pull/3",
"diff_url": "https://github.com/andrew/no-rabbits/pull/3.diff",
"patch_url": "https://github.com/andrew/no-rabbits/pull/3.patch",
"issue_url": "https://api.github.com/repos/andrew/no-rabbits/issues/3",
"number": 3,
"state": "open",
"locked": false,
"title": "Update README.md",
"user": {
"login": "bertie",
"id": 7251939,
"node_id": "MDQ6VXNlcjcyNTE5Mzk=",
"avatar_url": "https://avatars0.githubusercontent.com/u/7251939?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bertie",
"html_url": "https://github.com/bertie",
"followers_url": "https://api.github.com/users/bertie/followers",
"following_url": "https://api.github.com/users/bertie/following{/other_user}",
"gists_url": "https://api.github.com/users/bertie/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bertie/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bertie/subscriptions",
"organizations_url": "https://api.github.com/users/bertie/orgs",
"repos_url": "https://api.github.com/users/bertie/repos",
"events_url": "https://api.github.com/users/bertie/events{/privacy}",
"received_events_url": "https://api.github.com/users/bertie/received_events",
"type": "User",
"site_admin": false
},
"body": "",
"created_at": "2018-07-26T14:45:31Z",
"updated_at": "2018-08-22T10:32:06Z",
"closed_at": null,
"merged_at": null,
"merge_commit_sha": "208f8c09b3c88b2c3caad3d587325deb3172d6ac",
"assignee": null,
"assignees": [
],
"requested_reviewers": [
],
"requested_teams": [
],
"labels": [
],
"milestone": null,
"commits_url": "https://api.github.com/repos/andrew/no-rabbits/pulls/3/commits",
"review_comments_url": "https://api.github.com/repos/andrew/no-rabbits/pulls/3/comments",
"review_comment_url": "https://api.github.com/repos/andrew/no-rabbits/pulls/comments{/number}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/issues/3/comments",
"statuses_url": "https://api.github.com/repos/andrew/no-rabbits/statuses/5c1875a4873e5d12219a343cc7e075e87e5f420f",
"head": {
"label": "andrew:bertie-patch-1",
"ref": "bertie-patch-1",
"sha": "5c1875a4873e5d12219a343cc7e075e87e5f420f",
"user": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 139431141,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzk0MzExNDE=",
"name": "no-rabbits",
"full_name": "andrew/no-rabbits",
"owner": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"private": true,
"html_url": "https://github.com/andrew/no-rabbits",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/andrew/no-rabbits",
"forks_url": "https://api.github.com/repos/andrew/no-rabbits/forks",
"keys_url": "https://api.github.com/repos/andrew/no-rabbits/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/andrew/no-rabbits/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/andrew/no-rabbits/teams",
"hooks_url": "https://api.github.com/repos/andrew/no-rabbits/hooks",
"issue_events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/events{/number}",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/events",
"assignees_url": "https://api.github.com/repos/andrew/no-rabbits/assignees{/user}",
"branches_url": "https://api.github.com/repos/andrew/no-rabbits/branches{/branch}",
"tags_url": "https://api.github.com/repos/andrew/no-rabbits/tags",
"blobs_url": "https://api.github.com/repos/andrew/no-rabbits/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/andrew/no-rabbits/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/andrew/no-rabbits/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/andrew/no-rabbits/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/andrew/no-rabbits/statuses/{sha}",
"languages_url": "https://api.github.com/repos/andrew/no-rabbits/languages",
"stargazers_url": "https://api.github.com/repos/andrew/no-rabbits/stargazers",
"contributors_url": "https://api.github.com/repos/andrew/no-rabbits/contributors",
"subscribers_url": "https://api.github.com/repos/andrew/no-rabbits/subscribers",
"subscription_url": "https://api.github.com/repos/andrew/no-rabbits/subscription",
"commits_url": "https://api.github.com/repos/andrew/no-rabbits/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/andrew/no-rabbits/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/andrew/no-rabbits/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/andrew/no-rabbits/contents/{+path}",
"compare_url": "https://api.github.com/repos/andrew/no-rabbits/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/andrew/no-rabbits/merges",
"archive_url": "https://api.github.com/repos/andrew/no-rabbits/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/andrew/no-rabbits/downloads",
"issues_url": "https://api.github.com/repos/andrew/no-rabbits/issues{/number}",
"pulls_url": "https://api.github.com/repos/andrew/no-rabbits/pulls{/number}",
"milestones_url": "https://api.github.com/repos/andrew/no-rabbits/milestones{/number}",
"notifications_url": "https://api.github.com/repos/andrew/no-rabbits/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/labels{/name}",
"releases_url": "https://api.github.com/repos/andrew/no-rabbits/releases{/id}",
"deployments_url": "https://api.github.com/repos/andrew/no-rabbits/deployments",
"created_at": "2018-07-02T10:57:41Z",
"updated_at": "2018-08-02T09:03:24Z",
"pushed_at": "2018-08-02T09:03:22Z",
"git_url": "git://github.com/andrew/no-rabbits.git",
"ssh_url": "git@github.com:andrew/no-rabbits.git",
"clone_url": "https://github.com/andrew/no-rabbits.git",
"svn_url": "https://github.com/andrew/no-rabbits",
"homepage": null,
"size": 3,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"open_issues_count": 2,
"license": null,
"forks": 0,
"open_issues": 2,
"watchers": 0,
"default_branch": "master"
}
},
"base": {
"label": "andrew:master",
"ref": "master",
"sha": "3632305028b86b5764c8b8398aefc1473525bdbc",
"user": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 139431141,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzk0MzExNDE=",
"name": "no-rabbits",
"full_name": "andrew/no-rabbits",
"owner": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"private": true,
"html_url": "https://github.com/andrew/no-rabbits",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/andrew/no-rabbits",
"forks_url": "https://api.github.com/repos/andrew/no-rabbits/forks",
"keys_url": "https://api.github.com/repos/andrew/no-rabbits/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/andrew/no-rabbits/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/andrew/no-rabbits/teams",
"hooks_url": "https://api.github.com/repos/andrew/no-rabbits/hooks",
"issue_events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/events{/number}",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/events",
"assignees_url": "https://api.github.com/repos/andrew/no-rabbits/assignees{/user}",
"branches_url": "https://api.github.com/repos/andrew/no-rabbits/branches{/branch}",
"tags_url": "https://api.github.com/repos/andrew/no-rabbits/tags",
"blobs_url": "https://api.github.com/repos/andrew/no-rabbits/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/andrew/no-rabbits/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/andrew/no-rabbits/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/andrew/no-rabbits/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/andrew/no-rabbits/statuses/{sha}",
"languages_url": "https://api.github.com/repos/andrew/no-rabbits/languages",
"stargazers_url": "https://api.github.com/repos/andrew/no-rabbits/stargazers",
"contributors_url": "https://api.github.com/repos/andrew/no-rabbits/contributors",
"subscribers_url": "https://api.github.com/repos/andrew/no-rabbits/subscribers",
"subscription_url": "https://api.github.com/repos/andrew/no-rabbits/subscription",
"commits_url": "https://api.github.com/repos/andrew/no-rabbits/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/andrew/no-rabbits/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/andrew/no-rabbits/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/andrew/no-rabbits/contents/{+path}",
"compare_url": "https://api.github.com/repos/andrew/no-rabbits/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/andrew/no-rabbits/merges",
"archive_url": "https://api.github.com/repos/andrew/no-rabbits/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/andrew/no-rabbits/downloads",
"issues_url": "https://api.github.com/repos/andrew/no-rabbits/issues{/number}",
"pulls_url": "https://api.github.com/repos/andrew/no-rabbits/pulls{/number}",
"milestones_url": "https://api.github.com/repos/andrew/no-rabbits/milestones{/number}",
"notifications_url": "https://api.github.com/repos/andrew/no-rabbits/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/labels{/name}",
"releases_url": "https://api.github.com/repos/andrew/no-rabbits/releases{/id}",
"deployments_url": "https://api.github.com/repos/andrew/no-rabbits/deployments",
"created_at": "2018-07-02T10:57:41Z",
"updated_at": "2018-08-02T09:03:24Z",
"pushed_at": "2018-08-02T09:03:22Z",
"git_url": "git://github.com/andrew/no-rabbits.git",
"ssh_url": "git@github.com:andrew/no-rabbits.git",
"clone_url": "https://github.com/andrew/no-rabbits.git",
"svn_url": "https://github.com/andrew/no-rabbits",
"homepage": null,
"size": 3,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"open_issues_count": 2,
"license": null,
"forks": 0,
"open_issues": 2,
"watchers": 0,
"default_branch": "master"
}
},
"_links": {
"self": {
"href": "https://api.github.com/repos/andrew/no-rabbits/pulls/3"
},
"html": {
"href": "https://github.com/andrew/no-rabbits/pull/3"
},
"issue": {
"href": "https://api.github.com/repos/andrew/no-rabbits/issues/3"
},
"comments": {
"href": "https://api.github.com/repos/andrew/no-rabbits/issues/3/comments"
},
"review_comments": {
"href": "https://api.github.com/repos/andrew/no-rabbits/pulls/3/comments"
},
"review_comment": {
"href": "https://api.github.com/repos/andrew/no-rabbits/pulls/comments{/number}"
},
"commits": {
"href": "https://api.github.com/repos/andrew/no-rabbits/pulls/3/commits"
},
"statuses": {
"href": "https://api.github.com/repos/andrew/no-rabbits/statuses/5c1875a4873e5d12219a343cc7e075e87e5f420f"
}
},
"author_association": "COLLABORATOR",
"merged": false,
"mergeable": null,
"rebaseable": null,
"mergeable_state": "unknown",
"merged_by": null,
"comments": 0,
"review_comments": 0,
"maintainer_can_modify": false,
"commits": 1,
"additions": 2,
"deletions": 0,
"changed_files": 1
},
"repository": {
"id": 139431141,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzk0MzExNDE=",
"name": "no-rabbits",
"full_name": "andrew/no-rabbits",
"owner": {
"login": "andrew",
"id": 1060,
"node_id": "MDQ6VXNlcjEwNjA=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1060?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/andrew",
"html_url": "https://github.com/andrew",
"followers_url": "https://api.github.com/users/andrew/followers",
"following_url": "https://api.github.com/users/andrew/following{/other_user}",
"gists_url": "https://api.github.com/users/andrew/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andrew/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andrew/subscriptions",
"organizations_url": "https://api.github.com/users/andrew/orgs",
"repos_url": "https://api.github.com/users/andrew/repos",
"events_url": "https://api.github.com/users/andrew/events{/privacy}",
"received_events_url": "https://api.github.com/users/andrew/received_events",
"type": "User",
"site_admin": false
},
"private": true,
"html_url": "https://github.com/andrew/no-rabbits",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/andrew/no-rabbits",
"forks_url": "https://api.github.com/repos/andrew/no-rabbits/forks",
"keys_url": "https://api.github.com/repos/andrew/no-rabbits/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/andrew/no-rabbits/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/andrew/no-rabbits/teams",
"hooks_url": "https://api.github.com/repos/andrew/no-rabbits/hooks",
"issue_events_url": "https://api.github.com/repos/andrew/no-rabbits/issues/events{/number}",
"events_url": "https://api.github.com/repos/andrew/no-rabbits/events",
"assignees_url": "https://api.github.com/repos/andrew/no-rabbits/assignees{/user}",
"branches_url": "https://api.github.com/repos/andrew/no-rabbits/branches{/branch}",
"tags_url": "https://api.github.com/repos/andrew/no-rabbits/tags",
"blobs_url": "https://api.github.com/repos/andrew/no-rabbits/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/andrew/no-rabbits/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/andrew/no-rabbits/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/andrew/no-rabbits/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/andrew/no-rabbits/statuses/{sha}",
"languages_url": "https://api.github.com/repos/andrew/no-rabbits/languages",
"stargazers_url": "https://api.github.com/repos/andrew/no-rabbits/stargazers",
"contributors_url": "https://api.github.com/repos/andrew/no-rabbits/contributors",
"subscribers_url": "https://api.github.com/repos/andrew/no-rabbits/subscribers",
"subscription_url": "https://api.github.com/repos/andrew/no-rabbits/subscription",
"commits_url": "https://api.github.com/repos/andrew/no-rabbits/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/andrew/no-rabbits/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/andrew/no-rabbits/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/andrew/no-rabbits/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/andrew/no-rabbits/contents/{+path}",
"compare_url": "https://api.github.com/repos/andrew/no-rabbits/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/andrew/no-rabbits/merges",
"archive_url": "https://api.github.com/repos/andrew/no-rabbits/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/andrew/no-rabbits/downloads",
"issues_url": "https://api.github.com/repos/andrew/no-rabbits/issues{/number}",
"pulls_url": "https://api.github.com/repos/andrew/no-rabbits/pulls{/number}",
"milestones_url": "https://api.github.com/repos/andrew/no-rabbits/milestones{/number}",
"notifications_url": "https://api.github.com/repos/andrew/no-rabbits/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/andrew/no-rabbits/labels{/name}",
"releases_url": "https://api.github.com/repos/andrew/no-rabbits/releases{/id}",
"deployments_url": "https://api.github.com/repos/andrew/no-rabbits/deployments",
"created_at": "2018-07-02T10:57:41Z",
"updated_at": "2018-08-02T09:03:24Z",
"pushed_at": "2018-08-02T09:03:22Z",
"git_url": "git://github.com/andrew/no-rabbits.git",
"ssh_url": "git@github.com:andrew/no-rabbits.git",
"clone_url": "https://github.com/andrew/no-rabbits.git",
"svn_url": "https://github.com/andrew/no-rabbits",
"homepage": null,
"size": 3,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"open_issues_count": 2,
"license": null,
"forks": 0,
"open_issues": 2,
"watchers": 0,
"default_branch": "master"
},
"sender": {
"login": "bertie",
"id": 7251939,
"node_id": "MDQ6VXNlcjcyNTE5Mzk=",
"avatar_url": "https://avatars0.githubusercontent.com/u/7251939?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bertie",
"html_url": "https://github.com/bertie",
"followers_url": "https://api.github.com/users/bertie/followers",
"following_url": "https://api.github.com/users/bertie/following{/other_user}",
"gists_url": "https://api.github.com/users/bertie/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bertie/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bertie/subscriptions",
"organizations_url": "https://api.github.com/users/bertie/orgs",
"repos_url": "https://api.github.com/users/bertie/repos",
"events_url": "https://api.github.com/users/bertie/events{/privacy}",
"received_events_url": "https://api.github.com/users/bertie/received_events",
"type": "User",
"site_admin": false
},
"installation": {
"id": 293804
}
}

Просмотреть файл

@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'test_helper'
class AppInstallationTest < ActiveSupport::TestCase
setup do
@app_installation = create(:app_installation)
end
test 'must have a github id' do
@app_installation.github_id = nil
refute @app_installation.valid?
end
test 'must have a unique github_id' do
app_installation = build(:app_installation, github_id: @app_installation.github_id)
refute app_installation.valid?
end
test 'must have an account login' do
@app_installation.account_login = nil
refute @app_installation.valid?
end
test 'must have a account id' do
@app_installation.account_id = nil
refute @app_installation.valid?
end
end

Просмотреть файл

@ -21,6 +21,16 @@ class RepositoryTest < ActiveSupport::TestCase
refute repository.valid? refute repository.valid?
end end
test 'github_app_installed if app_installation_id present' do
@repository.app_installation_id = 1
assert @repository.github_app_installed?
end
test 'github_app_installed if app_installation_id missing' do
@repository.app_installation_id = nil
refute @repository.github_app_installed?
end
test 'finds subjects by full_name' do test 'finds subjects by full_name' do
subject = create(:subject, url: "https://api.github.com/repos/#{@repository.full_name}/issues/1") subject = create(:subject, url: "https://api.github.com/repos/#{@repository.full_name}/issues/1")
subject2 = create(:subject, url: "https://api.github.com/repos/foo/bar/issues/1") subject2 = create(:subject, url: "https://api.github.com/repos/foo/bar/issues/1")

Просмотреть файл

@ -23,11 +23,6 @@ class UserTest < ActiveSupport::TestCase
refute user.valid? refute user.valid?
end end
test 'must have an access_token' do
@user.access_token = nil
refute @user.valid?
end
test 'must have a unique access_token' do test 'must have a unique access_token' do
user = User.create(github_id: 42, access_token: @user.access_token) user = User.create(github_id: 42, access_token: @user.access_token)
refute user.valid? refute user.valid?