bcnd 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/README.md +131 -0
- data/Rakefile +7 -0
- data/bin/bcnd +4 -0
- data/lib/bcnd.rb +6 -0
- data/lib/bcnd/ci.rb +97 -0
- data/lib/bcnd/quay_io.rb +111 -0
- data/lib/bcnd/runner.rb +58 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 703e6c4a7228ffff25568739b2f1e18d57ffb086
|
4
|
+
data.tar.gz: 8977b5932bd0bc0ed00cb7063af63f46e30f44c9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4fe701eeb0cd98f0985bd6dd9adc8fc725f9a92c8263524ce99c16815afe37e65b430080d4eff619b237b247a644ed3cfd70b6a3156baf71f1082d8a41feba7a
|
7
|
+
data.tar.gz: 4f74ad4442c5e3d867f34bca44bb6d77c898797c9dcb921fc789d1689b8fea463c785fa0c3d61a8879aa8171f858e0b71a22cf098fd59bd5c41bbc668d6e8419
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Barcelona Deployer
|
2
|
+
|
3
|
+
Degica's opinionated deploy pipeline tool
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```
|
8
|
+
gem 'bcnd'
|
9
|
+
```
|
10
|
+
|
11
|
+
## Prerequisites
|
12
|
+
|
13
|
+
bcnd is very opinionated about your development configurations.
|
14
|
+
So there are lots of requirements you need to prepare beforehand.
|
15
|
+
|
16
|
+
### Quay.io
|
17
|
+
|
18
|
+
You need quay.io as your docker image registry
|
19
|
+
|
20
|
+
### Barcelona
|
21
|
+
|
22
|
+
where your applications will be deployed
|
23
|
+
|
24
|
+
### GitHub
|
25
|
+
|
26
|
+
bcnd only supports GitHub as a source code repository.
|
27
|
+
Your GitHub repository must have 2 branches: mainline and stable
|
28
|
+
bcnd doesn't offer a customizability for the branch names.
|
29
|
+
|
30
|
+
#### mainline branch
|
31
|
+
|
32
|
+
a mainline branch includes all reviewed commits. By default mainline branch is `master`.
|
33
|
+
bcnd automatically deploys mainline branch to your mainline environment.
|
34
|
+
|
35
|
+
#### stable branch
|
36
|
+
|
37
|
+
a stable branch includes stable commits. By default stable branch is `production`.
|
38
|
+
bcnd automatically deploys stable branch to your stable environment.
|
39
|
+
|
40
|
+
### Webhooks
|
41
|
+
|
42
|
+
- webhook for quay.io automated builds
|
43
|
+
- webhook for your CI service
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
Setup your CI configuration as follows:
|
48
|
+
|
49
|
+
- Install barcelona client
|
50
|
+
- Install bcnd
|
51
|
+
- Execute `bcnd` in your CI's "after success" script
|
52
|
+
|
53
|
+
Here's the example for travis CI
|
54
|
+
|
55
|
+
```yml
|
56
|
+
before_script:
|
57
|
+
- npm install -g barcelona
|
58
|
+
- gem install bcnd
|
59
|
+
script:
|
60
|
+
- bundle excec rspec
|
61
|
+
- bcnd
|
62
|
+
```
|
63
|
+
|
64
|
+
### Environment variables
|
65
|
+
|
66
|
+
Set the following environment variables to your CI build environment:
|
67
|
+
|
68
|
+
- `QUAY_TOKEN`
|
69
|
+
- **Required**
|
70
|
+
- a quay.io oauth token. you can create a new oauth token at quay.io organization page -> OAuth Applications -> Create New Application -> Generate Token
|
71
|
+
- `GITHUB_TOKEN`
|
72
|
+
- **Conditional** If you have stable branch this is required.
|
73
|
+
- a github oauth token which has a permission to read your application's repository
|
74
|
+
- `BARCELONA_ENDPOINT`
|
75
|
+
- **Required**
|
76
|
+
- A URL of Barcelona service e.g. `https://barcelona.your-domain.com`
|
77
|
+
- `MAINLINE_HERITAGE_TOKEN`
|
78
|
+
- **Required**
|
79
|
+
- Barcelona's heritage token for the mainline application
|
80
|
+
- `STABLE_HERITAGE_TOKEN`
|
81
|
+
- **Conditional** If you have stable branch this is required.
|
82
|
+
- Barcelona's heritage token for the stable application
|
83
|
+
- `QUAY_REPOSITORY`
|
84
|
+
- **Optional**
|
85
|
+
- A name of your quay repository. It should be `[organization]/[repo name]`. If you don't set this variable bcnd uses github repository name as a quay repository name.
|
86
|
+
|
87
|
+
### Configurations
|
88
|
+
|
89
|
+
You can customize mainline branch name, mainline environment(where mainline code is deployed), stable branch name, and stable environment with `barcelona.yml`
|
90
|
+
|
91
|
+
```yaml
|
92
|
+
# /barcelona.yml example
|
93
|
+
---
|
94
|
+
environments:
|
95
|
+
# your barcelona configurations. bcnd doesn't touch this.
|
96
|
+
bcnd:
|
97
|
+
mainline_branch: dev # default: master
|
98
|
+
mainline_environment: dev # default: staging
|
99
|
+
stable_branch: master # default: production
|
100
|
+
stable_environment: master # default: production
|
101
|
+
```
|
102
|
+
|
103
|
+
## How It Works
|
104
|
+
|
105
|
+
### Deploying to a staging environment
|
106
|
+
|
107
|
+
When a commit is pushed into a mainline branch, bcnd deploys your application to barcelona's mainline environment.
|
108
|
+
Here's what bcnd actually does:
|
109
|
+
|
110
|
+
- Wait for quay.io automated build to be finished
|
111
|
+
- Attach a docker image tag to the `latest` docker image.
|
112
|
+
- The tag is the latest mainline branch's commit hash
|
113
|
+
- Call Barcelona deploy API with the tag name
|
114
|
+
- `bcn deploy -e [mainline env] --tag [git_commit_hash]`
|
115
|
+
|
116
|
+
### Deploying to a production environment
|
117
|
+
|
118
|
+
When a commit is pushed into a stable branch, bcnd deploys your application to barcelona's stable environment
|
119
|
+
Here's what bcnd actually does:
|
120
|
+
|
121
|
+
- Compare `master` and `production` branch
|
122
|
+
- If there is a difference between the branches, bcnd raises an error
|
123
|
+
- Get master branch's latest commit hash
|
124
|
+
- Find a docker image from quay.io with the tag of the master commit hash
|
125
|
+
- Call Barcelona deploy API with the tag name
|
126
|
+
- `bcn deploy -e [stable env] --tag [git_commit_hash]`
|
127
|
+
|
128
|
+
|
129
|
+
## LICENSE
|
130
|
+
|
131
|
+
MIT
|
data/Rakefile
ADDED
data/bin/bcnd
ADDED
data/lib/bcnd.rb
ADDED
data/lib/bcnd/ci.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module Bcnd
|
3
|
+
class CI
|
4
|
+
DEFAULT_CONFIG = {
|
5
|
+
"mainline_branch" => "master",
|
6
|
+
"mainline_environment" => "staging",
|
7
|
+
"stable_branch" => "production",
|
8
|
+
"stable_environment" => "production"
|
9
|
+
}
|
10
|
+
|
11
|
+
attr_accessor :repository,
|
12
|
+
:commit,
|
13
|
+
:branch,
|
14
|
+
:quay_repository,
|
15
|
+
:quay_token,
|
16
|
+
:github_token,
|
17
|
+
:mainline_heritage_token,
|
18
|
+
:stable_heritage_token,
|
19
|
+
:stage_config
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
load_ci_environment
|
23
|
+
load_stage_config
|
24
|
+
self.quay_token = ENV['QUAY_TOKEN']
|
25
|
+
self.github_token = ENV['GITHUB_TOKEN']
|
26
|
+
self.mainline_heritage_token = ENV['MAINLINE_HERITAGE_TOKEN']
|
27
|
+
self.stable_heritage_token = ENV['STABLE_HERITAGE_TOKEN']
|
28
|
+
self.quay_repository = ENV['QUAY_REPOSITORY'] || self.repository
|
29
|
+
end
|
30
|
+
|
31
|
+
def pull_request?
|
32
|
+
case ci_service
|
33
|
+
when :travis
|
34
|
+
ENV['TRAVIS_PULL_REQUEST'] != 'false'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def ci_service
|
39
|
+
if ENV['TRAVIS']
|
40
|
+
:travis
|
41
|
+
else
|
42
|
+
:unknown
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def mainline_branch
|
47
|
+
stage_config[:mainline][:branch]
|
48
|
+
end
|
49
|
+
|
50
|
+
def stable_branch
|
51
|
+
stage_config[:stable][:branch]
|
52
|
+
end
|
53
|
+
|
54
|
+
def deploy_stage
|
55
|
+
{
|
56
|
+
mainline_branch => :mainline,
|
57
|
+
stable_branch => :stable
|
58
|
+
}[self.branch]
|
59
|
+
end
|
60
|
+
|
61
|
+
def deploy_environment
|
62
|
+
stage_config[deploy_stage][:environment]
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def load_ci_environment
|
68
|
+
case ci_service
|
69
|
+
when :travis
|
70
|
+
self.repository = ENV['TRAVIS_REPO_SLUG']
|
71
|
+
self.commit = ENV['TRAVIS_COMMIT']
|
72
|
+
self.branch = ENV['TRAVIS_BRANCH']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def load_stage_config
|
77
|
+
config = DEFAULT_CONFIG.merge(load_config_file)
|
78
|
+
self.stage_config = {
|
79
|
+
mainline: {
|
80
|
+
branch: config["mainline_branch"],
|
81
|
+
environment: config["mainline_environment"]
|
82
|
+
},
|
83
|
+
stable: {
|
84
|
+
branch: config["stable_branch"],
|
85
|
+
environment: config["stable_environment"]
|
86
|
+
}
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def load_config_file
|
91
|
+
file = File.read('barcelona.yml')
|
92
|
+
YAML.load(file)["bcnd"] || {}
|
93
|
+
rescue Errno::ENOENT => e
|
94
|
+
{}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/bcnd/quay_io.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Bcnd
|
5
|
+
class QuayIo
|
6
|
+
class Connection
|
7
|
+
BASE_URL = 'https://quay.io/api/v1'
|
8
|
+
attr_accessor :token
|
9
|
+
|
10
|
+
def initialize(token)
|
11
|
+
@token = token
|
12
|
+
end
|
13
|
+
|
14
|
+
def request(method: :get, path:, body: {}, query_params: {})
|
15
|
+
response = RestClient::Request.execute(
|
16
|
+
method: method,
|
17
|
+
url: "#{BASE_URL}#{path}",
|
18
|
+
payload: body.empty? ? nil : body.to_json,
|
19
|
+
headers: {
|
20
|
+
"Authorization" => "Bearer #{token}",
|
21
|
+
"Content-Type" => "application/json",
|
22
|
+
params: query_params
|
23
|
+
}
|
24
|
+
)
|
25
|
+
JSON.load(response.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get(path:, body: {}, query_params: {})
|
29
|
+
request(method: :get, path: path, body: body, query_params: query_params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def put(path:, body: {}, query_params: {})
|
33
|
+
request(method: :put, path: path, body: body, query_params: query_params)
|
34
|
+
end
|
35
|
+
|
36
|
+
def post(path:, body: {}, query_params: {})
|
37
|
+
request(method: :post, path: path, body: body, query_params: query_params)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(path:, body: {}, query_params: {})
|
41
|
+
request(method: :delete, path: path, body: body, query_params: query_params)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_accessor :conn
|
46
|
+
|
47
|
+
def initialize(token)
|
48
|
+
@conn = Connection.new(token)
|
49
|
+
end
|
50
|
+
|
51
|
+
def automated_builds_for(repo:, git_sha:)
|
52
|
+
builds = conn.get(path: "/repository/#{repo}/build/")["builds"]
|
53
|
+
builds.select do |b|
|
54
|
+
b["trigger_metadata"]["commit"] == git_sha.downcase
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def automated_build_status(repo:, git_sha:)
|
59
|
+
builds = automated_builds_for(repo: repo, git_sha: git_sha)
|
60
|
+
phases = builds.map { |b| b["phase"] }
|
61
|
+
|
62
|
+
if !phases.include?("complete") && phases.include?("error")
|
63
|
+
return :failed
|
64
|
+
end
|
65
|
+
|
66
|
+
if phases.include?("complete")
|
67
|
+
return :finished
|
68
|
+
else
|
69
|
+
return :building
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def wait_for_automated_build(repo:, git_sha:, timeout: 3600)
|
74
|
+
loop do
|
75
|
+
status = automated_build_status(repo: repo, git_sha: git_sha)
|
76
|
+
case status
|
77
|
+
when :failed
|
78
|
+
raise "The docker build failed"
|
79
|
+
when :finished
|
80
|
+
puts ""
|
81
|
+
return
|
82
|
+
when :building
|
83
|
+
print '.'
|
84
|
+
sleep 5
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def docker_image_id_for_tag(repo:, tag:)
|
90
|
+
resp = conn.get(
|
91
|
+
path: "/repository/#{repo}/tag/",
|
92
|
+
query_params: {
|
93
|
+
"specificTag" => tag
|
94
|
+
}
|
95
|
+
)
|
96
|
+
tags = resp["tags"]
|
97
|
+
tags.find { |tag|
|
98
|
+
tag["end_ts"].nil?
|
99
|
+
}["docker_image_id"]
|
100
|
+
end
|
101
|
+
|
102
|
+
def put_tag(repo:, image_id:, tag:)
|
103
|
+
conn.put(
|
104
|
+
path: "/repository/#{repo}/tag/#{tag}",
|
105
|
+
body: {
|
106
|
+
image: image_id
|
107
|
+
}
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/bcnd/runner.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
|
3
|
+
module Bcnd
|
4
|
+
class Runner
|
5
|
+
attr_accessor :env
|
6
|
+
def initialize
|
7
|
+
self.env = Bcnd::CI.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def deploy
|
11
|
+
return if env.pull_request?
|
12
|
+
|
13
|
+
case env.deploy_stage
|
14
|
+
when :mainline
|
15
|
+
deploy_mainline
|
16
|
+
when :stable
|
17
|
+
deploy_stable
|
18
|
+
else
|
19
|
+
puts "Can't recognize the current stage"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def deploy_mainline
|
26
|
+
quay.wait_for_automated_build(repo: env.quay_repository, git_sha: env.commit)
|
27
|
+
image_id = quay.docker_image_id_for_tag(repo: env.quay_repository, tag: 'latest') # FIXME
|
28
|
+
quay.put_tag(repo: env.quay_repository, image_id: image_id, tag: env.commit)
|
29
|
+
puts "attached tag #{env.commit} to image #{image_id}"
|
30
|
+
bcn_deploy(env.commit, env.mainline_heritage_token)
|
31
|
+
end
|
32
|
+
|
33
|
+
def deploy_stable
|
34
|
+
comp = github.compare(env.repository, env.mainline_branch, env.stable_branch)
|
35
|
+
tag = comp.merge_base_commit.sha
|
36
|
+
image_id = quay.docker_image_id_for_tag(repo: env.quay_repository, tag: tag)
|
37
|
+
raise "There is no docker image to be deployed" unless image_id
|
38
|
+
|
39
|
+
bcn_deploy(tag, env.stable_heritage_token)
|
40
|
+
end
|
41
|
+
|
42
|
+
def quay
|
43
|
+
@quay ||= Bcnd::QuayIo.new(env.quay_token)
|
44
|
+
end
|
45
|
+
|
46
|
+
def github
|
47
|
+
@github ||= Octokit::Client.new(access_token: env.github_token)
|
48
|
+
end
|
49
|
+
|
50
|
+
def bcn_deploy(tag, token)
|
51
|
+
system "bcn deploy -e #{env.deploy_environment} --tag #{tag} --heritage-token #{token} 1> /dev/null"
|
52
|
+
puts "deploy triggered with tag #{tag} to #{env.deploy_environment} environment"
|
53
|
+
if $?.exitstatus != 0
|
54
|
+
raise "bcn returned non-zero exitcode #{$?.exitstatus}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bcnd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kazunori Kajihiro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: octokit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rest-client
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.22'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.22'
|
69
|
+
description: Degica's opinionated deployment tool.
|
70
|
+
email:
|
71
|
+
- kkajihiro@degica.com
|
72
|
+
executables:
|
73
|
+
- bcnd
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- bin/bcnd
|
80
|
+
- lib/bcnd.rb
|
81
|
+
- lib/bcnd/ci.rb
|
82
|
+
- lib/bcnd/quay_io.rb
|
83
|
+
- lib/bcnd/runner.rb
|
84
|
+
homepage: https://github.com/degica/bcnd
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.4.5.1
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Degica's opinionated deployment tool
|
108
|
+
test_files: []
|