knife-github 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,21 +3,35 @@ knife-github
3
3
 
4
4
  Chef knife plugin to interact with the github enterprise appliance.
5
5
 
6
- Attributes
6
+ Configurations
7
7
  ==========
8
8
 
9
- You can configure the following attributes within your knife.rb
9
+ ### Central Configuration.
10
+ When working on customer admin machines, it's recommended to used an central configuration file.
11
+ This file should be created in: /etc/githubrc.rb and can contain any attribute in the following structure:
12
+
13
+ github_url "https://github.schubergphilis.com"
14
+ github_link "ssh"
15
+ github_organizations [ "TLA-Cookbooks", "SBP-Cookbooks" ]
16
+
17
+ Please note: these options are recommended for the central config file:
18
+
19
+ ### Personal Configuration.
20
+ You can also configure attributes within your ~/.chef/knife.rb in the following structure:
10
21
 
11
- knife[:github_url] = 'https://github.company.lan'
12
- knife[:github_organizations] = [ 'customer-cookbooks', 'central-cookbooks' ]
13
- knife[:github_link] = 'ssh'
22
+ knife[:github_token] = '28374928374928374923874923842'
14
23
  knife[:github_api_version] = 'v3'
15
24
  knife[:github_ssl_verify_mode] = 'verify_none'
16
25
 
26
+ Please note: these settings will overwrite the central settings.
27
+ In a perfect world, your personal configuration file only contains your token information.
28
+
17
29
  ###### github_url
18
- This will be the URL to your local github appliance.
30
+ This will be the URL to your (personal) github enterprise appliance.
19
31
  Here you can also use the github.com address if you don't have an internal appliance.
20
32
 
33
+ Attributes
34
+ ==========
21
35
  ###### github_organizations
22
36
  Here you specify the organizations that you want to taget when searching for cookbooks.
23
37
  The first entry will have priority over the other entries.
@@ -37,7 +51,7 @@ Other
37
51
  =====
38
52
 
39
53
  Cache files will be created into the: ~/.chef directory.
40
- We use cache files to offload the api calls and increase the performance for additional executions.
54
+ We use cache files to offload the api calls and increase the performance for repetitive executions
41
55
  Updated to any repo inside the organization will cause the cache files to update.
42
56
  But in case of any problems, the cache files can be safely deleted.
43
57
 
data/knife-github.gemspec CHANGED
@@ -19,5 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
  s.license = 'Apache 2.0'
21
21
  s.add_dependency "mixlib-versioning", ">= 1.0.0"
22
+ s.add_dependency "ruby-termios", ">= 0.9.6"
22
23
  s.add_dependency "chef", ">= 10.0.0"
23
24
  end
@@ -22,36 +22,36 @@ class Chef
22
22
 
23
23
  def self.included(includer)
24
24
  includer.class_eval do
25
-
25
+
26
26
  option :fields,
27
- :long => "--fields 'NAME, NAME'",
28
- :description => "The fields to output, comma-separated"
29
-
27
+ :long => "--fields 'NAME, NAME'",
28
+ :description => "The fields to output, comma-separated"
29
+
30
30
  option :fieldlist,
31
- :long => "--fieldlist",
32
- :description => "The available fields to output/filter",
33
- :boolean => true
34
-
31
+ :long => "--fieldlist",
32
+ :description => "The available fields to output/filter",
33
+ :boolean => true
34
+
35
35
  option :noheader,
36
- :long => "--noheader",
37
- :description => "Removes header from output",
38
- :boolean => true
39
-
36
+ :long => "--noheader",
37
+ :description => "Removes header from output",
38
+ :boolean => true
39
+
40
40
  def display_info(data, columns, match = [])
41
41
  object_list = []
42
-
42
+
43
43
  if config[:fields]
44
44
  config[:fields].split(',').each { |n| object_list << ui.color(("#{n}").capitalize.strip, :bold) }
45
45
  else
46
46
  columns.each { |c| r = c.split(","); object_list << ui.color(("#{r.last}").strip, :bold) }
47
47
  end
48
-
48
+
49
49
  col = object_list.count
50
50
  object_list = [] if config[:noheader]
51
-
51
+
52
52
  data.each do |k,v|
53
53
  if config[:fields]
54
- config[:fields].downcase.split(',').each { |n| object_list << ((v["#{n}".strip]).to_s || 'n/a') }
54
+ config[:fields].downcase.split(',').each { |n| object_list << ((v["#{n}".strip]).to_s || 'n/a') }
55
55
  else
56
56
  color = :white
57
57
  if !match.empty? && !config[:all]
@@ -59,25 +59,25 @@ class Chef
59
59
  if matches.uniq.count == 1
60
60
  next if config[:mismatch]
61
61
  else
62
- color = :yellow
62
+ color = :yellow
63
63
  end
64
64
  end
65
65
  columns.each { |c| r = c.split(","); object_list << ui.color((v["#{r.first}"]).to_s, color) || 'n/a' }
66
66
  end
67
67
  end
68
-
68
+
69
69
  puts ui.list(object_list, :uneven_columns_across, col)
70
70
  display_object_fields(data) if locate_config_value(:fieldlist)
71
71
  end
72
-
72
+
73
73
  def display_object_fields(object)
74
74
  exit 1 if object.nil? || object.empty?
75
75
  object_fields = [
76
76
  ui.color('Key', :bold),
77
77
  ui.color('Type', :bold),
78
78
  ui.color('Value', :bold)
79
- ]
80
- object.first.each do |n|
79
+ ]
80
+ object.first.each do |n|
81
81
  if n.class == Hash
82
82
  n.keys.each do |k,v|
83
83
  object_fields << ui.color(k, :yellow, :bold)
@@ -15,22 +15,17 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
- # ---------------------------------------------------------------------------- #
19
- # Abstract
20
- # ---------------------------------------------------------------------------- #
21
- # This code is intended to help you cleaning up your locate repo's.
22
- # It will check if your repo if insync with the github and will not touch if
23
- # this is not the case. Then it will check if you have any branches local
24
- # and not on the github.
25
- #
26
- # If it cannot find any uncommitted changes, it will safely remove your repo.
27
- # It's good practice to cleanup and re-download repos because this way they
28
- # can move from organization to organization.
29
- # ---------------------------------------------------------------------------- #
30
- #
31
18
  require 'chef/knife'
32
19
 
33
20
  module KnifeGithubCleanup
21
+ # This module is intended to help you cleaning up your locate repo's.
22
+ # It will check if your repo if insync with the github and will not touch if
23
+ # this is not the case. Then it will check if you have any branches local
24
+ # and not on the github.
25
+ #
26
+ # If it cannot find any uncommitted changes, it will safely remove your repo.
27
+ # It's good practice to cleanup and re-download repos because this way they
28
+ # can move from organization to organization.
34
29
  class GithubCleanup < Chef::Knife
35
30
 
36
31
  deps do
@@ -18,10 +18,11 @@
18
18
 
19
19
  require 'chef/knife'
20
20
 
21
- class Chef
22
- class Knife
21
+ module KnifeGitHubClone
23
22
 
24
- class GithubClone < Knife
23
+ # Clones a cookbook version to your local cookbooks directory
24
+ #
25
+ class GithubClone < Chef::Knife
25
26
 
26
27
  deps do
27
28
  require 'chef/knife/github_base'
@@ -101,6 +102,5 @@ class Chef
101
102
  end
102
103
  end
103
104
 
104
- end
105
105
  end
106
106
  end
@@ -18,9 +18,9 @@
18
18
 
19
19
  require 'chef/knife'
20
20
 
21
- class Chef
22
- class Knife
23
- class GithubCompare < Knife
21
+ module KnifeGithubCompare
22
+ # Compare cookbooks in chef with those in the repository
23
+ class GithubCompare < Chef::Knife
24
24
 
25
25
  deps do
26
26
  require 'chef/knife/github_base'
@@ -92,6 +92,5 @@ class Chef
92
92
  end
93
93
 
94
94
  end
95
- end
96
- end
95
+ end
97
96
  end
@@ -1,58 +1,57 @@
1
1
  require 'chef/knife'
2
2
 
3
- class Chef
4
- class Knife
5
- # Implements the knife github deploy function
6
- # @author:: Ian Southam (<isoutham@schubergphilis.com>)
7
- # Copyright:: Copyright (c) 2013 Ian Southam.
8
- # This code is specific to our company workflow
9
- #
10
- # == Overview
11
- # All modes presume you have used github download to download a cookbook or
12
- # are creating a new cookbook
13
- #
14
- # === Examples
15
- # Deploy a development version of cookbook to your chef server
16
- # knife github deploy cookbook_name
17
- #
18
- # Deploy a release version of cookbook to your chef server
19
- # knife github deploy cookbook_name -f
20
- #
21
- # === Options
22
- # -f Operate in final release mode
23
- # -p Update the patch component of the version
24
- # -m Update the minor component of the version
25
- # -M Update the minor component of the version
26
- #
27
- # == Operation Modes
28
- # Development (default)
29
- #
30
- # This will take a cookbook name
31
- # Do some basic version checks (if the current cookbook is frozen) and
32
- # upload it
33
- #
34
- # If the cookbook is frozen it will force you to choose a new version
35
- # and update the metadata accordingly
36
- #
37
- # Release (-f)
38
- #
39
- # You will be forced to select a new version.
40
- # You can choose via the options whether to increment the Major/minor or patch
41
- # revision numbers
42
- # The version will be tagged
43
- # Uploaded to the Chef server and frozen
44
- #
45
- # == Version numbers
46
- #
47
- # You can choose a specific version number by specifying it on the command
48
- # line.
49
- #
50
- # If you do not specify a version, the version will be the version in your
51
- # cookbook's metadata
52
- #
53
- # A warning is issued if the version is lower than the version in github
54
- #
55
- class GithubDeploy < Knife
3
+ module KnifeGithubDeploy
4
+ # Implements the knife github deploy function
5
+ # @author:: Ian Southam (<isoutham@schubergphilis.com>)
6
+ # Copyright:: Copyright (c) 2013 Ian Southam.
7
+ # This code is specific to our company workflow
8
+ #
9
+ # == Overview
10
+ # All modes presume you have used github download to download a cookbook or
11
+ # are creating a new cookbook
12
+ #
13
+ # === Examples
14
+ # Deploy a development version of cookbook to your chef server
15
+ # knife github deploy cookbook_name
16
+ #
17
+ # Deploy a release version of cookbook to your chef server
18
+ # knife github deploy cookbook_name -f
19
+ #
20
+ # === Options
21
+ # -f Operate in final release mode
22
+ # -p Update the patch component of the version
23
+ # -m Update the minor component of the version
24
+ # -M Update the major component of the version
25
+ #
26
+ # == Operation Modes
27
+ # Development (default)
28
+ #
29
+ # This will take a cookbook name
30
+ # Do some basic version checks (if the current cookbook is frozen) and
31
+ # upload it
32
+ #
33
+ # If the cookbook is frozen it will force you to choose a new version
34
+ # and update the metadata accordingly
35
+ #
36
+ # Release (-f)
37
+ #
38
+ # You will be forced to select a new version.
39
+ # You can choose via the options whether to increment the Major/minor or patch
40
+ # revision numbers
41
+ # The version will be tagged
42
+ # Uploaded to the Chef server and frozen
43
+ #
44
+ # == Version numbers
45
+ #
46
+ # You can choose a specific version number by specifying it on the command
47
+ # line.
48
+ #
49
+ # If you do not specify a version, the version will be the version in your
50
+ # cookbook's metadata
51
+ #
52
+ # A warning is issued if the version is lower than the version in github
53
+ #
54
+ class GithubDeploy < Chef::Knife
56
55
  deps do
57
56
  require 'chef/knife/github_base'
58
57
  include Chef::Knife::GithubBase
@@ -333,6 +332,5 @@ class Chef
333
332
  return true
334
333
  end
335
334
 
336
- end
337
335
  end
338
336
  end
@@ -18,10 +18,7 @@
18
18
 
19
19
  require 'chef/knife'
20
20
 
21
- class Chef
22
- class Knife
23
-
24
- class GithubDiff < Knife
21
+ module GithubDiff
25
22
  # Implements a diff function between your downloaded copy from git and what is in the Chef Server
26
23
  #
27
24
  # By default, it expects that you have already done knife github download COOKBOOK
@@ -31,6 +28,7 @@ class Chef
31
28
  # You can also diff a cookbook against the github version bu using the -g option
32
29
  #
33
30
  # You can also optionally give a version on the command line
31
+ class GithubDiff < Chef::Knife
34
32
 
35
33
  deps do
36
34
  require 'chef/knife/github_base'
@@ -150,7 +148,5 @@ class Chef
150
148
  end
151
149
  return version
152
150
  end
153
-
154
- end
155
151
  end
156
152
  end
@@ -18,10 +18,9 @@
18
18
 
19
19
  require 'chef/knife'
20
20
 
21
- class Chef
22
- class Knife
21
+ module KnifeGithubList
23
22
 
24
- class GithubList < Knife
23
+ class GithubList < Chef::Knife
25
24
 
26
25
  deps do
27
26
  require 'chef/knife/github_base'
@@ -94,6 +93,5 @@ class Chef
94
93
 
95
94
  end
96
95
 
97
- end
98
96
  end
99
97
  end
@@ -18,8 +18,7 @@
18
18
 
19
19
  require 'chef/knife'
20
20
 
21
- class Chef
22
- class Knife
21
+ module KnifeGithubPin
23
22
 
24
23
  # Pin a specific cookbook version to an environment
25
24
  #
@@ -28,7 +27,7 @@ class Chef
28
27
  # In some respects does duplicate some functionality that can be found
29
28
  # in spork but, I want a single set of tools that would help people
30
29
  # to get quickly up to speed with using chef in an industrialised environment
31
- class GithubPin < Knife
30
+ class GithubPin < Chef::Knife
32
31
  deps do
33
32
  require 'chef/knife/github_base'
34
33
  require 'chef/knife/core/object_loader'
@@ -53,8 +52,9 @@ class Chef
53
52
  @version = nil
54
53
  @env = nil
55
54
 
56
- if @cookbook_name.empty?
55
+ if @cookbook_name.nil?
57
56
  Chef::Log.error("You must specify a cookbook name to use this module")
57
+ exit 1;
58
58
  end
59
59
 
60
60
  # Parameter 2 can be a version or an environment (if version is not given) or nothing
@@ -133,6 +133,5 @@ class Chef
133
133
  @env
134
134
  end
135
135
 
136
- end
137
136
  end
138
137
  end
@@ -16,24 +16,34 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
-
20
- #
21
- #
22
- # BE AWARE THIS COMMAND IS STILL UNDER HEAVY DEVELOPMENT!
23
- #
24
- #
25
19
  require 'chef/knife'
26
20
 
27
- module KnifeGithubCreate
28
- class GithubCreate < Chef::Knife
29
-
21
+ module KnifeGithubRepoCreate
22
+ class GithubRepoCreate < Chef::Knife
23
+ # Implements the knife github repo create function
24
+ #
25
+ # == Overview
26
+ # The command will create a empty cookbook structure and it will commit this one into the github.
27
+ #
28
+ # === Examples
29
+ # Create a new cookbook:
30
+ # knife github repo create <name> <here you give your cookbook description>
31
+ #
32
+ # # Deploy a release version of cookbook to your chef server
33
+ # # knife github deploy cookbook_name -f
34
+ #
35
+ # === Options
36
+ # -t --github_token Authentication token for the github.
37
+ # -U --github_user_repo Create the cookbook in the user environment.
38
+ #
39
+
30
40
  deps do
31
41
  require 'chef/knife/github_base'
32
42
  include Chef::Knife::GithubBase
33
43
  require 'chef/mixin/shell_out'
34
44
  end
35
45
 
36
- banner "knife github create STRING (options)"
46
+ banner "knife github repo create <name> <description> (options)"
37
47
  category "github"
38
48
 
39
49
  option :github_token,
@@ -58,6 +68,8 @@ module KnifeGithubCreate
58
68
 
59
69
  # Get the name_args from the command line
60
70
  name = name_args.first
71
+ name_args.shift
72
+ desc = name_args.join(" ")
61
73
 
62
74
  # Get the organization name from config
63
75
  org = locate_config_value('github_organizations').first
@@ -66,7 +78,12 @@ module KnifeGithubCreate
66
78
  Chef::Log.error("Please specify a repository name")
67
79
  exit 1
68
80
  end
69
-
81
+
82
+ if desc.nil? || desc.empty?
83
+ Chef::Log.error("Please specify a repository description")
84
+ exit 1
85
+ end
86
+
70
87
  if config[:github_user_repo]
71
88
  url = @github_url + "/api/" + @github_api_version + "/user/repos"
72
89
  Chef::Log.debug("Creating repository in user environment")
@@ -75,34 +92,73 @@ module KnifeGithubCreate
75
92
  Chef::Log.debug("Creating repository in organization: #{org}")
76
93
  end
77
94
 
95
+ @github_tmp = locate_config_value("github_tmp") || '/var/tmp/gitcreate'
96
+ @github_tmp = "#{@github_tmp}#{Process.pid}"
97
+
78
98
  # Get token information
79
99
  token = get_github_token()
80
100
 
81
101
  # Get body data for post
82
- body = get_body_json(name)
102
+ body = get_body_json(name, desc)
83
103
 
84
- # Creating the repository
85
- Chef::Log.debug("Creating the github repository")
86
- repo = post_request(url, body, token)
104
+ # Creating the local repository or using existing one.
105
+ cookbook_dir = ""
106
+ cookbook_dir = get_cookbook_path(name)
87
107
 
88
- Chef::Log.debug("Creating the local repository based on template")
89
- create_cookbook(name)
108
+ if File.exists?(cookbook_dir)
109
+ Chef::Log.debug("Using local repository from #{cookbook_dir}")
90
110
 
91
- cookbook_path = get_cookbook_path(name)
111
+ # Creating the github repository
112
+ Chef::Log.debug("Creating the github repository")
113
+ repo = post_request(url, body, token)
114
+ github_ssh_url = repo['ssh_url']
115
+
116
+ Chef::Log.debug("Commit and push local repository")
117
+ # Initialize the local git repo
118
+ git_commit_and_push(cookbook_dir, github_ssh_url)
92
119
 
93
- # Updating README.md if needed.
94
- update_readme(cookbook_path)
120
+ puts "Finished creating #{name} and uploading #{cookbook_dir}"
121
+ else
122
+ Chef::Log.debug("Creating the local repository based on template")
123
+ create_cookbook(name, @github_tmp)
124
+
125
+ cookbook_dir = File.join(@github_tmp, name)
126
+
127
+ # Updating README.md if needed.
128
+ update_readme(cookbook_dir)
129
+
130
+ # Updateing metadata.rb if needed.
131
+ update_metadata(cookbook_dir)
132
+
133
+ # Creating the github repository
134
+ Chef::Log.debug("Creating the github repository")
135
+ repo = post_request(url, body, token)
136
+ github_ssh_url = repo['ssh_url']
137
+
138
+ Chef::Log.debug("Commit and push local repository")
139
+ # Initialize the local git repo
140
+ git_commit_and_push(cookbook_dir, github_ssh_url)
141
+
142
+ Chef::Log.debug("Removing temp files")
143
+ FileUtils.remove_entry(@github_tmp)
144
+ puts "Finished creating and uploading #{name}"
145
+ end
146
+ end
95
147
 
96
- # Updateing metadata.rb if needed.
97
- update_metadata(cookbook_path)
98
-
99
- github_ssh_url = repo['ssh_url']
100
-
101
- shell_out!("git init", :cwd => cookbook_path )
102
- shell_out!("git add .", :cwd => cookbook_path )
103
- shell_out!("git commit -m 'creating initial cookbook structure from the knife-github plugin' ", :cwd => cookbook_path )
104
- shell_out!("git remote add origin #{github_ssh_url} ", :cwd => cookbook_path )
105
- shell_out!("git push -u origin master", :cwd => cookbook_path )
148
+ # Set the username in README.md
149
+ # @param cookbook_path [String] cookbook path
150
+ # github_ssh_url [String] github ssh url from repo
151
+ def git_commit_and_push(cookbook_path, github_ssh_url)
152
+ if File.exists?(File.join(cookbook_path, ".git"))
153
+ shell_out("git remote rm origin", :cwd => cookbook_path)
154
+ else
155
+ shell_out!("git init", :cwd => cookbook_path)
156
+ end
157
+ shell_out!("echo - $(date): Uploaded with knife github plugin. >> CHANGELOG.md ", :cwd => cookbook_path)
158
+ shell_out!("git add .", :cwd => cookbook_path)
159
+ shell_out!("git commit -m 'creating initial cookbook structure from the knife-github plugin' ", :cwd => cookbook_path)
160
+ shell_out!("git remote add origin #{github_ssh_url} ", :cwd => cookbook_path)
161
+ shell_out!("git push -u origin master", :cwd => cookbook_path)
106
162
  end
107
163
 
108
164
  # Set the username in README.md
@@ -157,18 +213,20 @@ module KnifeGithubCreate
157
213
 
158
214
  # Create the cookbook template for upload
159
215
  # @param name [String] cookbook name
160
- def create_cookbook(cookbook_name)
161
- args = [ cookbook_name ]
216
+ # tmp [String] temp location
217
+ def create_cookbook(name, tmp)
218
+ args = [ name ]
162
219
  create = Chef::Knife::CookbookCreate.new(args)
220
+ create.config[:cookbook_path] = tmp
163
221
  create.run
164
222
  end
165
223
 
166
224
  # Create the json body with repo config for POST information
167
225
  # @param name [String] cookbook name
168
- def get_body_json(cookbook_name)
226
+ def get_body_json(cookbook_name, description="Please fill in the description.")
169
227
  body = {
170
228
  "name" => cookbook_name,
171
- "description" => "We should ask for an description",
229
+ "description" => description,
172
230
  "private" => false,
173
231
  "has_issues" => true,
174
232
  "has_wiki" => true,
@@ -0,0 +1,273 @@
1
+ #
2
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
3
+ # Copyright:: Copyright (c) 2013 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+
21
+ module KnifeGithubRepoDestroy
22
+ class GithubRepoDestroy < Chef::Knife
23
+ # Implements the knife github repo destroy function
24
+ #
25
+ # == Overview
26
+ # The command will delete and destroy your repo on the github.
27
+ #
28
+ # === Examples
29
+ # Destroy a repository:
30
+ # knife github repo destroy <name>
31
+ #
32
+ # === Options
33
+ # -t --github_token Authentication token for the github.
34
+ # -U --github_user_repo Destroy the cookbook in the user environment.
35
+ #
36
+
37
+ deps do
38
+ require 'chef/knife/github_base'
39
+ include Chef::Knife::GithubBase
40
+ require 'chef/mixin/shell_out'
41
+ end
42
+
43
+ banner "knife github repo destroy <name> (options)"
44
+ category "github"
45
+
46
+ option :github_token,
47
+ :short => "-t",
48
+ :long => "--github_token",
49
+ :description => "Your github token for OAuth authentication"
50
+
51
+ option :github_user_repo,
52
+ :short => "-U",
53
+ :long => "--github_user_repo",
54
+ :description => "Create the repo within your user environment",
55
+ :boolean => true
56
+
57
+ def run
58
+ extend Chef::Mixin::ShellOut
59
+
60
+ # validate base options from base module.
61
+ validate_base_options
62
+
63
+ # Display information if debug mode is on.
64
+ display_debug_info
65
+
66
+ # Get the repo name from the command line
67
+ name = name_args.first
68
+
69
+ # Get the organization name from config
70
+ org = locate_config_value('github_organizations').first
71
+
72
+ if name.nil? || name.empty?
73
+ Chef::Log.error("Please specify a repository name")
74
+ exit 1
75
+ end
76
+
77
+ user = get_userlogin
78
+
79
+ if config[:github_user_repo]
80
+ url = @github_url + "/api/" + @github_api_version + "/repos/#{user}/#{name}"
81
+ Chef::Log.debug("Destroying repository in user environment: #{user}")
82
+ else
83
+ url = @github_url + "/api/" + @github_api_version + "/repos/#{org}/#{name}"
84
+ Chef::Log.debug("Destroying repository in organization: #{org}")
85
+ end
86
+
87
+ # @github_tmp = locate_config_value("github_tmp") || '/var/tmp/gitcreate'
88
+ # @github_tmp = "#{@github_tmp}#{Process.pid}"
89
+
90
+ # Get token information
91
+ token = get_github_token()
92
+
93
+ # Get body data for post
94
+ # body = get_body_json(name, desc)
95
+
96
+ # Creating the local repository
97
+ # Chef::Log.debug("Creating the local repository based on template")
98
+ # create_cookbook(name, @github_tmp)
99
+
100
+ # cookbook_path = File.join(@github_tmp, name)
101
+
102
+ # Updating README.md if needed.
103
+ # update_readme(cookbook_path)
104
+
105
+ # Updateing metadata.rb if needed.
106
+ # update_metadata(cookbook_path)
107
+
108
+ # Creating the github repository
109
+ repo = delete_request(url, token)
110
+ puts "Repo: #{name} is deleted" if repo.nil?
111
+
112
+ # github_ssh_url = repo['ssh_url']
113
+
114
+ # Chef::Log.debug("Commit and push local repository")
115
+ # Initialize the local git repo
116
+ # git_commit_and_push(cookbook_path, github_ssh_url)
117
+
118
+ # Chef::Log.debug("Removing temp files")
119
+ # FileUtils.remove_entry(@github_tmp)
120
+ end
121
+
122
+ # Set the username in README.md
123
+ # @param cookbook_path [String] cookbook path
124
+ # github_ssh_url [String] github ssh url from repo
125
+ def git_commit_and_push(cookbook_path, github_ssh_url)
126
+ shell_out!("git init", :cwd => cookbook_path )
127
+ shell_out!("git add .", :cwd => cookbook_path )
128
+ shell_out!("git commit -m 'creating initial cookbook structure from the knife-github plugin' ", :cwd => cookbook_path )
129
+ shell_out!("git remote add origin #{github_ssh_url} ", :cwd => cookbook_path )
130
+ shell_out!("git push -u origin master", :cwd => cookbook_path )
131
+ end
132
+
133
+ # Set the username in README.md
134
+ # @param name [String] cookbook path
135
+ def update_readme(cookbook_path)
136
+ contents = ''
137
+ username = get_username
138
+ readme = File.join(cookbook_path, "README.md")
139
+ File.foreach(readme) do |line|
140
+ line.gsub!(/TODO: List authors/,"#{username}\n")
141
+ contents = contents << line
142
+ end
143
+ File.open(readme, 'w') {|f| f.write(contents) }
144
+ return nil
145
+ end
146
+
147
+ # Set the username and email in metadata.rb
148
+ # @param name [String] cookbook path
149
+ def update_metadata(cookbook_path)
150
+ contents = ''
151
+ username = get_username
152
+ email = get_useremail
153
+ metadata = File.join(cookbook_path, "metadata.rb")
154
+ File.foreach(metadata) do |line|
155
+ line.gsub!(/YOUR_COMPANY_NAME/,username) if username
156
+ line.gsub!(/YOUR_EMAIL/,email) if email
157
+ contents = contents << line
158
+ end
159
+ File.open(metadata, 'w') {|f| f.write(contents) }
160
+ return nil
161
+ end
162
+
163
+ # Get the username from passwd file or git config
164
+ # @param nil
165
+ def get_username()
166
+ username = ENV['USER']
167
+ passwd_user = %x(getent passwd #{username} | cut -d ':' -f 5).chomp
168
+ username = passwd_user if passwd_user
169
+ git_user_name = %x(git config user.name).strip
170
+ username = git_user_name if git_user_name
171
+ username
172
+ end
173
+
174
+ # Get the email from passwd file or git config
175
+ # @param nil
176
+ def get_useremail()
177
+ email = nil
178
+ git_user_email = %x(git config user.email).strip
179
+ email = git_user_email if git_user_email
180
+ email
181
+ end
182
+
183
+ # Get the email from passwd file or git config
184
+ # @param nil
185
+ def get_userlogin()
186
+ email = get_useremail()
187
+ unless email
188
+ puts "Cannot continue without login information. Please define the git email address."
189
+ exit 1
190
+ end
191
+ login = email.split('@').first
192
+ end
193
+
194
+
195
+ # Create the cookbook template for upload
196
+ # @param name [String] cookbook name
197
+ # tmp [String] temp location
198
+ def create_cookbook(name, tmp)
199
+ args = [ name ]
200
+ create = Chef::Knife::CookbookCreate.new(args)
201
+ create.config[:cookbook_path] = tmp
202
+ create.run
203
+ end
204
+
205
+ # Create the json body with repo config for POST information
206
+ # @param name [String] cookbook name
207
+ def get_body_json()
208
+ body = {
209
+ "scopes" => ["public_repo"]
210
+ }.to_json
211
+ end
212
+
213
+ # Get the OAuth authentication token from config or command line
214
+ # @param nil
215
+ def get_github_token()
216
+ token = locate_config_value('github_token')
217
+ if token.nil? || token.empty?
218
+ Chef::Log.error("Cannot find any token information!")
219
+ Chef::Log.error("Please use: knife github token create")
220
+ exit 1
221
+ end
222
+ token
223
+ end
224
+
225
+ # Send DELETE command to API OAuth authentication token from config or command line
226
+ # @param url [String] target url (organization or user)
227
+ # body [JSON] json data with repo configuration
228
+ # token [String] token sring
229
+ def delete_request(url, token)
230
+
231
+ # if @github_ssl_verify_mode == "verify_none"
232
+ # config[:ssl_verify_mode] = :verify_none
233
+ # elsif @github_ssl_verify_mode == "verify_peer"
234
+ # config[:ssl_verify_mode] = :verify_peer
235
+ # end
236
+
237
+ Chef::Log.debug("URL: " + url.to_s)
238
+
239
+ uri = URI.parse(url)
240
+ http = Net::HTTP.new(uri.host,uri.port)
241
+ if uri.scheme == "https"
242
+ http.use_ssl = true
243
+ if @github_ssl_verify_mode == "verify_none"
244
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
245
+ else
246
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
247
+ end
248
+ end
249
+
250
+ req = Net::HTTP::Delete.new(uri.path, initheader = {"Authorization" => "token #{token}"})
251
+ req.body = get_body_json()
252
+ response = http.request(req)
253
+
254
+ return nil if response.code == "204"
255
+
256
+ unless response.code == "201" then
257
+ puts "Error #{response.code}: #{response.message}"
258
+ puts JSON.pretty_generate(JSON.parse(response.body))
259
+ puts "URL: #{url}"
260
+ exit 1
261
+ end
262
+
263
+ begin
264
+ json = JSON.parse(response.body)
265
+ rescue
266
+ ui.warn "The result on the RESTRequest is not in json format"
267
+ ui.warn "Output: " + response.body
268
+ exit 1
269
+ end
270
+ json
271
+ end
272
+ end
273
+ end
@@ -24,8 +24,6 @@ module KnifeGithubSearch
24
24
  deps do
25
25
  require 'chef/knife/github_base'
26
26
  include Chef::Knife::GithubBase
27
- require 'chef/knife/github_baselist'
28
- include Chef::Knife::GithubBaseList
29
27
  end
30
28
 
31
29
  banner "knife github search STRING (options)"
@@ -80,8 +78,7 @@ module KnifeGithubSearch
80
78
 
81
79
  url = @github_url + "/api/" + @github_api_version + "/legacy/repos/search/" + query
82
80
  Chef::Log.debug("URL: #{url}")
83
-
84
- send_request(url)
81
+ connection.send_get_request(url, params = {})
85
82
  end
86
83
 
87
84
  end
@@ -0,0 +1,239 @@
1
+ #
2
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
3
+ # Copyright:: Copyright (c) 2013 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+
21
+ module KnifeGithubTokenCreate
22
+ class GithubTokenCreate < Chef::Knife
23
+ # Implements the knife github token create function
24
+ #
25
+ # == Overview
26
+ # The command will create a authorization token in order to communicate with the github enterprise appliance.
27
+ #
28
+ # === Examples
29
+ # Create a new token:
30
+ # knife github token create <username>
31
+ #
32
+ # # Deploy a release version of cookbook to your chef server
33
+ # # knife github deploy cookbook_name -f
34
+ #
35
+ # === Options
36
+ # -t --github_token Authentication token for the github.
37
+ # -U --github_user_repo Create the cookbook in the user environment.
38
+ #
39
+
40
+ deps do
41
+ require 'knife-github/password'
42
+ require 'chef/knife/github_base'
43
+ include Chef::Knife::GithubBase
44
+ require 'chef/mixin/shell_out'
45
+ end
46
+
47
+ banner "knife github token create <username> (options)"
48
+ category "github"
49
+
50
+ option :force,
51
+ :short => "-f",
52
+ :long => "--force",
53
+ :description => "Force token creation for OAuth authentication",
54
+ :boolean => true
55
+
56
+ option :github_user_repo,
57
+ :short => "-U",
58
+ :long => "--github_user_repo",
59
+ :description => "Create the repo within your user environment",
60
+ :boolean => true
61
+
62
+ def run
63
+ extend Chef::Mixin::ShellOut
64
+
65
+ # validate base options from base module.
66
+ validate_base_options
67
+
68
+ # Display information if debug mode is on.
69
+ display_debug_info
70
+
71
+ # Get the name_args from the command line
72
+ username = name_args.first
73
+
74
+ # Get token information
75
+ token = get_github_token() unless config[:force]
76
+
77
+ # Create github token if needed
78
+ if token.nil?
79
+ token = validate_github_token(username)
80
+ update_knife_config(token)
81
+ end
82
+
83
+ puts "Finished updating your token. Using key:#{token}"
84
+ end
85
+
86
+ # Updates the knife configuration with the token information inside ~/.chef/knife.rb
87
+ # @param token [String] token key
88
+ #
89
+ def update_knife_config(token)
90
+ contents = ''
91
+ update = false
92
+ config = File.join(ENV["HOME"], ".chef/knife.rb")
93
+ File.foreach(config) do |line|
94
+ if line =~ /^\s*knife\[:github_token\].*/ && !token.nil?
95
+ Chef::Log.debug("Replacing current token with: #{token}")
96
+ contents = contents << "knife[:github_token] = \"#{token}\"\n"
97
+ update = true
98
+ else
99
+ contents = contents << line
100
+ end
101
+ end
102
+ unless update
103
+ Chef::Log.debug("Updating configuration with token: #{token}")
104
+ contents = contents << "knife[:github_token] = \"#{token}\"\n"
105
+ end
106
+ File.open(config, 'w') {|f| f.write(contents) }
107
+ return true
108
+ end
109
+
110
+ # Get the OAuth authentication token from config or command line
111
+ # @param none
112
+ def get_github_token()
113
+ token = locate_config_value('github_token')
114
+ if token.nil? || token.empty?
115
+ return nil
116
+ else
117
+ return token
118
+ end
119
+ end
120
+
121
+ # Validate the OAuth authentication token for the knife-github application.
122
+ # @param username [String] validates the token for specific user. (default is ENV['USER'])
123
+ #
124
+ def validate_github_token(username=nil)
125
+ params = {}
126
+ username = ENV["USER"] if username.nil?
127
+
128
+ params[:url] = @github_url + "/api/" + @github_api_version + "/authorizations"
129
+ Chef::Log.debug("Validating token information for user: #{username}.")
130
+
131
+ params[:username] = username
132
+ params[:password] = Github::Password.get( "Please enter github password for #{username} :" )
133
+ params[:action] = "GET"
134
+
135
+ token_key = nil
136
+
137
+ result = send_request(params)
138
+ result.each do |token|
139
+ if token['app'] && token['app']['name'] == "knife-github (API)"
140
+ if token['scopes'].include?("delete_repo")
141
+ Chef::Log.debug("Found and using token: #{token_key}")
142
+ token_key = token['token']
143
+ else
144
+ Chef::Log.debug("Found token: #{token_key} but wrong scope, deleting token.")
145
+ params[:id] = token['id']
146
+ delete_github_token(params)
147
+ end
148
+ end
149
+ end
150
+
151
+ if token_key.nil?
152
+ result = create_github_token(params)
153
+ token_key = result['token']
154
+ end
155
+
156
+ return token_key
157
+ end
158
+
159
+
160
+ # Create the OAuth authentication token for the knife-github application.
161
+ # @param params [Hash] Hash containing all options
162
+ # params[:username] [String] Username if no token specified
163
+ # params[:password] [String] Password if no token specified
164
+ #
165
+ def create_github_token(params)
166
+ Chef::Log.debug("Creating new application token for user: #{username}.")
167
+ params[:url] = @github_url + "/api/" + @github_api_version + "/authorizations"
168
+ params[:body] = '{"note":"knife-github","scopes":["delete_repo", "user", "public_repo", "repo", "gist"]"}'
169
+ params[:action] = "POST"
170
+ send_request(params)
171
+ end
172
+
173
+ def delete_github_token(params)
174
+ Chef::Log.debug("Deleting token id: #{params[':id']}")
175
+ params[:url] = @github_url + "/api/" + @github_api_version + "/authorizations/#{params[:id]}"
176
+ params[:action] = "DELETE"
177
+ send_request(params)
178
+ end
179
+
180
+ # Post Get the OAuth authentication token from config or command line
181
+ # @param params [Hash] Hash containing all options
182
+ # params[:url] [String] Url to target
183
+ # params[:body] [JSON] json data for the request
184
+ # params[:token] [String] OAuth token
185
+ # params[:username] [String] Username if no token specified
186
+ # params[:password] [String] Password if no token specified
187
+ #
188
+ def send_request(params)
189
+ url = params[:url]
190
+
191
+ Chef::Log.debug("URL: " + url.to_s)
192
+
193
+ uri = URI.parse(url)
194
+ http = Net::HTTP.new(uri.host,uri.port)
195
+ if uri.scheme == "https"
196
+ http.use_ssl = true
197
+ if @github_ssl_verify_mode == "verify_none"
198
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
199
+ else
200
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
201
+ end
202
+ end
203
+
204
+ if params[:action] == "GET"
205
+ req = Net::HTTP::Get.new(uri.path)
206
+ elsif params[:action] == "POST"
207
+ req = Net::HTTP::Post.new(uri.path)
208
+ elsif params[:action] == "DELETE"
209
+ req = Net::HTTP::Delete.new(uri.path)
210
+ end
211
+
212
+ if params[:token].nil?
213
+ req.basic_auth params[:username], params[:password]
214
+ else
215
+ req.initheader = ({"Authorization" => "token #{params[:token]}"})
216
+ end
217
+ req.body = params[:body] if params[:body]
218
+ response = http.request(req)
219
+
220
+ unless response.code =~ /^2../ then
221
+ puts "Error #{response.code}: #{response.message}"
222
+ puts JSON.pretty_generate(JSON.parse(response.body))
223
+ puts "URL: #{url}"
224
+ exit 1
225
+ end
226
+
227
+ return nil if response.body.nil? || response.body.empty?
228
+
229
+ begin
230
+ json = JSON.parse(response.body)
231
+ rescue
232
+ ui.warn "The result on the REST Request is not in json format"
233
+ ui.warn "Output: " + response.body
234
+ exit 1
235
+ end
236
+ json
237
+ end
238
+ end
239
+ end
@@ -6,6 +6,9 @@ module Github
6
6
  config_context :knife do
7
7
  configurable :github_url
8
8
  configurable :github_organizations
9
+ configurable :github_link
10
+ configurable :github_api_version
11
+ configurable :github_ssl_verify_mode
9
12
  end
10
13
  end
11
14
  end
@@ -0,0 +1,85 @@
1
+ require 'termios'
2
+
3
+ module Github
4
+ class Password < String
5
+
6
+ # Turn local terminal echo on or off. This method is used for securing the
7
+ # display, so that a soon to be entered password will not be echoed to the
8
+ # screen. It is also used for restoring the display afterwards.
9
+ #
10
+ # If _masked_ is +true+, the keyboard is put into unbuffered mode, allowing
11
+ # the retrieval of characters one at a time. _masked_ has no effect when
12
+ # _on_ is +false+. You are unlikely to need this method in the course of
13
+ # normal operations.
14
+ #
15
+ def Password.echo(on=true, masked=false)
16
+ term = Termios::getattr( $stdin )
17
+
18
+ if on
19
+ term.c_lflag |= ( Termios::ECHO | Termios::ICANON )
20
+ else # off
21
+ term.c_lflag &= ~Termios::ECHO
22
+ term.c_lflag &= ~Termios::ICANON if masked
23
+ end
24
+
25
+ Termios::setattr( $stdin, Termios::TCSANOW, term )
26
+ end
27
+
28
+
29
+ # Get a password from _STDIN_, using buffered line input and displaying
30
+ # _message_ as the prompt. No output will appear while the password is being
31
+ # typed. Hitting <b>[Enter]</b> completes password entry. If _STDIN_ is not
32
+ # connected to a tty, no prompt will be displayed.
33
+ #
34
+ def Password.get(message="Password: ")
35
+ begin
36
+ if $stdin.tty?
37
+ Password.echo false
38
+ print message if message
39
+ end
40
+
41
+ pw = Password.new( $stdin.gets || "" )
42
+ pw.chomp!
43
+
44
+ ensure
45
+ if $stdin.tty?
46
+ Password.echo true
47
+ print "\n"
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ # Get a password from _STDIN_ in unbuffered mode, i.e. one key at a time.
54
+ # _message_ will be displayed as the prompt and each key press with echo
55
+ # _mask_ to the terminal. There is no need to hit <b>[Enter]</b> at the end.
56
+ #
57
+ def Password.getc(message="Password: ", mask='*')
58
+ # Save current buffering mode
59
+ buffering = $stdout.sync
60
+
61
+ # Turn off buffering
62
+ $stdout.sync = true
63
+
64
+ begin
65
+ Password.echo(false, true)
66
+ print message if message
67
+ pw = ""
68
+
69
+ while ( char = $stdin.getc ) != 10 # break after [Enter]
70
+ putc mask
71
+ pw << char
72
+ end
73
+
74
+ ensure
75
+ Password.echo true
76
+ print "\n"
77
+ end
78
+
79
+ # Restore original buffering mode
80
+ $stdout.sync = buffering
81
+
82
+ Password.new( pw )
83
+ end
84
+ end
85
+ end
@@ -1,6 +1,6 @@
1
1
  module Knife
2
2
  module Github
3
- VERSION = "0.0.9"
3
+ VERSION = "0.1.0"
4
4
  MAJOR, MINOR, TINY = VERSION.split('.')
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-11-18 00:00:00.000000000 Z
13
+ date: 2013-12-31 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: mixlib-versioning
@@ -28,6 +28,22 @@ dependencies:
28
28
  - - ! '>='
29
29
  - !ruby/object:Gem::Version
30
30
  version: 1.0.0
31
+ - !ruby/object:Gem::Dependency
32
+ name: ruby-termios
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 0.9.6
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 0.9.6
31
47
  - !ruby/object:Gem::Dependency
32
48
  name: chef
33
49
  requirement: !ruby/object:Gem::Requirement
@@ -91,14 +107,17 @@ files:
91
107
  - lib/chef/knife/github_cleanup.rb
92
108
  - lib/chef/knife/github_clone.rb
93
109
  - lib/chef/knife/github_compare.rb
94
- - lib/chef/knife/github_create.rb
95
110
  - lib/chef/knife/github_deploy.rb
96
111
  - lib/chef/knife/github_diff.rb
97
112
  - lib/chef/knife/github_list.rb
98
113
  - lib/chef/knife/github_pin.rb
114
+ - lib/chef/knife/github_repo_create.rb
115
+ - lib/chef/knife/github_repo_destroy.rb
99
116
  - lib/chef/knife/github_search.rb
117
+ - lib/chef/knife/github_token_create.rb
100
118
  - lib/knife-github/config.rb
101
119
  - lib/knife-github/connection.rb
120
+ - lib/knife-github/password.rb
102
121
  - lib/knife-github/repo.rb
103
122
  - lib/knife-github/version.rb
104
123
  homepage: https://github.com/schubergphilis/knife-github