├── data └── .gitkeep ├── output └── .gitkeep ├── .ruby-version ├── .rerun ├── lib ├── models │ ├── item.rb │ ├── items.rb │ ├── list.rb │ └── model.rb ├── error_reporting.rb ├── data_dragon.rb ├── seeder.rb ├── data_dragon │ ├── tail.rb │ ├── items.rb │ ├── client.rb │ └── downloader.rb ├── build │ ├── html.rb │ ├── haml_context.rb │ └── icons.rb ├── build.rb └── loldb.rb ├── .gitignore ├── loldb ├── README.md ├── Gemfile ├── Guardfile ├── LICENSE ├── Rakefile ├── Gemfile.lock └── DEPLOYMENT.md /data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /output/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.1 2 | -------------------------------------------------------------------------------- /.rerun: -------------------------------------------------------------------------------- 1 | -x 2 | --ignore 'output/*' 3 | time bundle exec rake build:html -------------------------------------------------------------------------------- /lib/models/item.rb: -------------------------------------------------------------------------------- 1 | class Models::Item < Models::Model 2 | set_fields :name, :description, :image_path 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /data 2 | /build/icons/ 3 | /build/css/icons.css 4 | /build/icons.png 5 | /output 6 | .DS_Store 7 | /spec/fixtures/vcr_cassettes 8 | /.env 9 | -------------------------------------------------------------------------------- /lib/error_reporting.rb: -------------------------------------------------------------------------------- 1 | if defined?(Rollbar) 2 | Rollbar.configure do |config| 3 | config.access_token = ENV['ROLLBAR_TOKEN'] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /loldb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec >>/home/ubuntu/loldb.log 2>&1 4 | set -e 5 | set -x 6 | 7 | export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 8 | export HOME="/home/ubuntu" 9 | 10 | . ~/environment 11 | cd ~/loldb 12 | bundle exec rake -------------------------------------------------------------------------------- /lib/data_dragon.rb: -------------------------------------------------------------------------------- 1 | module DataDragon 2 | def self.data_unpacked_path 3 | Build.data_path + 'unpacked' 4 | end 5 | 6 | def self.version_path 7 | Build.data_path + "version" 8 | end 9 | 10 | def self.data_pack_path 11 | Build.data_path + "pack.tgz" 12 | end 13 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | First, install the dependencies: 2 | 3 | `bundle install` 4 | 5 | Then, to generate LoL schedule: 6 | 7 | 1. run `rake data` 8 | 2. run `rake build` 9 | 10 | This will generate `index.html` and `icons.png`. 11 | 12 | For deployment instructions, see [DEPLOYMENT](DEPLOYMENT.md). -------------------------------------------------------------------------------- /lib/models/items.rb: -------------------------------------------------------------------------------- 1 | class Models::Items < Models::List 2 | def self.load 3 | list = new 4 | JSON.parse(Build.items_path.read).each { |hash| list << Models::Item.new(hash) } 5 | list 6 | end 7 | 8 | def save 9 | Build.items_path.write(map(&:to_h).to_json) 10 | end 11 | end -------------------------------------------------------------------------------- /lib/seeder.rb: -------------------------------------------------------------------------------- 1 | class Seeder 2 | def initialize 3 | end 4 | 5 | def seed 6 | Build.models_path.mkpath 7 | tail = DataDragon::Tail.new 8 | 9 | items = Models::Items.new 10 | DataDragon::Items.new(tail).each { |item| items << item } 11 | items.save 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/data_dragon/tail.rb: -------------------------------------------------------------------------------- 1 | class DataDragon::Tail 2 | def initialize 3 | @version = DataDragon.version_path.read 4 | raise unless @version && @version != "" 5 | end 6 | 7 | def tail_path 8 | DataDragon.data_unpacked_path + @version 9 | end 10 | 11 | def item_path 12 | tail_path + "data" + "en_AU" + "item.json" 13 | end 14 | 15 | def item_image_path(id) 16 | tail_path + "img" + "item" + "#{id}.png" 17 | end 18 | end -------------------------------------------------------------------------------- /lib/models/list.rb: -------------------------------------------------------------------------------- 1 | class Models::List 2 | extend Forwardable 3 | def_delegators :to_a, :each, :map, :select, :first 4 | 5 | def initialize 6 | @index = {} 7 | end 8 | 9 | def <<(record) 10 | @index[record.id] = record 11 | self 12 | end 13 | 14 | def to_a 15 | @index.values 16 | end 17 | alias :all :to_a 18 | 19 | def find(id) 20 | @index[id] 21 | end 22 | 23 | def find_all(ids) 24 | @index.values_at(*ids).compact 25 | end 26 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | ruby "~> 2.4.0" 4 | gem 'rake' 5 | gem 'excon' 6 | gem 'activesupport', require: false 7 | gem 'hamlit' 8 | gem 'addressable' 9 | gem 'rmagick' 10 | gem 'sprite-factory', require: 'sprite_factory' 11 | gem 'dotenv' 12 | 13 | group :guard do 14 | gem 'guard' 15 | gem 'guard-yield' 16 | gem 'guard-livereload' 17 | end 18 | 19 | group :test do 20 | gem 'rspec' 21 | gem 'vcr' 22 | end 23 | 24 | group :production do 25 | gem 'rollbar' 26 | end -------------------------------------------------------------------------------- /lib/build/html.rb: -------------------------------------------------------------------------------- 1 | class Build::Html 2 | PAGES = { 3 | "items" => Models::Items 4 | } 5 | 6 | def build 7 | haml_context = Build::HamlContext.new(Build.build_path) 8 | 9 | PAGES.each_pair do |file, klass| 10 | (Build.output_path + "#{file}.html").write(haml_context.render("#{file}.haml", context(klass))) 11 | end 12 | end 13 | 14 | def context(klass) 15 | { 16 | list: klass.load, 17 | generated: Time.now.iso8601, 18 | data_generated: DataDragon.version_path.mtime.iso8601 19 | } 20 | end 21 | end -------------------------------------------------------------------------------- /lib/models/model.rb: -------------------------------------------------------------------------------- 1 | class Models::Model 2 | class << self 3 | attr_reader :fields 4 | 5 | def set_fields(*fields) 6 | @fields = [:id] + fields 7 | 8 | attr_accessor *@fields 9 | end 10 | end 11 | 12 | def initialize(attributes = {}) 13 | attributes.each_pair do |attr, value| 14 | send("#{attr}=", value) 15 | end 16 | end 17 | 18 | def to_h 19 | Hash[self.class.fields.map { |field| [field.to_s, send(field)] }] 20 | end 21 | 22 | def inspect 23 | values = to_h.map { |k, v| "#{k}=#{v.inspect}" }.join(", ") 24 | "#<#{self.class.name}:#{object_id} #{values}>" 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/build/haml_context.rb: -------------------------------------------------------------------------------- 1 | class Build::HamlContext 2 | def initialize(build_path) 3 | @build_path = build_path 4 | @haml_engine_cache = {} 5 | end 6 | 7 | def render(template, locals = {}) 8 | haml_engine(template).render(self, locals) 9 | end 10 | 11 | def partial(name, locals = {}) 12 | render("_#{name}.haml", locals) 13 | end 14 | 15 | def include(path) 16 | (@build_path + path).read 17 | end 18 | 19 | def haml_engine(path) 20 | unless @haml_engine_cache.key?(path) 21 | @haml_engine_cache[path] = Hamlit::Template.new(filename: path) { include(path) } 22 | end 23 | @haml_engine_cache[path] 24 | end 25 | end -------------------------------------------------------------------------------- /lib/data_dragon/items.rb: -------------------------------------------------------------------------------- 1 | class DataDragon::Items 2 | def initialize(tail) 3 | @tail = tail 4 | @data = JSON.parse(@tail.item_path.read) 5 | end 6 | 7 | def each 8 | basic = @data["basic"] 9 | 10 | @data["data"].each_pair do |id, attrs| 11 | attrs = basic.deep_merge(attrs) 12 | 13 | next if attrs["hideFromAll"] 14 | 15 | yield Models::Item.new(build_item(id, attrs)) 16 | end 17 | end 18 | 19 | def build_item(id, attrs) 20 | 21 | { 22 | id: id, 23 | name: attrs["name"], 24 | description: attrs["description"], 25 | image_path: @tail.item_image_path(id) 26 | } 27 | 28 | end 29 | end -------------------------------------------------------------------------------- /lib/build.rb: -------------------------------------------------------------------------------- 1 | module Build 2 | def self.root_path 3 | Pathname.new(__FILE__).dirname.parent 4 | end 5 | 6 | def self.data_path 7 | if ENV.key?('LOLDB_DATA_DIR') 8 | Pathname.new(ENV['LOLDB_DATA_DIR']) 9 | else 10 | root_path + 'data' 11 | end 12 | end 13 | 14 | def self.models_path 15 | data_path + 'models' 16 | end 17 | 18 | def self.items_path 19 | models_path + "items.json" 20 | end 21 | 22 | def self.build_path 23 | root_path + 'build' 24 | end 25 | 26 | def self.icons_path 27 | build_path + 'icons' 28 | end 29 | 30 | def self.output_path 31 | if ENV.key?('LOLDB_OUTPUT_DIR') 32 | Pathname.new(ENV['LOLDB_OUTPUT_DIR']) 33 | else 34 | root_path + 'output' 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /lib/loldb.rb: -------------------------------------------------------------------------------- 1 | require 'cgi' 2 | require 'fileutils' 3 | require 'forwardable' 4 | require 'json' 5 | require 'pathname' 6 | require 'time' 7 | require 'open-uri' 8 | require 'zlib' 9 | 10 | require 'rubygems' 11 | require 'rubygems/package' 12 | require 'bundler/setup' 13 | ENV['LOLDB_ENV'] ||= 'development' 14 | Bundler.require(:default, ENV['LOLDB_ENV']) 15 | 16 | require 'active_support/core_ext/hash/deep_merge' 17 | 18 | $:.unshift(File.dirname(__FILE__)) 19 | 20 | Dotenv.load 21 | 22 | module Models 23 | end 24 | 25 | require 'error_reporting' 26 | require 'build' 27 | require 'data_dragon' 28 | require 'data_dragon/client' 29 | require 'data_dragon/downloader' 30 | require 'data_dragon/tail' 31 | require 'data_dragon/items' 32 | require 'models/list' 33 | require 'models/model' 34 | require 'models/item' 35 | require 'models/items' 36 | require 'seeder' 37 | require 'build/haml_context' 38 | require 'build/icons' 39 | require 'build/html' -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | require_relative './lib/loldb.rb' 2 | 3 | def yield_hash(object, &block) 4 | run_proc = proc do |*args| 5 | begin 6 | block.call(*args) 7 | rescue => e 8 | puts "Error: #{e.message}\n#{e.backtrace.join("\n")}" 9 | end 10 | end 11 | 12 | { 13 | object: object, 14 | run_on_additions: run_proc, 15 | run_on_modifications: run_proc, 16 | run_on_removals: run_proc 17 | } 18 | end 19 | 20 | 21 | icons_options = yield_hash(Build::Icons.new) { |icons| icons.download } 22 | sprites_options = yield_hash(Build::Icons.new) { |icons| icons.build_sprites } 23 | build_options = yield_hash(Build::Html.new) { |builder| builder.build } 24 | 25 | guard :yield, icons_options do 26 | watch(%r{^data/.*$}) 27 | end 28 | 29 | guard :yield, sprites_options do 30 | watch(%r{^build/icons/.*$}) 31 | end 32 | 33 | guard :yield, build_options do 34 | watch(%r{^build/.*$}) 35 | watch(%r{^data/.*$}) 36 | end 37 | 38 | guard 'livereload' do 39 | watch(%r{^output/.*$}) 40 | end -------------------------------------------------------------------------------- /lib/build/icons.rb: -------------------------------------------------------------------------------- 1 | class Build::Icons 2 | def download 3 | source = Models::Persistence.load(Build.source_path) 4 | 5 | Build.icons_path.mkpath 6 | 7 | source.teams.each do |team| 8 | file = Build.icons_path + "#{team.slug}.png" 9 | 10 | unless file.exist? 11 | next if team.logo.start_with?("http://na.lolesports.com") 12 | puts "Downloading #{team.logo}" 13 | body = URI.parse("http://am-a.akamaihd.net/image/?f=#{team.logo}&resize=50:50").read 14 | img = Magick::Image.from_blob(body)[0] 15 | small = img.resize_to_fit(36, 36) 16 | small.write(file.to_s) 17 | end 18 | end 19 | end 20 | 21 | def clean 22 | Build.icons_path.children.each { |file| file.rmtree } 23 | end 24 | 25 | def build_sprites 26 | SpriteFactory.run!( 27 | Build.icons_path.to_s, 28 | output_image: Build.output_path + 'icons.png', 29 | output_style: Build.build_path + 'css' + 'icons.css', 30 | margin: 1, 31 | selector: '.', 32 | nocomments: true, 33 | layout: :packed 34 | ) 35 | end 36 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Brenton Fletcher (http://bloople.net i@bloople.net) 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/data_dragon/client.rb: -------------------------------------------------------------------------------- 1 | class DataDragon::Client 2 | HOST = "https://ddragon.leagueoflegends.com" 3 | REALM_ENDPOINT = "/realms/oce.json" 4 | 5 | HEADERS = { 6 | "Accept" => "application/json, text/javascript, */*; q=0.01", 7 | "Accept-Encoding" => "gzip, deflate, sdch", 8 | "Accept-Language" => ":en-GB,en;q=0.8,en-US;q=0.6", 9 | "Connection" => "keep-alive" 10 | } 11 | 12 | def initialize 13 | @connection = Excon.new(HOST, 14 | persistent: true, 15 | middlewares: Excon.defaults[:middlewares] + [Excon::Middleware::Decompress], 16 | omit_default_port: true 17 | ) 18 | end 19 | 20 | def request_url(path, &block) 21 | if block_given? 22 | @connection.get(path: path, headers: HEADERS, response_block: block) 23 | nil 24 | else 25 | response = @connection.get(path: path, headers: HEADERS) 26 | response.body 27 | end 28 | end 29 | 30 | def retrieve_json(path) 31 | JSON.parse(request_url(path)) 32 | end 33 | 34 | def realm 35 | retrieve_json(REALM_ENDPOINT) 36 | end 37 | 38 | def dragontail(version, &block) 39 | request_url("/cdn/dragontail-#{version}.tgz", &block) 40 | end 41 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require_relative './lib/loldb.rb' 2 | 3 | desc 'Download data' 4 | task :data do 5 | DataDragon::Downloader.new.download 6 | Seeder.new.seed 7 | end 8 | 9 | namespace :build do 10 | desc 'Build HTML page containing schedule' 11 | task :html do 12 | Build::Html.new.build 13 | end 14 | end 15 | 16 | desc 'Build complete HTML page and team icons' 17 | task build: ['build:icons:download', 'build:icons:sprite', 'build:html'] 18 | 19 | namespace :clean do 20 | desc 'Delete generated HTML page and icons sprite sheet' 21 | task :build do 22 | Build::Icons.new.clean 23 | 24 | icons_css_path = (Build.build_path + 'css' + 'icons.css') 25 | icons_css_path.delete if icons_css_path.exist? 26 | 27 | Build::Html::SEASONS.each_pair do |year, file| 28 | html_path = (Build.output_path + file) 29 | html_path.delete if html_path.exist? 30 | end 31 | 32 | icons_png_path = (Build.output_path + 'icons.png') 33 | icons_png_path.delete if icons_png_path.exist? 34 | end 35 | end 36 | 37 | desc 'Start an IRB console with the project and data loaded' 38 | task :console do 39 | require 'irb' 40 | ARGV.clear 41 | IRB.start 42 | end 43 | 44 | desc 'Load the generated HTML page in your default browser' 45 | task :output do 46 | output_path = URI.join('file:///', (Build.output_path + 'index.html').realpath.to_s).to_s 47 | `xdg-open #{output_path}` 48 | end 49 | 50 | task :develop do 51 | exec('find . | entr rake -t build') 52 | end 53 | 54 | desc 'Download data and then build HTML page and icons' 55 | task default: [:data, :build] 56 | -------------------------------------------------------------------------------- /lib/data_dragon/downloader.rb: -------------------------------------------------------------------------------- 1 | class DataDragon::Downloader 2 | def initialize 3 | @client = DataDragon::Client.new 4 | end 5 | 6 | def download 7 | return if online_version == local_version 8 | 9 | delete_data 10 | download_pack 11 | extract_pack 12 | remove_data 13 | save_version 14 | end 15 | 16 | def delete_data 17 | DataDragon.data_unpacked_path.rmtree if DataDragon.data_unpacked_path.directory? 18 | end 19 | 20 | def download_pack 21 | DataDragon.data_pack_path.open("w") do |file| 22 | @client.dragontail(online_version) do |chunk, remaining, total| 23 | file.write(chunk) 24 | puts "Downloading, remaining: #{remaining}, total: #{total}" 25 | end 26 | end 27 | end 28 | 29 | # untars the given IO into the specified 30 | # directory 31 | def extract_pack 32 | io = Zlib::GzipReader.new(DataDragon.data_pack_path.open) 33 | 34 | Gem::Package::TarReader.new(io) do |tar| 35 | tar.each do |tarfile| 36 | destination_file = (DataDragon.data_unpacked_path + tarfile.full_name) 37 | 38 | if tarfile.directory? 39 | destination_file.mkpath 40 | else 41 | destination_directory = destination_file.dirname 42 | destination_directory.mkpath unless destination_directory.directory? 43 | destination_file.write(tarfile.read) 44 | end 45 | end 46 | end 47 | end 48 | 49 | def remove_data 50 | DataDragon.data_pack_path.delete if DataDragon.data_pack_path.directory? 51 | Build.models_path.rmtree if Build.models_path.directory? 52 | end 53 | 54 | def online_version 55 | @online_version ||= @client.realm["v"] 56 | end 57 | 58 | def local_version 59 | DataDragon.version_path.read 60 | rescue 61 | nil 62 | end 63 | 64 | def save_version 65 | DataDragon.version_path.write(online_version) 66 | end 67 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activesupport (5.2.0) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 0.7, < 2) 7 | minitest (~> 5.1) 8 | tzinfo (~> 1.1) 9 | addressable (2.4.0) 10 | coderay (1.1.1) 11 | concurrent-ruby (1.0.5) 12 | diff-lcs (1.2.5) 13 | dotenv (2.1.1) 14 | em-websocket (0.5.1) 15 | eventmachine (>= 0.12.9) 16 | http_parser.rb (~> 0.6.0) 17 | eventmachine (1.2.0.1) 18 | excon (0.51.0) 19 | ffi (1.9.10) 20 | formatador (0.2.5) 21 | guard (2.13.0) 22 | formatador (>= 0.2.4) 23 | listen (>= 2.7, <= 4.0) 24 | lumberjack (~> 1.0) 25 | nenv (~> 0.1) 26 | notiffany (~> 0.0) 27 | pry (>= 0.9.12) 28 | shellany (~> 0.0) 29 | thor (>= 0.18.1) 30 | guard-compat (1.2.1) 31 | guard-livereload (2.5.2) 32 | em-websocket (~> 0.5) 33 | guard (~> 2.8) 34 | guard-compat (~> 1.0) 35 | multi_json (~> 1.8) 36 | guard-yield (0.1.0) 37 | guard-compat (~> 1.0) 38 | hamlit (2.8.7) 39 | temple (>= 0.8.0) 40 | thor 41 | tilt 42 | http_parser.rb (0.6.0) 43 | i18n (1.0.1) 44 | concurrent-ruby (~> 1.0) 45 | listen (3.1.1) 46 | rb-fsevent (>= 0.9.3) 47 | rb-inotify (>= 0.9.7) 48 | lumberjack (1.0.10) 49 | method_source (0.8.2) 50 | minitest (5.11.3) 51 | multi_json (1.11.3) 52 | nenv (0.3.0) 53 | notiffany (0.0.8) 54 | nenv (~> 0.1) 55 | shellany (~> 0.0) 56 | pry (0.10.3) 57 | coderay (~> 1.1.0) 58 | method_source (~> 0.8.1) 59 | slop (~> 3.4) 60 | rake (10.4.2) 61 | rb-fsevent (0.9.7) 62 | rb-inotify (0.9.7) 63 | ffi (>= 0.5.0) 64 | rmagick (2.15.4) 65 | rollbar (2.12.0) 66 | multi_json 67 | rspec (3.5.0) 68 | rspec-core (~> 3.5.0) 69 | rspec-expectations (~> 3.5.0) 70 | rspec-mocks (~> 3.5.0) 71 | rspec-core (3.5.1) 72 | rspec-support (~> 3.5.0) 73 | rspec-expectations (3.5.0) 74 | diff-lcs (>= 1.2.0, < 2.0) 75 | rspec-support (~> 3.5.0) 76 | rspec-mocks (3.5.0) 77 | diff-lcs (>= 1.2.0, < 2.0) 78 | rspec-support (~> 3.5.0) 79 | rspec-support (3.5.0) 80 | shellany (0.0.1) 81 | slop (3.6.0) 82 | sprite-factory (1.7.1) 83 | temple (0.8.0) 84 | thor (0.19.1) 85 | thread_safe (0.3.6) 86 | tilt (2.0.8) 87 | tzinfo (1.2.5) 88 | thread_safe (~> 0.1) 89 | vcr (3.0.3) 90 | 91 | PLATFORMS 92 | ruby 93 | 94 | DEPENDENCIES 95 | activesupport 96 | addressable 97 | dotenv 98 | excon 99 | guard 100 | guard-livereload 101 | guard-yield 102 | hamlit 103 | rake 104 | rmagick 105 | rollbar 106 | rspec 107 | sprite-factory 108 | vcr 109 | 110 | RUBY VERSION 111 | ruby 2.4.2p198 112 | 113 | BUNDLED WITH 114 | 1.16.1 115 | -------------------------------------------------------------------------------- /DEPLOYMENT.md: -------------------------------------------------------------------------------- 1 | # Notes on deployment 2 | 3 | ## Install packages required to run project 4 | 5 | ````bash 6 | # As root 7 | add-apt-repository ppa:brightbox/ruby-ng 8 | add-apt-repository ppa:certbot/certbot 9 | apt-get update 10 | apt-get install build-essential git ruby2.4 ruby2.4-dev imagemagick libmagickwand-dev nginx certbot 11 | gem install bundler 12 | ```` 13 | 14 | ## User setup & Web root configuration 15 | 16 | ````bash 17 | # As root 18 | mkhomedir_helper ubuntu 19 | chsh ubuntu -s /bin/bash 20 | useradd -G www-data ubuntu 21 | chown -R ubuntu:www-data /var/www/html 22 | chmod -R g+rwx /var/www/html 23 | chmod g+s /var/www/html 24 | rm /var/www/html/index.nginx-debian.html 25 | ```` 26 | 27 | ## Switch to ubuntu and configure SSH 28 | 29 | ````bash 30 | su ubuntu 31 | cd ~ 32 | mkdir .ssh 33 | chmod 700 .ssh 34 | cd .ssh 35 | touch authorized_keys 36 | chmod 600 authorized_keys 37 | # Edit authorized_keys file to add your SSH public key 38 | cd ~ 39 | ```` 40 | 41 | ## Bare Git repo setup 42 | 43 | ````bash 44 | git clone --bare https://github.com/bloopletech/loldb.git /home/ubuntu/loldb.git 45 | cat < /home/ubuntu/loldb.git/hooks/post-receive 46 | #!/bin/bash 47 | git --work-tree=/home/ubuntu/loldb --git-dir=/home/ubuntu/loldb.git checkout -f 48 | cd /home/ubuntu/loldb 49 | . /home/ubuntu/environment 50 | bundle install --without development test guard --deployment 51 | EOF 52 | chmod +x /home/ubuntu/loldb.git/hooks/post-receive 53 | ```` 54 | 55 | ## Create loldb directory 56 | 57 | ````bash 58 | mkdir /home/ubuntu/loldb 59 | ```` 60 | 61 | ## Environment variables 62 | 63 | ````bash 64 | cat < /home/ubuntu/environment 65 | export LOLDB_ENV="production" 66 | export LOLDB_OUTPUT_DIR="/var/www/html" 67 | export ROLLBAR_TOKEN="" 68 | EOF 69 | ```` 70 | 71 | ## Configure Git remote and first push 72 | 73 | ````bash 74 | # On your local machine, in loldb directory 75 | git remote add droplet ssh://ubuntu@/home/ubuntu/loldb.git 76 | git commit --allow-empty -m "Bump" 77 | git push droplet master 78 | ```` 79 | 80 | ## Crontab configuration 81 | 82 | ````bash 83 | # As root 84 | cat < /var/spool/cron/crontabs/ubuntu 85 | */10 * * * * /home/ubuntu/loldb/loldb 86 | EOF 87 | 88 | chown ubuntu:ubuntu /var/spool/cron/crontabs/ubuntu 89 | service cron restart 90 | ```` 91 | 92 | ## Nginx gzip config block 93 | 94 | ```` 95 | gzip on; 96 | gzip_disable "msie6"; 97 | 98 | gzip_vary on; 99 | gzip_proxied any; 100 | gzip_comp_level 6; 101 | gzip_buffers 16 8k; 102 | gzip_http_version 1.1; 103 | gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; 104 | ```` 105 | 106 | ## Nginx server config block 107 | 108 | ```` 109 | # As root 110 | cat < /etc/nginx/sites-available/default 111 | server { 112 | listen 80; 113 | listen [::]:80 ipv6only=on; 114 | return 301 https://\$host\$request_uri; 115 | } 116 | 117 | server { 118 | listen 443 default_server ssl; 119 | 120 | ssl_certificate /etc/letsencrypt/live/lol.bloople.net/fullchain.pem; 121 | ssl_certificate_key /etc/letsencrypt/live/lol.bloople.net/privkey.pem; 122 | 123 | ssl_dhparam /etc/ssl/private/dhparams.pem; 124 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 125 | ssl_prefer_server_ciphers on; 126 | ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; 127 | #ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; 128 | ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 129 | ssl_session_cache shared:SSL:10m; 130 | ssl_session_tickets off; # Requires nginx >= 1.5.9 131 | ssl_stapling on; # Requires nginx >= 1.3.7 132 | ssl_stapling_verify on; # Requires nginx => 1.3.7 133 | resolver 8.8.4.4 8.8.8.8 valid=300s; 134 | resolver_timeout 5s; 135 | 136 | root /var/www/html; 137 | index index.html; 138 | server_name lol.bloople.net; 139 | 140 | location / { 141 | # First attempt to serve request as file, then 142 | # as directory, then fall back to displaying a 404. 143 | try_files \$uri \$uri/ =404; 144 | expires 10m; 145 | } 146 | 147 | location /icons.png { 148 | expires 1h; 149 | } 150 | 151 | access_log off; 152 | } 153 | EOF 154 | 155 | service nginx restart 156 | ```` 157 | 158 | ## SSL config 159 | 160 | ````bash 161 | # As root 162 | openssl dhparam -dsaparam -out /etc/ssl/private/dhparams.pem 4096 163 | certbot certonly --webroot -w /var/www/html -d lol.bloople.net 164 | ```` 165 | 166 | ## Logrotate config 167 | 168 | ````bash 169 | # As root 170 | cat < /etc/logrotate.d/loldb 171 | /home/ubuntu/loldb.log { 172 | daily 173 | missingok 174 | rotate 14 175 | compress 176 | delaycompress 177 | notifempty 178 | create 640 ubuntu ubuntu 179 | } 180 | EOF 181 | ```` 182 | --------------------------------------------------------------------------------