dlss-capistrano-docker 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7b180b31a5354ceec3029befccbdeac4ba5c86321eb75a1beb369d0c24a5ebbd
4
+ data.tar.gz: 30865848d3ef1b7c5e86565641aed355c011cf85e5219999c112a1d234d353eb
5
+ SHA512:
6
+ metadata.gz: f90641ba2d8b8c60c7f22e45dfc477f163732e9aeb3b89244b5b1563e09f10913c51cdb59464dc8d015c7b5f558655d01d7b9729fbe11b541603ab0504828a53
7
+ data.tar.gz: dddb973eaecac7511a4a6128cdf6a0e2b065226e0e8c40547204b191ae101cb5eb3010669551831ce1a8685fb10b8e86f5854ada8044815f3d548c59c71524c8
data/.gitignore ADDED
@@ -0,0 +1,56 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ # Ignore Byebug command history file.
17
+ .byebug_history
18
+
19
+ ## Specific to RubyMotion:
20
+ .dat*
21
+ .repl_history
22
+ build/
23
+ *.bridgesupport
24
+ build-iPhoneOS/
25
+ build-iPhoneSimulator/
26
+
27
+ ## Specific to RubyMotion (use of CocoaPods):
28
+ #
29
+ # We recommend against adding the Pods directory to your .gitignore. However
30
+ # you should judge for yourself, the pros and cons are mentioned at:
31
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32
+ #
33
+ # vendor/Pods/
34
+
35
+ ## Documentation cache and generated files:
36
+ /.yardoc/
37
+ /_yardoc/
38
+ /doc/
39
+ /rdoc/
40
+
41
+ ## Environment normalization:
42
+ /.bundle/
43
+ /vendor/bundle
44
+ /lib/bundler/man/
45
+
46
+ # for a library or gem, you might want to ignore these files since the code is
47
+ # intended to run in multiple environments; otherwise, check them in:
48
+ # Gemfile.lock
49
+ # .ruby-version
50
+ # .ruby-gemset
51
+
52
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
+ .rvmrc
54
+
55
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56
+ # .rubocop-https?--*
data/.rubocop.yml ADDED
@@ -0,0 +1,178 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+ SuggestExtensions: false
4
+ DisplayCopNames: true
5
+ Include:
6
+ - './Rakefile' # rake only
7
+ - '**/*.rb'
8
+ - '**/*.rake' # rake only
9
+
10
+ Bundler/GemComment:
11
+ OnlyFor:
12
+ - restrictive_version_specifiers
13
+ - branch
14
+
15
+ # Configuration parameters: AllowURI, URISchemes.
16
+ Layout/LineLength:
17
+ Max: 120
18
+
19
+ # consider if refactoring will improve adherence to SOLID and/or improve readability
20
+ # Sometimes splitting the class up won't improve readablity, it just makes reader jump around in the code.
21
+ Metrics/ClassLength:
22
+ Max: 150 # default 100
23
+
24
+ # consider if refactoring will improve adherence to SOLID and/or improve readability
25
+ # Sometimes splitting the method up won't improve readablity, it just makes reader jump around in the code.
26
+ # Good method names help.
27
+ Metrics/MethodLength:
28
+ Max: 15 # default 10
29
+
30
+ # capistrano tasks tend to be long.
31
+ Metrics/BlockLength:
32
+ Enabled: false
33
+
34
+ # "> 0", "== 0", "< 0" are not less readable than ".positive?", ".zero?" or ".negative?"
35
+ Style/NumericPredicate:
36
+ Enabled: false
37
+
38
+ Style/RegexpLiteral:
39
+ Enabled: false
40
+
41
+ Style/SymbolArray:
42
+ MinSize: 4 # default 2
43
+
44
+ Style/WordArray:
45
+ MinSize: 4 # default 2
46
+
47
+ Gemspec/DeprecatedAttributeAssignment: # new in 1.30
48
+ Enabled: true
49
+ Gemspec/RequireMFA: # new in 1.23
50
+ Enabled: true
51
+ Layout/LineContinuationLeadingSpace: # new in 1.31
52
+ Enabled: true
53
+ Layout/LineContinuationSpacing: # new in 1.31
54
+ Enabled: true
55
+ Layout/LineEndStringConcatenationIndentation: # new in 1.18
56
+ Enabled: true
57
+ Layout/SpaceBeforeBrackets: # new in 1.7
58
+ Enabled: true
59
+ Lint/AmbiguousAssignment: # new in 1.7
60
+ Enabled: true
61
+ Lint/AmbiguousOperatorPrecedence: # new in 1.21
62
+ Enabled: true
63
+ Lint/AmbiguousRange: # new in 1.19
64
+ Enabled: true
65
+ Lint/ConstantOverwrittenInRescue: # new in 1.31
66
+ Enabled: true
67
+ Lint/DeprecatedConstants: # new in 1.8
68
+ Enabled: true
69
+ Lint/DuplicateBranch: # new in 1.3
70
+ Enabled: true
71
+ Lint/DuplicateRegexpCharacterClassElement: # new in 1.1
72
+ Enabled: true
73
+ Lint/EmptyBlock: # new in 1.1
74
+ Enabled: true
75
+ Lint/EmptyClass: # new in 1.3
76
+ Enabled: true
77
+ Lint/EmptyInPattern: # new in 1.16
78
+ Enabled: true
79
+ Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21
80
+ Enabled: true
81
+ Lint/LambdaWithoutLiteralBlock: # new in 1.8
82
+ Enabled: true
83
+ Lint/NoReturnInBeginEndBlocks: # new in 1.2
84
+ Enabled: true
85
+ Lint/NonAtomicFileOperation: # new in 1.31
86
+ Enabled: true
87
+ Lint/NumberedParameterAssignment: # new in 1.9
88
+ Enabled: true
89
+ Lint/OrAssignmentToConstant: # new in 1.9
90
+ Enabled: true
91
+ Lint/RedundantDirGlobSort: # new in 1.8
92
+ Enabled: true
93
+ Lint/RefinementImportMethods: # new in 1.27
94
+ Enabled: true
95
+ Lint/RequireRangeParentheses: # new in 1.32
96
+ Enabled: true
97
+ Lint/RequireRelativeSelfPath: # new in 1.22
98
+ Enabled: true
99
+ Lint/SymbolConversion: # new in 1.9
100
+ Enabled: true
101
+ Lint/ToEnumArguments: # new in 1.1
102
+ Enabled: true
103
+ Lint/TripleQuotes: # new in 1.9
104
+ Enabled: true
105
+ Lint/UnexpectedBlockArity: # new in 1.5
106
+ Enabled: true
107
+ Lint/UnmodifiedReduceAccumulator: # new in 1.1
108
+ Enabled: true
109
+ Lint/UselessRuby2Keywords: # new in 1.23
110
+ Enabled: true
111
+ Naming/BlockForwarding: # new in 1.24
112
+ Enabled: true
113
+ Security/CompoundHash: # new in 1.28
114
+ Enabled: true
115
+ Security/IoMethods: # new in 1.22
116
+ Enabled: true
117
+ Style/ArgumentsForwarding: # new in 1.1
118
+ Enabled: true
119
+ Style/CollectionCompact: # new in 1.2
120
+ Enabled: true
121
+ Style/DocumentDynamicEvalDefinition: # new in 1.1
122
+ Enabled: true
123
+ Style/EmptyHeredoc: # new in 1.32
124
+ Enabled: true
125
+ Style/EndlessMethod: # new in 1.8
126
+ Enabled: true
127
+ Style/EnvHome: # new in 1.29
128
+ Enabled: true
129
+ Style/FetchEnvVar: # new in 1.28
130
+ Enabled: true
131
+ Style/FileRead: # new in 1.24
132
+ Enabled: true
133
+ Style/FileWrite: # new in 1.24
134
+ Enabled: true
135
+ Style/HashConversion: # new in 1.10
136
+ Enabled: true
137
+ Style/HashExcept: # new in 1.7
138
+ Enabled: true
139
+ Style/IfWithBooleanLiteralBranches: # new in 1.9
140
+ Enabled: true
141
+ Style/InPatternThen: # new in 1.16
142
+ Enabled: true
143
+ Style/MagicCommentFormat: # new in 1.35
144
+ Enabled: true
145
+ Style/MapCompactWithConditionalBlock: # new in 1.30
146
+ Enabled: true
147
+ Style/MapToHash: # new in 1.24
148
+ Enabled: true
149
+ Style/MultilineInPatternThen: # new in 1.16
150
+ Enabled: true
151
+ Style/NegatedIfElseCondition: # new in 1.2
152
+ Enabled: true
153
+ Style/NestedFileDirname: # new in 1.26
154
+ Enabled: true
155
+ Style/NilLambda: # new in 1.3
156
+ Enabled: true
157
+ Style/NumberedParameters: # new in 1.22
158
+ Enabled: true
159
+ Style/NumberedParametersLimit: # new in 1.22
160
+ Enabled: true
161
+ Style/ObjectThen: # new in 1.28
162
+ Enabled: true
163
+ Style/OpenStructUse: # new in 1.23
164
+ Enabled: true
165
+ Style/QuotedSymbols: # new in 1.16
166
+ Enabled: true
167
+ Style/RedundantArgument: # new in 1.4
168
+ Enabled: true
169
+ Style/RedundantInitialize: # new in 1.27
170
+ Enabled: true
171
+ Style/RedundantSelfAssignmentBranch: # new in 1.19
172
+ Enabled: true
173
+ Style/SelectByRegexp: # new in 1.22
174
+ Enabled: true
175
+ Style/StringChars: # new in 1.12
176
+ Enabled: true
177
+ Style/SwapValues: # new in 1.1
178
+ Enabled: true
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Dependencies are defined in dlss-capistrano-docker.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,67 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ dlss-capistrano-docker (1.0.0)
5
+ capistrano (~> 3.0)
6
+ capistrano-one_time_key
7
+ capistrano-shared_configs
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ airbrussh (1.4.1)
13
+ sshkit (>= 1.6.1, != 1.7.0)
14
+ ast (2.4.2)
15
+ byebug (11.1.3)
16
+ capistrano (3.17.1)
17
+ airbrussh (>= 1.0.0)
18
+ i18n
19
+ rake (>= 10.0.0)
20
+ sshkit (>= 1.9.0)
21
+ capistrano-one_time_key (0.1.0)
22
+ capistrano (~> 3.0)
23
+ capistrano-shared_configs (0.2.2)
24
+ concurrent-ruby (1.1.10)
25
+ i18n (1.12.0)
26
+ concurrent-ruby (~> 1.0)
27
+ json (2.6.2)
28
+ net-scp (1.2.1)
29
+ net-ssh (>= 2.6.5)
30
+ net-ssh (7.0.1)
31
+ parallel (1.22.1)
32
+ parser (3.1.2.1)
33
+ ast (~> 2.4.1)
34
+ rainbow (3.1.1)
35
+ rake (13.0.6)
36
+ regexp_parser (2.5.0)
37
+ rexml (3.2.5)
38
+ rubocop (1.36.0)
39
+ json (~> 2.3)
40
+ parallel (~> 1.10)
41
+ parser (>= 3.1.2.1)
42
+ rainbow (>= 2.2.2, < 4.0)
43
+ regexp_parser (>= 1.8, < 3.0)
44
+ rexml (>= 3.2.5, < 4.0)
45
+ rubocop-ast (>= 1.20.1, < 2.0)
46
+ ruby-progressbar (~> 1.7)
47
+ unicode-display_width (>= 1.4.0, < 3.0)
48
+ rubocop-ast (1.21.0)
49
+ parser (>= 3.1.1.0)
50
+ ruby-progressbar (1.11.0)
51
+ sshkit (1.21.3)
52
+ net-scp (>= 1.1.2)
53
+ net-ssh (>= 2.8.0)
54
+ unicode-display_width (2.3.0)
55
+
56
+ PLATFORMS
57
+ x86_64-darwin-21
58
+
59
+ DEPENDENCIES
60
+ bundler
61
+ byebug
62
+ dlss-capistrano-docker!
63
+ rake (>= 12.3.3)
64
+ rubocop (~> 1.0)
65
+
66
+ BUNDLED WITH
67
+ 2.3.22
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # dlss-capistrano-docker
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/dlss-capistrano-docker.svg)](https://badge.fury.io/rb/dlss-capistrano-docker)
4
+
5
+ This gem provides Capistrano Docker deployment tasks used by Stanford Libraries' Digital Library Systems and Services group.
6
+
7
+ It requires that the services to be run on a server be specified by a docker compose configuration file.
8
+
9
+ ## Included Tasks
10
+ * `ssh`: establishes an SSH connection to the host running in environment, and changes into the current deployment directory.
11
+ * `honeybadger:notify`: notifies Honeybadger of deploy using curl (thus, ruby is not required on host).
12
+ * `docker:login`: log into Docker Hub. Logging in is required to avoid strict image download limits.
13
+ * `docker:logout`: log out of Docker Hub.
14
+ * `docker:prune`: prune docker artifacts to avoid filling disk space.
15
+ * `docker_compose:migrate`: migrate the database.
16
+ * `docker_compose:seed`: seed the database.
17
+ * `docker_compose:build`: build images for the services.
18
+ * `docker_compose:setup_rabbit`: create channel, queues, etc for RabbitMQ.
19
+ * `docker_compose:restart`: down then up services.
20
+ * `docker_compose:up`: stop services.
21
+ * `docker_compose:down`: tear down services.
22
+ * `docker_compose:copy_assets`: copy assets from a container to the server so they can be served by Apache.
23
+ * `docker_compose:dereference_linked_files`: turn linked files into actual files.
24
+ * `docker_compose:dereference_linked_dirs`: turn linked directories into actual directories containing actual files.
25
+
26
+ ## Hooks
27
+ Hooks into the Capistrano lifecycle are provided for most of these tasks. These hooks can be controlled with variables, e.g., `docker_hub_use_hooks` to control the hooks for `docker:login` and `docker:logout`.
28
+
29
+ In addition to tasks included in this gem, a hook is provided for `shared_configs:update`.
30
+
31
+ ## Usage
32
+ For an example of any of the following, see https://github.com/sul-dlss/happy-heron
33
+
34
+ ### `Gemfile`
35
+ * Add `gem 'dlss-capistrano-docker', require: false` to the deployment group.
36
+ * Remove any gems that are removed from `Capfile` below.
37
+ * Move puma gem from development/test dependencies to the application dependencies. (Puma will be used as the application server instead of Passenger.)
38
+
39
+ ### `Capfile`
40
+ * Remove any of the following:
41
+ ```
42
+ require 'capistrano/bundler'
43
+ require 'capistrano/rails'
44
+ require 'capistrano/honeybadger'
45
+ require 'capistrano/passenger'
46
+ require 'whenever/capistrano'
47
+ require 'dlss/capistrano'
48
+ ```
49
+ * Add `require 'dlss/docker/capistrano`
50
+
51
+ ### `config/deploy.rb`
52
+ * For `:linked_files` remove `config/database.yml` and `config/secrets.yml` if present. Both of these will be provided by environment variables.
53
+ * For `:linked_dirs` remove `tmp/pids`, `tmp/cache`, `tmp/sockets`, `public/system` if present. `log` and `config/settings` should remain.
54
+ * Remove any sidekiq, sneakers, passenger, whenever, or shared_configs related settings.
55
+ * Add `set :docker_compose_file, 'docker-compose.prod.yml'` to reference the production docker-compose file.
56
+ * Optionally, add `set :docker_compose_seed_use_hooks, true` to perform seeding.
57
+ * Optionally, add `set :docker_compose_rabbitmq_use_hooks, true` to perform Rabbitmq setup.
58
+ * Note that there are other settings supported by this gem for additional configuration.
59
+
60
+ ### `config/deploy/<environment>.rb`
61
+ * Each server should have the `app` role and any roles that correspond with profiles in the docker-compose file. This is what controls what services are run on the server.
62
+
63
+ ### `config/puma.rb`
64
+ * Change `RAILS_MAX_THREAD` to `PUMA_MAX_THREAD` and `RAILS_MIN_THREAD` to `PUMA_MIN_THREAD`.
65
+ * Enable workers with `workers ENV.fetch('PUMA_WORKERS', 2)`
66
+ * Enable preload with `preload_app!`.
67
+
68
+ ### `config/schedule.rb`
69
+ * Add `set :job_template, nil` to execute with sh instead of bash.
70
+
71
+ ### `Dockerfile`
72
+ * When possible, base the image on Alpine (e.g., `FROM ruby:3.1-alpine`).
73
+ * Processes should run as the application user (e.g., `h2`) not root, with user id and group id set to match the server.
74
+ * If using Whenever, install Supercronic (which does not have to run as root) and write the crontab file during the build with `RUN sh -c 'bundle exec whenever . | tee -a config/crontab'`.
75
+ * When possible, write to both a log file and stdout.
76
+ * Precompile assets (`RUN bin/rails assets:precompile`) as part of the build. `SECRET_KEY_BASE` will need to be available for precompiling.
77
+
78
+ ### `docker-compose.prod.yml`
79
+ * Duplicate configuration can be shared with fragments (e.g., `&environment` / `*environment`).
80
+ * The command for a container can be overridden with `command:`, e.g., `command: bin/bundle exec rake sneakers:run 2>&1 | tee -a log/sneakers.log`.
81
+ * Each service should be assigned to one or more profiles. These correspond to capistrano roles. For example:
82
+ ```
83
+ profiles:
84
+ - cron
85
+ ```
86
+ * Containers should link in the shared logs directory. For example:
87
+ ```
88
+ volumes:
89
+ - /opt/app/h2/happy-heron/shared/log:/app/log
90
+ ```
91
+ * Environment variables provided by the server should be passed through to the container. For example:
92
+ ```
93
+ environment:
94
+ - HONEYBADGER_API_KEY
95
+ ```
96
+
97
+ ### `.dockerignore`
98
+ Ignore `public.assets`
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'bundler'
4
+
5
+ Dir.glob('lib/tasks/*.rake').each { |r| import r }
6
+
7
+ begin
8
+ Bundler.setup(:default, :development)
9
+ rescue Bundler::BundlerError => e
10
+ $stderr.puts e.message
11
+ $stderr.puts "Run `bundle install` to install missing gems"
12
+ exit e.status_code
13
+ end
14
+
15
+ require "bundler/gem_tasks"
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dlss-capistrano-docker"
7
+ s.version = '1.0.0'
8
+
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ['Justin Littman']
11
+ s.email = ['justinlittman@stanford.edu']
12
+ s.summary = "Capistrano recipes for use in SUL/DLSS projects for docker deployment"
13
+ s.homepage = 'https://github.com/sul-dlss/dlss-capistrano-docker'
14
+ s.license = "Apache-2.0"
15
+
16
+ s.required_ruby_version = '>= 3.0', '< 4'
17
+
18
+ # All dependencies are runtime dependencies, since this gem's "runtime" is
19
+ # the dependent gem's development-time.
20
+ s.add_dependency "capistrano", "~> 3.0"
21
+ s.add_dependency "capistrano-one_time_key"
22
+ s.add_dependency "capistrano-shared_configs"
23
+
24
+ s.add_development_dependency 'bundler'
25
+ s.add_development_dependency 'byebug'
26
+ s.add_development_dependency 'rake', '>= 12.3.3'
27
+ s.add_development_dependency 'rubocop', '~> 1.0'
28
+
29
+ s.files = `git ls-files`.split($/)
30
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
31
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
32
+ s.metadata['rubygems_mfa_required'] = 'true'
33
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Capistrano plugin hook to set default values
4
+ namespace :load do
5
+ task :defaults do
6
+ set :docker_prune_use_hooks, fetch(:docker_prune_use_hooks, true)
7
+ set :docker_hub_use_hooks, fetch(:docker_hub_use_hooks, true)
8
+ end
9
+ end
10
+
11
+ # Integrate hooks into Capistrano
12
+ namespace :deploy do
13
+ before :starting, :add_docker_hooks do
14
+ invoke 'docker:add_prune_hooks' if fetch(:docker_prune_use_hooks)
15
+ invoke 'docker:add_hub_hooks' if fetch(:docker_hub_use_hooks)
16
+ end
17
+ end
18
+
19
+ namespace :docker do
20
+ task :add_prune_hooks do
21
+ # Cleaning up docker on start so that images/logs are available for troubleshooting.
22
+ after 'deploy:starting', 'docker:prune'
23
+ end
24
+
25
+ task :add_hub_hooks do
26
+ after 'deploy:starting', 'docker:login'
27
+ after 'deploy:finishing', 'docker:logout'
28
+ end
29
+
30
+ desc 'Log in to Docker Hub'
31
+ task :login do
32
+ on roles(:app) do
33
+ execute(:docker, 'login', '-u', '$DOCKER_USERNAME', '-p', '$DOCKER_PASSWORD')
34
+ end
35
+ end
36
+
37
+ desc 'Log out of Docker Hub'
38
+ task :logout do
39
+ on roles(:app) do
40
+ execute(:docker, 'logout')
41
+ end
42
+ end
43
+
44
+ desc 'Prune unused images/containers'
45
+ task :prune do
46
+ on roles(:app) do
47
+ execute(:docker, 'system', 'prune', '-af')
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Capistrano plugin hook to set default values
4
+ namespace :load do
5
+ task :defaults do
6
+ set :docker_compose_file, fetch(:docker_compose_file, 'docker-compose.yml')
7
+
8
+ set :docker_compose_migrate_use_hooks, fetch(:docker_compose_migrate_use_hooks, true)
9
+ set :migration_role, fetch(:migration_role, :db)
10
+ set :migration_servers, -> { primary(fetch(:migration_role)) }
11
+ set :migration_command, fetch(:migration_command, 'db:migrate')
12
+
13
+ set :docker_compose_seed_use_hooks, fetch(:docker_compose_seed_use_hooks, false)
14
+ set :seed_command, fetch(:migration_command, 'db:seed')
15
+
16
+ set :docker_compose_build_use_hooks, fetch(:docker_compose_build_use_hooks, true)
17
+ set :build_roles, fetch(:build_roles, [:app])
18
+ set :dereference_files, fetch(:dereference_files)
19
+ set :dereference_dirs, fetch(:dereference_dirs, [])
20
+
21
+ set :docker_compose_rabbitmq_use_hooks, fetch(:docker_compose_rabbit_use_hooks, false)
22
+ set :rabbitmq_role, fetch(:rabbitmq_role, :app)
23
+ set :rabbitmq_servers, -> { primary(fetch(:rabbitmq_role)) }
24
+ set :rabbitmq_command, fetch(:rabbitmq_command, 'rabbitmq:setup')
25
+
26
+ set :docker_compose_restart_use_hooks, fetch(:docker_compose_restart_use_hooks, true)
27
+
28
+ set :copy_assets_role, fetch(:copy_assets_role, :app)
29
+ set :assets_path, fetch(:assets_path, 'public/assets')
30
+ set :docker_compose_copy_assets_use_hooks, fetch(:docker_compose_copy_assets_use_hooks, true)
31
+ end
32
+ end
33
+
34
+ # Integrate hooks into Capistrano
35
+ namespace :deploy do
36
+ before :starting, :add_docker_hooks do
37
+ invoke 'docker_compose:add_migrate_hooks' if fetch(:docker_compose_migrate_use_hooks)
38
+ invoke 'docker_compose:add_seed_hooks' if fetch(:docker_compose_seed_use_hooks)
39
+ invoke 'docker_compose:add_build_hooks' if fetch(:docker_compose_build_use_hooks)
40
+ invoke 'docker_compose:add_rabbitmq_hooks' if fetch(:docker_compose_rabbitmq_use_hooks)
41
+ invoke 'docker_compose:add_restart_hooks' if fetch(:docker_compose_restart_use_hooks)
42
+ invoke 'docker_compose:add_copy_assets_hooks' if fetch(:docker_compose_copy_assets_use_hooks)
43
+ end
44
+ end
45
+
46
+ namespace :docker_compose do
47
+ task :add_migrate_hooks do
48
+ after 'deploy:publishing', 'docker_compose:migrate'
49
+ end
50
+
51
+ task :add_seed_hooks do
52
+ after 'deploy:publishing', 'docker_compose:seed'
53
+ end
54
+
55
+ task :add_build_hooks do
56
+ after 'deploy:updating', 'docker_compose:build'
57
+ end
58
+
59
+ task :add_rabbitmq_hooks do
60
+ after 'deploy:publishing', 'docker_compose:setup_rabbitmq'
61
+ end
62
+
63
+ task :add_restart_hooks do
64
+ after 'deploy:publishing', 'docker_compose:restart'
65
+ end
66
+
67
+ task :add_copy_assets_hooks do
68
+ after 'deploy:publishing', 'docker_compose:copy_assets'
69
+ end
70
+
71
+ desc 'Migrate database'
72
+ task :migrate do
73
+ on fetch(:migration_servers) do
74
+ within current_path do
75
+ execute(:docker, 'compose', '-f', fetch(:docker_compose_file), 'run', 'app', 'bin/rails',
76
+ fetch(:migration_command))
77
+ end
78
+ info 'Db migrated'
79
+ end
80
+ end
81
+
82
+ desc 'Seed database'
83
+ task :seed do
84
+ on fetch(:migration_servers) do
85
+ within current_path do
86
+ execute(:docker, 'compose', '-f', fetch(:docker_compose_file), 'run', 'app', 'bin/rails',
87
+ fetch(:seed_command))
88
+ end
89
+ info 'Db migrated'
90
+ end
91
+ end
92
+
93
+ desc 'Build images'
94
+ # Docker build does not dereference symlinks.
95
+ task build: [:dereference_linked_files, :dereference_linked_dirs] do
96
+ on roles(fetch(:build_roles)) do
97
+ info 'Building images'
98
+ within release_path do
99
+ execute(:docker, 'compose', '-f', fetch(:docker_compose_file), 'build')
100
+ end
101
+ info 'Docker images built'
102
+ end
103
+ end
104
+
105
+ desc 'Setup RabbitMQ'
106
+ task :setup_rabbitmq do
107
+ on fetch(:rabbitmq_servers) do
108
+ within current_path do
109
+ execute(:docker, 'compose', '-f', fetch(:docker_compose_file), 'run', 'app', 'bin/rake',
110
+ fetch(:rabbitmq_command))
111
+ end
112
+ info 'RabbitMQ setup'
113
+ end
114
+ end
115
+
116
+ desc 'Restart containers (down then up)'
117
+ task restart: [:down, :up]
118
+
119
+ desc 'Start containers'
120
+ task :up do
121
+ on roles(:app) do |server|
122
+ cmd = cmd_with_profiles(server)
123
+ cmd.concat(['up', '-d'])
124
+ within current_path do
125
+ execute(*cmd)
126
+ info 'Containers started'
127
+ end
128
+ end
129
+ end
130
+
131
+ desc 'Tear down containers'
132
+ task :down do
133
+ on roles(:app) do |server|
134
+ cmd = cmd_with_profiles(server)
135
+ cmd.concat(['down'])
136
+ within current_path do
137
+ execute(*cmd)
138
+ info 'Containers down'
139
+ end
140
+ end
141
+ end
142
+
143
+ desc 'Copy assets'
144
+ task :copy_assets do
145
+ assets_path = fetch(:assets_path)
146
+ on roles(fetch(:copy_assets_role)) do
147
+ within current_path do
148
+ # Can't do a direct copy to public/assets because it is a symlink.
149
+ execute(:docker, 'compose', '-f', fetch(:docker_compose_file), 'cp',
150
+ 'app:/app/public/assets', '.')
151
+ execute(:rm, '-fr', "#{assets_path}/*")
152
+ execute(:mkdir, assets_path)
153
+ execute(:cp, 'assets/*', "#{assets_path}/")
154
+ end
155
+ info 'Assets copied'
156
+ end
157
+ end
158
+
159
+ desc 'Dereference linked files'
160
+ task :dereference_linked_files do
161
+ dereference_files = fetch(:dereference_files) || fetch(:linked_files, [])
162
+ next if dereference_files.empty?
163
+
164
+ on roles(fetch(:build_roles)) do
165
+ dereference_files.each do |file|
166
+ target = release_path.join(file)
167
+ source = shared_path.join(file)
168
+ execute :rm, target if test "[ -L #{target} ]"
169
+ execute :cp, source, target
170
+ end
171
+ end
172
+ end
173
+
174
+ desc 'Dereference linked directories'
175
+ task :dereference_linked_dirs do
176
+ dereference_dirs = fetch(:dereference_dirs)
177
+ next if dereference_dirs.empty?
178
+
179
+ on roles(fetch(:build_roles)) do
180
+ dereference_dirs.each do |dir|
181
+ target = release_path.join(dir)
182
+ source = shared_path.join(dir)
183
+ next unless test "[ -L #{target} ]"
184
+
185
+ execute :rm, target
186
+ execute :cp, '-r', source, target
187
+ end
188
+ end
189
+ end
190
+
191
+ def cmd_with_profiles(server)
192
+ cmd = [:docker, 'compose', '-f', fetch(:docker_compose_file)]
193
+ server.roles.each do |role|
194
+ cmd.concat(['--profile', role.to_s])
195
+ end
196
+ cmd
197
+ end
198
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Capistrano plugin hook to set default values
4
+ namespace :load do
5
+ task :defaults do
6
+ set :honeybadger_use_hooks, fetch(:honeybadger_use_hooks, true)
7
+ end
8
+ end
9
+
10
+ # Integrate hooks into Capistrano
11
+ namespace :deploy do
12
+ before :starting, :add_honeybadger_hooks do
13
+ invoke 'honeybadger:add_hooks' if fetch(:honeybadger_use_hooks)
14
+ end
15
+ end
16
+
17
+ namespace :honeybadger do
18
+ task :add_hooks do
19
+ after 'deploy:finishing', 'honeybadger:notify'
20
+ end
21
+
22
+ # Replaces honeybadger:deploy to use curl instead of invoking ruby.
23
+ # Adapted from https://github.com/honeybadger-io/honeybadger-ruby/blob/master/vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap
24
+ desc 'Notify Honeybadger of a deploy (using the API via curl)'
25
+ task notify: %i[deploy:set_current_revision] do
26
+ fetch(:honeybadger_server) do
27
+ if (s = primary(:app))
28
+ set(:honeybadger_server, s.select?({ exclude: :no_release }) ? s : nil)
29
+ end
30
+ end
31
+
32
+ if (server = fetch(:honeybadger_server))
33
+ revision = fetch(:current_revision)
34
+
35
+ on server do |_host|
36
+ info 'Notifying Honeybadger of deploy.'
37
+
38
+ honeybadger_config = nil
39
+ within release_path do
40
+ honeybadger_config = capture(:cat, 'config/honeybadger.yml')
41
+ end
42
+ remote_api_key = YAML.safe_load(honeybadger_config)['api_key']
43
+ remote_api_key = capture(:echo, '$HONEYBADGER_API_KEY') if remote_api_key.nil?
44
+
45
+ options = {
46
+ 'deploy[environment]' => fetch(:honeybadger_env, fetch(:rails_env, 'production')),
47
+ 'deploy[local_username]' => fetch(:honeybadger_user, ENV['USER'] || ENV.fetch('USERNAME', nil)),
48
+ 'deploy[revision]' => revision,
49
+ 'deploy[repository]' => fetch(:repo_url),
50
+ 'api_key' => fetch(:honeybadger_api_key, ENV.fetch('HONEYBADGER_API_KEY', nil)) || remote_api_key
51
+ }
52
+ data = options.to_a.map { |pair| pair.join('=') }.join('&')
53
+ execute(:curl, '--no-progress-meter', '--data', "\"#{data}\"", 'https://api.honeybadger.io/v1/deploys')
54
+ info 'Honeybadger notification complete.'
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Capistrano plugin hook to set default values
4
+ namespace :load do
5
+ task :defaults do
6
+ set :shared_configs_use_hooks, fetch(:shared_configs_use_hooks, true)
7
+ end
8
+ end
9
+
10
+ # Integrate hooks into Capistrano
11
+ namespace :deploy do
12
+ before :starting, :add_shared_configs_hooks do
13
+ invoke 'shared_configs:add_hooks' if fetch(:shared_configs_use_hooks)
14
+ end
15
+ end
16
+
17
+ namespace :shared_configs do
18
+ task :add_hooks do
19
+ before 'deploy:updating', 'shared_configs:update'
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'ssh to the current directory on the server'
4
+ task :ssh do
5
+ on roles(:app), primary: true do |host|
6
+ command = "cd #{fetch(:deploy_to)}/current && exec $SHELL -l"
7
+ puts command if fetch(:log_level) == :debug
8
+ exec "ssh -l #{host.user} #{host.hostname} -p #{host.port || 22} -t '#{command}'"
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capistrano/one_time_key'
4
+ require 'capistrano/shared_configs'
5
+
6
+ Dir.glob("#{__dir__}/capistrano/tasks/*.rake").each { |r| import r }
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dlss-capistrano-docker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Justin Littman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-09-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: capistrano
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: capistrano-one_time_key
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: capistrano-shared_configs
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 12.3.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 12.3.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ description:
112
+ email:
113
+ - justinlittman@stanford.edu
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rubocop.yml"
120
+ - Gemfile
121
+ - Gemfile.lock
122
+ - README.md
123
+ - Rakefile
124
+ - dlss-capistrano-docker.gemspec
125
+ - lib/dlss/docker/capistrano.rb
126
+ - lib/dlss/docker/capistrano/tasks/docker.rake
127
+ - lib/dlss/docker/capistrano/tasks/docker_compose.rake
128
+ - lib/dlss/docker/capistrano/tasks/honeybadger.rake
129
+ - lib/dlss/docker/capistrano/tasks/shared_configs.rake
130
+ - lib/dlss/docker/capistrano/tasks/ssh.rake
131
+ homepage: https://github.com/sul-dlss/dlss-capistrano-docker
132
+ licenses:
133
+ - Apache-2.0
134
+ metadata:
135
+ rubygems_mfa_required: 'true'
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '3.0'
145
+ - - "<"
146
+ - !ruby/object:Gem::Version
147
+ version: '4'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubygems_version: 3.3.7
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: Capistrano recipes for use in SUL/DLSS projects for docker deployment
158
+ test_files: []