capistrano-nomad 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +55 -0
- data/LICENSE.txt +21 -0
- data/README.md +35 -0
- data/Rakefile +4 -0
- data/capistrano-nomad.gemspec +42 -0
- data/lib/capistrano/nomad/helpers/base.rb +3 -0
- data/lib/capistrano/nomad/helpers/docker.rb +134 -0
- data/lib/capistrano/nomad/helpers/dsl.rb +127 -0
- data/lib/capistrano/nomad/helpers/git.rb +9 -0
- data/lib/capistrano/nomad/helpers/nomad.rb +275 -0
- data/lib/capistrano/nomad/scm/git_local.rb +25 -0
- data/lib/capistrano/nomad/tasks/nomad.rake +70 -0
- data/lib/capistrano/nomad/version.rb +6 -0
- data/lib/capistrano/nomad.rb +20 -0
- data/sig/capistrano/nomad.rbs +6 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9766d262aebd461e4801a9bdde77735250dfeb7625faf9c4d6771f20db0516ee
|
4
|
+
data.tar.gz: 4afe079c96f410f5998adad6347e22cb73453d44d9a7a3b550503b5ad03166b1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce0f93781fdd9ea9f4a97130cff43421bd272052f588a9240aa1718cbf8640c0271f14fdb2fd14a52007f19a05eb9746dfdd02ec0f2ed623f586032a8e3bbc18
|
7
|
+
data.tar.gz: 3e5df7d419965dd0e289170ebb3c292d46d86dcfd7dee5fb534ea272f4e2f09b5912c2c5d09e1b30c0eaab5b1a367789615eccfb590bd5192ce59eff16d10da5
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
capistrano-nomad (0.1.0)
|
5
|
+
activesupport (= 7.0.8)
|
6
|
+
byebug
|
7
|
+
capistrano (= 3.14.1)
|
8
|
+
git (= 1.10.2)
|
9
|
+
rake (~> 13.0)
|
10
|
+
sshkit (= 1.21.2)
|
11
|
+
sshkit-interactive (= 0.3.0)
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: https://rubygems.org/
|
15
|
+
specs:
|
16
|
+
activesupport (7.0.8)
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
|
+
i18n (>= 1.6, < 2)
|
19
|
+
minitest (>= 5.1)
|
20
|
+
tzinfo (~> 2.0)
|
21
|
+
airbrussh (1.5.0)
|
22
|
+
sshkit (>= 1.6.1, != 1.7.0)
|
23
|
+
byebug (11.1.3)
|
24
|
+
capistrano (3.14.1)
|
25
|
+
airbrussh (>= 1.0.0)
|
26
|
+
i18n
|
27
|
+
rake (>= 10.0.0)
|
28
|
+
sshkit (>= 1.9.0)
|
29
|
+
concurrent-ruby (1.2.2)
|
30
|
+
git (1.10.2)
|
31
|
+
rchardet (~> 1.8)
|
32
|
+
i18n (1.14.1)
|
33
|
+
concurrent-ruby (~> 1.0)
|
34
|
+
minitest (5.20.0)
|
35
|
+
net-scp (4.0.0)
|
36
|
+
net-ssh (>= 2.6.5, < 8.0.0)
|
37
|
+
net-ssh (7.2.0)
|
38
|
+
rake (13.0.6)
|
39
|
+
rchardet (1.8.0)
|
40
|
+
sshkit (1.21.2)
|
41
|
+
net-scp (>= 1.1.2)
|
42
|
+
net-ssh (>= 2.8.0)
|
43
|
+
sshkit-interactive (0.3.0)
|
44
|
+
sshkit (~> 1.12)
|
45
|
+
tzinfo (2.0.6)
|
46
|
+
concurrent-ruby (~> 1.0)
|
47
|
+
|
48
|
+
PLATFORMS
|
49
|
+
arm64-darwin-21
|
50
|
+
|
51
|
+
DEPENDENCIES
|
52
|
+
capistrano-nomad!
|
53
|
+
|
54
|
+
BUNDLED WITH
|
55
|
+
2.3.5
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 James Hu
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# capistrano-nomad
|
2
|
+
|
3
|
+
Capistrano plugin for deploying and managing [Nomad](http://nomadproject.io) jobs
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem "capistrano-nomad"
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install capistrano-nomad
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/axsuul/capistrano-nomad.
|
32
|
+
|
33
|
+
## License
|
34
|
+
|
35
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/capistrano/nomad/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "capistrano-nomad"
|
7
|
+
spec.version = Capistrano::Nomad::VERSION
|
8
|
+
spec.authors = ["James Hu"]
|
9
|
+
|
10
|
+
spec.summary = "Capistrano plugin for deploying and managing Nomad jobs"
|
11
|
+
spec.description = "Capistrano plugin for deploying and managing Nomad jobs"
|
12
|
+
spec.homepage = "https://github.com/axsuul/capistrano-nomad"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.required_ruby_version = ">= 2.6.0"
|
15
|
+
|
16
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/axsuul/capistrano-nomad"
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
24
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
# Uncomment to register a new dependency of your gem
|
32
|
+
spec.add_dependency "activesupport", "7.0.8"
|
33
|
+
spec.add_dependency "byebug"
|
34
|
+
spec.add_dependency "capistrano", "3.14.1"
|
35
|
+
spec.add_dependency "git", "1.10.2"
|
36
|
+
spec.add_dependency "rake", "~> 13.0"
|
37
|
+
spec.add_dependency "sshkit", "1.21.2"
|
38
|
+
spec.add_dependency "sshkit-interactive", "0.3.0"
|
39
|
+
|
40
|
+
# For more information and examples about making a new gem, check out our
|
41
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
42
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
def capistrano_nomad_docker_image_types_manifest_path
|
2
|
+
shared_path.join("docker-image-types.json")
|
3
|
+
end
|
4
|
+
|
5
|
+
def capistrano_nomad_read_docker_image_types_manifest
|
6
|
+
manifest = {}
|
7
|
+
|
8
|
+
on(roles(:manager)) do |_host|
|
9
|
+
# Ensure file exists
|
10
|
+
execute("mkdir -p #{shared_path}")
|
11
|
+
execute("touch #{capistrano_nomad_docker_image_types_manifest_path}")
|
12
|
+
|
13
|
+
output = capture "cat #{capistrano_nomad_docker_image_types_manifest_path}"
|
14
|
+
|
15
|
+
unless output.blank?
|
16
|
+
manifest = JSON.parse(output)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
capistrano_nomad_deep_symbolize_hash_keys(manifest)
|
21
|
+
end
|
22
|
+
|
23
|
+
def capistrano_nomad_update_docker_image_types_manifest(image_type, properties = {})
|
24
|
+
on(roles(:manager)) do |_host|
|
25
|
+
# Read and update manifest
|
26
|
+
manifest = capistrano_nomad_read_docker_image_types_manifest
|
27
|
+
manifest[image_type] = (manifest[image_type] || {}).merge(properties.stringify_keys)
|
28
|
+
|
29
|
+
io = StringIO.new(JSON.pretty_generate(manifest))
|
30
|
+
|
31
|
+
# Write to manifest
|
32
|
+
upload!(io, capistrano_nomad_docker_image_types_manifest_path)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def capistrano_nomad_build_docker_image_alias(image_type, ref)
|
37
|
+
# Define nomad_docker_image_alias has a proc to return image alias
|
38
|
+
fetch(:nomad_docker_image_alias).call(image_type: image_type, ref: ref)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Fetch image alias from manifest
|
42
|
+
def current_docker_service_image_alias(image_type)
|
43
|
+
image_alias =
|
44
|
+
capistrano_nomad_read_docker_image_types_manifest
|
45
|
+
.try(:[], image_type.to_sym)
|
46
|
+
.try(:[], :image_alias)
|
47
|
+
|
48
|
+
return image_alias if image_alias
|
49
|
+
|
50
|
+
capistrano_nomad_build_docker_image_alias(image_type, capistrano_nomad_fetch_git_commit_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Builds docker image from image type
|
54
|
+
#
|
55
|
+
# @param image_type [String, Symbol]
|
56
|
+
def capistrano_nomad_build_docker_image(image_type, path, *args)
|
57
|
+
run_locally do
|
58
|
+
git_commit_id = capistrano_nomad_fetch_git_commit_id
|
59
|
+
|
60
|
+
[
|
61
|
+
capistrano_nomad_build_docker_image_alias(image_type, git_commit_id),
|
62
|
+
capistrano_nomad_build_docker_image_alias(image_type, "latest"),
|
63
|
+
"trunk/#{fetch(:stage)}/#{image_type}:latest",
|
64
|
+
].each do |tag|
|
65
|
+
args << "--tag #{tag}"
|
66
|
+
end
|
67
|
+
|
68
|
+
# If any of these files exist then we're in the middle of rebase so we should interrupt
|
69
|
+
if ["rebase-merge", "rebase-apply"].any? { |f| File.exist?("#{fetch(:git).dir.path}/.git/#{f}") }
|
70
|
+
raise StandardError, "still in the middle of git rebase, interrupting docker image build"
|
71
|
+
end
|
72
|
+
|
73
|
+
system "docker build #{args.join(' ')} #{path}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def capistrano_nomad_build_docker_image_for_type(image_type)
|
78
|
+
image_type = image_type.to_sym
|
79
|
+
|
80
|
+
attributes = fetch(:nomad_docker_image_types)[image_type]
|
81
|
+
|
82
|
+
return unless attributes
|
83
|
+
|
84
|
+
args = [
|
85
|
+
# Ensure images are built for x86_64 which is production env otherwise it will default to local development env
|
86
|
+
# which can be arm64 (Apple Silicon)
|
87
|
+
"--platform linux/amd64",
|
88
|
+
]
|
89
|
+
|
90
|
+
build_args =
|
91
|
+
if attributes[:build_args].is_a?(Proc)
|
92
|
+
proc_args = attributes[:build_args].arity == 1 ? [capistrano_nomad_fetch_git_commit_id] : []
|
93
|
+
|
94
|
+
attributes[:build_args].call(*proc_args)
|
95
|
+
else
|
96
|
+
attributes[:build_args]
|
97
|
+
end
|
98
|
+
|
99
|
+
if (target = attributes[:target])
|
100
|
+
args << "--target #{target}"
|
101
|
+
end
|
102
|
+
|
103
|
+
(build_args || []).each do |key, value|
|
104
|
+
args << "--build-arg #{key}=#{value}"
|
105
|
+
end
|
106
|
+
|
107
|
+
capistrano_nomad_build_docker_image(image_type, attributes[:path], args)
|
108
|
+
end
|
109
|
+
|
110
|
+
def capistrano_nomad_push_docker_image_for_type(image_type, is_manifest_updated: true)
|
111
|
+
# Allows end user to add hooks
|
112
|
+
invoke("nomad:docker_images:push")
|
113
|
+
|
114
|
+
run_locally do
|
115
|
+
git_commit_id = capistrano_nomad_fetch_git_commit_id
|
116
|
+
revision_image_alias = capistrano_nomad_build_docker_image_alias(image_type, git_commit_id)
|
117
|
+
latest_image_alias = capistrano_nomad_build_docker_image_alias(image_type, "latest")
|
118
|
+
|
119
|
+
[revision_image_alias, latest_image_alias].each do |image_alias|
|
120
|
+
# We should not proceed if image cannot be pushed
|
121
|
+
unless system("docker push #{image_alias}")
|
122
|
+
raise StandardError, "Docker image push unsuccessful!"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
return unless is_manifest_updated
|
127
|
+
|
128
|
+
# Update image type manifest
|
129
|
+
capistrano_nomad_update_docker_image_types_manifest(image_type,
|
130
|
+
ref: git_commit_id,
|
131
|
+
image_alias: revision_image_alias
|
132
|
+
)
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
def nomad_docker_image_type(image_type, attributes = {})
|
2
|
+
docker_image_types = fetch(:nomad_docker_image_types) || {}
|
3
|
+
docker_image_types[image_type] = attributes
|
4
|
+
|
5
|
+
set(:nomad_docker_image_types, docker_image_types)
|
6
|
+
end
|
7
|
+
|
8
|
+
def nomad_namespace(namespace, &block)
|
9
|
+
@nomad_namespace = namespace
|
10
|
+
|
11
|
+
instance_eval(&block)
|
12
|
+
|
13
|
+
@nomad_namespace = nil
|
14
|
+
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def nomad_job(name, attributes = {})
|
19
|
+
nomad_jobs = fetch(:nomad_jobs) || Hash.new { |h, n| h[n] = {} }
|
20
|
+
nomad_jobs[@nomad_namespace][name] = attributes
|
21
|
+
|
22
|
+
set(:nomad_jobs, nomad_jobs)
|
23
|
+
|
24
|
+
define_tasks = lambda do |namespace: nil|
|
25
|
+
description_name = ""
|
26
|
+
description_name << "#{namespace}/" if namespace
|
27
|
+
description_name << name.to_s
|
28
|
+
|
29
|
+
namespace(name) do
|
30
|
+
desc "Build #{description_name} job Docker images"
|
31
|
+
task :build do
|
32
|
+
capistrano_nomad_build_jobs_docker_images([name], namespace: namespace)
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Push #{description_name} job Docker images"
|
36
|
+
task :push do
|
37
|
+
capistrano_nomad_push_jobs_docker_images([name], namespace: namespace)
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Build and push #{description_name} job Docker images"
|
41
|
+
task :assemble do
|
42
|
+
capistrano_nomad_build_jobs_docker_images([name], namespace: namespace)
|
43
|
+
capistrano_nomad_push_jobs_docker_images([name], namespace: namespace)
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Upload #{description_name} job and related files"
|
47
|
+
task :upload do
|
48
|
+
capistrano_nomad_upload_jobs([name], namespace: namespace)
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Run #{description_name} job"
|
52
|
+
task :run do
|
53
|
+
capistrano_nomad_run_jobs([name], namespace: namespace)
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "Purge and run #{description_name} job again"
|
57
|
+
task :rerun do
|
58
|
+
capistrano_nomad_rerun_jobs([name], namespace: namespace)
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "Purge #{description_name} job"
|
62
|
+
task :purge do
|
63
|
+
capistrano_nomad_purge_jobs([name], namespace: namespace, is_detached: false)
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "Display status of #{description_name} job"
|
67
|
+
task :status do
|
68
|
+
capistrano_nomad_display_job_status(name, namespace: namespace)
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Upload and plan #{description_name} job"
|
72
|
+
task :upload_plan do
|
73
|
+
capistrano_nomad_upload_plan_jobs([name], namespace: namespace)
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "Upload and run #{description_name} job"
|
77
|
+
task :upload_run do
|
78
|
+
capistrano_nomad_upload_run_jobs([name], namespace: namespace)
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "Upload and re-run #{description_name} job"
|
82
|
+
task :upload_rerun do
|
83
|
+
capistrano_nomad_upload_rerun_jobs([name], namespace: namespace)
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "Deploy #{description_name} job"
|
87
|
+
task :deploy do
|
88
|
+
capistrano_nomad_deploy_jobs([name], namespace: namespace)
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "Restart #{description_name} job"
|
92
|
+
task :restart do
|
93
|
+
on roles :manager do
|
94
|
+
# We can't restart the job directly so we'll need to fetch all its running allocs and restart each of one
|
95
|
+
# individually instead
|
96
|
+
running_alloc_ids_output = capture(
|
97
|
+
"nomad job allocs " \
|
98
|
+
"-t '{{range .}}{{if eq .ClientStatus \"running\"}}{{ println .ID}}{{end}}{{end}}' " \
|
99
|
+
"#{name}",
|
100
|
+
)
|
101
|
+
|
102
|
+
running_alloc_ids = running_alloc_ids_output.strip.split("\n")
|
103
|
+
|
104
|
+
running_alloc_ids.each do |alloc_id|
|
105
|
+
capistrano_nomad_execute_nomad_command(:alloc, :restart, alloc_id)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
desc "Open console to #{description_name} job"
|
111
|
+
task :console do
|
112
|
+
capistrano_nomad_exec_within_job(name, "/bin/bash", namespace: namespace)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
namespace(:nomad) do
|
118
|
+
# Define tasks for service
|
119
|
+
if @nomad_namespace
|
120
|
+
namespace(@nomad_namespace) do
|
121
|
+
define_tasks.call(namespace: @nomad_namespace)
|
122
|
+
end
|
123
|
+
else
|
124
|
+
define_tasks.call
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,275 @@
|
|
1
|
+
require "active_support/core_ext/string"
|
2
|
+
require "sshkit/interactive"
|
3
|
+
|
4
|
+
class NomadErbNamespace
|
5
|
+
def initialize(context:, vars: {})
|
6
|
+
@context = context
|
7
|
+
|
8
|
+
vars.each do |key, value|
|
9
|
+
instance_variable_set("@#{key}", value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Use default value passed in unless `nomad_job_task_cpu_resource` is set
|
14
|
+
def build_nomad_job_task_cpu_resource(default:)
|
15
|
+
nomad_job_task_cpu_resource == "null" ? default : nomad_job_task_cpu_resource
|
16
|
+
end
|
17
|
+
|
18
|
+
# Use default value passed in unless `nomad_job_task_memory_resource` is set
|
19
|
+
def build_nomad_job_task_memory_resource(default:)
|
20
|
+
nomad_job_task_memory_resource == "null" ? default : nomad_job_task_memory_resource
|
21
|
+
end
|
22
|
+
|
23
|
+
# rubocop:disable Style/MissingRespondToMissing
|
24
|
+
def method_missing(name, *args)
|
25
|
+
instance_variable = "@#{name}"
|
26
|
+
|
27
|
+
# First try to see if it's a variable we're trying to access which is stored in an instance variable otherwise try
|
28
|
+
# to see if there's a local method defineds
|
29
|
+
if instance_variable_defined?(instance_variable)
|
30
|
+
instance_variable_get(instance_variable)
|
31
|
+
elsif respond_to?(name)
|
32
|
+
send(name, *args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# rubocop:enable Style/MissingRespondToMissing
|
36
|
+
end
|
37
|
+
|
38
|
+
def capistrano_nomad_build_file_path(parent_path, basename, namespace: nil)
|
39
|
+
# Remove initial slash if it exists
|
40
|
+
parent_path = parent_path[1..] if parent_path[0] == "/"
|
41
|
+
|
42
|
+
segments = [parent_path]
|
43
|
+
segments << namespace if namespace
|
44
|
+
segments << "#{basename}.hcl"
|
45
|
+
|
46
|
+
segments.join("/")
|
47
|
+
end
|
48
|
+
|
49
|
+
def capistrano_nomad_build_base_job_path(*args)
|
50
|
+
capistrano_nomad_build_file_path(fetch(:nomad_jobs_path), *args)
|
51
|
+
end
|
52
|
+
|
53
|
+
def capistrano_nomad_build_base_var_file_path(*args)
|
54
|
+
capistrano_nomad_build_file_path(fetch(:nomad_var_files_path), *args)
|
55
|
+
end
|
56
|
+
|
57
|
+
def capistrano_nomad_build_local_job_path(name, *args)
|
58
|
+
local_path = fetch(:root).join(capistrano_nomad_build_base_job_path(name, *args))
|
59
|
+
|
60
|
+
# Determine if it has .erb appended or not
|
61
|
+
[local_path, "#{local_path}.erb"].find { |path| File.exist?(path) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def capistrano_nomad_build_release_job_path(*args)
|
65
|
+
"#{release_path}/#{capistrano_nomad_build_base_job_path(*args)}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def capistrano_nomad_build_release_var_file_path(*args)
|
69
|
+
"#{release_path}/#{capistrano_nomad_build_base_var_file_path(*args)}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def capistrano_nomad_execute_nomad_command(*args)
|
73
|
+
converted_args = args.each_with_object([]) do |arg, collection|
|
74
|
+
# If hash then convert it as options
|
75
|
+
if arg.is_a?(Hash)
|
76
|
+
arg.each do |key, value|
|
77
|
+
next unless value
|
78
|
+
|
79
|
+
option = "-#{key.to_s.dasherize}"
|
80
|
+
|
81
|
+
# Doesn't need a value if it's just meant to be a flag
|
82
|
+
option << "=#{value}" unless value == true
|
83
|
+
|
84
|
+
collection << option
|
85
|
+
end
|
86
|
+
else
|
87
|
+
collection << arg
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
on(roles(:manager)) do |host|
|
92
|
+
run_interactively(host) do
|
93
|
+
# Ignore errors
|
94
|
+
execute(:nomad, *converted_args, raise_on_non_zero_exit: false)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def capistrano_nomad_exec_within_job(name, command, namespace: nil)
|
100
|
+
capistrano_nomad_execute_nomad_command(:alloc, :exec, { namespace: namespace, task: name, job: name }, command)
|
101
|
+
end
|
102
|
+
|
103
|
+
def capistrano_nomad_upload_file(local_path:, remote_path:, erb_vars: {})
|
104
|
+
docker_image_types = fetch(:nomad_docker_image_types)
|
105
|
+
docker_image_types_manifest = capistrano_nomad_read_docker_image_types_manifest
|
106
|
+
|
107
|
+
# Merge manifest into image types
|
108
|
+
docker_image_types_manifest.each do |manifest_image_type, manifest_attributes|
|
109
|
+
docker_image_types[manifest_image_type]&.merge!(manifest_attributes) || {}
|
110
|
+
end
|
111
|
+
|
112
|
+
# Parse manifest files using ERB
|
113
|
+
erb = ERB.new(File.open(local_path).read, trim_mode: "-")
|
114
|
+
|
115
|
+
final_erb_vars = {
|
116
|
+
git_commit_id: fetch(:current_revision) || capistrano_nomad_fetch_git_commit_id,
|
117
|
+
docker_image_types: docker_image_types,
|
118
|
+
}
|
119
|
+
|
120
|
+
# Add global ERB vars
|
121
|
+
final_erb_vars.merge!(fetch(:nomad_erb_vars) || {})
|
122
|
+
|
123
|
+
# Add job-specific ERB vars
|
124
|
+
final_erb_vars.merge!(erb_vars)
|
125
|
+
|
126
|
+
# We use a custom namespace class so that we can include helper methods into the namespace to make them available for
|
127
|
+
# template to access
|
128
|
+
namespace = NomadErbNamespace.new(
|
129
|
+
context: self,
|
130
|
+
vars: final_erb_vars,
|
131
|
+
)
|
132
|
+
erb_io = StringIO.new(erb.result(namespace.instance_eval { binding }))
|
133
|
+
|
134
|
+
on(roles(:manager)) do
|
135
|
+
execute(:mkdir, "-p", File.dirname(remote_path))
|
136
|
+
upload!(erb_io, remote_path)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def capistrano_nomad_fetch_job_options(name, *args, namespace: nil)
|
141
|
+
fetch(:nomad_jobs).dig(namespace, name.to_sym, *args)
|
142
|
+
end
|
143
|
+
|
144
|
+
def capistrano_nomad_fetch_job_var_files(name, *args)
|
145
|
+
capistrano_nomad_fetch_job_options(name, :var_files, *args) || []
|
146
|
+
end
|
147
|
+
|
148
|
+
def capistrano_nomad_fetch_jobs_names_by_namespace
|
149
|
+
fetch(:nomad_jobs).transform_values(&:keys)
|
150
|
+
end
|
151
|
+
|
152
|
+
def capistrano_nomad_fetch_jobs_docker_image_types(names, namespace: nil)
|
153
|
+
names.map { |n| fetch(:nomad_jobs).dig(namespace, n.to_sym, :image_types) }.flatten.compact.uniq
|
154
|
+
end
|
155
|
+
|
156
|
+
def capistrano_nomad_build_jobs_docker_images(names, *args)
|
157
|
+
image_types = capistrano_nomad_fetch_jobs_docker_image_types(names, *args)
|
158
|
+
|
159
|
+
return false if image_types.empty?
|
160
|
+
|
161
|
+
image_types.each { |i| capistrano_nomad_build_docker_image_for_type(i, *args) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def capistrano_nomad_push_jobs_docker_images(names, *args)
|
165
|
+
image_types = capistrano_nomad_fetch_jobs_docker_image_types(names, *args)
|
166
|
+
|
167
|
+
return false if image_types.empty?
|
168
|
+
|
169
|
+
image_types.each { |i| capistrano_nomad_push_docker_image_for_type(i, *args) }
|
170
|
+
end
|
171
|
+
|
172
|
+
def capistrano_nomad_assemble_jobs_docker_images(names, *args)
|
173
|
+
capistrano_nomad_build_jobs_docker_images(names, *args)
|
174
|
+
capistrano_nomad_push_jobs_docker_images(names, *args)
|
175
|
+
end
|
176
|
+
|
177
|
+
def capistrano_nomad_upload_jobs(names, *args)
|
178
|
+
# Var files can be shared between jobs so don't upload duplicates
|
179
|
+
uniq_var_files = names.map { |n| capistrano_nomad_fetch_job_var_files(n, *args) }.flatten.uniq
|
180
|
+
|
181
|
+
uniq_var_files.each do |var_file|
|
182
|
+
capistrano_nomad_upload_file(
|
183
|
+
local_path: capistrano_nomad_build_base_var_file_path(var_file, *args),
|
184
|
+
remote_path: capistrano_nomad_build_release_var_file_path(var_file, *args),
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
run_locally do
|
189
|
+
names.each do |name|
|
190
|
+
nomad_job_options = capistrano_nomad_fetch_job_options(name, *args)
|
191
|
+
|
192
|
+
# Can set job-specific ERB vars
|
193
|
+
erb_vars = nomad_job_options[:erb_vars] || {}
|
194
|
+
|
195
|
+
# Can set a custom template instead
|
196
|
+
file_basename = nomad_job_options[:template] || name
|
197
|
+
|
198
|
+
capistrano_nomad_upload_file(
|
199
|
+
local_path: capistrano_nomad_build_base_job_path(file_basename, *args),
|
200
|
+
remote_path: capistrano_nomad_build_release_job_path(name, *args),
|
201
|
+
erb_vars: erb_vars,
|
202
|
+
)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def capistrano_nomad_plan_jobs(names, *args)
|
208
|
+
names.each do |name|
|
209
|
+
args = [capistrano_nomad_build_release_job_path(name, *args)]
|
210
|
+
|
211
|
+
capistrano_nomad_execute_nomad_command(:plan, *args)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def capistrano_nomad_run_jobs(names, namespace: nil)
|
216
|
+
names.each do |name|
|
217
|
+
run_options = {
|
218
|
+
namespace: namespace,
|
219
|
+
detach: true,
|
220
|
+
|
221
|
+
# Don't reset counts since they may have been scaled
|
222
|
+
preserve_counts: true,
|
223
|
+
}
|
224
|
+
|
225
|
+
capistrano_nomad_fetch_job_var_files(name, namespace: namespace).each do |var_file|
|
226
|
+
run_options[:var_file] = capistrano_nomad_build_release_var_file_path(var_file, namespace: namespace)
|
227
|
+
end
|
228
|
+
|
229
|
+
capistrano_nomad_execute_nomad_command(
|
230
|
+
:run,
|
231
|
+
run_options,
|
232
|
+
capistrano_nomad_build_release_job_path(name, namespace: namespace),
|
233
|
+
)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Remove job and run again
|
238
|
+
def capistrano_nomad_rerun_jobs(names)
|
239
|
+
names.each do |name|
|
240
|
+
# Wait for jobs to be purged before running again
|
241
|
+
capistrano_nomad_purge_jobs([name], is_detached: false)
|
242
|
+
|
243
|
+
capistrano_nomad_run_jobs([name])
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def capistrano_nomad_upload_plan_jobs(names, *args)
|
248
|
+
capistrano_nomad_upload_jobs(names, *args)
|
249
|
+
capistrano_nomad_plan_jobs(names, *args)
|
250
|
+
end
|
251
|
+
|
252
|
+
def capistrano_nomad_upload_run_jobs(names, *args)
|
253
|
+
capistrano_nomad_upload_jobs(names, *args)
|
254
|
+
capistrano_nomad_run_jobs(names, *args)
|
255
|
+
end
|
256
|
+
|
257
|
+
def capistrano_nomad_upload_rerun_jobs(names, *args)
|
258
|
+
capistrano_nomad_upload_jobs(names, *args)
|
259
|
+
capistrano_nomad_rerun_jobs(names, *args)
|
260
|
+
end
|
261
|
+
|
262
|
+
def capistrano_nomad_deploy_jobs(names, *args)
|
263
|
+
capistrano_nomad_assemble_jobs_docker_images(names, *args)
|
264
|
+
capistrano_nomad_upload_run_jobs(names, *args)
|
265
|
+
end
|
266
|
+
|
267
|
+
def capistrano_nomad_purge_jobs(names, namespace: nil, is_detached: true)
|
268
|
+
names.each do |name|
|
269
|
+
capistrano_nomad_execute_nomad_command(:stop, { namespace: namespace, purge: true, detach: is_detached }, name)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def capistrano_nomad_display_job_status(name, *args)
|
274
|
+
capistrano_nomad_execute_nomad_command(:status, *args, name)
|
275
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "capistrano/scm/plugin"
|
2
|
+
require_relative "../helpers/git"
|
3
|
+
|
4
|
+
class Capistrano::SCM::GitLocal < Capistrano::SCM::Plugin
|
5
|
+
def define_tasks
|
6
|
+
namespace :git_local do
|
7
|
+
task :set_current_revision do
|
8
|
+
on release_roles :manager do
|
9
|
+
set :current_revision, capistrano_nomad_fetch_git_commit_id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task :create_release do
|
14
|
+
on release_roles :manager do
|
15
|
+
execute :mkdir, "-p", release_path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def register_hooks
|
22
|
+
before "deploy:set_current_revision", "git_local:set_current_revision"
|
23
|
+
after "deploy:new_release_path", "git_local:create_release"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
namespace :nomad do
|
2
|
+
desc "Show version"
|
3
|
+
task :version do
|
4
|
+
capistrano_nomad_execute_nomad_command(:version)
|
5
|
+
end
|
6
|
+
|
7
|
+
namespace :docker_images do
|
8
|
+
desc "Used for adding hooks before or after pushing Docker images"
|
9
|
+
task :push
|
10
|
+
end
|
11
|
+
|
12
|
+
namespace :jobs do
|
13
|
+
desc "Build all job Docker images"
|
14
|
+
task :build do
|
15
|
+
capistrano_nomad_fetch_jobs_names_by_namespace.each do |namespace, names|
|
16
|
+
capistrano_nomad_push_jobs_docker_images(names, namespace: namespace)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Push all job Docker images"
|
21
|
+
task :push do
|
22
|
+
capistrano_nomad_fetch_jobs_names_by_namespace.each do |namespace, names|
|
23
|
+
capistrano_nomad_push_jobs_docker_images(names, namespace: namespace)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Build and push all job Docker images"
|
28
|
+
task :assemble do
|
29
|
+
capistrano_nomad_fetch_jobs_names_by_namespace.each do |namespace, names|
|
30
|
+
capistrano_nomad_assemble_jobs_docker_images(names, namespace: namespace)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Upload and run all jobs"
|
35
|
+
task :upload_run do
|
36
|
+
capistrano_nomad_fetch_jobs_names_by_namespace.each do |namespace, names|
|
37
|
+
capistrano_nomad_upload_run_jobs(names, namespace: namespace)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Deploy all jobs"
|
42
|
+
task :deploy do
|
43
|
+
capistrano_nomad_fetch_jobs_names_by_namespace.each do |namespace, names|
|
44
|
+
capistrano_nomad_deploy_jobs(names, namespace: namespace)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Rerun all jobs"
|
49
|
+
task :rerun do
|
50
|
+
capistrano_nomad_fetch_jobs_names_by_namespace.each do |namespace, names|
|
51
|
+
capistrano_nomad_rerun_jobs(names, namespace: namespace)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Purge all jobs"
|
56
|
+
task :purge do
|
57
|
+
capistrano_nomad_fetch_jobs_names_by_namespace.each do |namespace, names|
|
58
|
+
capistrano_nomad_purge_jobs(names, namespace: namespace)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
namespace :system do
|
64
|
+
desc "Clean up Nomad"
|
65
|
+
task :clean do
|
66
|
+
capistrano_nomad_execute_nomad_command(:system, :gc)
|
67
|
+
capistrano_nomad_execute_nomad_command(:system, :reconcile, :summaries)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "capistrano/plugin"
|
2
|
+
require_relative "nomad/helpers/base"
|
3
|
+
require_relative "nomad/helpers/docker"
|
4
|
+
require_relative "nomad/helpers/dsl"
|
5
|
+
require_relative "nomad/helpers/git"
|
6
|
+
require_relative "nomad/helpers/nomad"
|
7
|
+
|
8
|
+
module Capistrano
|
9
|
+
class Nomad < Capistrano::Plugin
|
10
|
+
def set_defaults
|
11
|
+
set_if_empty(:nomad_jobs_path, "/nomad/jobs")
|
12
|
+
set_if_empty(:nomad_var_files_path, "/nomad/var_files")
|
13
|
+
set_if_empty(:nomad_docker_image_alias, ->(**) {})
|
14
|
+
end
|
15
|
+
|
16
|
+
def define_tasks
|
17
|
+
eval_rakefile(File.expand_path("nomad/tasks/nomad.rake", __dir__))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capistrano-nomad
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Hu
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-10-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.0.8
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 7.0.8
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: byebug
|
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
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.14.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.14.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: git
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.10.2
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.10.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '13.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '13.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sshkit
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.21.2
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.21.2
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sshkit-interactive
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.3.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.3.0
|
111
|
+
description: Capistrano plugin for deploying and managing Nomad jobs
|
112
|
+
email:
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- CHANGELOG.md
|
118
|
+
- Gemfile
|
119
|
+
- Gemfile.lock
|
120
|
+
- LICENSE.txt
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- capistrano-nomad.gemspec
|
124
|
+
- lib/capistrano/nomad.rb
|
125
|
+
- lib/capistrano/nomad/helpers/base.rb
|
126
|
+
- lib/capistrano/nomad/helpers/docker.rb
|
127
|
+
- lib/capistrano/nomad/helpers/dsl.rb
|
128
|
+
- lib/capistrano/nomad/helpers/git.rb
|
129
|
+
- lib/capistrano/nomad/helpers/nomad.rb
|
130
|
+
- lib/capistrano/nomad/scm/git_local.rb
|
131
|
+
- lib/capistrano/nomad/tasks/nomad.rake
|
132
|
+
- lib/capistrano/nomad/version.rb
|
133
|
+
- sig/capistrano/nomad.rbs
|
134
|
+
homepage: https://github.com/axsuul/capistrano-nomad
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata:
|
138
|
+
allowed_push_host: https://rubygems.org
|
139
|
+
homepage_uri: https://github.com/axsuul/capistrano-nomad
|
140
|
+
source_code_uri: https://github.com/axsuul/capistrano-nomad
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 2.6.0
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubygems_version: 3.1.6
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: Capistrano plugin for deploying and managing Nomad jobs
|
160
|
+
test_files: []
|