capistrano-redmine-deployment 1.0.1 → 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/.gitignore +0 -0
- data/.rspec +2 -0
- data/Gemfile +1 -0
- data/README.md +43 -1
- data/docs/CHANGELOG.md +13 -0
- data/lib/capistrano/redmine/deployment/client.rb +175 -8
- data/lib/capistrano/redmine/deployment/config.rb +37 -2
- data/lib/capistrano/redmine/deployment/gem_version.rb +2 -2
- data/lib/capistrano/redmine/deployment/receipts.rb +0 -0
- data/lib/capistrano/redmine/deployment/tasks/setup.rake +5 -0
- data/lib/capistrano/redmine/deployment/tasks/verify.rake +57 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5586e101b5eda84c25017c252b6a01d26715c4fbab7ee71e609a84a20b7e47cc
|
|
4
|
+
data.tar.gz: 9e85e888ef2336c3bdc760188cd2d18f10586b1dbabab0367d631f07f4c729da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 61c1a9c03f4f60a48f3e4c17f601020b1c6802bee6dcca09d26a1d4f79a777a145696c588dedf0927ddf7e58e665f6cd659653ba033c91ded060555d0e73756e
|
|
7
|
+
data.tar.gz: e18d575a41748df42b3cb263031fdf6b9152258a5a3bc42f31edf303c15a12f02bbd8cfcb50c5448f27c66a873e26a67c6981fc856a8046ad4aeba45765c2b73
|
data/.gitignore
CHANGED
|
File without changes
|
data/.rspec
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -49,14 +49,55 @@ set(:redmine_repository, "target-redmine-repository-identifier")
|
|
|
49
49
|
|
|
50
50
|
# in case of `SSL` issues that are caused by *CRL* (i.e. by LetsEncrypt certificates)
|
|
51
51
|
set(:redmine_host_verification, false)
|
|
52
|
+
|
|
53
|
+
# alternatively, for instances with a custom CA, point to a CA bundle instead of
|
|
54
|
+
# disabling verification entirely (preferred over :redmine_host_verification)
|
|
55
|
+
set(:redmine_ca_file, "/path/to/ca-bundle.pem")
|
|
52
56
|
```
|
|
53
57
|
|
|
54
58
|
### User-specific credentials
|
|
55
59
|
|
|
60
|
+
The `api_key` is resolved from the following sources, **later ones win**:
|
|
61
|
+
|
|
62
|
+
1. capistrano variables: `:redmine_api_key`, or `:redmine_api_key_command` (its stdout is used as the key) as fallback
|
|
63
|
+
2. `.redmine` file in the current directory (`$PWD`) or `$HOME`
|
|
64
|
+
3. the `REDMINE_API_KEY` ENV variable
|
|
65
|
+
|
|
66
|
+
#### Option A: macOS Keychain (recommended)
|
|
67
|
+
|
|
68
|
+
Store the key once per developer in the keychain (use a dedicated entry):
|
|
69
|
+
|
|
70
|
+
$ security add-generic-password -s ri-redmine-deploy -a "$USER" -w 'YOUR_KEY'
|
|
71
|
+
|
|
72
|
+
Then point capistrano at a command that reads it, in your `config/deploy.rb`:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
set :redmine_api_key_command, 'security find-generic-password -s ri-redmine-deploy -w'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
No secret ends up in `deploy.rb` or your SCM — only the command. Once every developer
|
|
79
|
+
has switched over, the `.redmine` file can be removed entirely (it stays supported as a
|
|
80
|
+
fallback).
|
|
81
|
+
|
|
82
|
+
> Use a **dedicated** keychain entry (e.g. `ri-redmine-deploy`).
|
|
83
|
+
|
|
84
|
+
#### Option B: `.redmine` file via rake-task
|
|
85
|
+
|
|
56
86
|
Setup redmine `API-KEY` through rake-task:
|
|
57
87
|
|
|
58
|
-
$ rake capistrano:redmine:
|
|
88
|
+
$ rake capistrano:redmine:deployment:setup
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## Verifying the setup
|
|
92
|
+
|
|
93
|
+
Once configured, verify that the credentials, host, project and repository resolve
|
|
94
|
+
to a reachable `redmine_deployment` endpoint:
|
|
95
|
+
|
|
96
|
+
$ cap <stage> redmine:verify
|
|
59
97
|
|
|
98
|
+
This receives a single deployment entry from redmine. A response with **no** entries
|
|
99
|
+
(i.e. no deployment has been logged yet) still counts as a success — it only fails
|
|
100
|
+
when the host is unreachable or the credentials / project / repository are wrong.
|
|
60
101
|
|
|
61
102
|
## Redmine requirements
|
|
62
103
|
|
|
@@ -64,6 +105,7 @@ Install the plugin `redmine_deployment`.
|
|
|
64
105
|
|
|
65
106
|
## Features
|
|
66
107
|
* logs (success/failed) deployment to associated redmine repository
|
|
108
|
+
* verifies redmine access / configuration (`redmine:verify`)
|
|
67
109
|
|
|
68
110
|
-----
|
|
69
111
|
|
data/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Capistrano::Redmine::Deployment - CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - 2026-07-10
|
|
4
|
+
* **[add]** `redmine:verify` capistrano task to verify config & access by receiving a single deployment
|
|
5
|
+
* **[add]** `Client#receive_deployment` (and `Client.receive_deployment`) - GETs a single deployment entry; an empty (no entries) 2xx response is still treated as valid
|
|
6
|
+
* **[add]** rspec test suite for `Client#receive_deployment`
|
|
7
|
+
|
|
8
|
+
## [1.1.0] - 2026-07-10
|
|
9
|
+
* **[add]** ENV layer for config (`REDMINE_API_KEY`, `REDMINE_HOST`, `REDMINE_PROJECT`, `REDMINE_REPOSITORY`, `REDMINE_CA_FILE`)
|
|
10
|
+
* **[add]** `:redmine_api_key` capistrano variable, with `:redmine_api_key_command` (command stdout, e.g. macOS keychain) as fallback
|
|
11
|
+
* **[add]** `:redmine_ca_file` config (and `REDMINE_CA_FILE` ENV) as a safe alternative to disabling host verification
|
|
12
|
+
* **[add]** rspec test suite for `Config.resolve` layer precedence
|
|
13
|
+
* **[ref]** config resolution order: capistrano is the base, then `.redmine` files, then ENV win over it
|
|
14
|
+
* **[fix]** enable SSL by URI scheme (`https`) instead of only port 443, so HTTPS on non-standard ports works
|
|
15
|
+
|
|
3
16
|
## [1.0.1] - 2025-10-24
|
|
4
17
|
* **[add]** config for `host_verification` to skip issues with OpenSSL / CRL issues
|
|
5
18
|
* **[add]** warning message during deployment while host verification is disabled
|
|
@@ -7,37 +7,105 @@ require 'openssl'
|
|
|
7
7
|
module Capistrano
|
|
8
8
|
module Redmine
|
|
9
9
|
module Deployment
|
|
10
|
+
# HTTP layer that talks to the Redmine-side +redmine_deployment+ plugin.
|
|
11
|
+
#
|
|
12
|
+
# The client POSTs deployment receipts to, and GETs deployment entries
|
|
13
|
+
# from, +{host}/projects/{project}/deploy/{repository}.json+, authenticating
|
|
14
|
+
# with an +X-Redmine-API-Key+ header. A response carrying a +deployment+ key
|
|
15
|
+
# is treated as success; anything else (or an +errors+ array) is a failure.
|
|
16
|
+
#
|
|
17
|
+
# All connection details (host, project, repository, api_key, ca_file,
|
|
18
|
+
# host_verification) are read from the supplied {Config}. SSL is enabled
|
|
19
|
+
# automatically when the host URI uses the +https+ scheme. Console logging
|
|
20
|
+
# is on by default and can be turned off via {#silent!} or the +logging:+
|
|
21
|
+
# initializer flag.
|
|
22
|
+
#
|
|
23
|
+
# @example Log a successful deployment
|
|
24
|
+
# Client.deploy_success!(config, from_revision: 'abc', to_revision: 'def')
|
|
25
|
+
#
|
|
26
|
+
# @example Verify connectivity without writing anything
|
|
27
|
+
# Client.receive_deployment(config) # => {"id"=>7, ...}, nil, or false
|
|
10
28
|
class Client
|
|
11
29
|
|
|
30
|
+
# @return [Config] the resolved configuration this client reads from.
|
|
12
31
|
attr_reader :config
|
|
13
32
|
|
|
14
33
|
class << self
|
|
34
|
+
# Logs a successful deployment.
|
|
35
|
+
#
|
|
36
|
+
# Tags the deployment as +'success'+ and POSTs it to Redmine via a
|
|
37
|
+
# fresh instance.
|
|
38
|
+
#
|
|
39
|
+
# @param config [Config] the resolved configuration.
|
|
40
|
+
# @param deployment [Hash] the deployment payload (mutated in place to
|
|
41
|
+
# set +:result+).
|
|
42
|
+
# @return [Boolean] +true+ when Redmine accepted the deployment.
|
|
15
43
|
def deploy_success!(config, deployment)
|
|
16
44
|
deployment[:result] = 'success'
|
|
17
45
|
|
|
18
46
|
new(config).deploy!(deployment)
|
|
19
47
|
end
|
|
20
48
|
|
|
49
|
+
# Logs a failed deployment.
|
|
50
|
+
#
|
|
51
|
+
# Tags the deployment as +'fail'+ and POSTs it to Redmine via a fresh
|
|
52
|
+
# instance.
|
|
53
|
+
#
|
|
54
|
+
# @param config [Config] the resolved configuration.
|
|
55
|
+
# @param deployment [Hash] the deployment payload (mutated in place to
|
|
56
|
+
# set +:result+).
|
|
57
|
+
# @return [Boolean] +true+ when Redmine accepted the deployment.
|
|
21
58
|
def deploy_fail!(config, deployment)
|
|
22
59
|
deployment[:result] = 'fail'
|
|
23
60
|
|
|
24
61
|
new(config).deploy!(deployment)
|
|
25
62
|
end
|
|
63
|
+
|
|
64
|
+
# Fetches a single deployment entry to verify connectivity.
|
|
65
|
+
#
|
|
66
|
+
# Convenience wrapper that builds a fresh instance and delegates to
|
|
67
|
+
# {#receive_deployment}.
|
|
68
|
+
#
|
|
69
|
+
# @param config [Config] the resolved configuration.
|
|
70
|
+
# @return [Hash, nil, false] the deployment hash, +nil+ when the
|
|
71
|
+
# endpoint is reachable but empty, or +false+ on a failed request.
|
|
72
|
+
def receive_deployment(config)
|
|
73
|
+
new(config).receive_deployment
|
|
74
|
+
end
|
|
26
75
|
end
|
|
27
76
|
|
|
77
|
+
# @param config [Config] the resolved configuration to read connection
|
|
78
|
+
# details and credentials from.
|
|
79
|
+
# @param logging [Boolean] whether to print progress to STDOUT
|
|
80
|
+
# (default +true+). See {#silent!}.
|
|
28
81
|
def initialize(config, logging: true)
|
|
29
82
|
@config = config
|
|
30
83
|
@logging = logging
|
|
31
84
|
end
|
|
32
85
|
|
|
86
|
+
# Disables console logging for this client.
|
|
87
|
+
#
|
|
88
|
+
# @return [false]
|
|
33
89
|
def silent!
|
|
34
90
|
@logging = false
|
|
35
91
|
end
|
|
36
92
|
|
|
93
|
+
# @return [Boolean] whether console logging is currently enabled.
|
|
37
94
|
def log?
|
|
38
95
|
@logging
|
|
39
96
|
end
|
|
40
97
|
|
|
98
|
+
# POSTs a deployment receipt to Redmine.
|
|
99
|
+
#
|
|
100
|
+
# Logs the outgoing deployment, sends it, and reports the outcome. A
|
|
101
|
+
# response containing a +deployment+ key counts as success; anything
|
|
102
|
+
# else is logged as an error.
|
|
103
|
+
#
|
|
104
|
+
# @param deployment [Hash] the deployment payload. Recognized keys
|
|
105
|
+
# include +:result+, +:from_revision+, +:to_revision+, +:environment+,
|
|
106
|
+
# +:branch+ and +:servers+.
|
|
107
|
+
# @return [Boolean] +true+ when Redmine created the deployment,
|
|
108
|
+
# +false+ otherwise.
|
|
41
109
|
def deploy!(deployment)
|
|
42
110
|
log_deploy(deployment) if log?
|
|
43
111
|
|
|
@@ -54,20 +122,107 @@ module Capistrano
|
|
|
54
122
|
end
|
|
55
123
|
end
|
|
56
124
|
|
|
125
|
+
# Receives a single deployment entry from the redmine repository.
|
|
126
|
+
#
|
|
127
|
+
# Used to verify that the configured credentials / host / project /
|
|
128
|
+
# repository actually resolve to a reachable redmine_deployment endpoint.
|
|
129
|
+
# A successful response with NO entries is still considered valid - it
|
|
130
|
+
# just means no deployment has been logged yet.
|
|
131
|
+
#
|
|
132
|
+
# @return [Hash] the single deployment entry when one exists.
|
|
133
|
+
# @return [nil] when the endpoint is reachable but has no entries yet.
|
|
134
|
+
# @return [false] when the request itself failed (non-2xx / errors).
|
|
135
|
+
def receive_deployment
|
|
136
|
+
response = fetch_deployments
|
|
137
|
+
|
|
138
|
+
return false unless response.is_a?(Hash)
|
|
139
|
+
return false if response['errors']
|
|
140
|
+
|
|
141
|
+
extract_deployment(response)
|
|
142
|
+
end
|
|
143
|
+
|
|
57
144
|
private
|
|
58
145
|
|
|
146
|
+
# Builds and performs the POST request carrying the deployment payload.
|
|
147
|
+
#
|
|
148
|
+
# @param deployment [Hash] the deployment payload to serialize as JSON.
|
|
149
|
+
# @return [Hash, String] the parsed JSON response, or the raw body when
|
|
150
|
+
# it is not valid JSON.
|
|
59
151
|
def send_deployment(deployment)
|
|
60
|
-
uri =
|
|
61
|
-
|
|
62
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
63
|
-
http.use_ssl = true if uri.port == 443
|
|
64
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.host_verification == false
|
|
152
|
+
uri = deploy_uri
|
|
65
153
|
|
|
66
154
|
request = Net::HTTP::Post.new(uri.request_uri)
|
|
67
155
|
request["Content-Type"] = "application/json"
|
|
68
156
|
request['X-Redmine-API-Key'] = config.api_key
|
|
69
157
|
request.body = { deployment: deployment }.to_json
|
|
70
158
|
|
|
159
|
+
perform(uri, request)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Builds and performs the GET request that fetches deployment entries.
|
|
163
|
+
#
|
|
164
|
+
# Requests only a single entry (+limit=1+) since that is all that is
|
|
165
|
+
# needed to confirm access.
|
|
166
|
+
#
|
|
167
|
+
# @return [Hash, String] the parsed JSON response, or the raw body when
|
|
168
|
+
# it is not valid JSON.
|
|
169
|
+
def fetch_deployments
|
|
170
|
+
uri = deploy_uri
|
|
171
|
+
# only a single entry is needed to verify access
|
|
172
|
+
uri.query = 'limit=1'
|
|
173
|
+
|
|
174
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
|
175
|
+
request["Content-Type"] = "application/json"
|
|
176
|
+
request['X-Redmine-API-Key'] = config.api_key
|
|
177
|
+
|
|
178
|
+
perform(uri, request)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Pulls a single deployment out of an index/show response. The
|
|
182
|
+
# redmine_deployment plugin may return either a single +deployment+
|
|
183
|
+
# object or a +deployments+ collection - an empty collection is fine.
|
|
184
|
+
#
|
|
185
|
+
# @param response [Object] the parsed response body.
|
|
186
|
+
# @return [Hash] the +deployment+ object, or the first entry of a
|
|
187
|
+
# +deployments+ collection.
|
|
188
|
+
# @return [nil] when +response+ is not a hash, or when a +deployments+
|
|
189
|
+
# collection is empty.
|
|
190
|
+
def extract_deployment(response)
|
|
191
|
+
return nil unless response.is_a?(Hash)
|
|
192
|
+
|
|
193
|
+
if response['deployment']
|
|
194
|
+
response['deployment']
|
|
195
|
+
elsif response['deployments']
|
|
196
|
+
response['deployments'].first
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# @return [URI::Generic] the deploy endpoint URI for the configured
|
|
201
|
+
# host / project / repository.
|
|
202
|
+
def deploy_uri
|
|
203
|
+
URI("#{config.host}/projects/#{config.project}/deploy/#{config.repository}.json")
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Performs an HTTP request, applying SSL and host-verification settings.
|
|
207
|
+
#
|
|
208
|
+
# SSL is enabled when the URI scheme is +https+. A configured +ca_file+
|
|
209
|
+
# takes precedence; otherwise +host_verification == false+ disables peer
|
|
210
|
+
# verification (the LetsEncrypt/CRL workaround).
|
|
211
|
+
#
|
|
212
|
+
# @param uri [URI::Generic] the request target (supplies host/port/scheme).
|
|
213
|
+
# @param request [Net::HTTPRequest] the prepared request to send.
|
|
214
|
+
# @return [Hash, Array, String] the parsed JSON response, or the raw body
|
|
215
|
+
# when it is not valid JSON.
|
|
216
|
+
def perform(uri, request)
|
|
217
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
218
|
+
http.use_ssl = true if uri.scheme == 'https'
|
|
219
|
+
|
|
220
|
+
if config.ca_file
|
|
221
|
+
http.ca_file = config.ca_file
|
|
222
|
+
elsif config.host_verification == false
|
|
223
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
224
|
+
end
|
|
225
|
+
|
|
71
226
|
response = http.request(request)
|
|
72
227
|
|
|
73
228
|
begin
|
|
@@ -77,9 +232,14 @@ module Capistrano
|
|
|
77
232
|
end
|
|
78
233
|
end
|
|
79
234
|
|
|
235
|
+
# Prints the outgoing deployment details (and any SSL warnings) to STDOUT.
|
|
236
|
+
#
|
|
237
|
+
# @param deployment [Hash] the deployment payload being sent.
|
|
238
|
+
# @return [void]
|
|
80
239
|
def log_deploy(deployment)
|
|
81
240
|
puts "Sending deployment information to #{config.host} (project: '#{config.project}' | repo: '#{config.repository}')"
|
|
82
|
-
puts "\e[33m
|
|
241
|
+
puts "\e[33m Using custom CA file: #{config.ca_file}\e[0m" if config.ca_file
|
|
242
|
+
puts "\e[33m WARNING: Host verification disabled!\e[0m" if config.ca_file.nil? && config.host_verification == false
|
|
83
243
|
puts ""
|
|
84
244
|
puts " Commits......: #{deployment[:from_revision]} ... #{deployment[:to_revision]}"
|
|
85
245
|
puts " Environment..: #{deployment[:environment] || '-'}"
|
|
@@ -89,11 +249,20 @@ module Capistrano
|
|
|
89
249
|
puts ""
|
|
90
250
|
end
|
|
91
251
|
|
|
252
|
+
# Prints a success line naming the created deployment id.
|
|
253
|
+
#
|
|
254
|
+
# @param response [Hash] the parsed success response.
|
|
255
|
+
# @return [void]
|
|
92
256
|
def log_deploy_done(response)
|
|
93
257
|
puts "\e[32mSuccessfully created deployment ##{response['deployment']['id']}\e[0m"
|
|
94
258
|
puts ""
|
|
95
259
|
end
|
|
96
260
|
|
|
261
|
+
# Prints a failure line, listing +errors+ when present or the raw
|
|
262
|
+
# response otherwise.
|
|
263
|
+
#
|
|
264
|
+
# @param response [Object] the parsed failure response.
|
|
265
|
+
# @return [void]
|
|
97
266
|
def log_deploy_errors(response)
|
|
98
267
|
if response['errors']
|
|
99
268
|
puts "\e[31mFailed to created deployment: #{response['errors'].join(', ')}\e[0m"
|
|
@@ -106,5 +275,3 @@ module Capistrano
|
|
|
106
275
|
end
|
|
107
276
|
end
|
|
108
277
|
end
|
|
109
|
-
|
|
110
|
-
|
|
@@ -14,7 +14,7 @@ module Capistrano
|
|
|
14
14
|
# build new empty config
|
|
15
15
|
config = new
|
|
16
16
|
|
|
17
|
-
#
|
|
17
|
+
# capistrano is the base - files and ENV win over it
|
|
18
18
|
config.assign!(config_from_capistrano(capistrano)) if capistrano
|
|
19
19
|
|
|
20
20
|
# try to resolve from current PWD
|
|
@@ -22,23 +22,58 @@ module Capistrano
|
|
|
22
22
|
config.assign!(config_from_file(File.join(ENV['HOME'], '.redmine')))
|
|
23
23
|
config.assign!(config_from_file(file)) if file
|
|
24
24
|
|
|
25
|
+
# ENV wins over files and capistrano
|
|
26
|
+
config.assign!(config_from_env)
|
|
27
|
+
|
|
25
28
|
config
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def config_from_capistrano(capistrano)
|
|
29
32
|
config = {
|
|
33
|
+
api_key: capistrano.fetch(:redmine_api_key) || api_key_from_command(capistrano),
|
|
30
34
|
host: capistrano.fetch(:redmine_host),
|
|
31
35
|
project: capistrano.fetch(:redmine_project) || capistrano.fetch(:redmine_project_id),
|
|
32
36
|
repository: capistrano.fetch(:redmine_repository),
|
|
33
|
-
host_verification: capistrano.fetch(:redmine_host_verification)
|
|
37
|
+
host_verification: capistrano.fetch(:redmine_host_verification),
|
|
38
|
+
ca_file: capistrano.fetch(:redmine_ca_file)
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
new(config)
|
|
37
42
|
end
|
|
38
43
|
|
|
44
|
+
# Resolves config from ENV variables. Values are filtered by `assign!`,
|
|
45
|
+
# so unset (nil/empty) variables never overwrite an existing value.
|
|
46
|
+
def config_from_env
|
|
47
|
+
new({
|
|
48
|
+
api_key: ENV['REDMINE_API_KEY'],
|
|
49
|
+
host: ENV['REDMINE_HOST'],
|
|
50
|
+
project: ENV['REDMINE_PROJECT'],
|
|
51
|
+
repository: ENV['REDMINE_REPOSITORY'],
|
|
52
|
+
ca_file: ENV['REDMINE_CA_FILE']
|
|
53
|
+
})
|
|
54
|
+
end
|
|
55
|
+
|
|
39
56
|
def config_from_file(file)
|
|
40
57
|
new(file: file)
|
|
41
58
|
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# Resolves the api_key by running the `:redmine_api_key_command` capistrano
|
|
63
|
+
# variable (if set) and taking its stdout. This keeps the secret out of
|
|
64
|
+
# `deploy.rb` - only the command (e.g. a keychain lookup) lives there.
|
|
65
|
+
#
|
|
66
|
+
# On any failure (non-zero exit or empty stdout) nil is returned so the
|
|
67
|
+
# file/ENV fallback still applies.
|
|
68
|
+
def api_key_from_command(capistrano)
|
|
69
|
+
cmd = capistrano.fetch(:redmine_api_key_command)
|
|
70
|
+
return nil unless cmd && cmd != ''
|
|
71
|
+
|
|
72
|
+
key = `#{cmd}`.strip
|
|
73
|
+
return nil unless $?.success? && !key.empty?
|
|
74
|
+
|
|
75
|
+
key
|
|
76
|
+
end
|
|
42
77
|
end
|
|
43
78
|
|
|
44
79
|
def initialize(config = {}, file: nil)
|
|
File without changes
|
|
@@ -24,6 +24,11 @@ Configure the redmine credentials for deployment.
|
|
|
24
24
|
puts ""
|
|
25
25
|
puts " > HINT: Keep configs empty to prevent to overwrite existing configs."
|
|
26
26
|
puts ""
|
|
27
|
+
puts " > ALTERNATIVE (recommended): the api_key can also be provided without this file"
|
|
28
|
+
puts " via the ENV variable 'REDMINE_API_KEY' or the capistrano variable"
|
|
29
|
+
puts " ':redmine_api_key_command' (e.g. a macOS keychain lookup). Both win over"
|
|
30
|
+
puts " the '.redmine' file. See the README for details."
|
|
31
|
+
puts ""
|
|
27
32
|
puts "******************************************************************************************************"
|
|
28
33
|
puts ""
|
|
29
34
|
puts ""
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
namespace :capistrano do
|
|
2
|
+
namespace :redmine do
|
|
3
|
+
namespace :deployment do
|
|
4
|
+
desc <<-END_DESC
|
|
5
|
+
Verify the redmine credentials & access for deployment.
|
|
6
|
+
END_DESC
|
|
7
|
+
|
|
8
|
+
task :verify do
|
|
9
|
+
require 'capistrano/redmine/deployment/config'
|
|
10
|
+
require 'capistrano/redmine/deployment/client'
|
|
11
|
+
|
|
12
|
+
# resolve configs (from ENV / .redmine file - no capistrano context here)
|
|
13
|
+
config = Capistrano::Redmine::Deployment::Config.resolve
|
|
14
|
+
|
|
15
|
+
puts "******************************************************************************************************"
|
|
16
|
+
puts "== Capistrano::Redmine::Deployment - verify =="
|
|
17
|
+
puts "******************************************************************************************************"
|
|
18
|
+
puts ""
|
|
19
|
+
puts " This task verifies your redmine deployment configuration & access."
|
|
20
|
+
puts " It receives a single deployment entry from the associated redmine repository."
|
|
21
|
+
puts ""
|
|
22
|
+
puts " Shared settings are resolved from ENV or the '.redmine' file (host, project,"
|
|
23
|
+
puts " repository, api_key). Run 'rake capistrano:redmine:deployment:setup' to configure them."
|
|
24
|
+
puts ""
|
|
25
|
+
puts "******************************************************************************************************"
|
|
26
|
+
puts ""
|
|
27
|
+
puts ""
|
|
28
|
+
|
|
29
|
+
# -- BEGIN over here ...
|
|
30
|
+
|
|
31
|
+
unless config.valid?
|
|
32
|
+
puts "\e[31mYour redmine configuration is missing or unfinished.\e[0m"
|
|
33
|
+
puts "Run 'rake capistrano:redmine:deployment:setup' to configure the credentials."
|
|
34
|
+
puts ""
|
|
35
|
+
puts "******************************************************************************************************"
|
|
36
|
+
puts ""
|
|
37
|
+
next
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# An empty result (no deployments logged yet) is fine - `false` means the
|
|
41
|
+
# request itself failed (unreachable host, bad credentials, wrong project/repo).
|
|
42
|
+
result = Capistrano::Redmine::Deployment::Client.receive_deployment(config)
|
|
43
|
+
|
|
44
|
+
puts ""
|
|
45
|
+
if result == false
|
|
46
|
+
puts "\e[31mVerification FAILED. Check host, project, repository and api_key.\e[0m"
|
|
47
|
+
else
|
|
48
|
+
puts "\e[32mVerification succeeded.\e[0m"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
puts ""
|
|
52
|
+
puts "******************************************************************************************************"
|
|
53
|
+
puts ""
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: capistrano-redmine-deployment
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tobias Gonsior
|
|
@@ -19,6 +19,7 @@ extensions: []
|
|
|
19
19
|
extra_rdoc_files: []
|
|
20
20
|
files:
|
|
21
21
|
- ".gitignore"
|
|
22
|
+
- ".rspec"
|
|
22
23
|
- Gemfile
|
|
23
24
|
- LICENSE.txt
|
|
24
25
|
- README.md
|
|
@@ -35,6 +36,7 @@ files:
|
|
|
35
36
|
- lib/capistrano/redmine/deployment/railtie.rb
|
|
36
37
|
- lib/capistrano/redmine/deployment/receipts.rb
|
|
37
38
|
- lib/capistrano/redmine/deployment/tasks/setup.rake
|
|
39
|
+
- lib/capistrano/redmine/deployment/tasks/verify.rake
|
|
38
40
|
- lib/capistrano/redmine/deployment/version.rb
|
|
39
41
|
homepage: https://github.com/ruby-smart/capistrano-redmine-deployment
|
|
40
42
|
licenses:
|