summaryrefslogtreecommitdiff
path: root/bin/changelog
diff options
context:
space:
mode:
authorLin Jen-Shin <godfat@godfat.org>2016-11-04 02:41:01 +0800
committerLin Jen-Shin <godfat@godfat.org>2016-11-04 02:41:01 +0800
commit6a00cf1b9da3bc8bb1a9a270defd154bf7c93580 (patch)
treee82ee2a2bc25249f1a0f1a52344f277f87c3c010 /bin/changelog
parentf3c3d8e63ba078e55c0ce516e19ec11cea429e43 (diff)
parent3ac3106e585273b1d9c673c71f794ae018698f83 (diff)
downloadgitlab-ce-6a00cf1b9da3bc8bb1a9a270defd154bf7c93580.tar.gz
Merge remote-tracking branch 'upstream/master' into show-status-from-branch
* upstream/master: (74 commits) Clarify the author field for the changelog documentation Add and update .gitignore & .gitlab-ci.yml templates for 8.14 Update "Installation from source" guide for 8.14.0 Add CHANGELOG entries for latest patches Merge branch 'fix/import-export-symlink-vulnerability' into 'security' Merge branch 'fix/import-projectmember-security' into 'security' Use stubs instead of modifying global states Add changelog instructions to CHANGELOG.md Try not to include anything globally! Update help banner for bin/changelog Add a `--force` option to bin/changelog Update examples in changelog docs to use single quotes around title Use the server's base URL without relative URL part when creating links in JIRA Update docs and test description Update docs and unexpose token Make ESLint ignore instrumented files for coverage analysis (!7236) Initialize form validation on new group form. Check that JavaScript file names match convention (!7238) Unchange username_validator. Move snake_case to camelCase. ...
Diffstat (limited to 'bin/changelog')
-rwxr-xr-xbin/changelog170
1 files changed, 170 insertions, 0 deletions
diff --git a/bin/changelog b/bin/changelog
new file mode 100755
index 00000000000..b6586ebb6aa
--- /dev/null
+++ b/bin/changelog
@@ -0,0 +1,170 @@
+#!/usr/bin/env ruby
+#
+# Generate a changelog entry file in the correct location.
+#
+# Automatically stages the file and amends the previous commit if the `--amend`
+# argument is used.
+
+require 'optparse'
+require 'yaml'
+
+Options = Struct.new(
+ :amend,
+ :author,
+ :dry_run,
+ :force,
+ :merge_request,
+ :title
+)
+
+class ChangelogOptionParser
+ def self.parse(argv)
+ options = Options.new
+
+ parser = OptionParser.new do |opts|
+ opts.banner = "Usage: #{__FILE__} [options] [title]\n\n"
+
+ # Note: We do not provide a shorthand for this in order to match the `git
+ # commit` interface
+ opts.on('--amend', 'Amend the previous commit') do |value|
+ options.amend = value
+ end
+
+ opts.on('-f', '--force', 'Overwrite an existing entry') do |value|
+ options.force = value
+ end
+
+ opts.on('-m', '--merge-request [integer]', Integer, 'Merge Request ID') do |value|
+ options.merge_request = value
+ end
+
+ opts.on('-n', '--dry-run', "Don't actually write anything, just print") do |value|
+ options.dry_run = value
+ end
+
+ opts.on('-u', '--git-username', 'Use Git user.name configuration as the author') do |value|
+ options.author = git_user_name if value
+ end
+
+ opts.on('-h', '--help', 'Print help message') do
+ $stdout.puts opts
+ exit
+ end
+ end
+
+ parser.parse!(argv)
+
+ # Title is everything that remains, but let's clean it up a bit
+ options.title = argv.join(' ').strip.squeeze(' ').tr("\r\n", '')
+
+ options
+ end
+
+ def self.git_user_name
+ %x{git config user.name}.strip
+ end
+end
+
+class ChangelogEntry
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+
+ assert_feature_branch!
+ assert_new_file!
+ assert_title!
+
+ $stdout.puts "\e[32mcreate\e[0m #{file_path}"
+ $stdout.puts contents
+
+ unless options.dry_run
+ write
+ amend_commit if options.amend
+ end
+ end
+
+ def contents
+ YAML.dump(
+ 'title' => title,
+ 'merge_request' => options.merge_request,
+ 'author' => options.author
+ )
+ end
+
+ def write
+ File.write(file_path, contents)
+ end
+
+ def amend_commit
+ %x{git add #{file_path}}
+ exec("git commit --amend")
+ end
+
+ private
+
+ def fail_with(message)
+ $stderr.puts "\e[31merror\e[0m #{message}"
+ exit 1
+ end
+
+ def assert_feature_branch!
+ return unless branch_name == 'master'
+
+ fail_with "Create a branch first!"
+ end
+
+ def assert_new_file!
+ return unless File.exist?(file_path)
+ return if options.force
+
+ fail_with "#{file_path} already exists! Use `--force` to overwrite."
+ end
+
+ def assert_title!
+ return if options.title.length > 0 || options.amend
+
+ fail_with "Provide a title for the changelog entry or use `--amend`" \
+ " to use the title from the previous commit."
+ end
+
+ def title
+ if options.title.empty?
+ last_commit_subject
+ else
+ options.title
+ end
+ end
+
+ def last_commit_subject
+ %x{git log --format="%s" -1}.strip
+ end
+
+ def file_path
+ File.join(
+ unreleased_path,
+ branch_name.gsub(/[^\w-]/, '-') << '.yml'
+ )
+ end
+
+ def unreleased_path
+ File.join('changelogs', 'unreleased').tap do |path|
+ path << '-ee' if ee?
+ end
+ end
+
+ def ee?
+ @ee ||= File.exist?(File.expand_path('../CHANGELOG-EE.md', __dir__))
+ end
+
+ def branch_name
+ @branch_name ||= %x{git symbolic-ref HEAD}.strip.sub(%r{\Arefs/heads/}, '')
+ end
+end
+
+if $0 == __FILE__
+ options = ChangelogOptionParser.parse(ARGV)
+ ChangelogEntry.new(options)
+end
+
+# vim: ft=ruby