gerd 0.0.1

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.
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