capistrano-dockerbuild 1.0.0 → 1.2.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 +4 -4
- data/README.md +31 -1
- data/capistrano-dockerbuild.gemspec +2 -2
- data/lib/capistrano/dockerbuild.rb +29 -0
- data/lib/capistrano/tasks/docker.rake +59 -22
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed984b515b7f0d43d3985d442dba5bf85c1539f3109d8360bf9864db5a549a2d
|
|
4
|
+
data.tar.gz: cbcea200c07484a17aec1edb9d6e5e99ec2548811e3bc3de34cec1f2deb993a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb19d9ee91eb190e367a5dfe3fe75c9a695465afae9eff6d9a6f9d79c1548b4e6ede86a66f004bc4d9d23d5bbc56e70b719ca157903b59f8ca4251b4fc6342e7
|
|
7
|
+
data.tar.gz: 98752e45ab74d37285903f283efa3c8c2e167d51e8fd366c381099bc33b33b947c9a03ea018d93e90bfa91bcfbfd5a302288db93630a6700e671e7c9490f44e0
|
data/README.md
CHANGED
|
@@ -88,6 +88,8 @@ Use common variables
|
|
|
88
88
|
| keep_docker_image_count | no | 10 | |
|
|
89
89
|
| git_http_username | no | nil | See below |
|
|
90
90
|
| git_http_password | no | nil | See below |
|
|
91
|
+
| git_auth_token | no | nil | See below |
|
|
92
|
+
| update_git_submodule | no | false | Run `git submodule update --init --recursive` after checkout |
|
|
91
93
|
|
|
92
94
|
If you want to use GitHub Apps installation access token or something to authorize repository access using HTTPS protocol. You can set variables in your config/deploy.rb:
|
|
93
95
|
|
|
@@ -105,17 +107,43 @@ end
|
|
|
105
107
|
|
|
106
108
|
Update remote URL always if you set proper value to all of `repo_url`, `git_http_username`, and `git_http_password`.
|
|
107
109
|
|
|
110
|
+
Alternatively, you can authenticate via an HTTP `Authorization` header using `:git_auth_token`. This is useful when the build server has no SSH key access to the repository (e.g. using a GitHub Apps installation access token or a personal access token).
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
set :git_auth_token, -> { ENV["GIT_AUTH_TOKEN"] }
|
|
114
|
+
set :repo_url, "git@github.com:owner/repo.git" # SSH or HTTPS both work
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
When `:git_auth_token` is set, `docker:setup` creates a temporary `.gitconfig` on each remote build host containing:
|
|
118
|
+
|
|
119
|
+
- `http.extraheader = Authorization: Basic <token>` — injects the token into every HTTPS git request
|
|
120
|
+
- `url."https://<host>/".insteadOf` rules — rewrites SSH-style URLs (`git@host:` and `ssh://git@host/`) to HTTPS so the token is used regardless of how `repo_url` is written
|
|
121
|
+
|
|
122
|
+
The host is derived automatically from `:repo_url`, so this works with GitHub, GitLab, Bitbucket, or any self-hosted git server.
|
|
123
|
+
|
|
124
|
+
The temporary directory is removed automatically by `docker:clean` after `docker:build`.
|
|
125
|
+
|
|
108
126
|
## Tasks
|
|
109
127
|
|
|
128
|
+
#### docker:setup
|
|
129
|
+
- Create a temporary `.gitconfig` on each remote build host when `:git_auth_token` is set
|
|
130
|
+
- Automatically invoked before `docker:check`
|
|
131
|
+
|
|
132
|
+
#### docker:clean
|
|
133
|
+
- Remove the temporary directory created by `docker:setup`
|
|
134
|
+
- Automatically invoked after `docker:build`
|
|
135
|
+
|
|
110
136
|
#### docker:check
|
|
111
137
|
- Ensure `#{docker_build_base_dir}`
|
|
112
138
|
- Ensure git reachable
|
|
113
139
|
|
|
114
140
|
#### docker:clone
|
|
115
141
|
- Clone repo to `#{docker_build_base_dir}` as mirror
|
|
142
|
+
- Serialized with `docker:update_mirror` via a `flock` keyed by `#{docker_build_base_dir}`'s basename, since both mutate the shared repo directory (a mirror by default, or a regular checkout under `:docker_build_no_worktree`) at `#{docker_build_base_dir}`. This matters when multiple deploys (e.g. CI jobs for different branches) target the same build host concurrently — without it, concurrent `git clone`/`git remote update` on the shared directory can corrupt its `.git/config` or fail with transport errors. Keying the lock by basename keeps unrelated consumers that share the same parent directory from serializing against each other.
|
|
116
143
|
|
|
117
144
|
#### docker:update_mirror
|
|
118
145
|
- git remote update `#{docker_build_base_dir}`
|
|
146
|
+
- Serialized with `docker:clone` via the same `flock` (see above)
|
|
119
147
|
|
|
120
148
|
#### docker:build
|
|
121
149
|
- Create `#{branch}` worktree to `#{docker_tag}-#{timestamp}`
|
|
@@ -132,7 +160,9 @@ Update remote URL always if you set proper value to all of `repo_url`, `git_http
|
|
|
132
160
|
|
|
133
161
|
## Task Dependency
|
|
134
162
|
|
|
135
|
-
docker:push => docker:build => docker:update_mirror => docker:clone => docker:check
|
|
163
|
+
docker:push => docker:build => docker:update_mirror => docker:clone => docker:check => docker:setup
|
|
164
|
+
|
|
165
|
+
docker:clean is triggered automatically after docker:build
|
|
136
166
|
|
|
137
167
|
## Development
|
|
138
168
|
|
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "capistrano-dockerbuild"
|
|
7
|
-
spec.version = "1.
|
|
7
|
+
spec.version = "1.2.0"
|
|
8
8
|
spec.authors = ["joker1007"]
|
|
9
9
|
spec.email = ["kakyoin.hierophant@gmail.com"]
|
|
10
10
|
|
|
@@ -22,6 +22,6 @@ Gem::Specification.new do |spec|
|
|
|
22
22
|
|
|
23
23
|
spec.add_runtime_dependency "capistrano", ">= 3.2"
|
|
24
24
|
|
|
25
|
-
spec.add_development_dependency "bundler", "
|
|
25
|
+
spec.add_development_dependency "bundler", ">= 2.3"
|
|
26
26
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
27
27
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
require "cgi"
|
|
2
2
|
require "uri"
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "shellwords"
|
|
3
5
|
|
|
4
6
|
class Capistrano::Dockerbuild < Capistrano::Plugin
|
|
5
7
|
def set_defaults
|
|
@@ -15,6 +17,7 @@ class Capistrano::Dockerbuild < Capistrano::Plugin
|
|
|
15
17
|
set_if_empty :keep_docker_image_count, 10
|
|
16
18
|
set_if_empty :git_gc_prune_date, "3.days.ago"
|
|
17
19
|
set_if_empty :docker_build_no_worktree, false
|
|
20
|
+
set_if_empty :update_git_submodule, false
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
def define_tasks
|
|
@@ -26,6 +29,16 @@ class Capistrano::Dockerbuild < Capistrano::Plugin
|
|
|
26
29
|
Pathname(fetch(:docker_build_base_dir))
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
# Lock serializing access to the shared repo dir at docker_build_base_path
|
|
33
|
+
# across concurrent deploys on the same build host (see README for the race).
|
|
34
|
+
#
|
|
35
|
+
# Keyed by basename, not a fixed name, so unrelated consumers sharing a parent
|
|
36
|
+
# directory don't serialize against each other.
|
|
37
|
+
def docker_build_lock_path
|
|
38
|
+
base = docker_build_base_path
|
|
39
|
+
base.dirname.join("capistrano_dockerbuild.#{base.basename}.lock").to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
29
42
|
def git_repo_url
|
|
30
43
|
if fetch(:git_http_username) && fetch(:git_http_password)
|
|
31
44
|
URI.parse(repo_url).tap do |repo_uri|
|
|
@@ -40,4 +53,20 @@ class Capistrano::Dockerbuild < Capistrano::Plugin
|
|
|
40
53
|
repo_url
|
|
41
54
|
end
|
|
42
55
|
end
|
|
56
|
+
|
|
57
|
+
def git_repo_host
|
|
58
|
+
URI.parse(repo_url.sub(/^git@.*/) {|m| "https://#{m}" }).host
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def tmp_home(host)
|
|
62
|
+
@tmp_host ||= {}
|
|
63
|
+
@tmp_host[host.properties.arch] ||= "/tmp/capistrano-dockerbuild-#{SecureRandom.hex(8)}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def git_env(host)
|
|
67
|
+
return {} if fetch(:git_auth_token).to_s.empty?
|
|
68
|
+
|
|
69
|
+
@git_env ||= {}
|
|
70
|
+
@git_env[host.properties.arch] ||= { HOME: tmp_home(host), GIT_CONFIG_NOSYSTEM: "1" }
|
|
71
|
+
end
|
|
43
72
|
end
|
|
@@ -1,32 +1,57 @@
|
|
|
1
1
|
dockerbuild_plugin = self
|
|
2
2
|
|
|
3
3
|
namespace :docker do
|
|
4
|
+
desc "setup temporary gitconfig for HTTP authentication"
|
|
5
|
+
task :setup do
|
|
6
|
+
on roles(:docker_build) do |host|
|
|
7
|
+
unless fetch(:git_auth_token).to_s.empty?
|
|
8
|
+
tmp_home = dockerbuild_plugin.tmp_home(host)
|
|
9
|
+
git_host = dockerbuild_plugin.git_repo_host
|
|
10
|
+
execute :mkdir, "-p", tmp_home
|
|
11
|
+
execute :git, :config, "--file", "#{tmp_home}/.gitconfig", "http.extraheader", "'Authorization: Basic #{fetch(:git_auth_token)}'"
|
|
12
|
+
execute :git, :config, "--file", "#{tmp_home}/.gitconfig", %Q(url."https://#{git_host}/".insteadOf), "git@#{git_host}:"
|
|
13
|
+
execute :git, :config, "--file", "#{tmp_home}/.gitconfig", "--add", %Q(url."https://#{git_host}/".insteadOf), "ssh://git@#{git_host}/"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc "clean up temporary gitconfig"
|
|
19
|
+
task :clean do
|
|
20
|
+
on roles(:docker_build) do |host|
|
|
21
|
+
unless fetch(:git_auth_token).to_s.empty?
|
|
22
|
+
tmp_home = dockerbuild_plugin.tmp_home(host)
|
|
23
|
+
execute :rm, "-rf", tmp_home
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
4
28
|
desc "check directory exist"
|
|
5
|
-
task :
|
|
29
|
+
task check: [:"docker:setup"] do
|
|
6
30
|
on roles(:docker_build) do |host|
|
|
7
|
-
|
|
8
|
-
|
|
31
|
+
with dockerbuild_plugin.git_env(host) do
|
|
32
|
+
execute :mkdir, "-p", dockerbuild_plugin.docker_build_base_path.dirname.to_s
|
|
33
|
+
execute :git, :'ls-remote', dockerbuild_plugin.git_repo_url, "HEAD"
|
|
34
|
+
end
|
|
9
35
|
end
|
|
10
36
|
end
|
|
11
37
|
|
|
12
38
|
desc "Clone the repo to docker build base directory"
|
|
13
39
|
task :clone => [:'docker:check'] do
|
|
14
40
|
on roles(:docker_build) do |host|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
end
|
|
41
|
+
within dockerbuild_plugin.docker_build_base_path.dirname do
|
|
42
|
+
with dockerbuild_plugin.git_env(host) do
|
|
43
|
+
path = dockerbuild_plugin.docker_build_base_path.to_s.shellescape
|
|
44
|
+
url = dockerbuild_plugin.git_repo_url.shellescape
|
|
45
|
+
# Existence check runs inside the same flock as the clone itself, not before
|
|
46
|
+
# acquiring it — otherwise two concurrent deploys can both observe the repo
|
|
47
|
+
# missing and the second `git clone` fails once the first has created it.
|
|
48
|
+
commands =
|
|
49
|
+
if fetch(:docker_build_no_worktree)
|
|
50
|
+
"[ -f #{path}/.git/HEAD ] || git clone #{url} #{path}"
|
|
51
|
+
else
|
|
52
|
+
"[ -f #{path}/HEAD ] || git clone --mirror #{url} #{path}"
|
|
53
|
+
end
|
|
54
|
+
execute :flock, dockerbuild_plugin.docker_build_lock_path.shellescape, "-c", commands.shellescape
|
|
30
55
|
end
|
|
31
56
|
end
|
|
32
57
|
end
|
|
@@ -36,8 +61,10 @@ namespace :docker do
|
|
|
36
61
|
task update_mirror: :'docker:clone' do
|
|
37
62
|
on roles(:docker_build) do |host|
|
|
38
63
|
within dockerbuild_plugin.docker_build_base_path do
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
with dockerbuild_plugin.git_env(host) do
|
|
65
|
+
commands = "git remote set-url origin #{dockerbuild_plugin.git_repo_url.shellescape} && git remote update --prune"
|
|
66
|
+
execute :flock, dockerbuild_plugin.docker_build_lock_path.shellescape, "-c", commands.shellescape
|
|
67
|
+
end
|
|
41
68
|
end
|
|
42
69
|
end
|
|
43
70
|
end
|
|
@@ -55,8 +82,11 @@ namespace :docker do
|
|
|
55
82
|
end
|
|
56
83
|
within dockerbuild_plugin.docker_build_base_path do
|
|
57
84
|
if fetch(:docker_build_no_worktree)
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
submodule_cmd = fetch(:update_git_submodule) ? "; git submodule update --init --recursive" : ""
|
|
86
|
+
commands = "sha1=$(git rev-parse #{fetch(:branch)}); git reset --hard ${sha1}#{submodule_cmd}; #{build_cmd.map {|c| c.to_s.shellescape }.join(" ")}"
|
|
87
|
+
with dockerbuild_plugin.git_env(host) do
|
|
88
|
+
execute(:flock, dockerbuild_plugin.docker_build_lock_path.shellescape, "-c", "'#{commands}'")
|
|
89
|
+
end
|
|
60
90
|
else
|
|
61
91
|
timestamp = Time.now.to_i
|
|
62
92
|
git_sha1 = `git rev-parse #{fetch(:branch)}`.chomp
|
|
@@ -66,6 +96,11 @@ namespace :docker do
|
|
|
66
96
|
|
|
67
97
|
begin
|
|
68
98
|
within worktree_dir_name do
|
|
99
|
+
if fetch(:update_git_submodule)
|
|
100
|
+
with dockerbuild_plugin.git_env(host) do
|
|
101
|
+
execute :git, :submodule, :update, "--init", "--recursive"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
69
104
|
execute(*build_cmd)
|
|
70
105
|
end
|
|
71
106
|
ensure
|
|
@@ -136,3 +171,5 @@ namespace :docker do
|
|
|
136
171
|
end
|
|
137
172
|
end
|
|
138
173
|
end
|
|
174
|
+
|
|
175
|
+
after "docker:build", "docker:clean"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: capistrano-dockerbuild
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- joker1007
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: capistrano
|
|
@@ -27,14 +27,14 @@ dependencies:
|
|
|
27
27
|
name: bundler
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '2.3'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '2.3'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
@@ -86,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
86
86
|
- !ruby/object:Gem::Version
|
|
87
87
|
version: '0'
|
|
88
88
|
requirements: []
|
|
89
|
-
rubygems_version:
|
|
89
|
+
rubygems_version: 4.0.16
|
|
90
90
|
specification_version: 4
|
|
91
91
|
summary: Capistrano task definition for `docker build`
|
|
92
92
|
test_files: []
|