semaphoreapp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in semaphoreapp.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Alessandro Morandi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # Semaphoreapp
2
+
3
+ A Ruby client for the Semaphore API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'semaphoreapp'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install semaphoreapp
18
+
19
+ ## Configuration
20
+
21
+ To use the [Semaphore API](http://docs.semaphoreapp.com/api), you need an `auth_token`. You can find your token through the Semaphore web interface.
22
+
23
+ To configure the gem with your token, add a line such as:
24
+
25
+ ```ruby
26
+ Semaphoreapp.auth_token = 'Yds3w6o26FLfJTnVK2y9'
27
+ ```
28
+
29
+ The token will be cached, so you will only need to set it once.
30
+
31
+ ## Usage
32
+
33
+ ### Projects
34
+
35
+ To get all the projects associated with your auth token:
36
+
37
+ ```ruby
38
+ projects = Semaphoreapp::Project.all
39
+ ```
40
+
41
+ To get a specific project (based on its name):
42
+
43
+ ```ruby
44
+ project = Semaphoreapp::Project.find_by_name('testapp-sphinx')
45
+ ```
46
+
47
+ Once you have a project, you can get all its branches:
48
+
49
+ ```ruby
50
+ project.get_branches
51
+ ```
52
+
53
+ Note that, due to the way the API works, the following call will return all the branch *statuses* for a given project:
54
+
55
+ ```ruby
56
+ project.branches
57
+ ```
58
+
59
+ ### Branches
60
+
61
+ Note that all the class methods for `Branch` require the hash id of the project to be passed in as a parameter (`3f1004b8343faabda63d441734526c854380ab89` in the examples below).
62
+
63
+ To get a specific branch (based on its name):
64
+
65
+ ```ruby
66
+ branch = Semaphoreapp::Branch.find_by_name('3f1004b8343faabda63d441734526c854380ab89', 'master')
67
+ ```
68
+
69
+ Once you have a branch, you can get its current status:
70
+
71
+ ```ruby
72
+ branch.get_status
73
+ ```
74
+
75
+ or its build history:
76
+
77
+ ```ruby
78
+ branch.get_builds
79
+ ```
80
+
81
+ ## Object model
82
+
83
+ The gem uses classes to represent objects returned by API calls. The classes are:
84
+
85
+ * `Project`: a project Semaphore is running CI for. It may have many `BranchStatus` objects.
86
+ * `BranchStatus`: the status of a branch in a project. It may have a `Commit` object.
87
+ * `Branch`: a branch in a project.
88
+ * `Build`: a CI build. It may have a `Commit` object.
89
+ * `Commit`: a Git commit.
90
+
91
+ ## Tests
92
+
93
+ To run all the specs for this project:
94
+
95
+ ```bash
96
+ $ rake
97
+ ```
98
+
99
+ ## Contributing
100
+
101
+ 1. Fork it
102
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
103
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
104
+ 4. Push to the branch (`git push origin my-new-feature`)
105
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,75 @@
1
+ module Semaphoreapp
2
+ class Api
3
+ class Error < RuntimeError; end
4
+
5
+ BASE_URL = 'https://semaphoreapp.com'
6
+ API_URL = 'api/v1'
7
+
8
+ def self.get_projects(options={})
9
+ set_auth_token(options)
10
+ send_request(projects_url).body
11
+ end
12
+
13
+ def self.get_branches(project_hash_id, options={})
14
+ set_auth_token(options)
15
+ send_request(branches_url(project_hash_id)).body
16
+ end
17
+
18
+ def self.get_branch_history(project_hash_id, id, options={})
19
+ set_auth_token(options)
20
+ response = send_request(branch_history_url(project_hash_id, id, options))
21
+ return response.body, response['pagination']
22
+ end
23
+
24
+ def self.get_branch_status(project_hash_id, id, options={})
25
+ set_auth_token(options)
26
+ send_request(branch_status_url(project_hash_id, id)).body
27
+ end
28
+
29
+ def self.projects_url
30
+ url_with_auth_token("#{API_URL}/projects")
31
+ end
32
+
33
+ def self.branches_url(project_hash_id)
34
+ url_with_auth_token("#{API_URL}/projects/#{project_hash_id}/branches")
35
+ end
36
+
37
+ def self.branch_history_url(project_hash_id, id, options={})
38
+ url_with_auth_token("#{API_URL}/projects/#{project_hash_id}/#{id}", options)
39
+ end
40
+
41
+ def self.branch_status_url(project_hash_id, id)
42
+ url_with_auth_token("#{API_URL}/projects/#{project_hash_id}/#{id}/status")
43
+ end
44
+
45
+ def self.url_with_auth_token(url, options={})
46
+ "/#{url}?auth_token=#{Semaphoreapp.auth_token}" << begin
47
+ options[:page].nil? ? '' : "&page=#{options[:page]}"
48
+ end
49
+ end
50
+
51
+
52
+ private
53
+
54
+ def self.send_request url
55
+ https = setup_https(BASE_URL)
56
+ https.start do |session|
57
+ puts url if Semaphoreapp.debug?
58
+ req = Net::HTTP::Get.new(url)
59
+ session.request(req)
60
+ end
61
+ end
62
+
63
+ def self.setup_https(endpoint)
64
+ url = URI.parse(endpoint)
65
+ https = Net::HTTP.new(url.host, url.port)
66
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE
67
+ https.use_ssl = true
68
+ return https
69
+ end
70
+
71
+ def self.set_auth_token(options)
72
+ Semaphoreapp.auth_token = options[:auth_token] unless options[:auth_token].nil?
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,28 @@
1
+ module Semaphoreapp
2
+ class Base < OpenStruct
3
+
4
+ def self.build(source, *params)
5
+ if source.is_a?(Hash)
6
+ build_from_hash(source, *params)
7
+ elsif source.is_a?(Array)
8
+ build_from_array(source, *params)
9
+ end
10
+ end
11
+
12
+
13
+ private
14
+
15
+ def self.build_from_hash(source_hash, &block)
16
+ self.new(
17
+ source_hash.dup.tap do |hash|
18
+ block.call(hash) unless block.nil?
19
+ end
20
+ )
21
+ end
22
+
23
+ def self.build_from_array(source_array, *params)
24
+ source_array.map{ |source_item| self.build_from_hash(source_item, *params) }
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ module Semaphoreapp
2
+ class Branch < Base
3
+
4
+ def get_status
5
+ Semaphoreapp::BranchStatus.build( Semaphoreapp::JsonApi.get_branch_status(project_hash_id, id) )
6
+ end
7
+
8
+ # The only supported option is ':page => <number>', with <number> defaulting to 1 if not present“
9
+ def get_builds(options={})
10
+ history = Semaphoreapp::JsonApi.get_branch_history(project_hash_id, id, options)
11
+ Semaphoreapp::Build.build(history['builds'])
12
+ end
13
+
14
+ def self.find(project_hash_id, branch_id)
15
+ all_by_project_hash_id(project_hash_id).find{ |branch| branch.id.to_s == branch_id.to_s }
16
+ end
17
+
18
+ def self.find_by_name(project_hash_id, name)
19
+ all_by_project_hash_id(project_hash_id).find{ |branch| branch.name == name }
20
+ end
21
+
22
+ def self.find_master(project_hash_id)
23
+ find_by_name(project_hash_id, 'master')
24
+ end
25
+
26
+ def self.all_by_project_hash_id(project_hash_id)
27
+ build(Semaphoreapp::JsonApi.get_branches(project_hash_id), project_hash_id)
28
+ end
29
+
30
+ def self.build(source, project_hash_id)
31
+ super(source, project_hash_id)
32
+ end
33
+
34
+
35
+ private
36
+
37
+ def self.build_from_hash(branch, project_hash_id)
38
+ super(branch) do |hash|
39
+ hash['project_hash_id'] = project_hash_id
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ module Semaphoreapp
2
+ class BranchStatus < Base
3
+
4
+
5
+ private
6
+
7
+ def self.build_from_hash(branch_status)
8
+ super do |hash|
9
+ hash['commit'] = Semaphoreapp::Commit.build(hash['commit'])
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Semaphoreapp
2
+ class Build < Base
3
+
4
+ private
5
+
6
+ def self.build_from_hash(build)
7
+ super do |hash|
8
+ hash['commit'] = Semaphoreapp::Commit.build(hash['commit'])
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Semaphoreapp
2
+ class Commit < Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ module Semaphoreapp
2
+ class JsonApi < Api
3
+ def self.get_projects(options={})
4
+ raise_if_error(JSON.parse(super))
5
+ end
6
+
7
+ def self.get_branches(project_hash_id, options={})
8
+ raise_if_error(JSON.parse(super))
9
+ end
10
+
11
+ def self.get_branch_history(project_hash_id, id, options={})
12
+ # require "pry"; binding.pry
13
+ raise_if_error(parse(*super))
14
+ end
15
+
16
+ def self.get_branch_status(project_hash_id, id, options={})
17
+ raise_if_error(JSON.parse(super))
18
+ end
19
+
20
+
21
+ private
22
+
23
+ def self.raise_if_error(obj, pagination=nil)
24
+ raise Semaphoreapp::Api::Error, obj['error'] if obj.is_a?(Hash) && obj.has_key?('error')
25
+ return obj, pagination
26
+ end
27
+
28
+ def self.parse(body, pagination)
29
+ require "pry"; binding.pry
30
+ PaginatedArray.new(JSON.parse(pagination), JSON.parse(body))
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Semaphoreapp
2
+ class Project < Base
3
+
4
+ def get_branches
5
+ Semaphoreapp::Branch.all_by_project_hash_id(hash_id)
6
+ end
7
+
8
+ def master_branch_status
9
+ branches.find{ |branch_status| branch_status.branch_name == 'master'}
10
+ end
11
+
12
+ def self.all
13
+ build(Semaphoreapp::JsonApi.get_projects)
14
+ end
15
+
16
+ def self.find(project_hash_id)
17
+ all.find{ |project| project.hash_id == project_hash_id }
18
+ end
19
+
20
+ def self.find_by_name(name)
21
+ all.find{ |project| project.name == name }
22
+ end
23
+
24
+
25
+ private
26
+
27
+ def self.build_from_hash(project)
28
+ super do |hash|
29
+ hash['branches'] = Semaphoreapp::BranchStatus.build(hash['branches'])
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Semaphoreapp
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,38 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'openssl'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'ostruct'
7
+
8
+ require "semaphoreapp/api"
9
+ require "semaphoreapp/base"
10
+ require "semaphoreapp/branch"
11
+ require "semaphoreapp/branch_status"
12
+ require "semaphoreapp/build"
13
+ require "semaphoreapp/commit"
14
+ require "semaphoreapp/json_api"
15
+ require "semaphoreapp/paginated_array"
16
+ require "semaphoreapp/project"
17
+ require "semaphoreapp/version"
18
+
19
+ module Semaphoreapp
20
+ @@debug = false
21
+
22
+ def self.auth_token= auth_token
23
+ @@auth_token = auth_token
24
+ end
25
+
26
+ def self.auth_token
27
+ @@auth_token
28
+ end
29
+
30
+ def self.debug= debug
31
+ @@debug = debug
32
+ end
33
+
34
+ def self.debug?
35
+ @@debug
36
+ end
37
+
38
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/semaphoreapp/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Alessandro Morandi"]
6
+ gem.email = ["webmaster@simbul.net"]
7
+ gem.description = %q{A client for the Semaphore API}
8
+ gem.summary = %q{A client for the Semaphore API}
9
+ gem.homepage = "http://github.com/Simbul/semaphoreapp"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "semaphoreapp"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Semaphoreapp::VERSION
17
+
18
+ gem.add_runtime_dependency 'json'
19
+
20
+ gem.add_development_dependency 'awesome_print'
21
+ gem.add_development_dependency 'pry'
22
+ gem.add_development_dependency 'pry-debugger'
23
+ gem.add_development_dependency 'rspec'
24
+ end
@@ -0,0 +1,42 @@
1
+ [
2
+ {
3
+ "branch_name": "master",
4
+ "branch_url": "http://10.0.1.76:3000/projects/61/branches/85",
5
+ "branch_status_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/85/status?auth_token=Yds3w6o26FLfJTnVK2y9",
6
+ "branch_history_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/85?auth_token=Yds3w6o26FLfJTnVK2y9",
7
+ "project_name": "testapp-sphinx",
8
+ "build_url": "http://10.0.1.76:3000/projects/61/branches/85/builds/1",
9
+ "build_number": 1,
10
+ "result": "passed",
11
+ "started_at": "2012-09-04T11:55:07Z",
12
+ "finished_at": "2012-09-04T12:01:16Z",
13
+ "commit": {
14
+ "id": "a31d32d5de89613369f934eb7d30fbeb08883334",
15
+ "url": "https://github.com/renderedtext/base-app/commit/a31d32d5de89613369f934eb7d30fbeb08883334",
16
+ "author_name": "Vladimir Saric",
17
+ "author_email": "vladimir@renderedtext.com",
18
+ "message": "Update 'shoulda' gem.",
19
+ "timestamp": "2012-10-02T07:00:14Z"
20
+ }
21
+ },
22
+ {
23
+ "branch_name": "testbranch",
24
+ "branch_url": "http://10.0.1.76:3000/projects/61/branches/86",
25
+ "branch_status_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/86/status?auth_token=Yds3w6o26FLfJTnVK2y9",
26
+ "branch_history_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/86?auth_token=Yds3w6o26FLfJTnVK2y9",
27
+ "project_name": "testapp-sphinx",
28
+ "build_url": "http://10.0.1.76:3000/projects/61/branches/86/builds/1",
29
+ "build_number": 1,
30
+ "result": "failed",
31
+ "started_at": "2012-09-05T11:55:07Z",
32
+ "finished_at": "2012-09-05T12:01:16Z",
33
+ "commit": {
34
+ "id": "73fce130ad23f265add5d55ee1da1c23b38f85a4",
35
+ "url": "https://github.com/renderedtext/base-app/commit/73fce130ad23f265add5d55ee1da1c23b38f85a4",
36
+ "author_name": "Marko Anastasov",
37
+ "author_email": "marko@renderedtext.com",
38
+ "message": "Update 'factory_girl_rails' gem and use new short FactoryGirl syntax.",
39
+ "timestamp": "2012-10-02T07:00:14Z"
40
+ }
41
+ }
42
+ ]
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "id": 1324,
4
+ "name": "new-build-page"
5
+ },
6
+ {
7
+ "id": 1120,
8
+ "name": "development"
9
+ },
10
+ {
11
+ "id": 987,
12
+ "name": "branches_api"
13
+ }
14
+ ]
@@ -0,0 +1,32 @@
1
+ [
2
+ {
3
+ "build_url": "http://semaphoreapp.com/projects/57/branches/80/builds/46",
4
+ "build_number": 46,
5
+ "result": "failed",
6
+ "started_at": "2012-10-02T15:01:41Z",
7
+ "finished_at": "2012-10-02T15:03:53Z",
8
+ "commit": {
9
+ "id": "a31d32d5de89613369f934eb7d30fbeb08883334",
10
+ "url": "https://github.com/renderedtext/base-app/commit/a31d32d5de89613369f934eb7d30fbeb08883334",
11
+ "author_name": "Vladimir Saric",
12
+ "author_email": "vladimir@renderedtext.com",
13
+ "message": "Update 'shoulda' gem.",
14
+ "timestamp": "2012-10-02T07:00:14Z"
15
+ }
16
+ },
17
+ {
18
+ "build_url": "http://semaphoreapp.com/projects/57/branches/80/builds/45",
19
+ "build_number": 45,
20
+ "result": "passed",
21
+ "started_at": "2012-10-02T14:47:06Z",
22
+ "finished_at": "2012-10-02T14:51:35Z",
23
+ "commit": {
24
+ "id": "73fce130ad23f265add5d55ee1da1c23b38f85a4",
25
+ "url": "https://github.com/renderedtext/base-app/commit/73fce130ad23f265add5d55ee1da1c23b38f85a4",
26
+ "author_name": "Marko Anastasov",
27
+ "author_email": "marko@renderedtext.com",
28
+ "message": "Update 'factory_girl_rails' gem and use new short FactoryGirl syntax.",
29
+ "timestamp": "2012-10-02T07:00:14Z"
30
+ }
31
+ }
32
+ ]
@@ -0,0 +1,8 @@
1
+ {
2
+ "id": "dc395381e650f3bac18457909880829fc20e34ba",
3
+ "url": "https://github.com/renderedtext/base-app/commit/dc395381e650f3bac18457909880829fc20e34ba",
4
+ "author_name": "Vladimir Saric",
5
+ "author_email": "vladimir@renderedtext.com",
6
+ "message": "Update 'shoulda' gem.",
7
+ "timestamp": "2012-07-04T18:14:08Z"
8
+ }
@@ -0,0 +1,46 @@
1
+ [
2
+ {
3
+ "id": 61,
4
+ "hash_id": "3f1004b8343faabda63d441734526c854380ab89",
5
+ "name": "testapp-sphinx",
6
+ "owner": "renderedtext",
7
+ "created_at": "2012-09-04T11:53:22Z",
8
+ "updated_at": "2012-09-04T12:01:17Z",
9
+ "branches": [
10
+ {
11
+ "branch_name": "master",
12
+ "branch_url": "http://10.0.1.76:3000/projects/61/branches/85",
13
+ "branch_status_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/85/status?auth_token=Yds3w6o26FLfJTnVK2y9",
14
+ "branch_history_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/85?auth_token=Yds3w6o26FLfJTnVK2y9",
15
+ "project_name": "testapp-sphinx",
16
+ "build_url": "http://10.0.1.76:3000/projects/61/branches/85/builds/1",
17
+ "build_number": 1,
18
+ "result": "passed",
19
+ "started_at": "2012-09-04T11:55:07Z",
20
+ "finished_at": "2012-09-04T12:01:16Z"
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "id": 63,
26
+ "hash_id": "649e584dc507ca4b73e1374d3125ef0b567a949c",
27
+ "name": "testapp-mongodb",
28
+ "owner": "renderedtext",
29
+ "created_at": "2012-09-14T10:53:38Z",
30
+ "updated_at": "2012-09-14T11:16:51Z",
31
+ "branches": [
32
+ {
33
+ "branch_name": "mongoid3",
34
+ "branch_url": "http://10.0.1.76:3000/projects/63/branches/89",
35
+ "branch_status_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/85/status?auth_token=Yds3w6o26FLfJTnVK2y9",
36
+ "branch_history_url": "http://semaphoreapp.com/api/v1/projects/3f1004b8343faabda63d441734526c854380ab89/85?auth_token=Yds3w6o26FLfJTnVK2y9",
37
+ "project_name": "testapp-mongodb",
38
+ "build_url": "http://10.0.1.76:3000/projects/63/branches/89/builds/3",
39
+ "build_number": 3,
40
+ "result": "passed",
41
+ "started_at": "2012-09-14T11:11:39Z",
42
+ "finished_at": "2012-09-14T11:16:51Z"
43
+ }
44
+ ]
45
+ }
46
+ ]