gerd 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +115 -0
- data/Guardfile +43 -0
- data/README.md +83 -0
- data/Rakefile +11 -0
- data/bin/gerd +4 -0
- data/gerd.gemspec +33 -0
- data/lib/gerd.rb +6 -0
- data/lib/gerd/audit/audit.rb +79 -0
- data/lib/gerd/audit/builders.rb +0 -0
- data/lib/gerd/cli.rb +74 -0
- data/lib/gerd/exceptions.rb +16 -0
- data/lib/gerd/formatters.rb +39 -0
- data/lib/gerd/github_client.rb +35 -0
- data/lib/gerd/inspections/actions/change_repo_privacy.rb +16 -0
- data/lib/gerd/inspections/actions/create_repo.rb +26 -0
- data/lib/gerd/inspections/actions/create_team.rb +31 -0
- data/lib/gerd/inspections/actions/delete_repo.rb +29 -0
- data/lib/gerd/inspections/actions/delete_team.rb +19 -0
- data/lib/gerd/inspections/actions/update_name_action.rb +19 -0
- data/lib/gerd/inspections/diff.rb +17 -0
- data/lib/gerd/inspections/diffs/organisation.rb +89 -0
- data/lib/gerd/inspections/diffs/repositories.rb +94 -0
- data/lib/gerd/invoke.rb +18 -0
- data/lib/gerd/model/members.rb +0 -0
- data/lib/gerd/model/model.rb +95 -0
- data/lib/gerd/model/organisation.rb +0 -0
- data/lib/gerd/model/repositories.rb +0 -0
- data/lib/gerd/model/teams.rb +0 -0
- data/lib/gerd/validation/organisations/apply.rb +0 -0
- data/lib/gerd/validation/organisations/diff.rb +0 -0
- data/lib/gerd/validation/organisations/introspect.rb +0 -0
- data/lib/gerd/validators.rb +55 -0
- data/lib/gerd/version.rb +4 -0
- data/local.sh +10 -0
- data/spec/model_spec.rb +149 -0
- data/spec/organisation_spec.rb +76 -0
- data/spec/repositories_spec.rb +223 -0
- data/spec/spec_helper.rb +6 -0
- metadata +246 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 983d8728d4f0012f08c44ed368b666c4d1bc6dac
|
4
|
+
data.tar.gz: 6462bf63921e00b4cd299ae3ee2c58e96ab3a431
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87930548690f0c3ecff8027ab358377be85979e4ee8f59de9ebfb37296cb1fe24a3f06d4483439aead3e47717ecf37d0c59a6fc0604b7a0704c8aeef50acd37c
|
7
|
+
data.tar.gz: 2ecad588ed161cec9ce1cabc03d9f92b83a0486431566d7befa8e000bcd959eb6d169b91048d3ce6e965c3142b77fb0fd55b3dbe5deb443fa8de9115dea93f35
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gerd (0.0.1)
|
5
|
+
require_all
|
6
|
+
thor
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.4.0)
|
12
|
+
aruba (0.13.0)
|
13
|
+
childprocess (~> 0.5.6)
|
14
|
+
contracts (~> 0.9)
|
15
|
+
cucumber (>= 1.3.19)
|
16
|
+
ffi (~> 1.9.10)
|
17
|
+
rspec-expectations (>= 2.99)
|
18
|
+
thor (~> 0.19)
|
19
|
+
builder (3.2.2)
|
20
|
+
childprocess (0.5.9)
|
21
|
+
ffi (~> 1.0, >= 1.0.11)
|
22
|
+
coderay (1.1.0)
|
23
|
+
contracts (0.13.0)
|
24
|
+
crack (0.4.3)
|
25
|
+
safe_yaml (~> 1.0.0)
|
26
|
+
cucumber (2.3.2)
|
27
|
+
builder (>= 2.1.2)
|
28
|
+
cucumber-core (~> 1.4.0)
|
29
|
+
cucumber-wire (~> 0.0.1)
|
30
|
+
diff-lcs (>= 1.1.3)
|
31
|
+
gherkin (~> 3.2.0)
|
32
|
+
multi_json (>= 1.7.5, < 2.0)
|
33
|
+
multi_test (>= 0.1.2)
|
34
|
+
cucumber-core (1.4.0)
|
35
|
+
gherkin (~> 3.2.0)
|
36
|
+
cucumber-wire (0.0.1)
|
37
|
+
diff-lcs (1.2.5)
|
38
|
+
ffi (1.9.10)
|
39
|
+
formatador (0.2.5)
|
40
|
+
gherkin (3.2.0)
|
41
|
+
guard (2.13.0)
|
42
|
+
formatador (>= 0.2.4)
|
43
|
+
listen (>= 2.7, <= 4.0)
|
44
|
+
lumberjack (~> 1.0)
|
45
|
+
nenv (~> 0.1)
|
46
|
+
notiffany (~> 0.0)
|
47
|
+
pry (>= 0.9.12)
|
48
|
+
shellany (~> 0.0)
|
49
|
+
thor (>= 0.18.1)
|
50
|
+
guard-compat (1.2.1)
|
51
|
+
guard-rspec (4.6.4)
|
52
|
+
guard (~> 2.1)
|
53
|
+
guard-compat (~> 1.1)
|
54
|
+
rspec (>= 2.99.0, < 4.0)
|
55
|
+
hashdiff (0.3.0)
|
56
|
+
listen (3.0.6)
|
57
|
+
rb-fsevent (>= 0.9.3)
|
58
|
+
rb-inotify (>= 0.9.7)
|
59
|
+
lumberjack (1.0.10)
|
60
|
+
method_source (0.8.2)
|
61
|
+
multi_json (1.11.2)
|
62
|
+
multi_test (0.1.2)
|
63
|
+
nenv (0.3.0)
|
64
|
+
notiffany (0.0.8)
|
65
|
+
nenv (~> 0.1)
|
66
|
+
shellany (~> 0.0)
|
67
|
+
pry (0.10.3)
|
68
|
+
coderay (~> 1.1.0)
|
69
|
+
method_source (~> 0.8.1)
|
70
|
+
slop (~> 3.4)
|
71
|
+
rake (10.5.0)
|
72
|
+
rb-fsevent (0.9.7)
|
73
|
+
rb-inotify (0.9.7)
|
74
|
+
ffi (>= 0.5.0)
|
75
|
+
require_all (1.3.3)
|
76
|
+
rspec (3.4.0)
|
77
|
+
rspec-core (~> 3.4.0)
|
78
|
+
rspec-expectations (~> 3.4.0)
|
79
|
+
rspec-mocks (~> 3.4.0)
|
80
|
+
rspec-core (3.4.2)
|
81
|
+
rspec-support (~> 3.4.0)
|
82
|
+
rspec-expectations (3.4.0)
|
83
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
84
|
+
rspec-support (~> 3.4.0)
|
85
|
+
rspec-mocks (3.4.1)
|
86
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
87
|
+
rspec-support (~> 3.4.0)
|
88
|
+
rspec-support (3.4.1)
|
89
|
+
safe_yaml (1.0.4)
|
90
|
+
shellany (0.0.1)
|
91
|
+
slop (3.6.0)
|
92
|
+
thor (0.19.1)
|
93
|
+
vcr (3.0.1)
|
94
|
+
webmock (1.22.6)
|
95
|
+
addressable (>= 2.3.6)
|
96
|
+
crack (>= 0.3.2)
|
97
|
+
hashdiff
|
98
|
+
|
99
|
+
PLATFORMS
|
100
|
+
ruby
|
101
|
+
|
102
|
+
DEPENDENCIES
|
103
|
+
aruba
|
104
|
+
bundler
|
105
|
+
cucumber
|
106
|
+
gerd!
|
107
|
+
guard
|
108
|
+
guard-rspec
|
109
|
+
rake
|
110
|
+
rspec
|
111
|
+
vcr
|
112
|
+
webmock
|
113
|
+
|
114
|
+
BUNDLED WITH
|
115
|
+
1.11.2
|
data/Guardfile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
#directories %w(lib spec) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
|
27
|
+
guard :rspec, cmd: "bundle exec rspec --color" do
|
28
|
+
require "guard/rspec/dsl"
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
30
|
+
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
32
|
+
|
33
|
+
# RSpec files
|
34
|
+
rspec = dsl.rspec
|
35
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
36
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
37
|
+
watch(rspec.spec_files)
|
38
|
+
|
39
|
+
# Ruby files
|
40
|
+
ruby = dsl.ruby
|
41
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
42
|
+
|
43
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/georgecodes/gerd.png?branch=master)](https://travis-ci.org/georgecodes/gerd)
|
2
|
+
|
3
|
+
# Gerd - Github Herder
|
4
|
+
|
5
|
+
Declaratively manage your Github estate using gerd.
|
6
|
+
|
7
|
+
## Introduction
|
8
|
+
|
9
|
+
Gerd is a tool which declaratively manages your GitHub estate. In a nutshell, it will create a metadata file for you which models a GitHub organisation, and will let you make changes to that organisation in the file, which will then be reflected in GitHub.
|
10
|
+
|
11
|
+
Currently, the only support is for the creation and deletion of repositories, but new features will be added.
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
### Installation
|
16
|
+
|
17
|
+
Gerd is a Ruby gem. Install it using
|
18
|
+
|
19
|
+
$ gem install gerd
|
20
|
+
|
21
|
+
### Configuration
|
22
|
+
|
23
|
+
Gerd needs a GitHub API token to work. You can provide this on the command line
|
24
|
+
|
25
|
+
$ gerd -t wpergih3409gihjp2g4brjep
|
26
|
+
$ gerd --token wpergih3409gihjp2g4brjep
|
27
|
+
|
28
|
+
Or as an environment variable
|
29
|
+
|
30
|
+
$ export GERD_TOKEN=wpergih3409gihjp2g4brjep
|
31
|
+
|
32
|
+
Or in a .gerd file in the local directory
|
33
|
+
|
34
|
+
{
|
35
|
+
"token": "wpergih3409gihjp2g4brjep"
|
36
|
+
}
|
37
|
+
|
38
|
+
Or in a .gerd file in your home directory.
|
39
|
+
|
40
|
+
Gerd will look for a token in these places, in order.
|
41
|
+
|
42
|
+
### Commands
|
43
|
+
|
44
|
+
#### gerd audit
|
45
|
+
|
46
|
+
First steps are to create a snapshot of your organisation
|
47
|
+
|
48
|
+
$ gerd audit meet-gerd -f gerd.json
|
49
|
+
|
50
|
+
This captures the data in a file which now models your organisation. It will look like this
|
51
|
+
```
|
52
|
+
{
|
53
|
+
"organisation": "meet-gerd",
|
54
|
+
"teams": {
|
55
|
+
},
|
56
|
+
"repositories": {
|
57
|
+
},
|
58
|
+
"members": {
|
59
|
+
"georgecodes": {
|
60
|
+
"id": 99999
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
```
|
65
|
+
#### gerd validate
|
66
|
+
|
67
|
+
At any point, you can see if your model file matches reality
|
68
|
+
|
69
|
+
$ gerd validate meet-gerd --expected gerd.json
|
70
|
+
|
71
|
+
You will get a short report on the recognised differences
|
72
|
+
|
73
|
+
#### gerd sync
|
74
|
+
|
75
|
+
This is what will propagate changes in your gerd model to GitHub.
|
76
|
+
|
77
|
+
$ gerd sync meet-gerd --file gerd.json
|
78
|
+
|
79
|
+
Note that as a layer of protection, destructive behaviour - deleting repos - requires an explicit flag to perform
|
80
|
+
|
81
|
+
$ gerd sync meet-gerd --file gerd.json --delete
|
82
|
+
|
83
|
+
|
data/Rakefile
ADDED
data/bin/gerd
ADDED
data/gerd.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gerd/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "gerd"
|
8
|
+
spec.version = Gerd::VERSION
|
9
|
+
spec.authors = ["George McIntosh"]
|
10
|
+
spec.email = ["george@elevenware.com"]
|
11
|
+
spec.description = %q{Githur hErd: manage GitHub repositories declaratively}
|
12
|
+
spec.summary = %q{Manage your GitHub estate declaratively}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "vcr"
|
24
|
+
spec.add_development_dependency "webmock"
|
25
|
+
spec.add_development_dependency "cucumber"
|
26
|
+
spec.add_development_dependency "aruba"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "guard"
|
29
|
+
spec.add_development_dependency "guard-rspec"
|
30
|
+
|
31
|
+
spec.add_dependency "thor"
|
32
|
+
spec.add_dependency "require_all"
|
33
|
+
end
|
data/lib/gerd.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
require 'octokit/repository'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Gerd
|
6
|
+
class Audit
|
7
|
+
|
8
|
+
def initialize(client, org)
|
9
|
+
@org = org
|
10
|
+
@client = client
|
11
|
+
end
|
12
|
+
|
13
|
+
def full_audit
|
14
|
+
audit = {}
|
15
|
+
members = find_members
|
16
|
+
teams = find_teams
|
17
|
+
repositories = find_repos
|
18
|
+
audit['organisation'] = @org
|
19
|
+
audit['teams'] = teams
|
20
|
+
audit['repositories'] = repositories
|
21
|
+
audit['members'] = members
|
22
|
+
audit
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_repos
|
26
|
+
repos = @client.org_repos @org
|
27
|
+
repositories = {}
|
28
|
+
|
29
|
+
repos.each do | repo |
|
30
|
+
repo_conf = {
|
31
|
+
:private => repo.private
|
32
|
+
}
|
33
|
+
repositories[repo.name] = repo_conf
|
34
|
+
end
|
35
|
+
repositories
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_teams
|
39
|
+
teams = @client.org_teams @org
|
40
|
+
team_conf = {}
|
41
|
+
teams.each do | team |
|
42
|
+
members = @client.team_members(team.id)
|
43
|
+
team_members = []
|
44
|
+
members.each do | member |
|
45
|
+
team_members << member.login
|
46
|
+
|
47
|
+
end
|
48
|
+
repos = @client.team_repos(team.id)
|
49
|
+
team_repos = []
|
50
|
+
repos.each do | repo |
|
51
|
+
team_repos << {
|
52
|
+
:id => repo.id,
|
53
|
+
:name => repo.name
|
54
|
+
}
|
55
|
+
end
|
56
|
+
team_conf[team.name] = {
|
57
|
+
:id => team.id,
|
58
|
+
:description => team.description,
|
59
|
+
:privacy => team.privacy,
|
60
|
+
:members => team_members,
|
61
|
+
:repos => team_repos
|
62
|
+
}
|
63
|
+
end
|
64
|
+
team_conf
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_members
|
68
|
+
members = @client.org_members @org
|
69
|
+
members_conf = {}
|
70
|
+
members.each do | member |
|
71
|
+
members_conf[member.login] = {
|
72
|
+
:id => member.id
|
73
|
+
}
|
74
|
+
end
|
75
|
+
members_conf
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
File without changes
|
data/lib/gerd/cli.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'json'
|
4
|
+
require 'gerd/audit/audit'
|
5
|
+
require 'gerd/github_client'
|
6
|
+
require 'gerd/formatters'
|
7
|
+
require 'gerd/validators'
|
8
|
+
require 'gerd/model/model'
|
9
|
+
require 'gerd/invoke'
|
10
|
+
|
11
|
+
module Gerd
|
12
|
+
class CLI < Thor
|
13
|
+
|
14
|
+
desc "audit <organization>", "Introspects the GitHub environment for the given organisation"
|
15
|
+
option :token, :type => :string, :aliases => ['t']
|
16
|
+
option :file, :type => :string, :aliases => ['f']
|
17
|
+
option :overwrite, :type => :boolean, :aliases => ['o']
|
18
|
+
def audit(organisation)
|
19
|
+
token = options[:token] if options[:token]
|
20
|
+
client = Gerd::GHClient.create(token)
|
21
|
+
auditor = Gerd::Audit.new(client, organisation)
|
22
|
+
content = auditor.full_audit
|
23
|
+
formatter = Gerd::Formatters.find_formatter(options[:file])
|
24
|
+
formatter.print(content, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "validate <organisation>", "Checks whether the organisation actually has the configuration supplied"
|
28
|
+
option :expected, :type => :string, :aliases => ['e'], :required => true
|
29
|
+
option :token, :type => :string, :aliases => ['t']
|
30
|
+
option :file, :type => :string, :aliases => ['f']
|
31
|
+
def validate(organisation)
|
32
|
+
token = options[:token] if options[:token]
|
33
|
+
client = Gerd::GHClient.create(token)
|
34
|
+
auditor = Gerd::Audit.new(client, organisation)
|
35
|
+
expected_state = Gerd::Model::GithubState.from_json(File.read(options[:expected]))
|
36
|
+
actual_state = Gerd::Model::GithubState.new(auditor.full_audit)
|
37
|
+
validator = Gerd::Validation::Validator.new(expected_state, actual_state)
|
38
|
+
content = validator.validate
|
39
|
+
formatter = Gerd::Formatters.find_formatter(options[:file])
|
40
|
+
formatter.print(content, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "apply <organisation>", "Applies supplied model to GitHub"
|
44
|
+
option :file, :type => :string, :aliases => ['f'], :required => true
|
45
|
+
option :token, :type => :string, :aliases => ['t']
|
46
|
+
option :delete, :type => :boolean
|
47
|
+
def apply(organisation)
|
48
|
+
token = options[:token] if options[:token]
|
49
|
+
client = Gerd::GHClient.create(token)
|
50
|
+
auditor = Gerd::Audit.new(client, organisation)
|
51
|
+
expected_state = Gerd::Model::GithubState.from_json(File.read(options[:file]))
|
52
|
+
actual_state = Gerd::Model::GithubState.new(auditor.full_audit)
|
53
|
+
validator = Gerd::Validation::Validator.new(expected_state, actual_state)
|
54
|
+
actions = validator.collect_actions.flatten
|
55
|
+
actions.each do | action |
|
56
|
+
action.invoke(client, options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "exec <script>", "Runs arbitrary scripts against your audit file"
|
61
|
+
option :input, :type => :string, :aliases => ['i'], :required => true
|
62
|
+
option :output, :type => :string, :aliases => ['o']
|
63
|
+
def exec(script)
|
64
|
+
current_state = Gerd::Model::GithubState.from_json(File.read(options[:input]))
|
65
|
+
script = File.read(script)
|
66
|
+
exec_helper = Gerd::Helpers::Exec.new(script)
|
67
|
+
exec_helper.exec(current_state)
|
68
|
+
formatter = Gerd::Formatters.find_formatter(options[:output])
|
69
|
+
formatter.print(current_state.serialize, options)
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|