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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 411b02ef1e66127fab930b7476e2f4e618276c0f0e60ac8327543e6f0051bd35
4
- data.tar.gz: ffed26862d2999bf476f464da9e2a11aa4844088a7cb34a01d01a401331524c5
3
+ metadata.gz: ed984b515b7f0d43d3985d442dba5bf85c1539f3109d8360bf9864db5a549a2d
4
+ data.tar.gz: cbcea200c07484a17aec1edb9d6e5e99ec2548811e3bc3de34cec1f2deb993a0
5
5
  SHA512:
6
- metadata.gz: 636f6d7114ee9ed6a1e156d0bd7c41d46ab91e52e747afa1142599b2e12c90ae5a3b8cf126fd4152b6729eab2e0a6b3ceefea181601b3f980762867173c34315
7
- data.tar.gz: 9f585640710fb132e198ddb94493d5a1fc600cee7e542e41cd97667ede164f508fe5393c922a22dbb97c3d307dbd34aa2ce889f6701839cbca0853a11413ba62
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.0.0"
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", "~> 2.3"
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 :check do
29
+ task check: [:"docker:setup"] do
6
30
  on roles(:docker_build) do |host|
7
- execute :mkdir, "-p", dockerbuild_plugin.docker_build_base_path.dirname.to_s
8
- execute :git, :'ls-remote', dockerbuild_plugin.git_repo_url, "HEAD"
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
- if fetch(:docker_build_no_worktree)
16
- if test " [ -f #{dockerbuild_plugin.docker_build_base_path}/.git/HEAD ] "
17
- info "The repository is at #{dockerbuild_plugin.docker_build_base_path}"
18
- else
19
- within dockerbuild_plugin.docker_build_base_path.dirname do
20
- execute :git, :clone, dockerbuild_plugin.git_repo_url, dockerbuild_plugin.docker_build_base_path.to_s
21
- end
22
- end
23
- else
24
- if test " [ -f #{dockerbuild_plugin.docker_build_base_path}/HEAD ] "
25
- info t(:mirror_exists, at: dockerbuild_plugin.docker_build_base_path.to_s)
26
- else
27
- within dockerbuild_plugin.docker_build_base_path.dirname do
28
- execute :git, :clone, "--mirror", dockerbuild_plugin.git_repo_url, dockerbuild_plugin.docker_build_base_path.to_s
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
- execute :git, :remote, "set-url", :origin, dockerbuild_plugin.git_repo_url
40
- execute :git, :remote, :update, "--prune"
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
- commands = "sha1=$(git rev-parse #{fetch(:branch)}); git reset --hard ${sha1}; #{build_cmd.map {|c| c.to_s.shellescape }.join(" ")}"
59
- execute(:flock, "capistrano_dockerbuild.lock", "-c", "'#{commands}'")
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.0.0
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: 2025-02-10 00:00:00.000000000 Z
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: 3.6.2
89
+ rubygems_version: 4.0.16
90
90
  specification_version: 4
91
91
  summary: Capistrano task definition for `docker build`
92
92
  test_files: []