-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Set-up env var replacement in generated index file * Render Mustaches in the Webpack bundle * Fix bundle name * Generate composite `REACT_APP_VARS_AS_JSON` env var at runtime * Add runtime config to slug * Fix compile path * Escape quotes so bundle replacement works * Set utf-8 for JSON encoding function, because old-skool Ruby 1.9 * Switch to Ruby script for env var to JSON conversion. * Fix runtime paths * Set utf-8 for JSON encoding function, because old-skool Ruby 1.9 * Fix for env values with unknown encoding. * Actually write the runtime bundle * Another level of escapes. * Fix for space char breaking sed expression * Escape forward slashes too; they break sed expression * Escape ampersand too; they break sed expression * Replace `sed` JSON injection with pure Ruby * Fix pure Ruby injector command with correct args * Fix file path to JS bundle * More escapes for JSON values * Fix so injected values just work without further escaping by developer. * TravisCI * Use rake to execute tests (for TravisCI) * Fix missing dependency * Simplification fix for double quote escaping. * Triple backslash escape for double-quote in JSON value. * Revise JSON escaping for control chars * Fail gracefully for old CRA versions * Improve "Injecting runtime" log message
- Loading branch information
Showing
11 changed files
with
378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#!/bin/bash | ||
|
||
# Fail immediately on non-zero exit code. | ||
set -e | ||
# Debug, echo every command | ||
#set -x | ||
|
||
# Each bundle is generated with a unique hash name | ||
# to bust browser cache. | ||
js_bundle=/app/build/static/js/main.*.js | ||
|
||
if [ -f $js_bundle ] | ||
then | ||
|
||
# Get exact filename. | ||
js_bundle_filename=`ls $js_bundle` | ||
|
||
echo "Injecting runtime env into $js_bundle_filename (from .profile.d/inject_react_app_env.sh)" | ||
|
||
# Render runtime env vars into bundle. | ||
ruby -E utf-8:utf-8 \ | ||
-r /app/.heroku/create-react-app/injectable_env.rb \ | ||
-e "InjectableEnv.replace('$js_bundle_filename')" | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--color | ||
--format documentation | ||
--require spec_helper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
language: ruby | ||
rvm: | ||
- 1.9.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# encoding: utf-8 | ||
# frozen_string_literal: true | ||
source "https://rubygems.org" | ||
ruby '1.9.3' | ||
|
||
group :test do | ||
gem 'rake' | ||
gem 'rspec' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
GEM | ||
remote: https://rubygems.org/ | ||
specs: | ||
diff-lcs (1.2.5) | ||
rake (11.3.0) | ||
rspec (3.5.0) | ||
rspec-core (~> 3.5.0) | ||
rspec-expectations (~> 3.5.0) | ||
rspec-mocks (~> 3.5.0) | ||
rspec-core (3.5.4) | ||
rspec-support (~> 3.5.0) | ||
rspec-expectations (3.5.0) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.5.0) | ||
rspec-mocks (3.5.0) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.5.0) | ||
rspec-support (3.5.0) | ||
|
||
PLATFORMS | ||
ruby | ||
|
||
DEPENDENCIES | ||
rake | ||
rspec | ||
|
||
RUBY VERSION | ||
ruby 1.9.3p551 | ||
|
||
BUNDLED WITH | ||
1.13.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
begin | ||
require 'rspec/core/rake_task' | ||
|
||
RSpec::Core::RakeTask.new(:spec) | ||
task :default => :spec | ||
|
||
rescue LoadError | ||
# no rspec available | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# encoding: utf-8 | ||
require 'json' | ||
|
||
class InjectableEnv | ||
DefaultVarMatcher = /^REACT_APP_/ | ||
Placeholder='{{REACT_APP_VARS_AS_JSON}}' | ||
|
||
def self.create(var_matcher=DefaultVarMatcher) | ||
vars = ENV.find_all {|name,value| var_matcher===name } | ||
|
||
json = '{' | ||
is_first = true | ||
vars.each do |name,value| | ||
json += ',' unless is_first | ||
json += "#{escape(name)}:#{escape(value)}" | ||
is_first = false | ||
end | ||
json += '}' | ||
end | ||
|
||
def self.render(*args) | ||
$stdout.write create(*args) | ||
$stdout.flush | ||
end | ||
|
||
def self.replace(file, *args) | ||
injectee = IO.read(file) | ||
return unless injectee.index(Placeholder) | ||
|
||
env = create(*args) | ||
head,_,tail = injectee.partition(Placeholder) | ||
injected = head + env + tail | ||
File.open(file, 'w') do |f| | ||
f.write(injected) | ||
end | ||
end | ||
|
||
# Escape JSON name/value double-quotes so payload can be injected | ||
# into Webpack bundle where embedded in a double-quoted string. | ||
# | ||
def self.escape(v) | ||
v.dup | ||
.force_encoding('utf-8') # UTF-8 encoding for content | ||
.to_json | ||
.gsub(/\\\\/, '\\\\\\\\\\\\\\\\') # single slash in content | ||
.gsub(/\\([bfnrt])/, '\\\\\\\\\1') # control sequence in content | ||
.gsub(/([^\A])\"([^\Z])/, '\1\\\\\\"\2') # double-quote in content | ||
.gsub(/(\A\"|\"\Z)/, '\\\"') # double-quote around JSON token | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# encoding: utf-8 | ||
require './lib/injectable_env' | ||
require 'yaml' | ||
require 'tempfile' | ||
|
||
RSpec.describe InjectableEnv do | ||
|
||
describe '.create' do | ||
it "returns empty object" do | ||
expect(InjectableEnv.create).to eq('{}') | ||
end | ||
|
||
describe 'for REACT_APP_ vars' do | ||
before do | ||
ENV['REACT_APP_HELLO'] = 'Hello World' | ||
ENV['REACT_APP_EMOJI'] = '🍒🍊🍍' | ||
ENV['REACT_APP_EMBEDDED_QUOTES'] = '"e=MC(2)"' | ||
ENV['REACT_APP_SLASH_CONTENT'] = '\\' | ||
ENV['REACT_APP_NEWLINE'] = "I am\na poet." | ||
end | ||
after do | ||
ENV.delete 'REACT_APP_HELLO' | ||
ENV.delete 'REACT_APP_EMOJI' | ||
ENV.delete 'REACT_APP_EMBEDDED_QUOTES' | ||
ENV.delete 'REACT_APP_SLASH_CONTENT' | ||
ENV.delete 'REACT_APP_NEWLINE' | ||
end | ||
|
||
it "returns entries" do | ||
result = InjectableEnv.create | ||
# puts result | ||
# puts unescape(result) | ||
object = JSON.parse(unescape(result)) | ||
expect(object['REACT_APP_HELLO']).to eq('Hello World') | ||
expect(object['REACT_APP_EMOJI']).to eq('🍒🍊🍍') | ||
expect(object['REACT_APP_EMBEDDED_QUOTES']).to eq('"e=MC(2)"') | ||
expect(object['REACT_APP_SLASH_CONTENT']).to eq('\\') | ||
expect(object['REACT_APP_NEWLINE']).to eq("I am\na poet.") | ||
end | ||
end | ||
|
||
describe 'for unmatches vars' do | ||
before do | ||
ENV['ANOTHER_HELLO'] = 'Hello World' | ||
end | ||
after do | ||
ENV.delete 'ANOTHER_HELLO' | ||
end | ||
|
||
it "ignores them" do | ||
result = InjectableEnv.create | ||
object = JSON.parse(unescape(result)) | ||
expect(object).not_to have_key('ANOTHER_HELLO') | ||
end | ||
end | ||
end | ||
|
||
describe '.render' do | ||
it "writes result to stdout" do | ||
expect { InjectableEnv.render }.to output('{}').to_stdout | ||
end | ||
end | ||
|
||
describe '.replace' do | ||
before do | ||
ENV['REACT_APP_HELLO'] = "Hello\n\"World\" we \\ prices today" | ||
end | ||
after do | ||
ENV.delete 'REACT_APP_HELLO' | ||
end | ||
|
||
it "writes into file" do | ||
begin | ||
file = Tempfile.new('injectable_env_test') | ||
file.write('var injected="{{REACT_APP_VARS_AS_JSON}}"') | ||
file.rewind | ||
|
||
InjectableEnv.replace(file.path) | ||
|
||
expected_value='var injected="{\\"REACT_APP_HELLO\\":\\"Hello\\\\n\\\\\"World\\\\\" we \\\\\\\\ prices today\\"}"' | ||
actual_value=file.read | ||
expect(actual_value).to eq(expected_value) | ||
ensure | ||
if file | ||
file.close | ||
file.unlink | ||
end | ||
end | ||
end | ||
|
||
it "does not write when the placeholder is missing" do | ||
begin | ||
file = Tempfile.new('injectable_env_test') | ||
file.write('template is not present in file') | ||
file.rewind | ||
|
||
InjectableEnv.replace(file.path) | ||
|
||
expected_value='template is not present in file' | ||
actual_value=file.read | ||
expect(actual_value).to eq(expected_value) | ||
ensure | ||
if file | ||
file.close | ||
file.unlink | ||
end | ||
end | ||
end | ||
end | ||
|
||
describe '.escape' do | ||
it 'slash-escapes the JSON token double-quotes' do | ||
expect(InjectableEnv.escape('value')).to eq('\\"value\\"') | ||
end | ||
it 'double-escapes double-quotes in the value' do | ||
# This looks insane, but the six-slashes '\\\\\\' test for three '\\\' | ||
expect(InjectableEnv.escape('"quoted"')).to eq('\\"\\\\\\"quoted\\\\\\"\\"') | ||
end | ||
end | ||
end | ||
|
||
# For the sake of parsing the test output, | ||
# undo the "injectable" JSON escape sequences. | ||
def unescape(s) | ||
YAML.load(%Q(---\n"#{s}"\n)) | ||
end |
Oops, something went wrong.