release_manager 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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