Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heroku deployment #4

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .slug-post-clean
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
frontend
tmp
3 changes: 3 additions & 0 deletions .slugignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.dockerdev
.github
.rubocop
2 changes: 2 additions & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
web: [[ "$ANYCABLE_DEPLOYMENT" == "true" ]] && bundle exec anycable --server-command="anycable-go" || bundle exec rails server -p $PORT -b 0.0.0.0
release: bundle exec rake heroku:release
7 changes: 7 additions & 0 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

Rails.application.configure do
# Configure session cookie to be stored for all subdomains
config.session_store :cookie_store, key: "_anycable_demo_sid", domain: :all

# Settings specified here will take precedence over those in config/application.rb.

# Code is not reloaded between requests.
Expand All @@ -23,6 +26,10 @@
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
config.public_file_server.headers = {
"Cache-Control" => "public, s-maxage=31536000, max-age=15552000",
"Expires" => 1.year.from_now.to_formatted_s(:rfc822)
}

# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com'
Expand Down
2 changes: 1 addition & 1 deletion config/puma.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
workers ENV.fetch("WEB_CONCURRENCY", 2)

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
Expand Down
81 changes: 81 additions & 0 deletions lib/tasks/heroku.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

require "net/http"

# A Rake task to run during the Heroku release phase
task "heroku:release" => ["db:migrate", "heroku:sync_configs"]

namespace :heroku do
task :sync_configs do
# The name or ID of the connected RPC app
rpc_app = ENV["HEROKU_ANYCABLE_RPC_APP"]
# We retrieve the name of the current app from the Heroku metadata.
# NOTE: You must enable runtime-dyno-metadata feature. See https://devcenter.heroku.com/articles/dyno-metadata
#
# Enable via CLI:
# heroku labs:enable runtime-dyno-metadata -a <app-name>
current_app = ENV["HEROKU_APP_ID"]
next unless rpc_app && current_app

# Use Heroku CLI to generate a token:
# heroku auth:token
token = ENV.fetch("HEROKU_API_TOKEN")

fetch_config = proc do |app|
uri = URI.parse("https://api.heroku.com/apps/#{app}/config-vars")
header = {
Accept: "application/vnd.heroku+json; version=3",
Authorization: "Bearer #{token}"
}

req = Net::HTTP::Get.new(uri.request_uri, header)

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req)
end

raise "Failed to fetch config vars for the current app: #{res.value}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)

JSON.parse(res.body).tap do |config|
# remove all Heroku metadata
config.delete_if { |k, _| k.start_with?("HEROKU_") }
end
end

current_config = fetch_config.call(current_app).tap do |config|
# remove protected RPC vars we don't want to sync
%w[].each { |k| config.delete(k) }
end

rpc_config = fetch_config.call(rpc_app).tap do |config|
# remove protected RPC vars we don't want to update/remove
%w[ANYCABLE_DEPLOYMENT ANYCABLE_HOST].each { |k| config.delete(k) }
end

keys_to_delete = rpc_config.keys - current_config.keys
missing_keys = current_config.keys - rpc_config.keys
sync_keys = (current_config.keys - missing_keys).select { |k| current_config[k] != rpc_config[k] }

$stdout.puts "RPC only keys: #{keys_to_delete.join(", ")}" unless keys_to_delete.empty?
$stdout.puts "APP only keys: #{missing_keys.join(", ")}" unless missing_keys.empty?
$stdout.puts "Out-of-sync keys: #{sync_keys.join(", ")}" unless sync_keys.empty?

payload = (keys_to_delete.map { |k| [k, nil] } + (sync_keys + missing_keys).map { |k| [k, current_config[k]] }).to_h

uri = URI.parse("https://api.heroku.com/apps/#{rpc_app}/config-vars")
header = {
Accept: "application/vnd.heroku+json; version=3",
"Content-Type": "application/json",
Authorization: "Bearer #{token}"
}

req = Net::HTTP::Patch.new(uri.request_uri, header)
req.body = payload.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req)
end

raise "Failed to update config vars for the RPC app: #{res.value}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
end
end
Loading