brut 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/CODE_OF_CONDUCT.txt +99 -0
- data/Dockerfile.dx +32 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +133 -0
- data/LICENSE.txt +370 -0
- data/README.md +21 -0
- data/Rakefile +1 -0
- data/bin/bin_kit.rb +39 -0
- data/bin/rake +27 -0
- data/bin/setup +145 -0
- data/brut.gemspec +60 -0
- data/docker-compose.dx.yml +16 -0
- data/dx/build +26 -0
- data/dx/docker-compose.env +22 -0
- data/dx/dx.sh.lib +24 -0
- data/dx/exec +58 -0
- data/dx/prune +19 -0
- data/dx/setupkit.sh.lib +144 -0
- data/dx/show-help-in-app-container-then-wait.sh +38 -0
- data/dx/start +30 -0
- data/dx/stop +23 -0
- data/lib/brut/back_end/action.rb +3 -0
- data/lib/brut/back_end/result.rb +46 -0
- data/lib/brut/back_end/seed_data.rb +24 -0
- data/lib/brut/back_end/validator.rb +3 -0
- data/lib/brut/back_end/validators/form_validator.rb +37 -0
- data/lib/brut/cli/app.rb +130 -0
- data/lib/brut/cli/app_runner.rb +219 -0
- data/lib/brut/cli/apps/build_assets.rb +123 -0
- data/lib/brut/cli/apps/db.rb +279 -0
- data/lib/brut/cli/apps/scaffold.rb +256 -0
- data/lib/brut/cli/apps/test.rb +200 -0
- data/lib/brut/cli/command.rb +130 -0
- data/lib/brut/cli/error.rb +12 -0
- data/lib/brut/cli/execution_results.rb +81 -0
- data/lib/brut/cli/executor.rb +37 -0
- data/lib/brut/cli/options.rb +46 -0
- data/lib/brut/cli/output.rb +30 -0
- data/lib/brut/cli.rb +24 -0
- data/lib/brut/factory_bot.rb +20 -0
- data/lib/brut/framework/app.rb +55 -0
- data/lib/brut/framework/config.rb +415 -0
- data/lib/brut/framework/container.rb +190 -0
- data/lib/brut/framework/errors/abstract_method.rb +9 -0
- data/lib/brut/framework/errors/bug.rb +14 -0
- data/lib/brut/framework/errors/not_found.rb +10 -0
- data/lib/brut/framework/errors.rb +14 -0
- data/lib/brut/framework/fussy_type_enforcement.rb +50 -0
- data/lib/brut/framework/mcp.rb +215 -0
- data/lib/brut/framework/project_environment.rb +18 -0
- data/lib/brut/framework.rb +13 -0
- data/lib/brut/front_end/asset_metadata.rb +76 -0
- data/lib/brut/front_end/component.rb +213 -0
- data/lib/brut/front_end/components/form_tag.rb +71 -0
- data/lib/brut/front_end/components/i18n_translations.rb +36 -0
- data/lib/brut/front_end/components/input.rb +13 -0
- data/lib/brut/front_end/components/inputs/csrf_token.rb +8 -0
- data/lib/brut/front_end/components/inputs/select.rb +100 -0
- data/lib/brut/front_end/components/inputs/text_field.rb +63 -0
- data/lib/brut/front_end/components/inputs/textarea.rb +51 -0
- data/lib/brut/front_end/components/locale_detection.rb +25 -0
- data/lib/brut/front_end/components/page_identifier.rb +13 -0
- data/lib/brut/front_end/components/timestamp.rb +33 -0
- data/lib/brut/front_end/download.rb +23 -0
- data/lib/brut/front_end/flash.rb +57 -0
- data/lib/brut/front_end/form.rb +171 -0
- data/lib/brut/front_end/forms/constraint_violation.rb +39 -0
- data/lib/brut/front_end/forms/input.rb +119 -0
- data/lib/brut/front_end/forms/input_definition.rb +100 -0
- data/lib/brut/front_end/forms/validity_state.rb +36 -0
- data/lib/brut/front_end/handler.rb +48 -0
- data/lib/brut/front_end/handlers/csp_reporting_handler.rb +11 -0
- data/lib/brut/front_end/handlers/locale_detection_handler.rb +22 -0
- data/lib/brut/front_end/handling_results.rb +14 -0
- data/lib/brut/front_end/http_method.rb +33 -0
- data/lib/brut/front_end/http_status.rb +16 -0
- data/lib/brut/front_end/middleware.rb +7 -0
- data/lib/brut/front_end/middlewares/reload_app.rb +31 -0
- data/lib/brut/front_end/page.rb +47 -0
- data/lib/brut/front_end/request_context.rb +82 -0
- data/lib/brut/front_end/route_hook.rb +15 -0
- data/lib/brut/front_end/route_hooks/age_flash.rb +8 -0
- data/lib/brut/front_end/route_hooks/csp_no_inline_scripts.rb +17 -0
- data/lib/brut/front_end/route_hooks/csp_no_inline_styles_or_scripts.rb +46 -0
- data/lib/brut/front_end/route_hooks/locale_detection.rb +24 -0
- data/lib/brut/front_end/route_hooks/setup_request_context.rb +11 -0
- data/lib/brut/front_end/routing.rb +236 -0
- data/lib/brut/front_end/session.rb +56 -0
- data/lib/brut/front_end/template.rb +32 -0
- data/lib/brut/front_end/templates/block_filter.rb +60 -0
- data/lib/brut/front_end/templates/erb_engine.rb +26 -0
- data/lib/brut/front_end/templates/erb_parser.rb +84 -0
- data/lib/brut/front_end/templates/escapable_filter.rb +18 -0
- data/lib/brut/front_end/templates/html_safe_string.rb +40 -0
- data/lib/brut/i18n/base_methods.rb +168 -0
- data/lib/brut/i18n/for_cli.rb +4 -0
- data/lib/brut/i18n/for_html.rb +4 -0
- data/lib/brut/i18n/http_accept_language.rb +68 -0
- data/lib/brut/i18n.rb +6 -0
- data/lib/brut/instrumentation/basic.rb +66 -0
- data/lib/brut/instrumentation/event.rb +19 -0
- data/lib/brut/instrumentation/http_event.rb +5 -0
- data/lib/brut/instrumentation/subscriber.rb +41 -0
- data/lib/brut/instrumentation.rb +11 -0
- data/lib/brut/junk_drawer.rb +88 -0
- data/lib/brut/sinatra_helpers.rb +183 -0
- data/lib/brut/spec_support/component_support.rb +49 -0
- data/lib/brut/spec_support/flash_support.rb +7 -0
- data/lib/brut/spec_support/general_support.rb +18 -0
- data/lib/brut/spec_support/handler_support.rb +7 -0
- data/lib/brut/spec_support/matcher.rb +9 -0
- data/lib/brut/spec_support/matchers/be_a_bug.rb +14 -0
- data/lib/brut/spec_support/matchers/be_page_for.rb +14 -0
- data/lib/brut/spec_support/matchers/be_routing_for.rb +11 -0
- data/lib/brut/spec_support/matchers/have_constraint_violation.rb +56 -0
- data/lib/brut/spec_support/matchers/have_html_attribute.rb +69 -0
- data/lib/brut/spec_support/matchers/have_rendered.rb +20 -0
- data/lib/brut/spec_support/matchers/have_returned_http_status.rb +27 -0
- data/lib/brut/spec_support/session_support.rb +3 -0
- data/lib/brut/spec_support.rb +12 -0
- data/lib/brut/version.rb +3 -0
- data/lib/brut.rb +38 -0
- data/lib/sequel/extensions/brut_instrumentation.rb +37 -0
- data/lib/sequel/extensions/brut_migrations.rb +98 -0
- data/lib/sequel/plugins/created_at.rb +14 -0
- data/lib/sequel/plugins/external_id.rb +45 -0
- data/lib/sequel/plugins/find_bang.rb +13 -0
- data/lib/sequel/plugins.rb +3 -0
- metadata +484 -0
data/bin/bin_kit.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "fileutils"
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
# We don't want the setup method to have to do all this error
|
6
|
+
# checking, and we also want to explicitly log what we are
|
7
|
+
# executing. Thus, we use this method instead of Kernel#system
|
8
|
+
def system!(*args)
|
9
|
+
if ENV["BRUT_BIN_KIT_DEBUG"] == "true"
|
10
|
+
log "Executing #{args}"
|
11
|
+
out,err,status = Open3.capture3(*args)
|
12
|
+
if status.success?
|
13
|
+
log "#{args} succeeded"
|
14
|
+
else
|
15
|
+
log "#{args} failed"
|
16
|
+
log "STDOUT:"
|
17
|
+
$stdout.puts out
|
18
|
+
log "STDERR:"
|
19
|
+
$stderr.puts err
|
20
|
+
abort
|
21
|
+
end
|
22
|
+
else
|
23
|
+
log "Executing #{args}"
|
24
|
+
if system(*args)
|
25
|
+
log "#{args} succeeded"
|
26
|
+
else
|
27
|
+
log "#{args} failed"
|
28
|
+
abort
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# It's helpful to know what messages came from this
|
34
|
+
# script, so we'll use log instead of `puts`
|
35
|
+
def log(message)
|
36
|
+
puts "[ #{$0} ] #{message}"
|
37
|
+
end
|
38
|
+
|
39
|
+
ROOT_DIR = ((Pathname(__dir__) / ".." ).expand_path)
|
data/bin/rake
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require "bundler/setup"
|
26
|
+
|
27
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/setup
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "bin_kit"
|
4
|
+
require "optparse"
|
5
|
+
require "pathname"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
def setup(update_gems:,setup_credentials:)
|
9
|
+
if update_gems
|
10
|
+
log "Updating gems"
|
11
|
+
system! "bundle update"
|
12
|
+
else
|
13
|
+
log "Installing gems"
|
14
|
+
# Only do bundle install if the much-faster
|
15
|
+
# bundle check indicates we need to
|
16
|
+
system! "bundle check --no-color || bundle install --no-color --quiet"
|
17
|
+
end
|
18
|
+
|
19
|
+
if setup_credentials
|
20
|
+
project_root = Pathname($0).dirname / ".."
|
21
|
+
credentials_dir = project_root / "dx" / "credentials"
|
22
|
+
if credentials_dir.exist?
|
23
|
+
if credentials_dir.directory?
|
24
|
+
log "#{credentials_dir} exists"
|
25
|
+
else
|
26
|
+
log "#{credentials_dir} is not a directory - please delete it or move it elsewhere and re-run this script"
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
else
|
30
|
+
log "#{credentials_dir} doesn't exist - creating"
|
31
|
+
FileUtils.mkdir_p credentials_dir
|
32
|
+
end
|
33
|
+
|
34
|
+
# https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=linux
|
35
|
+
key_file = credentials_dir / "id_ed25519"
|
36
|
+
|
37
|
+
if key_file.exist?
|
38
|
+
log "#{key_file} exists already"
|
39
|
+
else
|
40
|
+
git_config_command = "git config --get user.email"
|
41
|
+
email = `#{git_config_command}`.chomp
|
42
|
+
if !$?.success?
|
43
|
+
log "Could not determine your email via #{git_config_command} - is your git set up properly?"
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
log "Creating your key in #{key_file} using email #{email}."
|
47
|
+
log "You will be prompted for a passphrase, which you are encouraged to provide"
|
48
|
+
system! "ssh-keygen -t ed25519 -C #{email} -f \"#{key_file}\""
|
49
|
+
log ""
|
50
|
+
log "You must now add this to your GitHub profile in order to perform Git commands"
|
51
|
+
log ""
|
52
|
+
log "The key you just generated has a public key that should be available on your computer at"
|
53
|
+
log ""
|
54
|
+
log " #{key_file.relative_path_from(project_root)}.pub"
|
55
|
+
log ""
|
56
|
+
log "Copy its contents and head to:"
|
57
|
+
log ""
|
58
|
+
log " https://github.com/settings/keys"
|
59
|
+
log ""
|
60
|
+
log "Click 'New SSH key' and paste your key in, giving it a name like 'Brut Dev Env'"
|
61
|
+
log ""
|
62
|
+
log "(if this doesn't look right, check https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account)"
|
63
|
+
log ""
|
64
|
+
log "Hit return when done"
|
65
|
+
x = gets
|
66
|
+
end
|
67
|
+
|
68
|
+
log "Adding your SSH key to ssh-agent - you must provide your passphrase"
|
69
|
+
system! "ssh-add #{key_file}"
|
70
|
+
|
71
|
+
known_hosts_dest = Pathname("/") / "root" / ".ssh" / "known_hosts"
|
72
|
+
if known_hosts_dest.exist?
|
73
|
+
log "#{known_hosts_dest} exists, your ssh key should work with GitHub"
|
74
|
+
else
|
75
|
+
log "#{known_hosts_dest} does not exist"
|
76
|
+
known_hosts_source = credentials_dir / "known_hosts"
|
77
|
+
if known_hosts_source.exist?
|
78
|
+
log "#{known_hosts_source} exists - copying it to #{known_hosts_dest}"
|
79
|
+
FileUtils.mkdir_p known_hosts_dest.dirname
|
80
|
+
FileUtils.chmod(0700,known_hosts_dest.dirname)
|
81
|
+
FileUtils.cp known_hosts_source, known_hosts_dest
|
82
|
+
FileUtils.chmod(0600,known_hosts_dest)
|
83
|
+
else
|
84
|
+
log "#{known_hosts_source} also does not exist. To create it, we'll connect to github.com"
|
85
|
+
log "NOTE: it will show you a fingerprint to verify authenticity. You should check it against:"
|
86
|
+
log ""
|
87
|
+
log " https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints"
|
88
|
+
log ""
|
89
|
+
log " and proceed ONLY if the values match"
|
90
|
+
log ""
|
91
|
+
system("ssh -T git@github.com") # NOT system! because this may exit nonzero but still have succeeded
|
92
|
+
if $?.exitstatus == 255
|
93
|
+
log "SOMETHING MAY HAVE GONE WRONG!"
|
94
|
+
end
|
95
|
+
if known_hosts_dest.exist?
|
96
|
+
log "Copying #{known_hosts_dest} back to #{known_hosts_source} to use in the future"
|
97
|
+
FileUtils.cp known_hosts_dest,known_hosts_source
|
98
|
+
else
|
99
|
+
log "For some reason #{known_hosts_dest} was not created. Future ssh commands may ask you to verify GitHub's fingerprint"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
log "Your ssh key looks good"
|
104
|
+
|
105
|
+
gem_credentials_dest = Pathname("/") / "root" / ".gem" / "credentials"
|
106
|
+
if gem_credentials_dest.exist?
|
107
|
+
log "Gem credentials look good"
|
108
|
+
else
|
109
|
+
log "#{gem_credentials_dest} doesn't exist - creating"
|
110
|
+
FileUtils.mkdir_p gem_credentials_dest.dirname
|
111
|
+
gem_credentials_source = credentials_dir / "rubygems.credentials"
|
112
|
+
if gem_credentials_source.exist?
|
113
|
+
log "#{gem_credentials_source} exists - copying it to #{gem_credentials_dest}"
|
114
|
+
FileUtils.cp gem_credentials_source,gem_credentials_dest
|
115
|
+
else
|
116
|
+
log "#{gem_credentials_source} must contain a RubyGems credentials file"
|
117
|
+
log ""
|
118
|
+
log "Follow the instructions here:"
|
119
|
+
log ""
|
120
|
+
log " https://guides.rubygems.org/api-key-scopes/#creating-from-gem-cli"
|
121
|
+
log ""
|
122
|
+
log "Then copy ~/.gem/credentials into #{gem_credentials_source} and re-run this script"
|
123
|
+
exit 1
|
124
|
+
end
|
125
|
+
end
|
126
|
+
else
|
127
|
+
log "Not setting up GitHub or RubyGems credentials. You won't be able to push the gem"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
options = {
|
133
|
+
update_gems: false,
|
134
|
+
setup_credentials: true,
|
135
|
+
}
|
136
|
+
OptionParser.new do |opts|
|
137
|
+
opts.on("--update-gems","Update gems get the latest versions consistent with Gemfile / gemspec.") do
|
138
|
+
options[:update_gems] = true
|
139
|
+
end
|
140
|
+
opts.on("--no-credentials","If set, no GitHub or RubyGems credentials are required, but you can't push gems") do
|
141
|
+
options[:setup_credentials] = false
|
142
|
+
end
|
143
|
+
end.parse!
|
144
|
+
|
145
|
+
setup(**options)
|
data/brut.gemspec
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "brut/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "brut"
|
7
|
+
spec.version = Brut::VERSION
|
8
|
+
spec.authors = ["David Bryant Copeland"]
|
9
|
+
spec.email = ["davec@thirdtank.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{NOT YET RELEASED - Web Framework Built around Ruby, Web Standards, Simplicity, and Object-Orientation}
|
12
|
+
spec.description = %q{NOT YET RELEASED - An opinionated web framework build on web standards}
|
13
|
+
spec.homepage = "https://naildrivin5.com"
|
14
|
+
|
15
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
16
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
17
|
+
if spec.respond_to?(:metadata)
|
18
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
19
|
+
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://naildrivin5.com"
|
22
|
+
spec.metadata["changelog_uri"] = "https://naildrivin5.com"
|
23
|
+
else
|
24
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
25
|
+
"public gem pushes."
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specify which files should be added to the gem when it is released.
|
29
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
30
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
31
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
32
|
+
end
|
33
|
+
spec.bindir = "exe"
|
34
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
|
+
spec.require_paths = ["lib"]
|
36
|
+
|
37
|
+
spec.add_runtime_dependency "dotenv"
|
38
|
+
spec.add_runtime_dependency "ostruct" # squelch some warning - this is not used
|
39
|
+
spec.add_runtime_dependency "factory_bot"
|
40
|
+
spec.add_runtime_dependency "faker"
|
41
|
+
spec.add_runtime_dependency "i18n"
|
42
|
+
spec.add_runtime_dependency "nokogiri"
|
43
|
+
spec.add_runtime_dependency "prism"
|
44
|
+
spec.add_runtime_dependency "rack-protection"
|
45
|
+
spec.add_runtime_dependency "rackup"
|
46
|
+
spec.add_runtime_dependency "rexml"
|
47
|
+
spec.add_runtime_dependency "semantic_logger"
|
48
|
+
spec.add_runtime_dependency "sequel"
|
49
|
+
spec.add_runtime_dependency "sinatra"
|
50
|
+
spec.add_runtime_dependency "temple"
|
51
|
+
spec.add_runtime_dependency "tilt"
|
52
|
+
spec.add_runtime_dependency "tzinfo"
|
53
|
+
spec.add_runtime_dependency "tzinfo-data"
|
54
|
+
spec.add_runtime_dependency "zeitwerk"
|
55
|
+
|
56
|
+
spec.add_development_dependency "activesupport"
|
57
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
58
|
+
spec.add_development_dependency "bundler"
|
59
|
+
spec.add_development_dependency "rake"
|
60
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
services:
|
2
|
+
app:
|
3
|
+
image: ${IMAGE}
|
4
|
+
pull_policy: "missing"
|
5
|
+
init: true
|
6
|
+
volumes:
|
7
|
+
- type: bind
|
8
|
+
source: "./"
|
9
|
+
target: "/root/work"
|
10
|
+
consistency: "consistent"
|
11
|
+
- type: bind
|
12
|
+
source: ${GIT_CONFIG}
|
13
|
+
target: "/root/.gitconfig"
|
14
|
+
entrypoint: /root/show-help-in-app-container-then-wait.sh
|
15
|
+
working_dir: /root/work
|
16
|
+
|
data/dx/build
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
|
6
|
+
|
7
|
+
. "${SCRIPT_DIR}/dx.sh.lib"
|
8
|
+
|
9
|
+
require_command "docker"
|
10
|
+
load_docker_compose_env
|
11
|
+
|
12
|
+
usage_on_help "Builds the Docker image based on the Dockerfile" "" "build.pre" "build.post" "${@}"
|
13
|
+
|
14
|
+
exec_hook_if_exists "build.pre" Dockerfile.dx "${IMAGE}"
|
15
|
+
|
16
|
+
docker build \
|
17
|
+
--file Dockerfile.dx \
|
18
|
+
--tag "${IMAGE}" \
|
19
|
+
./
|
20
|
+
|
21
|
+
exec_hook_if_exists "build.post" Dockerfile.dx "${IMAGE}"
|
22
|
+
|
23
|
+
log "đ" "Your Docker image has been built tagged '${IMAGE}'"
|
24
|
+
log "đ" "You can now run dx/start to start it up, though you may need to stop it first with Ctrl-C"
|
25
|
+
|
26
|
+
# vim: ft=bash
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# IMAGE is the name of the image to be built for running
|
2
|
+
# your app. The recommended format is ORG/REPO:TAG
|
3
|
+
#
|
4
|
+
# ORG - your org on GitHub or DockerHub
|
5
|
+
# REPO - the name of the repo on GitHub or the app name
|
6
|
+
# TAG - a version identifier. Recommend you avoid latest as this is confusing
|
7
|
+
IMAGE=thirdtank/brut-dev:ruby-3.3
|
8
|
+
|
9
|
+
# This is used to tell docker compose what the name
|
10
|
+
# of your project is for the purpose of naming
|
11
|
+
# containers. It can be anything and is mostly
|
12
|
+
# used for pruning containers via bin/prune
|
13
|
+
PROJECT_NAME=brut-dev
|
14
|
+
|
15
|
+
# Use this to override the service name for your
|
16
|
+
# app in docker-compose.dx.yml
|
17
|
+
DEFAULT_SERVICE=app
|
18
|
+
|
19
|
+
# Path to the git configuration to use
|
20
|
+
GIT_CONFIG=~/.gitconfig
|
21
|
+
|
22
|
+
# vim: ft=bash
|
data/dx/dx.sh.lib
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# shellcheck shell=bash
|
2
|
+
|
3
|
+
. "${SCRIPT_DIR}/setupkit.sh.lib"
|
4
|
+
|
5
|
+
require_command "realpath"
|
6
|
+
require_command "cat"
|
7
|
+
|
8
|
+
ENV_FILE=$(realpath "${SCRIPT_DIR}")/docker-compose.env
|
9
|
+
|
10
|
+
load_docker_compose_env() {
|
11
|
+
. "${ENV_FILE}"
|
12
|
+
}
|
13
|
+
|
14
|
+
exec_hook_if_exists() {
|
15
|
+
script_name=$1
|
16
|
+
shift
|
17
|
+
if [ -x "${SCRIPT_DIR}"/"${script_name}" ]; then
|
18
|
+
log "đĒ" "${script_name} exists - executing"
|
19
|
+
"${SCRIPT_DIR}"/"${script_name}" "${@}"
|
20
|
+
else
|
21
|
+
debug "${script_name} does not exist"
|
22
|
+
fi
|
23
|
+
}
|
24
|
+
# vim: ft=bash
|
data/dx/exec
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
|
6
|
+
|
7
|
+
. "${SCRIPT_DIR}/dx.sh.lib"
|
8
|
+
|
9
|
+
require_command "docker"
|
10
|
+
load_docker_compose_env
|
11
|
+
|
12
|
+
usage_description="Execute a command inside the app's container with ssh-agent active."
|
13
|
+
usage_args="[-s service] [-A] command"
|
14
|
+
usage_pre="exec.pre"
|
15
|
+
usage_on_help "${usage_description}" "${usage_args}" "${usage_pre}" "" "${@}"
|
16
|
+
|
17
|
+
SERVICE="${SERVICE_NAME:-${DEFAULT_SERVICE}}"
|
18
|
+
SSH_AGENT="ssh-agent "
|
19
|
+
while getopts "s:A" opt "${@}"; do
|
20
|
+
case ${opt} in
|
21
|
+
s )
|
22
|
+
SERVICE="${OPTARG}"
|
23
|
+
;;
|
24
|
+
A )
|
25
|
+
SSH_AGENT=""
|
26
|
+
;;
|
27
|
+
\? )
|
28
|
+
log "đ" "Unknown option: ${opt}"
|
29
|
+
usage "${description}" "${usage_args}" "${usage_pre}"
|
30
|
+
;;
|
31
|
+
: )
|
32
|
+
log "đ" "Invalid option: ${opt} requires an argument"
|
33
|
+
usage "${description}" "${usage_args}" "${usage_pre}"
|
34
|
+
;;
|
35
|
+
esac
|
36
|
+
done
|
37
|
+
shift $((OPTIND -1))
|
38
|
+
|
39
|
+
if [ $# -eq 0 ]; then
|
40
|
+
log "đ" "You must provide a command e.g. bash or ls -l"
|
41
|
+
usage "${description}" "${usage_args}" "${usage_pre}"
|
42
|
+
fi
|
43
|
+
|
44
|
+
|
45
|
+
exec_hook_if_exists "exec.pre"
|
46
|
+
|
47
|
+
log "đ" "Running '${*}' inside container with service name '${SERVICE}'"
|
48
|
+
|
49
|
+
docker \
|
50
|
+
compose \
|
51
|
+
--file docker-compose.dx.yaml \
|
52
|
+
--project-name "${PROJECT_NAME}" \
|
53
|
+
--env-file "${ENV_FILE}" \
|
54
|
+
exec \
|
55
|
+
"${SERVICE}" \
|
56
|
+
${SSH_AGENT} "${@}"
|
57
|
+
|
58
|
+
# vim: ft=bash
|
data/dx/prune
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
|
6
|
+
|
7
|
+
. "${SCRIPT_DIR}/dx.sh.lib"
|
8
|
+
require_command "docker"
|
9
|
+
load_docker_compose_env
|
10
|
+
|
11
|
+
usage_on_help "Prune containers for this repo" "" "" "" "${@}"
|
12
|
+
|
13
|
+
for container_id in $(docker container ls -a -f "name=^${PROJECT_NAME}-.*-1$" --format="{{.ID}}"); do
|
14
|
+
log "đ" "Removing container with id '${container_id}'"
|
15
|
+
docker container rm "${container_id}"
|
16
|
+
done
|
17
|
+
echo "đ§ŧ" "Containers removed"
|
18
|
+
|
19
|
+
# vim: ft=bash
|
data/dx/setupkit.sh.lib
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# shellcheck shell=bash
|
2
|
+
|
3
|
+
fatal() {
|
4
|
+
remainder=${*:2}
|
5
|
+
if [ -z "$remainder" ]; then
|
6
|
+
log "đ" "${@}"
|
7
|
+
else
|
8
|
+
log "${@}"
|
9
|
+
fi
|
10
|
+
exit 1
|
11
|
+
}
|
12
|
+
|
13
|
+
log() {
|
14
|
+
emoji=$1
|
15
|
+
remainder=${*:2}
|
16
|
+
if [ -z "${NO_EMOJI}" ]; then
|
17
|
+
echo "[ ${0} ] ${*}"
|
18
|
+
else
|
19
|
+
# if remainder is empty that means no emoji was passed
|
20
|
+
if [ -z "$remainder" ]; then
|
21
|
+
echo "[ ${0} ] ${*}"
|
22
|
+
else # emoji was passed, but we ignore it
|
23
|
+
echo "[ ${0} ] ${remainder}"
|
24
|
+
fi
|
25
|
+
fi
|
26
|
+
}
|
27
|
+
|
28
|
+
debug() {
|
29
|
+
message=$1
|
30
|
+
if [ -z "${DOCKBOX_DEBUG}" ]; then
|
31
|
+
return
|
32
|
+
fi
|
33
|
+
log "đ" "${message}"
|
34
|
+
}
|
35
|
+
|
36
|
+
usage() {
|
37
|
+
description=$1
|
38
|
+
arg_names=$2
|
39
|
+
pre_hook=$3
|
40
|
+
post_hook=$4
|
41
|
+
echo "usage: ${0} [-h] ${arg_names}"
|
42
|
+
if [ -n "${description}" ]; then
|
43
|
+
echo
|
44
|
+
echo "DESCRIPTION"
|
45
|
+
echo " ${description}"
|
46
|
+
fi
|
47
|
+
if [ -n "${pre_hook}" ] || [ -n "${post_hook}" ]; then
|
48
|
+
echo
|
49
|
+
echo "HOOKS"
|
50
|
+
if [ -n "${pre_hook}" ]; then
|
51
|
+
echo " ${pre_hook} - if present, called before the main action"
|
52
|
+
fi
|
53
|
+
if [ -n "${post_hook}" ]; then
|
54
|
+
echo " ${post_hook} - if present, called after the main action"
|
55
|
+
fi
|
56
|
+
fi
|
57
|
+
exit 0
|
58
|
+
}
|
59
|
+
|
60
|
+
usage_on_help() {
|
61
|
+
description=$1
|
62
|
+
arg_names=$2
|
63
|
+
pre_hook=$3
|
64
|
+
post_hook=$4
|
65
|
+
# These are the args passed to the invocation so this
|
66
|
+
# function can determine if the user requested help
|
67
|
+
cli_args=( "${@:5}" )
|
68
|
+
|
69
|
+
for arg in "${cli_args[@]}"; do
|
70
|
+
if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ]; then
|
71
|
+
usage "${description}" "${arg_names}" "${pre_hook}" "${post_hook}"
|
72
|
+
fi
|
73
|
+
done
|
74
|
+
}
|
75
|
+
|
76
|
+
# Read user input into the variable 'INPUT'
|
77
|
+
#
|
78
|
+
# Args:
|
79
|
+
#
|
80
|
+
# [1] - an emoji to use for messages
|
81
|
+
# [2] - the message explaining what input is being requested
|
82
|
+
# [3] - a default value to use if no value is provided
|
83
|
+
#
|
84
|
+
# Respects NO_EMOJI when outputing messages to the user
|
85
|
+
user_input() {
|
86
|
+
emoji=$1
|
87
|
+
message=$2
|
88
|
+
default=$3
|
89
|
+
prompt=$4
|
90
|
+
|
91
|
+
if [ -z "$message" ]; then
|
92
|
+
echo "user_input requires a message"
|
93
|
+
exit 1
|
94
|
+
fi
|
95
|
+
|
96
|
+
INPUT=
|
97
|
+
|
98
|
+
if [ -z "${prompt}" ]; then
|
99
|
+
prompt=$(log "${emoji}" "Value: ")
|
100
|
+
if [ -n "${default}" ]; then
|
101
|
+
prompt=$(log "${emoji}" "Value (or hit return to use '${default}'): ")
|
102
|
+
fi
|
103
|
+
fi
|
104
|
+
|
105
|
+
while [ -z "${INPUT}" ]; do
|
106
|
+
|
107
|
+
log "$emoji" "$message"
|
108
|
+
read -r -p "${prompt}" INPUT
|
109
|
+
if [ -z "$INPUT" ]; then
|
110
|
+
INPUT=$default
|
111
|
+
fi
|
112
|
+
if [ -z "$INPUT" ]; then
|
113
|
+
log "đļ", "You must provide a value"
|
114
|
+
fi
|
115
|
+
done
|
116
|
+
}
|
117
|
+
|
118
|
+
user_confirm() {
|
119
|
+
user_input "$1" "$2" "$3" "y/n> "
|
120
|
+
}
|
121
|
+
|
122
|
+
require_not_exist() {
|
123
|
+
file=$1
|
124
|
+
message=$2
|
125
|
+
if [ -e "${file}" ]; then
|
126
|
+
fatal "$message"
|
127
|
+
fi
|
128
|
+
}
|
129
|
+
require_exist() {
|
130
|
+
file=$1
|
131
|
+
message=$2
|
132
|
+
if [ ! -e "${file}" ]; then
|
133
|
+
fatal "$message"
|
134
|
+
fi
|
135
|
+
}
|
136
|
+
|
137
|
+
require_command() {
|
138
|
+
command_name=$1
|
139
|
+
if ! command -v "${command_name}" >/dev/null 2>&1; then
|
140
|
+
fatal "Command '${command_name}' not found - it is required for this script to run"
|
141
|
+
fi
|
142
|
+
}
|
143
|
+
|
144
|
+
# vim: ft=bash
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
# Ideally, the message below is shown after everything starts up. We can't
|
6
|
+
# achieve this using healtchecks because the interval for a healtcheck is
|
7
|
+
# also an initial delay, and we don't really want to do healthchecks on
|
8
|
+
# our DB or Redis every 2 seconds. So, we sleep just a bit to let
|
9
|
+
# the other containers start up and vomit out their output first.
|
10
|
+
sleep 2
|
11
|
+
# Output some helpful messaging when invoking `dx/start` (which itself is
|
12
|
+
# a convenience script for `docker compose up`.
|
13
|
+
#
|
14
|
+
# Adding this to work around the mild inconvenience of the `app` container's
|
15
|
+
# entrypoint generating no output.
|
16
|
+
#
|
17
|
+
cat <<-'PROMPT'
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
đ Dev Environment Initialized! đ
|
22
|
+
|
23
|
+
âšī¸ To use this environment, open a new terminal and run
|
24
|
+
|
25
|
+
dx/exec bash
|
26
|
+
|
27
|
+
đš Use `ctrl-c` to exit.
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
PROMPT
|
32
|
+
|
33
|
+
# Using `sleep infinity` instead of `tail -f /dev/null`. This may be a
|
34
|
+
# performance improvement based on the conversation on a semi-related
|
35
|
+
# StackOverflow page.
|
36
|
+
#
|
37
|
+
# @see https://stackoverflow.com/a/41655546
|
38
|
+
sleep infinity
|
data/dx/start
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
|
6
|
+
|
7
|
+
. "${SCRIPT_DIR}/dx.sh.lib"
|
8
|
+
require_command "docker"
|
9
|
+
load_docker_compose_env
|
10
|
+
|
11
|
+
usage_on_help "Starts all services, including a container in which to run your app" "" "" "" "${@}"
|
12
|
+
|
13
|
+
log "đ" "Starting docker-compose.dx.yml"
|
14
|
+
|
15
|
+
BUILD=--build
|
16
|
+
if [ "${1}" == "--no-build" ]; then
|
17
|
+
BUILD=
|
18
|
+
fi
|
19
|
+
|
20
|
+
docker \
|
21
|
+
compose \
|
22
|
+
--file docker-compose.dx.yml \
|
23
|
+
--project-name "${PROJECT_NAME}" \
|
24
|
+
--env-file "${ENV_FILE}" \
|
25
|
+
up \
|
26
|
+
"${BUILD}" \
|
27
|
+
--timestamps \
|
28
|
+
--force-recreate
|
29
|
+
|
30
|
+
# vim: ft=bash
|
data/dx/stop
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
|
6
|
+
|
7
|
+
. "${SCRIPT_DIR}/dx.sh.lib"
|
8
|
+
require_command "docker"
|
9
|
+
load_docker_compose_env
|
10
|
+
|
11
|
+
usage_on_help "Stops all services, the container in which to run your app and removes any volumes" "" "" "" "${@}"
|
12
|
+
|
13
|
+
log "đ" "Stopping docker-compose.dx.yml"
|
14
|
+
|
15
|
+
docker \
|
16
|
+
compose \
|
17
|
+
--file docker-compose.dx.yml \
|
18
|
+
--project-name "${PROJECT_NAME}" \
|
19
|
+
--env-file "${ENV_FILE}" \
|
20
|
+
down \
|
21
|
+
--volumes
|
22
|
+
|
23
|
+
# vim: ft=bash
|