Add the basic Canary app structure
This is derped along without any tests hard enough because I don't feel like spending the time right now to come up with API shims for Sentry, whose data types are nested AF.
|
@ -1,3 +1,4 @@
|
|||
*.sw*
|
||||
*.gem
|
||||
*.rbc
|
||||
/.config
|
||||
|
@ -48,3 +49,4 @@ build-iPhoneSimulator/
|
|||
|
||||
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
||||
.rvmrc
|
||||
vendor/
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
FROM ruby:2.3-alpine
|
||||
|
||||
ARG APP_DIR=/webapp
|
||||
|
||||
RUN mkdir -p ${APP_DIR}/vendor
|
||||
|
||||
WORKDIR ${APP_DIR}
|
||||
|
||||
ADD vendor/ ${APP_DIR}/vendor/
|
||||
|
||||
ENV GEM_HOME=${APP_DIR}/vendor/gems
|
||||
ENV BUNDLE_PATH=${APP_DIR}/vendor/gems
|
||||
ENV BUNDLE_APP_CONFIG=${APP_DIR}/vendor/gems/.bundle
|
||||
ENV BUNDLE_DISABLE_SHARED_GEMS=true
|
||||
|
||||
ADD Gemfile* ${APP_DIR}/
|
||||
ADD app.rb ${APP_DIR}/
|
||||
ADD config.ru ${APP_DIR}/
|
||||
ADD views ${APP_DIR}/views/
|
||||
ADD assets ${APP_DIR}/assets/
|
||||
CMD bundle exec rackup -o 0.0.0.0
|
|
@ -0,0 +1,10 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'haml'
|
||||
gem 'sinatra'
|
||||
gem 'sentry-raven'
|
||||
gem 'sentry-api'
|
||||
|
||||
group :test do
|
||||
gem 'rspec'
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
diff-lcs (1.3)
|
||||
faraday (0.13.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
haml (5.0.2)
|
||||
temple (>= 0.8.0)
|
||||
tilt
|
||||
httmultiparty (0.3.16)
|
||||
httparty (>= 0.7.3)
|
||||
mimemagic
|
||||
multipart-post
|
||||
httparty (0.15.6)
|
||||
multi_xml (>= 0.5.2)
|
||||
mimemagic (0.3.2)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mustermann (1.0.1)
|
||||
rack (2.0.3)
|
||||
rack-protection (2.0.0)
|
||||
rack
|
||||
rspec (3.6.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-mocks (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-support (3.6.0)
|
||||
sentry-api (0.3.3)
|
||||
httmultiparty (~> 0.3.16)
|
||||
sentry-raven (2.6.3)
|
||||
faraday (>= 0.7.6, < 1.0)
|
||||
sinatra (2.0.0)
|
||||
mustermann (~> 1.0)
|
||||
rack (~> 2.0)
|
||||
rack-protection (= 2.0.0)
|
||||
tilt (~> 2.0)
|
||||
temple (0.8.0)
|
||||
tilt (2.0.8)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
haml
|
||||
rspec
|
||||
sentry-api
|
||||
sentry-raven
|
||||
sinatra
|
||||
|
||||
BUNDLED WITH
|
||||
1.15.3
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
all: check container
|
||||
|
||||
check: depends spec
|
||||
|
||||
spec:
|
||||
./scripts/ruby bundle exec rspec -c
|
||||
|
||||
depends: Gemfile
|
||||
./scripts/ruby bundle install
|
||||
|
||||
run: depends
|
||||
./scripts/ruby bundle exec rackup -o 0.0.0.0
|
||||
|
||||
container: depends Dockerfile
|
||||
docker build -t rtyler/codevalet-canary .
|
||||
|
||||
clean:
|
||||
rm -rf vendor
|
||||
|
||||
.PHONY: all depends clean run check container spec
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
image::https://codevalet.io/u/codevalet/job/CodeValet/job/canary/job/master/badge/icon[Build Status, link=http://codevalet.codevalet.io/blue/organizations/jenkins/CodeValet%2Fcanary/activit://codevalet.io/u/codevalet/blue/organizations/jenkins/CodeValet%2Fcanary/activity]
|
||||
|
||||
image::https://github.com/codevalet/canary/raw/master/assets/songbird-128.png[Grace]
|
||||
image::https://github.com/codevalet/canary/raw/master/assets/images/songbird-128.png[Grace]
|
||||
|
||||
Canary is the service providing public status and information for
|
||||
link:http://codevalet.io[Code Valet].
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'haml'
|
||||
require 'raven'
|
||||
require 'sinatra/base'
|
||||
require 'sentry-api'
|
||||
|
||||
module CodeValet
|
||||
module Canary
|
||||
class App < Sinatra::Base
|
||||
enable :sessions
|
||||
enable :raise_errors
|
||||
disable :show_exceptions
|
||||
|
||||
set :public_folder, File.dirname(__FILE__) + '/assets'
|
||||
|
||||
SentryApi.configure do |config|
|
||||
config.endpoint = 'https://app.getsentry.com/api/0'
|
||||
config.auth_token = ENV['SENTRY_API_TOKEN']
|
||||
config.default_org_slug = 'codevalet'
|
||||
end
|
||||
|
||||
get '/' do
|
||||
projects = []
|
||||
begin
|
||||
projects = SentryApi.projects
|
||||
rescue StandardError => exc
|
||||
Raven.capture_exception(exc)
|
||||
end
|
||||
|
||||
haml :index,
|
||||
:layout => :_base,
|
||||
:locals => {
|
||||
:projects => projects,
|
||||
}
|
||||
end
|
||||
|
||||
get '/issue/:id' do
|
||||
issue = nil
|
||||
events = []
|
||||
begin
|
||||
issue = SentryApi.issue(params['id'])
|
||||
events = SentryApi.issue_events(params['id'])
|
||||
rescue StandardError => exc
|
||||
Raven.capture_exception(exc)
|
||||
end
|
||||
|
||||
haml :issue,
|
||||
:layout => :_base,
|
||||
:locals => {
|
||||
:issue => issue,
|
||||
:events => events,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,12 @@
|
|||
|
||||
$LOAD_PATH << File.dirname(__FILE__)
|
||||
|
||||
ENV['RACK_ENV'] ||= 'development'
|
||||
|
||||
require 'app'
|
||||
require 'raven'
|
||||
|
||||
use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"
|
||||
use Raven::Rack
|
||||
|
||||
run CodeValet::Canary::App
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
exec docker run --rm -ti \
|
||||
-u $(id -u):$(id -g) \
|
||||
-w ${PWD} \
|
||||
-v ${PWD}:${PWD} \
|
||||
-e LANG=C.UTF-8 \
|
||||
-e GEM_HOME=${PWD}/vendor/gems \
|
||||
-e BUNDLE_PATH=${PWD}/vendor/gems \
|
||||
-e BUNDLE_APP_CONFIG=${PWD}/vendor/gems/.bundle \
|
||||
-e BUNDLE_DISABLE_SHARED_GEMS=true \
|
||||
-e SENTRY_API_TOKEN=$SENTRY_API_TOKEN \
|
||||
-p 9292:9292 \
|
||||
ruby:2.3 $@
|
|
@ -0,0 +1,69 @@
|
|||
!!! XML
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%meta{:name => 'referrer', :content => 'no-referrer'}/
|
||||
%link{:rel => 'stylesheet',
|
||||
:href => 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css',
|
||||
:integrity => 'sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ',
|
||||
:crossorigin => 'anonymous'}/
|
||||
%script{:src => 'https://code.jquery.com/jquery-3.2.1.slim.min.js',
|
||||
:integrity => 'sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN',
|
||||
:crossorigin => 'anonymous'}
|
||||
%script{:src => 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js',
|
||||
:crossorigin => 'anonymous',
|
||||
:integrity => 'sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn'}
|
||||
%meta{:charset => 'utf-8'}/
|
||||
%meta{:name => 'viewport',
|
||||
:content => 'width=device-width, initial-scale=1, shrink-to-fit=no'}/
|
||||
%link{:rel => 'icon', :type => 'image/ico', :href => '/images/favicon.ico'}/
|
||||
%title
|
||||
Code Valet - Canary
|
||||
%body
|
||||
%nav.navbar.navbar-inverse.bg-warning.navbar-toggleable
|
||||
%a.navbar-brand{:href => '/'}
|
||||
%img{:src => '/images/songbird-128.png',
|
||||
:width => '50', :height => '50', :alt => 'Grace the Canary in the Code Mine'}/
|
||||
Canary
|
||||
.navbar-collapse
|
||||
.navbar-nav
|
||||
%a.nav-item.nav-link{:href => 'https://codevalet.io/'}
|
||||
Return to Code Valet
|
||||
|
||||
.container.mt-5
|
||||
= yield
|
||||
|
||||
%hr/
|
||||
|
||||
.container
|
||||
.row
|
||||
.col-md-6
|
||||
%p
|
||||
© 2017
|
||||
%a{:href => 'https://github.com/rtyler'}
|
||||
R. Tyler Croy.
|
||||
.col-md-6
|
||||
%p.text-right
|
||||
%a{:href => 'https://twitter.com/codevalet'}
|
||||
%img{:src => '/images/twitter-32.png',
|
||||
:alt => 'Code Valet on Twitter',
|
||||
:title => 'Code Valet on Twitter'}/
|
||||
%a{:href => 'https://github.com/codevalet'}
|
||||
%img{:src => '/images/github-32.png',
|
||||
:alt => 'Code Valet on GitHub',
|
||||
:title => 'Code Valet on GitHub'}/
|
||||
.row
|
||||
%p
|
||||
Jenkins® is a registered trademark of
|
||||
%a{:href => 'http://spi-inc.org'}
|
||||
Software in the Public Interest, Inc.
|
||||
|
||||
:javascript
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-104976589-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
.container
|
||||
.row
|
||||
.col-md-12
|
||||
%h2
|
||||
Meet Canary
|
||||
%img{:src => '/images/songbird-128.png',
|
||||
:title => 'Grace the Canary in the Code Mine',
|
||||
:align => :right}
|
||||
%p
|
||||
Canary is part of
|
||||
%a{:href => 'https://codevalet.io/', :target => '_blank'}
|
||||
Code Valet
|
||||
which provides radically transparent feedback for the service's
|
||||
operation. Canary allows developers to triage and understand issues
|
||||
affecting Code Valet, or one of its components.
|
||||
|
||||
- projects.auto_paginate do |project|
|
||||
.row
|
||||
.col-md-12
|
||||
%h3= project.name
|
||||
%p
|
||||
- events = SentryApi.project_issues(project.slug)
|
||||
- unless events.size > 0
|
||||
No recent events (yay).
|
||||
- else
|
||||
- events.each do |event|
|
||||
- badge = 'danger'
|
||||
- if event.level != 'error'
|
||||
- badge = event.level
|
||||
.row
|
||||
.col-lg-10
|
||||
%strong
|
||||
= event.title
|
||||
|
||||
.mb-2.text-muted
|
||||
%span.badge{:class => "badge-#{badge}"}
|
||||
= event.count
|
||||
= event.culprit
|
||||
|
||||
%p
|
||||
%a{:href => "issue/#{event.id}"}
|
||||
View
|
||||
·
|
||||
%a{:href => event.permalink, :target => '_blank'}
|
||||
View in Sentry
|
||||
%hr/
|
|
@ -0,0 +1,49 @@
|
|||
.container
|
||||
.row
|
||||
.col-md-12
|
||||
- unless events
|
||||
Something went wrong when rendering this page :(
|
||||
- else
|
||||
%h2
|
||||
= issue.title
|
||||
%h3.text-muted
|
||||
= issue.culprit
|
||||
.row
|
||||
.col-md-12
|
||||
%small
|
||||
There are
|
||||
%strong
|
||||
= events.size
|
||||
events associated with this issue.
|
||||
|
||||
- events.each do |event|
|
||||
.row
|
||||
.col-md-12
|
||||
- event.tags.each do |tag|
|
||||
%span.badge.badge-info
|
||||
#{tag['key']}:#{tag['value']}
|
||||
- event.entries.each do |entry|
|
||||
/ #{entry.inspect}
|
||||
- if entry['type'] == 'message'
|
||||
%p
|
||||
- if entry['data']['formatted']
|
||||
= entry['data']['formatted']
|
||||
- else
|
||||
= entry['data']['message']
|
||||
|
||||
- if entry['type'] == 'exception'
|
||||
- entry['data']['values'].each do |val|
|
||||
%ul.list-unstyled
|
||||
- val['stacktrace']['frames'].reverse_each do |frame|
|
||||
%li
|
||||
%div.m-1
|
||||
%strong
|
||||
= frame['module']
|
||||
in
|
||||
%strong
|
||||
= frame['function']
|
||||
at line #{frame['lineNo']} of
|
||||
%code
|
||||
= frame['absPath']
|
||||
|
||||
- break
|