├── Procfile ├── config.ru ├── Gemfile ├── badges ├── badges.rb ├── http_utils.rb ├── badge_utils.rb └── badge.rb ├── Gemfile.lock ├── LICENSE ├── .gitignore ├── README.md └── main.rb /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec rackup config.ru -p $PORT -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require './main.rb' 2 | run Sinatra::Application -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '2.2.3' 4 | 5 | gem 'sinatra', '~> 1.4.6' 6 | -------------------------------------------------------------------------------- /badges/badges.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'json' 3 | require './badges/badge' 4 | require './badges/http_utils' 5 | require './badges/badge_utils' 6 | 7 | module Badges 8 | 9 | # Exception while interacting with badges 10 | class BadgeException < Exception 11 | end 12 | 13 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | rack (1.6.4) 5 | rack-protection (1.5.3) 6 | rack 7 | sinatra (1.4.7) 8 | rack (~> 1.5) 9 | rack-protection (~> 1.4) 10 | tilt (>= 1.3, < 3) 11 | tilt (2.0.5) 12 | 13 | PLATFORMS 14 | ruby 15 | 16 | DEPENDENCIES 17 | sinatra (~> 1.4.6) 18 | 19 | BUNDLED WITH 20 | 1.11.2 21 | -------------------------------------------------------------------------------- /badges/http_utils.rb: -------------------------------------------------------------------------------- 1 | module Badges 2 | 3 | # Utility method for Http interaction 4 | class HttpUtils 5 | 6 | # 7 | # Sends a request and returns a response 8 | # 9 | # @param [String] url Url for the request 10 | # @param [Hash] params Parameters for the get request 11 | # @return [Net::HTTPResponse] Response for the request 12 | def self.get(url, params=nil) 13 | uri = URI.parse(url) 14 | 15 | # Extra params for custom badges 16 | uri.query = URI.encode_www_form(params) unless params.nil? or params.empty? 17 | 18 | # Set up http client 19 | http = Net::HTTP.new(uri.host, uri.port) 20 | 21 | # Set up ssl mode 22 | # TODO Verify how to validate it 23 | http.use_ssl = true 24 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 25 | 26 | request = Net::HTTP::Get.new(uri.request_uri) 27 | 28 | http.request(request) 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /badges/badge_utils.rb: -------------------------------------------------------------------------------- 1 | module Badges 2 | 3 | class BadgeUtils 4 | 5 | # 6 | # Generate the Badge in SVG format. 7 | # 8 | # @param [String] text Text to be displayed in the left said of a badge 9 | # @param [String] status Value to be displayed in the right side of a badge 10 | # @param [String] color Color that can be one of the following ... // TODO Document this crap 11 | # @param [Hash] params Extra params for customizing the badge // TODO Document this crap too 12 | # @return [String] SVG for the badge 13 | def self.build_badge(text, status, color, params=nil) 14 | uri = "https://img.shields.io/badge/#{URI.encode(text.to_s)}-#{URI.encode(status.to_s)}-#{color}.svg" 15 | 16 | resp = Badges::HttpUtils.get(uri, params) 17 | 18 | # If can't create the badge, raise a exception with the message 19 | raise Badges::BadgeException, resp.body unless resp.code == '200' 20 | 21 | resp.body 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kennedy Oliveira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /badges/badge.rb: -------------------------------------------------------------------------------- 1 | module Badges 2 | 3 | # 4 | # Basic colors for badges 5 | # Its possible to use any color with hex code 6 | BASIC_COLORS = { :blue => 'blue', 7 | :red => 'red', 8 | :lightgray => 'lightgray', 9 | :green => 'green', 10 | :bgreen => 'brightgreen', 11 | :yellow => 'yellow', 12 | :yellowgreen => 'yellowgreen', 13 | :orange => 'orange', 14 | :pink => 'ff69b4', 15 | :lightblue => '00CCFF', 16 | :purple => '9900ff' } 17 | 18 | # Represents a badge for Sinatra response 19 | class Badge 20 | 21 | attr_reader :text, :body, :color, :status 22 | 23 | # Creates a new Badge that can be used as response for with Sinatra 24 | # @param [String] text Text to be displayed in the left said of a badge 25 | # @param [String] status Value to be displayed in the right side of a badge 26 | # @param [String] color Color that can be one of the Badges::BASIC_COLORS or any hex color definition 27 | # @param [Hash] params Extra params for customizing the badge // TODO Document this 28 | def initialize(text, status, color, params = nil) 29 | @text = text 30 | @status = status 31 | @color = color 32 | 33 | @body = Badges::BadgeUtils.build_badge(text, status, color, params) 34 | end 35 | 36 | # Sinatra response object must responde to Each method 37 | def each 38 | yield @body 39 | end 40 | end 41 | 42 | def self.build_vendor_error_badge 43 | Badge.new('vendor', 'error', BASIC_COLORS[:lightgray]) 44 | end 45 | end 46 | 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /test/tmp/ 9 | /test/version_tmp/ 10 | /tmp/ 11 | 12 | ## Specific to RubyMotion: 13 | .dat* 14 | .repl_history 15 | build/ 16 | 17 | ## Documentation cache and generated files: 18 | /.yardoc/ 19 | /_yardoc/ 20 | /doc/ 21 | /rdoc/ 22 | 23 | ## Environment normalisation: 24 | /.bundle/ 25 | /vendor/bundle 26 | /lib/bundler/man/ 27 | 28 | # for a library or gem, you might want to ignore these files since the code is 29 | # intended to run in multiple environments; otherwise, check them in: 30 | # Gemfile.lock 31 | # .ruby-version 32 | # .ruby-gemset 33 | 34 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 35 | .rvmrc 36 | 37 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 38 | 39 | *.iml 40 | 41 | ## Directory-based project format: 42 | .idea/ 43 | # if you remove the above rule, at least ignore the following: 44 | 45 | # User-specific stuff: 46 | # .idea/workspace.xml 47 | # .idea/tasks.xml 48 | # .idea/dictionaries 49 | 50 | # Sensitive or high-churn files: 51 | # .idea/dataSources.ids 52 | # .idea/dataSources.xml 53 | # .idea/sqlDataSources.xml 54 | # .idea/dynamic.xml 55 | # .idea/uiDesigner.xml 56 | 57 | # Gradle: 58 | # .idea/gradle.xml 59 | # .idea/libraries 60 | 61 | # Mongo Explorer plugin: 62 | # .idea/mongoSettings.xml 63 | 64 | ## File-based project format: 65 | *.ipr 66 | *.iws 67 | 68 | ## Plugin-specific files: 69 | 70 | # IntelliJ 71 | /out/ 72 | 73 | # mpeltonen/sbt-idea plugin 74 | .idea_modules/ 75 | 76 | # JIRA plugin 77 | atlassian-ide-plugin.xml 78 | 79 | # Crashlytics plugin (for Android Studio and IntelliJ) 80 | com_crashlytics_export_strings.xml 81 | crashlytics.properties 82 | crashlytics-build.properties -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Basic Badges 2 | 3 | [![GitHub Release](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg)]() 4 | [![GitHub Download Count](https://github-basic-badges.herokuapp.com/downloads/kennedyoliveira/github-basic-badges/total.svg)]() 5 | [![GitHub Issues Open](https://github-basic-badges.herokuapp.com/issues/kennedyoliveira/github-basic-badges.svg)]() 6 | 7 | Basic badges for using with GitHub, and a service that you can deploy and create your own service to serve the GitHub badges. 8 | 9 | ## Motivation 10 | 11 | I know there are alots of services that you can generate badges for GitHub, i just developed one more to play around with Ruby and Heroku. 12 | 13 | ### Badges 14 | 15 | All the URL Patterns showed in the table bellow must be used as `\`. 16 | 17 | | URL Pattern | Description | Preview | 18 | | ----------- | ----------- | ------- | 19 | | `downloads///total.svg` | Sum of downloads of all artifacts in the latest release. | [![GitHub Download Count](https://github-basic-badges.herokuapp.com/downloads/kennedyoliveira/github-basic-badges/total.svg)]() | 20 | | `downloads////total.svg` | Sum of downloads of all artifacts in the release with the `` | [![GitHub Download Count By Tag](https://github-basic-badges.herokuapp.com/downloads/kennedyoliveira/github-basic-badges/v1.0.0/total.svg)]() | 21 | | `downloads///.svg` | Total downloads of the artifact named `` in the latest release. | [![GitHub Download Count By Artifact](https://github-basic-badges.herokuapp.com/downloads/kennedyoliveira/github-basic-badges/dummy.txt.svg)]() | 22 | | `downloads////.svg` | Total downloads of an artifact named `` in the release with a tag name ``. | [![GitHub Download Count By Artifact and Release](https://github-basic-badges.herokuapp.com/downloads/kennedyoliveira/github-basic-badges/v1.0.0/dummy.txt.svg)]() | 23 | | `release//.svg` | Latest release tag name. | [![GitHub Release](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg)]() | 24 | | `issues//.svg` | Total issues open. | [![GitHub Issues Open](https://github-basic-badges.herokuapp.com/issues/kennedyoliveira/github-basic-badges.svg)]() | 25 | | `commits//.svg` | Total commits. | [![GitHub Commits](https://github-basic-badges.herokuapp.com/commits/kennedyoliveira/github-basic-badges.svg)]() | 26 | | `license//.svg` | Project LICENSE. | [![GitHub License](https://github-basic-badges.herokuapp.com/license/kennedyoliveira/github-basic-badges.svg)]() | 27 | | `pulls//.svg` | Project open pull requests. | [![GitHub Pull Requests](https://github-basic-badges.herokuapp.com/pulls/kennedyoliveira/github-basic-badges.svg)]() | 28 | 29 | ### Custom colors and text 30 | 31 | You can customize the colors and the text of the badges, just use `color` and `text` parameters in the url, like customizing the last release example, check below: 32 | 33 | [![GitHub Release Ex 1](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg?color=blue&text=last--release)]() 34 | [![GitHub Release Ex 2](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg?color=orange)]() 35 | [![GitHub Release Ex 3](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg?text=final--release)]() 36 | [![GitHub Release Ex 4](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg?color=green)]() 37 | [![GitHub Release Ex 5](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg?color=yellow)]() 38 | [![GitHub Release Ex 6](https://github-basic-badges.herokuapp.com/release/kennedyoliveira/github-basic-badges.svg?9900ff)]() 39 | 40 | For colors you can use any hex colors you want, and some basic already defined, check the file [badge.rb](https://github.com/kennedyoliveira/github-basic-badges/blob/master/badges/badge.rb) for some basic colors. 41 | 42 | ### Deploying on Heroku 43 | 44 | Install heroku toolbet and log to it after that, it's pretty straigth forward deploying on heroku, just do the following: 45 | 46 | ```` 47 | # Cloning the repository 48 | git clone https://github.com/kennedyoliveira/github-basic-badges.git 49 | cd github-basic-badges 50 | 51 | # Create a heroku app 52 | heroku create 53 | 54 | # Push the code to heroku 55 | git push heroku master 56 | 57 | # Open the app 58 | heroku open 59 | ```` 60 | 61 | ### Mine Heroku App 62 | 63 | My instance is running in the following url `https://github-basic-badges.herokuapp.com` you can use it to generate badges for your repos! 64 | 65 | If you have any question or suggestion, open an issue! -------------------------------------------------------------------------------- /main.rb: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler.setup 3 | 4 | require './badges/badges' 5 | require 'sinatra' 6 | 7 | # GitHub API URL 8 | GITHUB_API = 'https://api.github.com/repos' 9 | 10 | # 11 | # Generate badges with download count 12 | # Can be by tag, for the latest, summing all assets download or by a specified file name 13 | # 14 | get '/downloads/:user/:repo/?:tag?/:asset.svg' do 15 | tag = params['tag'] ||= 'latest' 16 | 17 | git_uri = "#{GITHUB_API}/#{params['user']}/#{params['repo']}/releases/" 18 | 19 | if tag == 'latest' 20 | git_uri += "#{tag}" 21 | else 22 | git_uri += "tags/#{tag}" 23 | end 24 | 25 | custom_params = { 'text' => 'downloads', 'color' => Badges::BASIC_COLORS[:bgreen] } 26 | 27 | badge_request(git_uri, custom_params) do |resp| 28 | # If is the total, i sum the total downloads for each asset 29 | if params['asset'] == 'total' 30 | downloads = resp['assets'].inject(0) { |total, asset| total + asset['download_count'] } 31 | 32 | next "#{downloads} #{tag}" unless tag == 'latest' 33 | downloads 34 | else 35 | # Else, i try to find the asset with the name especified 36 | asset = resp['assets'].find { |asset| asset['name'] == params['asset'] } 37 | 38 | # If i doesn't find, i return an error 39 | next nil if asset.nil? 40 | 41 | "#{asset['download_count']}_#{asset['name']}" 42 | end 43 | end 44 | end 45 | 46 | # 47 | # Get the last release tag name 48 | # 49 | get '/release/:user/:repo.svg' do 50 | git_uri = "#{GITHUB_API}/#{params['user']}/#{params['repo']}/releases/latest" 51 | 52 | custom_params = { 'text' => 'release', 'color' => Badges::BASIC_COLORS[:bgreen] } 53 | 54 | badge_request(git_uri, custom_params) { |resp| resp['tag_name'] } 55 | end 56 | 57 | # 58 | # Get the issues open 59 | # 60 | get '/issues/:user/:repo.svg' do 61 | custom_params = { 'text' => 'open--issues', 'color' => 'red' } 62 | 63 | git_uri = "#{GITHUB_API}/#{params['user']}/#{params['repo']}" 64 | 65 | badge_request(git_uri, custom_params) { |resp| resp['open_issues_count'] } 66 | end 67 | 68 | # 69 | # Get the total commits 70 | # 71 | get '/commits/:user/:repo.svg' do 72 | git_uri = "#{GITHUB_API}/#{params['user']}/#{params['repo']}/contributors" 73 | 74 | custom_parameters = { 'text' => 'commits', 'color' => 'blue' } 75 | 76 | badge_request(git_uri, custom_parameters) { |resp| resp.inject(0) { |sum, contrib| sum + contrib['contributions'] } } 77 | end 78 | 79 | # 80 | # Get the license 81 | # 82 | get '/license/:user/:repo.svg' do 83 | git_uri = "#{GITHUB_API}/#{params['user']}/#{params['repo']}/license" 84 | 85 | custom_parameters = { 'text' => 'license', 'color' => Badges::BASIC_COLORS[:blue] } 86 | 87 | badge_request(git_uri, custom_parameters) { |resp| resp['license']['name'] } 88 | end 89 | 90 | # 91 | # Get the pull requests 92 | # 93 | get '/pulls/:user/:repo.svg' do 94 | git_uri = "#{GITHUB_API}/#{params['user']}/#{params['repo']}/pulls" 95 | 96 | custom_parameters = { 'text' => 'pull--requests', 'color' => Badges::BASIC_COLORS[:red] } 97 | 98 | badge_request(git_uri, custom_parameters) { |resp| resp.size } 99 | end 100 | 101 | ERROR_PAGE_HTML = < 103 | 104 |

GitHub Basic Badges

105 |

You are using it wrong! :(

106 |

107 | Please, go to GitHub Repository to see how it works! 108 |

109 |

110 | Don't worry, it's not that hard ;) 111 |

112 | 113 | 114 | END 115 | 116 | not_found do 117 | ERROR_PAGE_HTML 118 | end 119 | 120 | error do 121 | ERROR_PAGE_HTML 122 | end 123 | 124 | # 125 | # Utility method that encapsulate the badge request logic 126 | # 127 | # Receives a url that will try to get, if got it succefully the body will be parsed as JSON and send to the block. 128 | # Will merge the custom parameters for badge creating too. 129 | # 130 | # @param [String] url URL to be called before the block, and the response will be passed to the block if succefully 131 | # @param [String] custom_params Custom parameters to be merged with parameters request, priorizing the parameters request, this parameters is default one for the badge request 132 | # @param block A block that will receive the Response as json and a Hash with parameters merged, and must return a Badges::Badge instance or a single value that will be used to create a Badge 133 | def badge_request(url, custom_params = nil, &block) 134 | raise 'Must pass a block for this method!' unless block_given? 135 | 136 | custom_params ||= {} 137 | 138 | # @type [Hash] 139 | new_params = custom_params.merge(params) 140 | 141 | resp = Badges::HttpUtils.get(url) 142 | 143 | # If failed to get GitHub valid api response, returns a vendor error badge 144 | return Badges.build_vendor_error_badge if resp.code != '200' 145 | 146 | # Call a block passing the response as a Json, and the new params for creating the Badge 147 | resp_badge = block.call(JSON.parse(resp.body), new_params) 148 | 149 | # If the blocks yields nil, then return Vendor Error 150 | return Badges.build_vendor_error_badge if resp_badge.nil? 151 | 152 | # Return the response 153 | response.header['Content-Type'] = 'image/svg+xml;charset=utf-8' 154 | response.header['Cache-Control'] = 'no-cache, no-store, must-revalidate' 155 | 156 | # If the return was a badge, returns this Badge 157 | return resp_badge if resp_badge.instance_of? Badges::Badge 158 | 159 | # If not a badge, create a new one 160 | Badges::Badge.new(new_params['text'], 161 | resp_badge, 162 | new_params.fetch('color', Badges::BASIC_COLORS[:bgreen])) 163 | end 164 | --------------------------------------------------------------------------------