pdksync 0.2.0 → 0.6.0

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