release_manager 0.3.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.bash_profile +27 -0
  3. data/.dockerignore +1 -0
  4. data/.env +3 -0
  5. data/.gitignore +13 -0
  6. data/.rspec +2 -0
  7. data/CHANGELOG.md +55 -0
  8. data/Dockerfile +14 -0
  9. data/Gemfile +14 -0
  10. data/Gemfile.lock +72 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +311 -0
  13. data/Rakefile +6 -0
  14. data/app_startup_script.sh +4 -0
  15. data/bin/console +14 -0
  16. data/bin/setup +8 -0
  17. data/docker-compose.yml +37 -0
  18. data/exe/bump-changelog +5 -0
  19. data/exe/deploy-mod +5 -0
  20. data/exe/release-mod +5 -0
  21. data/exe/sandbox-create +13 -0
  22. data/lib/release_manager.rb +33 -0
  23. data/lib/release_manager/changelog.rb +130 -0
  24. data/lib/release_manager/cli/deploy_mod_cli.rb +44 -0
  25. data/lib/release_manager/cli/release_mod_cli.rb +43 -0
  26. data/lib/release_manager/cli/sandbox_create_cli.rb +138 -0
  27. data/lib/release_manager/control_mod.rb +83 -0
  28. data/lib/release_manager/control_repo.rb +35 -0
  29. data/lib/release_manager/errors.rb +13 -0
  30. data/lib/release_manager/git/credentials.rb +98 -0
  31. data/lib/release_manager/git/utilites.rb +263 -0
  32. data/lib/release_manager/logger.rb +52 -0
  33. data/lib/release_manager/module_deployer.rb +77 -0
  34. data/lib/release_manager/puppet_module.rb +211 -0
  35. data/lib/release_manager/puppetfile.rb +148 -0
  36. data/lib/release_manager/release.rb +174 -0
  37. data/lib/release_manager/sandbox.rb +272 -0
  38. data/lib/release_manager/vcs_manager.rb +22 -0
  39. data/lib/release_manager/vcs_manager/gitlab_adapter.rb +112 -0
  40. data/lib/release_manager/vcs_manager/vcs_adapter.rb +22 -0
  41. data/lib/release_manager/version.rb +3 -0
  42. data/lib/release_manager/workflow_action.rb +5 -0
  43. data/release_manager.gemspec +38 -0
  44. data/setup_repos.rb +95 -0
  45. metadata +175 -0
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundle install
4
+ ssh-add
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "release_manager"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,37 @@
1
+ version: '2'
2
+ services:
3
+ app:
4
+ image: nwops/release_manager
5
+ build: .
6
+ entrypoint:
7
+ - ssh-agent
8
+ - /bin/bash
9
+ environment:
10
+ GEM_HOME: '/home/appuser/.gems'
11
+ BUNDLE_PATH: '/home/appuser/.bundle'
12
+ GITLAB_API_ENDPOINT: 'http://web/api/v3'
13
+ # the token may change so this really needs to be more dynamic
14
+ GITLAB_API_PRIVATE_TOKEN: 'A_zJJfgE8P-8mFGK2_r9'
15
+ R10K_REPO_URL: "git@web:devops/control-repo.git"
16
+ working_dir: /app
17
+ # command:
18
+ # - app_startup_script.sh
19
+ volumes:
20
+ - .:/app
21
+
22
+ web:
23
+ image: 'gitlab/gitlab-ce:latest'
24
+ restart: always
25
+ environment:
26
+ GITLAB_OMNIBUS_CONFIG: |
27
+ external_url 'http://web'
28
+ ports:
29
+ - '8000:80'
30
+ - '4433:443'
31
+ - '2201:22'
32
+ volumes:
33
+ - './srv/gitlab/config:/etc/gitlab'
34
+ - './srv/gitlab/logs:/var/log/gitlab'
35
+ - './srv/gitlab/data:/var/opt/gitlab'
36
+
37
+ # setup ssh key in gitlab
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'release_manager'
4
+
5
+ Changelog.run
data/exe/deploy-mod ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'release_manager'
3
+ require 'release_manager/cli/deploy_mod_cli'
4
+
5
+ ReleaseManager::DeployModCli.run
data/exe/release-mod ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'release_manager'
3
+ require 'release_manager/cli/release_mod_cli'
4
+
5
+ ReleaseManager::ReleaseModCli.run
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'release_manager'
4
+ require 'release_manager/cli/sandbox_create_cli'
5
+
6
+ # we also need to allow the user to list all the modules the sandbox currently controls
7
+ # in addition to allowing the user to update the list of modules
8
+ # sandbox-deploy
9
+ # sandbox-show or sandbox-list
10
+ # sandbox-update
11
+ # sandbox-create
12
+ #ENV['R10K_CONTROL_URL']
13
+ ReleaseManager::SandboxCreateCli.run
@@ -0,0 +1,33 @@
1
+ require "release_manager/version"
2
+ require "release_manager/module_deployer"
3
+ require "release_manager/release"
4
+ require "release_manager/changelog"
5
+ require 'release_manager/logger'
6
+ require 'release_manager/workflow_action'
7
+ require 'release_manager/sandbox'
8
+
9
+ class String
10
+ def colorize(color_code)
11
+ "\e[#{color_code}m#{self}\e[0m"
12
+ end
13
+
14
+ def red
15
+ colorize(31)
16
+ end
17
+
18
+ def green
19
+ colorize(32)
20
+ end
21
+
22
+ def fatal
23
+ red
24
+ end
25
+
26
+ def yellow
27
+ colorize(33)
28
+ end
29
+ end
30
+ module ReleaseManager
31
+
32
+ end
33
+
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ #
4
+ #Author: Corey Osman
5
+ #Purpose: updates the changelog.md file with the new version by reading the metadata.json file
6
+ #Usage: ./bump_log.rb [CHANGELOG.md]
7
+ #
8
+ require 'json'
9
+ require 'optparse'
10
+ require_relative 'puppet_module'
11
+ require 'release_manager/workflow_action'
12
+
13
+ class Changelog < WorkflowAction
14
+ attr_reader :root_dir, :version, :options
15
+
16
+ include ReleaseManager::Git::Utilities
17
+ include ReleaseManager::Logger
18
+
19
+ def initialize(module_path, version, options = {})
20
+ @options = options
21
+ @root_dir = module_path
22
+ @version = version
23
+ end
24
+
25
+ def create_changelog_file
26
+ return if File.exists?(changelog_file)
27
+ contents = "# Module Name\n\n## Unreleased\n"
28
+ File.write(changelog_file, contents)
29
+ logger.info("Creating initial changelog file")
30
+ commit_changelog("[ReleaseManager] - create empty changelog")
31
+ end
32
+
33
+ def path
34
+ @root_dir
35
+ end
36
+
37
+ def run
38
+ create_changelog_file
39
+ # write the new changelog unless it does not need updating
40
+ if already_released?
41
+ logger.fatal "Version #{version} had already been released, did you bump the version manually?"
42
+ exit 1
43
+ else
44
+ File.write(changelog_file, new_content)
45
+ commit_changelog if options[:commit]
46
+ logger.info "The changelog has been updated to version #{version}"
47
+ end
48
+ end
49
+
50
+ def self.run
51
+ options = {}
52
+ OptionParser.new do |opts|
53
+ opts.program_name = 'bump_changelog'
54
+ opts.version = ReleaseManager::VERSION
55
+ opts.on_head(<<-EOF
56
+
57
+ Summary: updates the changelog.md file with the new
58
+ version by reading the metadata.json file
59
+
60
+ EOF
61
+ )
62
+ opts.on("-c", "--[no-]commit", "Commit the updated changelog") do |c|
63
+ options[:commit] = c
64
+ end
65
+ opts.on("-f", "--changelog FILE", "Path to the changelog file") do |c|
66
+ options[:file] = c
67
+ end
68
+ end.parse!
69
+ unless options[:file]
70
+ puts "Must supply --changelog FILE"
71
+ exit 1
72
+ end
73
+ module_path = File.dirname(options[:file])
74
+ puppet_module = PuppetModule.new(module_path)
75
+ log = Changelog.new(module_path, puppet_module.version, options)
76
+ log.run
77
+ end
78
+
79
+ # @returns [String] the full path to the change log file
80
+ def changelog_file
81
+ options[:file] || File.join(root_dir, 'CHANGELOG.md')
82
+ end
83
+
84
+ # @returns [Array[String]]
85
+ def changelog_lines
86
+ @changelog_lines ||= File.readlines(changelog_file)
87
+ end
88
+
89
+ # @returns [Integer] line number of where the word unreleased is located
90
+ def unreleased_index
91
+ begin
92
+ linenum = changelog_lines.each_index.find {|index| changelog_lines[index] =~ /\A\s*\#{2}\s*Unreleased/i }
93
+ rescue ArgumentError => e
94
+ logger.fatal "Error with CHANGELOG.md #{e.message}"
95
+ exit 1
96
+ end
97
+ raise NoUnreleasedLine unless linenum
98
+ linenum
99
+ end
100
+
101
+ # @returns [Boolean] returns true if the Changelog has already released this version
102
+ def already_released?
103
+ !!changelog_lines.each_index.find {|index| changelog_lines[index] =~ /\A\s*\#{2}\s*Version #{version}/i }
104
+ end
105
+
106
+ # @returns [Array[String]] - inserts the version header in the change log and returns the entire array of lines
107
+ def update_unreleased
108
+ time = Time.now.strftime("%B %d, %Y")
109
+ changelog_lines.insert(unreleased_index + 1, "\n## Version #{version}\nReleased: #{time}\n")
110
+ end
111
+
112
+ # @returns [String] the string representation of the update Changelog file
113
+ def new_content
114
+ update_unreleased.join
115
+ end
116
+
117
+ # @return [String] the oid of the commit that was created
118
+ def commit_changelog(msg = nil)
119
+ add_file(changelog_file)
120
+ message = msg || "[ReleaseManager] - bump changelog to version #{version}"
121
+ create_commit(message)
122
+ end
123
+
124
+ # checks to make sure the unreleased line is valid, and the file exists
125
+ def self.check_requirements(path)
126
+ log = new(path, nil)
127
+ log.unreleased_index if File.exists?(log.changelog_file)
128
+ end
129
+ end
130
+
@@ -0,0 +1,44 @@
1
+ require 'release_manager/module_deployer'
2
+ require 'optparse'
3
+ require 'release_manager/version'
4
+ module ReleaseManager
5
+ class DeployModCli
6
+ def self.run
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.program_name = 'deploy-mod'
10
+ opts.version = ReleaseManager::VERSION
11
+ opts.on_head(<<-EOF
12
+
13
+ Summary: Gets the version of your module found in the metadata
14
+ and populates the r10k-control Puppetfile with the updated
15
+ tag version. Revmoes any branch or ref reference and replaces
16
+ with tag. Currently it is up to you to commit and push the Puppetfile change.
17
+
18
+ EOF
19
+ )
20
+ opts.on('-p', "--puppetfile [PUPPETFILE]", 'Path to R10k Puppetfile, defaults to ~/repos/r10k-control/Puppetfile') do |p|
21
+ options[:puppetfile] = p
22
+ end
23
+ opts.on('-m', '--modulepath [MODULEPATH]', "Path to to module, defaults to: #{Dir.getwd}") do |p|
24
+ options[:modulepath] = p
25
+ end
26
+ opts.on('-c', '--commit', 'Optionally, Commit the Puppetfile change') do |p|
27
+ options[:commit] = p
28
+ end
29
+ # removing this option as it seems a little dangerous for now
30
+ # opts.on('-u', '--push', 'Optionally, Push the changes to the remote') do |p|
31
+ # options[:push] = p
32
+ # end
33
+ # opts.on('-r', '--remote REMOTE', 'Optionally, specify a remote name or url to push changes to') do |p|
34
+ # options[:remote] = p
35
+ # end
36
+ opts.on('-d', 'Perform a dry run without making changes') do |p|
37
+ options[:dry_run] = p
38
+ end
39
+ end.parse!
40
+ m = ModuleDeployer.new(options)
41
+ m.run
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ require 'release_manager/release'
2
+ require 'optparse'
3
+ require 'release_manager/version'
4
+ module ReleaseManager
5
+ class ReleaseModCli
6
+ def self.run
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.program_name = 'release-mod'
10
+ opts.version = ReleaseManager::VERSION
11
+ opts.on_head(<<-EOF
12
+
13
+ Summary: Bumps the module version to the next revision and
14
+ updates the changelog.md file with the new
15
+ version by reading the metadata.json file. This should
16
+ be run inside a module directory.
17
+
18
+ EOF
19
+ )
20
+ opts.on("-d", "--dry-run", "Do a dry run, without making changes") do |c|
21
+ options[:dry_run] = c
22
+ end
23
+ opts.on('-a', '--auto', 'Run this script without interaction') do |c|
24
+ options[:auto] = c
25
+ end
26
+ opts.on('-m', '--module-path [MODULEPATH]', "The path to the module, defaults to #{Dir.getwd}") do |c|
27
+ options[:path] = c
28
+ end
29
+ opts.on('-b', '--no-bump', "Do not bump the version in metadata.json") do |c|
30
+ options[:bump] = c
31
+ end
32
+ opts.on('-r', '--repo [REPO]', "The repo to use, defaults to repo found in the metadata source") do |c|
33
+ options[:repo] = c
34
+ end
35
+ opts.on('--verbose', "Extra logging") do |c|
36
+ options[:verbose] = c
37
+ end
38
+ end.parse!
39
+ r = Release.new(options[:path], options)
40
+ r.run
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,138 @@
1
+ require 'release_manager/sandbox'
2
+ module ReleaseManager
3
+ class SandboxCreateCli
4
+
5
+ def self.gitlab_server
6
+ if ENV['GITLAB_API_ENDPOINT']
7
+ if data = ENV['GITLAB_API_ENDPOINT'].match(/(https?\:\/\/[\w\.]+)/)
8
+ return data[1]
9
+ end
10
+ end
11
+ 'https://gitlab.com'
12
+ end
13
+
14
+ def self.run
15
+ options = {}
16
+ OptionParser.new do |opts|
17
+ opts.program_name = 'sandbox-create'
18
+ opts.version = ReleaseManager::VERSION
19
+ opts.on_head(<<-EOF
20
+
21
+ Summary: Creates a new r10k sandbox by forking and creating a new branch on r10k-control,
22
+ creates a fork and branch for each module passed in, adds the upstream remote
23
+ for each module, updates the r10k-control Puppetfile to use those forks and branches
24
+ and pushes the branch to the upstream r10k-control.
25
+
26
+
27
+ Note: If you already have any of these modules cloned, this script will attempt to update those modules
28
+ using git fetch and git checkout -b sandbox_name upstream/master. So this should not destroy anything.
29
+
30
+ Configuration:
31
+
32
+ This script uses the following environment variables to automatically set some options, please ensure
33
+ they exist in your shell environment. You can set an environment variable in the shell or define
34
+ in your shell startup files.
35
+
36
+ Shell: export VARIABLE_NAME=value
37
+
38
+ R10K_REPO_URL - The git repo url to r10k-control (ie. git@gitlab.com:devops/r10k-control.git)
39
+ GITLAB_API_ENDPOINT - The api path to the gitlab server (ie. https://gitlab_server/api/v3)
40
+ replace gitlab_server with your server hostname
41
+ GITLAB_API_PRIVATE_TOKEN - The gitlab user api token.
42
+ You can get a token here (#{gitlab_server}/profile/personal_access_tokens,
43
+ Ensure api box is checked.
44
+ DEFAULT_MODULES - The default set of modules to fork use when
45
+ a sandbox (ie. export DEFAULT_MODULES='hieradata, roles')
46
+
47
+ DEFAULT_MEMBERS - The default members each forked project should add permissions
48
+ to ( ie, DEFAULT_MEMBERS='ci_runner,r10k_user' )
49
+
50
+ If your gitlab server has a invalid certificate you can set the following variable to "fix" that trust issue.
51
+ export GITLAB_API_HTTPARTY_OPTIONS="{verify: false}"
52
+
53
+ Examples:
54
+ #{opts.program_name} -n my_sandbox -m "roles,profiles,developer"
55
+ #{opts.program_name} -n my_sandbox -m "roles,profiles,developer" --members="p1dksk2,devops,ci_runner"
56
+
57
+ Options:
58
+ EOF
59
+ )
60
+ opts.on('--members DEFAULT_MEMBERS', "A comman seperated list of members to add to forked projects") do |m|
61
+ options[:default_members] = m.split(',').map(&:strip)
62
+ end
63
+ opts.on('-n', "--name NAME", "The name of your sandbox") do |n|
64
+ options[:sandbox_name] = n
65
+ end
66
+ opts.on('--control-url R10K_REPO_URL', "git url to the r10k-control repo, defaults to R10K_CONTROL_URL env variable") do |r|
67
+ options[:r10k_repo_url] = r
68
+ end
69
+ opts.on('-m', '--modules MODULES', "A comma separated list of modules names from the Puppetfile to fork and branch") do |c|
70
+ options[:modules] = c.split(',').map(&:strip)
71
+ end
72
+ opts.on('-r', '--repos-dir [REPOS_PATH]', "The repos path to clone modules to. " +
73
+ "Defaults to: #{File.expand_path(File.join(ENV['HOME'], 'repos'))}") do |c|
74
+ options[:repos_path] = c
75
+ end
76
+ opts.on('-c', '--control-dir [CONTROL_DIR]', "The r10k-control repo path. " +
77
+ "Defaults to: #{File.expand_path(File.join(ENV['HOME'], 'repos', 'r10k-control'))}") do |c|
78
+ options[:r10k_repo_path] = c
79
+ end
80
+ opts.on('--verbose', "Extra logging") do |c|
81
+ options[:verbose] = c
82
+ end
83
+ end.parse!
84
+ unless ENV['GITLAB_API_ENDPOINT']
85
+ puts "Please set the GITLAB_API_ENDPOINT environment variable".fatal
86
+ puts "Example: export GITLAB_API_ENDPOINT=https://gitlab.com/api/v3".fatal
87
+ exit 1
88
+ end
89
+
90
+ unless ENV['GITLAB_API_PRIVATE_TOKEN']
91
+ puts "Please set the GITLAB_API_PRIVATE_TOKEN environment variable".fatal
92
+ puts "Example: export GITLAB_API_PRIVATE_TOKEN=kdii2ljljijsldjfoa".fatal
93
+ exit 1
94
+ end
95
+
96
+ options[:modules] = add_defaults(:modules, options)
97
+ options[:default_members] = add_defaults(:default_members, options)
98
+
99
+ options[:r10k_repo_path] ||= File.expand_path(File.join(ENV['HOME'], 'repos', 'r10k-control'))
100
+ options[:repos_path] ||= File.expand_path(File.join(ENV['HOME'], 'repos'))
101
+ options[:r10k_repo_url] ||= ENV['R10K_REPO_URL']
102
+
103
+ unless options[:r10k_repo_url]
104
+ puts "Please set the R10K_REPO_URL environment variable or use the --control-url option".fatal
105
+ puts "Example: export R10K_REPO_URL='git@gitlab.com:devops/r10k-control.git'".fatal
106
+ exit 1
107
+ end
108
+ unless options[:sandbox_name]
109
+ puts "If you don't name your sandbox, you will not have anywhere to play".fatal
110
+ puts "Example: sandbox-create -n my_sandbox"
111
+ exit 1
112
+ end
113
+ unless options[:sandbox_name].length > 5
114
+ puts "Your sandbox name must be at least 6 characters long".fatal
115
+ puts "Example: sandbox-create -n my_sandbox"
116
+ exit 1
117
+ end
118
+ # check options to ensure all values are present
119
+ begin
120
+ s = Sandbox.create(options[:sandbox_name], options)
121
+ rescue InvalidToken => e
122
+ puts e.message.fatal
123
+ puts "Please update your Gitlab API token as it may be expired or incorrect".fatal
124
+ exit 1
125
+ end
126
+ end
127
+
128
+ def self.add_defaults(key, options)
129
+ if key == :modules
130
+ defaults = (ENV['DEFAULT_MODULES'] || '').split(',').map(&:strip)
131
+ ( options[:modules].to_a + defaults ).uniq
132
+ elsif key == :default_members
133
+ defaults = (ENV['DEFAULT_MEMBERS'] || '').split(',').map(&:strip)
134
+ ( options[:default_members].to_a + defaults ).uniq
135
+ end
136
+ end
137
+ end
138
+ end