pdksync 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ # PUPPET VERSION PLATFORM COMPATIBILITY
2
+ #
3
+ # Define the Puppet version as a root key, then specify the OS platform(s) and versions(s) that this Puppet version is
4
+ # compatible with AND that you wish to test on. If you wish to exclude a platform from testing, simply omit it.
5
+ #
6
+ # OS names and versions must conform to the VMPooler nomenclature and conventions.
7
+ #
8
+ # Running 'bundle exec rake 'pdksync:generate_vmpooler_release_checks[7]' will generate an entry in the 'provision.yaml'
9
+ # for each managed module that contains a configuration that satisfies both:
10
+ # - The module's compatible platforms
11
+ # - The Puppet version's compatible platforms (in this example: '7')
12
+ #
13
+ # NOTE: arch will always be assumed to be 'x86_64'
14
+ ---
15
+ 5:
16
+ centos: ['5', '6', '7', '8']
17
+ debian: ['8', '9', '10']
18
+ oracle: ['5', '6', '7']
19
+ redhat: ['5', '6', '7', '8']
20
+ sles: ['12', '15']
21
+ scientific: ['6', '7']
22
+ ubuntu: ['14.04', '16.04', '18.04']
23
+ win: ['2008r2', '2012r2', '2016', '2019', '10-pro']
24
+ 6:
25
+ centos: ['5', '6', '7', '8']
26
+ debian: ['8', '9', '10']
27
+ oracle: ['5', '6', '7']
28
+ redhat: ['5', '6', '7', '8']
29
+ sles: ['12', '15']
30
+ scientific: ['6', '7']
31
+ ubuntu: ['14.04', '16.04', '18.04', '20.04']
32
+ win: ['2008r2', '2012r2', '2016', '2019', '10-pro']
33
+ 7:
34
+ centos: ['7', '8']
35
+ debian: ['9', '10']
36
+ oracle: ['7']
37
+ redhat: ['7', '8']
38
+ sles: ['12', '15']
39
+ scientific: ['7']
40
+ ubuntu: ['18.04', '20.04']
41
+ win: ['2012r2', '2016', '2019', '10-pro']
@@ -0,0 +1,155 @@
1
+ require 'yaml'
2
+ require 'pdk/version'
3
+ require 'ostruct'
4
+
5
+ # @summary
6
+ # A class used to contain a set of configuration variables
7
+ # @note
8
+ # Configuration is loaded from `$HOME/.pdksync.yml`. If $HOME is not set, the config_path will use the current directory.
9
+ # The configuration filename and path can be overridden with env variable PDK_CONFIG_PATH
10
+ # Set PDKSYNC_LABEL to '' to disable adding a label during pdksync runs.
11
+ module PdkSync
12
+ class Configuration < OpenStruct
13
+ SUPPORTED_SCM_PLATFORMS = [:github, :gitlab].freeze
14
+ PDKSYNC_FILE_NAME = 'pdksync.yml'.freeze
15
+
16
+ # Any key value added to the default config or custom config
17
+ # will automatically be a new configuration item and referenced
18
+ # via Configuration.new.<key_name> ie.
19
+ # c = Configuration.new
20
+ # c.api_endpoint
21
+ DEFAULT_CONFIG = {
22
+ namespace: 'puppetlabs',
23
+ pdksync_dir: 'modules_pdksync',
24
+ pdksync_gem_dir: 'gems_pdksync',
25
+ push_file_destination: 'origin',
26
+ create_pr_against: 'main',
27
+ managed_modules: 'managed_modules.yml',
28
+ pdksync_label: 'maintenance',
29
+ git_platform: :github,
30
+ git_base_uri: 'https://github.com',
31
+ gitlab_api_endpoint: 'https://gitlab.com/api/v4',
32
+ api_endpoint: nil,
33
+ pdk_templates_prefix: nil,
34
+ pdk_templates_ref: PDK::VERSION,
35
+ pdk_templates_url: 'https://github.com/puppetlabs/pdk-templates.git',
36
+ jenkins_platform: :jenkins,
37
+ jenkins_base_uri: 'https://jenkins.io',
38
+ jenkins_api_endpoint: '',
39
+ jenkins_server_url: '',
40
+ module_is_authoritive: true
41
+ }.freeze
42
+
43
+ # @param config_path [String] - the path to the pdk config file
44
+ def initialize(config_path = ENV['PDKSYNC_CONFIG_PATH'])
45
+ @config_path = locate_config_path(config_path)
46
+ @custom_config = DEFAULT_CONFIG.merge(custom_config(@config_path))
47
+ @custom_config[:pdk_templates_ref] = "#{@custom_config[:pdk_templates_prefix]}#{@custom_config[:pdk_templates_ref]}"
48
+ super(@custom_config)
49
+ valid_scm?(git_platform)
50
+ valid_access_token?
51
+ end
52
+
53
+ # @return [Hash] - returns the access settings for git scm
54
+ def git_platform_access_settings
55
+ @git_platform_access_settings ||= {
56
+ access_token: access_token,
57
+ gitlab_api_endpoint: gitlab_api_endpoint || api_endpoint,
58
+ api_endpoint: api_endpoint
59
+
60
+ }
61
+ end
62
+
63
+ def jenkins_platform_access_settings
64
+ @jenkins_platform_access_settings ||= {
65
+ jenkins_username: ENV['JENKINS_USERNAME'].freeze,
66
+ jenkins_password: ENV['JENKINS_PASSWORD'].freeze,
67
+ jenkins_api_endpoint: ''
68
+ }
69
+ end
70
+
71
+ # @return [Hash] - returns the access settings for gemfury account
72
+ def gemfury_access_settings
73
+ valid_access_token_gem_fury?
74
+ @gemfury_access_token = access_token_gem_fury
75
+ end
76
+
77
+ # @return [String] return a rendered string for pdk to use the templates
78
+ def templates
79
+ "--template-url=#{pdk_templates_url} --template-ref=#{pdk_templates_ref}"
80
+ end
81
+
82
+ # @param path [String] path to the pdksync config file in yaml format
83
+ # @return [Hash] the custom configuration as a hash
84
+ def custom_config(path = nil)
85
+ return {} unless path
86
+ return {} unless File.exist?(path)
87
+ c = (YAML.load_file(path) || {}).transform_keys_to_symbols
88
+ c[:git_base_uri] ||= 'https://gitlab.com' if c[:git_platform].eql?(:gitlab)
89
+ c
90
+ end
91
+
92
+ # @return [String] the path the pdksync config file, nil if not found
93
+ def locate_config_path(custom_file = nil)
94
+ files = [
95
+ custom_file,
96
+ PDKSYNC_FILE_NAME,
97
+ File.join(ENV['HOME'], PDKSYNC_FILE_NAME)
98
+ ]
99
+ files.find { |file| file && File.exist?(file) }
100
+ end
101
+
102
+ private
103
+
104
+ # @return [Boolean] true if the supported platforms were specified correctly
105
+ # @param scm [Symbol] - the scm type (:github or :gitlab)
106
+ def valid_scm?(scm)
107
+ unless SUPPORTED_SCM_PLATFORMS.include?(scm)
108
+ raise ArgumentError, "Unsupported Git hosting platform '#{scm}'."\
109
+ " Supported platforms are: #{SUPPORTED_SCM_PLATFORMS.join(', ')}"
110
+ end
111
+ true
112
+ end
113
+
114
+ # @return [Boolean] true if the access token for the scm platform was supplied
115
+ def valid_access_token?
116
+ if access_token.nil?
117
+ raise ArgumentError, "Git platform access token for #{git_platform.capitalize} not set"\
118
+ " - use 'export #{git_platform.upcase}_TOKEN=\"<your token>\"' to set"
119
+ end
120
+ true
121
+ end
122
+
123
+ # @return [Boolean] true if the access token for the gemfury was supplied
124
+ def valid_access_token_gem_fury?
125
+ if access_token_gem_fury.nil?
126
+ raise 'Gemfury access token not set'\
127
+ " - use 'export GEMFURY_TOKEN=\"<your token>\"' to set"
128
+ end
129
+ true
130
+ end
131
+
132
+ # @return [String] the platform specific access token
133
+ def access_token
134
+ case git_platform
135
+ when :github
136
+ ENV['GITHUB_TOKEN'].freeze
137
+ when :gitlab
138
+ ENV['GITLAB_TOKEN'].freeze
139
+ end
140
+ end
141
+
142
+ # @return [String] the gem_fury access token
143
+ def access_token_gem_fury
144
+ ENV['GEMFURY_TOKEN'].freeze
145
+ end
146
+ end
147
+ end
148
+
149
+ # monkey patch
150
+ class Hash
151
+ # take keys of hash and transform those to a symbols
152
+ def transform_keys_to_symbols
153
+ each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v; }
154
+ end
155
+ end
@@ -0,0 +1,77 @@
1
+ require 'octokit'
2
+
3
+ # @summary
4
+ # This class wraps Octokit::Client and provides the method implementations
5
+ # required by pdksync main to access the Github API for creating pull
6
+ # requests, adding labels, and so forth.
7
+ class PdkSync::GithubClient
8
+ # @summary
9
+ # Creates a new Octokit::Client and logs in the user based on the
10
+ # supplied access token
11
+ # @param access_token
12
+ # The Github access token, required to access the Github API
13
+ def initialize(access_token, api_endpoint = nil)
14
+ # USE ENV['OCTOKIT_API_ENDPOINT'] or pass in the api_endpoint
15
+ Octokit.configure { |c| c.api_endpoint = api_endpoint } unless api_endpoint.nil?
16
+ @client = Octokit::Client.new(access_token: access_token.to_s)
17
+ @client.user.login
18
+ end
19
+
20
+ # @summary Checks if the supplied repository exists on the Git hosting platform
21
+ # @param [String] repository
22
+ # The full repository name, i.e. "namespace/repo_name"
23
+ # @return [Boolean] true if the repository exists, false otherwise
24
+ def repository?(repository)
25
+ @client.repository?(repository)
26
+ end
27
+
28
+ # @summary Creates a new pull request against the Git hosting platform
29
+ # @param [String] repo_name
30
+ # The full repository name, i.e. "namespace/repo_name" in which to create
31
+ # the pull request
32
+ # @param [String] create_pr_against
33
+ # The target branch against which to create the pull request
34
+ # @param [String] head
35
+ # The source branch from which to create the pull request
36
+ # @param [String] title
37
+ # The title/name of the pull request to create
38
+ # @param [String] message
39
+ # The pull request message/body
40
+ # @return An Octokit pull request object for the newly created pull request
41
+ def create_pull_request(repo_name, create_pr_against, head, title, message)
42
+ @client.create_pull_request(repo_name, create_pr_against, head, title, message)
43
+ end
44
+
45
+ # @summary Gets the labels available in the repository
46
+ # @param [String] repo_name
47
+ # The full repository name, i.e. "namespace/repo_name", from which to get
48
+ # the available labels
49
+ # @return [Array] List of available labels in the repository
50
+ def labels(repo_name)
51
+ @client.labels(repo_name)
52
+ end
53
+
54
+ # @summary Updates an existing issue/pull request in the repository
55
+ # @param [String] repo_name
56
+ # The full repository name, i.e. "namespace/repo_name" in which to update
57
+ # the issue
58
+ # @param [Integer] issue_number
59
+ # The id number of the issue/pull request to update
60
+ # @param [Hash] options
61
+ # A hash of options definint the changes to the issue
62
+ # @return An Octokit issue object of the updated issue
63
+ def update_issue(repo_name, issue_number, options)
64
+ @client.update_issue(repo_name, issue_number, options)
65
+ end
66
+
67
+ # @summary Deletes a branch in the repository
68
+ # @param [String] repo_name
69
+ # The full repository name, i.e. "namespace/repo_name" in which to delete
70
+ # the branch
71
+ # @param [String] branch_name
72
+ # The name of the branch to delete
73
+ # @return [Boolean] true on success, false on failure
74
+ def delete_branch(repo_name, branch_name)
75
+ @client.delete_branch(repo_name, branch_name)
76
+ end
77
+ end
@@ -0,0 +1,91 @@
1
+ require 'gitlab'
2
+
3
+ # @summary
4
+ # This class wraps Gitlab::Client and provides the method implementations
5
+ # required by pdksync main to access the Gitlab API for creating merge
6
+ # requests, adding labels, and so forth.
7
+ class PdkSync::GitlabClient
8
+ # @summary
9
+ # Creates a new Gitlab::Client and logs in the user based on the
10
+ # supplied access token and the Gitlab API endpoint URL
11
+ # @param [String] access_token
12
+ # The Gitlab private access token, required to access the Gitlab API
13
+ # @param [String] gitlab_api_endpoint
14
+ # URL to the Gitlab API endpoint against which to work
15
+ def initialize(access_token, gitlab_api_endpoint)
16
+ @client = Gitlab.client(endpoint: gitlab_api_endpoint, private_token: access_token)
17
+ end
18
+
19
+ # @summary Checks if the supplied project exists on the Git hosting platform
20
+ # @param [String] project
21
+ # The full repository name, i.e. "namespace/project"
22
+ # @return [Boolean] true if the project exists, false otherwise
23
+ def repository?(project)
24
+ @client.project(project)
25
+
26
+ true
27
+ rescue Gitlab::Error::NotFound
28
+ false
29
+ end
30
+
31
+ # @summary
32
+ # Creates a new merge request (i.e. pull request) against the Gitlab
33
+ # platform
34
+ # @param [String] project
35
+ # The full project name, i.e. "namespace/project" in which to create
36
+ # the merge request
37
+ # @param [String] target_branch
38
+ # The target branch against which to create the merge request
39
+ # @param [String] source_branch
40
+ # The source branch from which to create the merge request
41
+ # @param [String] title
42
+ # The title/name of the merge request to create
43
+ # @param [String] message
44
+ # The pull request message/body
45
+ # @return
46
+ # A Gitlab merge request object for the newly created merge request
47
+ def create_pull_request(project, target_branch, source_branch, title, message)
48
+ mr_options = {
49
+ source_branch: source_branch,
50
+ target_branch: target_branch,
51
+ description: message
52
+ }
53
+ @client.create_merge_request(project, title, mr_options)
54
+ end
55
+
56
+ # @summary Gets the labels available in the project
57
+ # @param [String] project
58
+ # The full project name, i.e. "namespace/project", from which to get
59
+ # the available labels
60
+ # @return [Array] List of available labels in the project
61
+ def labels(project)
62
+ @client.labels(project)
63
+ end
64
+
65
+ # @summary Updates an existing merge request in the repository
66
+ # @note This method is specifically used to set labels for a merge request
67
+ # @param [String] project
68
+ # The full project name, i.e. "namespace/project" in which to update
69
+ # the issue
70
+ # @param [Integer] id
71
+ # The id number of the merge request to update
72
+ # @param [Hash] options
73
+ # A hash of options defining the changes to the merge request
74
+ # @return A Gitlab merge request object of the updated merge request
75
+ def update_issue(project, id, options)
76
+ # Gitlab requires labels to be supplied as a comma-separated string
77
+ labels = options[:labels].join(',')
78
+ @client.update_merge_request(project, id, labels: labels)
79
+ end
80
+
81
+ # @summary Deletes a branch in the project
82
+ # @param [String] project
83
+ # The full project name, i.e. "namespace/project" in which to delete
84
+ # the branch
85
+ # @param [String] branch_name
86
+ # The name of the branch to delete
87
+ # @return [Boolean] true on success, false on failure
88
+ def delete_branch(project, branch_name)
89
+ @client.delete_branch(project, branch_name)
90
+ end
91
+ end
@@ -0,0 +1,109 @@
1
+ require 'pdksync/pullrequest'
2
+
3
+ # @summary
4
+ # The GitPlatformClient class creates a PdkSync::GithubClient or
5
+ # PdkSync::GitlabClient and provides methods wrapping the client's
6
+ # corresponding methods
7
+ class PdkSync::GitPlatformClient
8
+ # @summary
9
+ # Creates a PdkSync::GithubClient or PdkSync::GitlabClient based on the
10
+ # value of git_platform.
11
+ # @param [Symbol] git_platform
12
+ # The symbol designating the Git hosting platform to use and thus which
13
+ # client to create
14
+ # @param [Hash] git_platform_access_settings
15
+ # Hash of Git platform access settings, such as access_token or
16
+ # gitlab_api_endpoint. access_token is always required,
17
+ # gitlab_api_endpoint only for Gitlab.
18
+ def initialize(git_platform, git_platform_access_settings)
19
+ @git_platform = git_platform
20
+
21
+ # TODO: raise exceptions when git_platform_access_settings hash is not
22
+ # set up correctly? Or let PdkSync::GithubClient or PdkSync::GitlabClient
23
+ # raise errors later and let them propagate upwards?
24
+ access_token = git_platform_access_settings[:access_token]
25
+ @client = case git_platform
26
+ when :github
27
+ require 'pdksync/githubclient'
28
+
29
+ PdkSync::GithubClient.new(access_token, git_platform_access_settings[:api_endpoint])
30
+ when :gitlab
31
+ require 'pdksync/gitlabclient'
32
+
33
+ gitlab_api_endpoint = git_platform_access_settings[:gitlab_api_endpoint] || git_platform_access_settings[:api_endpoint]
34
+ PdkSync::GitlabClient.new(access_token, gitlab_api_endpoint)
35
+ end
36
+ end
37
+
38
+ # @summary Checks if the supplied project exists on the Git hosting platform
39
+ # @param [String] project
40
+ # The full repository name, i.e. "namespace/project"
41
+ # @return [Boolean] true if the project exists, false otherwise
42
+ def repository?(project)
43
+ @client.repository?(project)
44
+ end
45
+
46
+ # @summary
47
+ # Creates a new pull/merge request against the Git hosting platform and
48
+ # wraps the Github or Gitlab result in a PdkSync::PullRequest object for
49
+ # consumption by pdksync main
50
+ # @param [String] project
51
+ # The full project name, i.e. "namespace/project" in which to create
52
+ # the pull/merge request
53
+ # @param [String] target_branch
54
+ # The target branch against which to create the pull/merge request
55
+ # @param [String] source_branch
56
+ # The source branch from which to create the pull/merge request
57
+ # @param [String] title
58
+ # The title/name of the pull/merge request to create
59
+ # @param [String] message
60
+ # The pull/merge request message/body
61
+ # @return [PdkSync::PullRequest]
62
+ # A pdksync pull request object for the newly created pull/merge request
63
+ # for consumption by pdksync main
64
+ def create_pull_request(project, target_branch, source_branch, title, message)
65
+ client_pr = @client.create_pull_request(project, target_branch, source_branch, title, message)
66
+ pr = case @git_platform
67
+ when :github
68
+ PdkSync::PullRequest.github(client_pr)
69
+ when :gitlab
70
+ PdkSync::PullRequest.gitlab(client_pr)
71
+ end
72
+ pr
73
+ end
74
+
75
+ # @summary Gets the labels available in the project
76
+ # @param [String] project
77
+ # The full project name, i.e. "namespace/project", from which to get
78
+ # the available labels
79
+ # @return [Array] List of available labels in the project
80
+ def labels(project)
81
+ @client.labels(project)
82
+ end
83
+
84
+ # @summary Updates an existing pull/merge request in the repository
85
+ # @note
86
+ # This method is specifically used to set labels for a pull/merge request
87
+ # @param [String] project
88
+ # The full project name, i.e. "namespace/project" in which to update
89
+ # the issue
90
+ # @param [Integer] id
91
+ # The id number of the pull/merge request to update
92
+ # @param [Hash] options
93
+ # A hash of options defining the changes to the pull/merge request
94
+ # @return A pull/merge request object of the updated pull/merge request
95
+ def update_issue(project, id, options)
96
+ @client.update_issue(project, id, options)
97
+ end
98
+
99
+ # @summary Deletes a branch in the project
100
+ # @param [String] project
101
+ # The full project name, i.e. "namespace/project" in which to delete
102
+ # the branch
103
+ # @param [String] branch_name
104
+ # The name of the branch to delete
105
+ # @return [Boolean] true on success, false on failure
106
+ def delete_branch(project, branch_name)
107
+ @client.delete_branch(project, branch_name)
108
+ end
109
+ end