gerd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +115 -0
  6. data/Guardfile +43 -0
  7. data/README.md +83 -0
  8. data/Rakefile +11 -0
  9. data/bin/gerd +4 -0
  10. data/gerd.gemspec +33 -0
  11. data/lib/gerd.rb +6 -0
  12. data/lib/gerd/audit/audit.rb +79 -0
  13. data/lib/gerd/audit/builders.rb +0 -0
  14. data/lib/gerd/cli.rb +74 -0
  15. data/lib/gerd/exceptions.rb +16 -0
  16. data/lib/gerd/formatters.rb +39 -0
  17. data/lib/gerd/github_client.rb +35 -0
  18. data/lib/gerd/inspections/actions/change_repo_privacy.rb +16 -0
  19. data/lib/gerd/inspections/actions/create_repo.rb +26 -0
  20. data/lib/gerd/inspections/actions/create_team.rb +31 -0
  21. data/lib/gerd/inspections/actions/delete_repo.rb +29 -0
  22. data/lib/gerd/inspections/actions/delete_team.rb +19 -0
  23. data/lib/gerd/inspections/actions/update_name_action.rb +19 -0
  24. data/lib/gerd/inspections/diff.rb +17 -0
  25. data/lib/gerd/inspections/diffs/organisation.rb +89 -0
  26. data/lib/gerd/inspections/diffs/repositories.rb +94 -0
  27. data/lib/gerd/invoke.rb +18 -0
  28. data/lib/gerd/model/members.rb +0 -0
  29. data/lib/gerd/model/model.rb +95 -0
  30. data/lib/gerd/model/organisation.rb +0 -0
  31. data/lib/gerd/model/repositories.rb +0 -0
  32. data/lib/gerd/model/teams.rb +0 -0
  33. data/lib/gerd/validation/organisations/apply.rb +0 -0
  34. data/lib/gerd/validation/organisations/diff.rb +0 -0
  35. data/lib/gerd/validation/organisations/introspect.rb +0 -0
  36. data/lib/gerd/validators.rb +55 -0
  37. data/lib/gerd/version.rb +4 -0
  38. data/local.sh +10 -0
  39. data/spec/model_spec.rb +149 -0
  40. data/spec/organisation_spec.rb +76 -0
  41. data/spec/repositories_spec.rb +223 -0
  42. data/spec/spec_helper.rb +6 -0
  43. metadata +246 -0
@@ -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
@@ -0,0 +1,3 @@
1
+ pkg
2
+ *.json
3
+ script.rb
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.6
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -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
@@ -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
@@ -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
+
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do | t |
6
+ t.rspec_opts = "--color"
7
+ end
8
+ rescue LoadError
9
+ end
10
+
11
+ task :default => [:spec]
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gerd'
3
+
4
+ Gerd::CLI.start(ARGV)
@@ -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
@@ -0,0 +1,6 @@
1
+ require "gerd/version"
2
+ require "gerd/cli"
3
+
4
+ module Gerd
5
+ # Your code goes here...
6
+ end
@@ -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
@@ -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