From 8374fd15d771556dcbc43838fcd93f0c2c5d10c1 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Sun, 13 Nov 2016 22:20:38 +0100 Subject: [PATCH 1/6] Extract Project Name Finder class --- lib/github_changelog_generator/parser.rb | 114 +---------------- .../project_name_finder.rb | 117 ++++++++++++++++++ lib/github_changelog_generator/task.rb | 4 +- spec/unit/parser_spec.rb | 79 ------------ spec/unit/project_name_finder_spec.rb | 111 +++++++++++++++++ 5 files changed, 237 insertions(+), 188 deletions(-) create mode 100644 lib/github_changelog_generator/project_name_finder.rb create mode 100644 spec/unit/project_name_finder_spec.rb diff --git a/lib/github_changelog_generator/parser.rb b/lib/github_changelog_generator/parser.rb index 7268b8951..5b0479bfc 100755 --- a/lib/github_changelog_generator/parser.rb +++ b/lib/github_changelog_generator/parser.rb @@ -19,9 +19,12 @@ def self.parse_options abort [e, parser].join("\n") end - fetch_user_and_project(options) - - abort(parser.banner) unless options[:user] && options[:project] + unless options[:user] && options[:project] + user, project = ProjectNameFinder.new(options, ARGV.dup).call + options[:user] = user + options[:project] = project + abort(parser.banner) unless options[:user] && options[:project] + end print_options(options) @@ -224,110 +227,5 @@ def self.default_options http_cache: true ) end - - # If `:user` or `:project` not set in options, try setting them - # Valid unnamed parameters: - # 1) in 1 param: repo_name/project - # 2) in 2 params: repo name project - def self.fetch_user_and_project(options) - if options[:user].nil? || options[:project].nil? - user, project = user_and_project_from_git(options, ARGV[0], ARGV[1]) - options[:user] ||= user - options[:project] ||= project - end - end - - # Sets `:user` and `:project` in `options` from CLI arguments or `git remote` - # @param [String] arg0 first argument in cli - # @param [String] arg1 second argument in cli - # @return [Array] user and project, or nil if unsuccessful - def self.user_and_project_from_git(options, arg0 = nil, arg1 = nil) - user, project = user_project_from_option(arg0, arg1, options[:github_site]) - unless user && project - if ENV["RUBYLIB"] =~ /ruby-debug-ide/ - user = "skywinder" - project = "changelog_test" - else - remote = `git config --get remote.#{options[:git_remote]}.url` - user, project = user_project_from_remote(remote) - end - end - - [user, project] - end - - # Returns GitHub username and project from CLI arguments - # - # @param arg0 [String] This parameter takes two forms: Either a full - # GitHub URL, or a 'username/projectname', or - # simply a GitHub username - # @param arg1 [String] If arg0 is given as a username, - # then arg1 can given as a projectname - # @param github_site [String] Domain name of GitHub site - # - # @return [Array, nil] user and project, or nil if unsuccessful - def self.user_project_from_option(arg0, arg1, github_site) - user = nil - project = nil - github_site ||= "github.com" - if arg0 && !arg1 - # this match should parse strings such "https://github.com/skywinder/Github-Changelog-Generator" or - # "skywinder/Github-Changelog-Generator" to user and name - match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(arg0) - - begin - param = match[2].nil? - rescue - puts "Can't detect user and name from first parameter: '#{arg0}' -> exit'" - return - end - if param - return - else - user = match[1] - project = match[2] - end - end - [user, project] - end - - # These patterns match these formats: - # - # ``` - # origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch) - # git@github.com:skywinder/Github-Changelog-Generator.git - # ``` - # - # and - # - # ``` - # origin https://github.com/skywinder/ChangelogMerger (fetch) - # https://github.com/skywinder/ChangelogMerger - # ``` - GIT_REMOTE_PATTERNS = [ - /.*(?:[:\/])(?(?:-|\w|\.)*)\/(?(?:-|\w|\.)*)(?:\.git).*/, - /.*\/(?(?:-|\w|\.)*)\/(?(?:-|\w|\.)*).*/ - ] - - # Returns GitHub username and project from git remote output - # - # @param git_remote_output [String] Output of git remote command - # - # @return [Array] user and project - def self.user_project_from_remote(git_remote_output) - user = nil - project = nil - GIT_REMOTE_PATTERNS.each do |git_remote_pattern| - git_remote_pattern =~ git_remote_output - - if Regexp.last_match - user = Regexp.last_match(:user) - project = Regexp.last_match(:project) - break - end - end - - [user, project] - end end end diff --git a/lib/github_changelog_generator/project_name_finder.rb b/lib/github_changelog_generator/project_name_finder.rb new file mode 100644 index 000000000..06e279cc7 --- /dev/null +++ b/lib/github_changelog_generator/project_name_finder.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true +module GitHubChangelogGenerator + class ProjectNameFinder + attr_reader :options, :args + + # @param options [Options] + # @param args [Array] ARGV or the empty list + # @option args [String] 0 This parameter takes two forms: Either a full + # GitHub URL, or a 'username/projectname', or + # simply a GitHub username + # @option args [String] 1 If args[0] is given as a username, + # then args[1] can given as a projectname + def initialize(options, args) + @options = options + @args = args + end + + FIXED_TEST_PROJECT = %w(skywinder changelog_test) + + # Returns a tuple of user and project from CLI arguments or git remote. + # + # @return [Array] user and project, or nil if unsuccessful + def call + [ + -> { from_cli_option }, + -> { FIXED_TEST_PROJECT if in_development? }, + -> { from_git_remote } + ].find(-> { proc { [nil, nil] } }) do |strategy| + user, project = strategy.call + user && project + end.call + end + + def in_development? + ENV["RUBYLIB"] =~ /ruby-debug-ide/ + end + + # Returns GitHub username and project from CLI arguments + # + # @return [Array, nil] user and project, or nil if unsuccessful + def from_cli_option + user = nil + project = nil + if args[0] && !args[1] + # this match should parse strings such "https://github.com/skywinder/Github-Changelog-Generator" or + # "skywinder/Github-Changelog-Generator" to user and name + match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(args[0]) + + begin + param = match[2].nil? + rescue + puts "Can't detect user and name from first parameter: '#{args[0]}' -> exit'" + return + end + if param + return [nil, nil] + else + user = match[1] + project = match[2] + end + end + [user, project] + end + + # These patterns match these formats: + # + # ``` + # origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch) + # git@github.com:skywinder/Github-Changelog-Generator.git + # ``` + # + # and + # + # ``` + # origin https://github.com/skywinder/ChangelogMerger (fetch) + # https://github.com/skywinder/ChangelogMerger + # ``` + GIT_REMOTE_PATTERNS = [ + /.*(?:[:\/])(?(?:-|\w|\.)*)\/(?(?:-|\w|\.)*)(?:\.git).*/, + /.*\/(?(?:-|\w|\.)*)\/(?(?:-|\w|\.)*).*/ + ] + + # Returns GitHub username and project from git remote output + # + # @return [Array] user and project + def from_git_remote + user = nil + project = nil + GIT_REMOTE_PATTERNS.each do |git_remote_pattern| + git_remote_pattern =~ git_remote_content + + if Regexp.last_match + user = Regexp.last_match(:user) + project = Regexp.last_match(:project) + break + end + end + + [user, project] + end + + # @return [String] Output of git remote command + def git_remote_content + @git_remote_content ||= `git config --get remote.#{git_remote}.url` + end + + private + + def github_site + options[:github_site] || "github.com" + end + + def git_remote + options[:git_remote] + end + end +end diff --git a/lib/github_changelog_generator/task.rb b/lib/github_changelog_generator/task.rb index c9dfd6e04..373a21db9 100644 --- a/lib/github_changelog_generator/task.rb +++ b/lib/github_changelog_generator/task.rb @@ -48,7 +48,9 @@ def define(args, &task_block) # mimick parse_options options = Parser.default_options - Parser.fetch_user_and_project(options) + user, project = ProjectNameFinder.new(options, []).call + options[:user] = user + options[:project] = project OPTIONS.each do |o| v = instance_variable_get("@#{o}") diff --git a/spec/unit/parser_spec.rb b/spec/unit/parser_spec.rb index e90900b7e..de376cffe 100644 --- a/spec/unit/parser_spec.rb +++ b/spec/unit/parser_spec.rb @@ -1,83 +1,4 @@ # frozen_string_literal: true describe GitHubChangelogGenerator::Parser do - describe ".user_project_from_remote" do - context "when remote is type 1" do - subject { GitHubChangelogGenerator::Parser.user_project_from_remote("origin https://github.com/skywinder/ActionSheetPicker-3.0 (fetch)") } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - context "when remote is type 2" do - subject { GitHubChangelogGenerator::Parser.user_project_from_remote("https://github.com/skywinder/ActionSheetPicker-3.0") } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - context "when remote is type 3" do - subject { GitHubChangelogGenerator::Parser.user_project_from_remote("https://github.com/skywinder/ActionSheetPicker-3.0") } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - context "when remote is type 4" do - subject { GitHubChangelogGenerator::Parser.user_project_from_remote("origin git@github.com:skywinder/ActionSheetPicker-3.0.git (fetch)") } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - context "when remote is invalid" do - subject { GitHubChangelogGenerator::Parser.user_project_from_remote("some invalid text") } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } - end - end - describe ".user_project_from_option" do - context "when option is invalid" do - it("should return nil") { expect(GitHubChangelogGenerator::Parser.user_project_from_option("blah", nil, nil)).to be_nil } - end - - context "when option is valid" do - subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, nil) } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - context "when option nil" do - subject { GitHubChangelogGenerator::Parser.user_project_from_option(nil, nil, nil) } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } - end - context "when site is nil" do - subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, nil) } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - context "when site is valid" do - subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, "https://codeclimate.com") } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - context "when second arg is not nil" do - subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", "blah", nil) } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } - end - context "when all args is not nil" do - subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", "blah", "https://codeclimate.com") } - it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } - end - end - describe ".fetch_user_and_project" do - before do - stub_const("ARGV", ["https://github.com/skywinder/github-changelog-generator"]) - end - - context do - let(:valid_user) { "initialized_user" } - let(:options) { { user: valid_user } } - let(:options_before_change) { options.dup } - it "should leave user unchanged" do - expect { GitHubChangelogGenerator::Parser.fetch_user_and_project(options) }.to change { options } - .from(options_before_change) - .to(options_before_change.merge(project: "github-changelog-generator")) - end - end - end end diff --git a/spec/unit/project_name_finder_spec.rb b/spec/unit/project_name_finder_spec.rb new file mode 100644 index 000000000..3181f8056 --- /dev/null +++ b/spec/unit/project_name_finder_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true +require "github_changelog_generator/project_name_finder" +RSpec.describe GitHubChangelogGenerator::ProjectNameFinder do + context "with the empty options" do + it "finds nothing" do + expect(described_class.new({}, []).call).to eq [nil, nil] + end + end + + describe "#from_git_remote" do + subject do + finder = described_class.new({}, []) + allow(finder).to receive(:git_remote_content) { prepared_content } + finder.from_git_remote + end + + context "when remote is type 1" do + let(:prepared_content) { "origin https://github.com/skywinder/ActionSheetPicker-3.0 (fetch)" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + end + + context "when remote is type 2" do + let(:prepared_content) { "https://github.com/skywinder/ActionSheetPicker-3.0" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + end + + context "when remote is type 3" do + let(:prepared_content) { "https://github.com/skywinder/ActionSheetPicker-3.0" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + end + + context "when remote is type 4" do + let(:prepared_content) { "origin git@github.com:skywinder/ActionSheetPicker-3.0.git (fetch)" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + end + + context "when remote is invalid" do + let(:prepared_content) { "some invalid text" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array([nil, nil]) } + end + end + + describe "#from_cli_option" do + let(:github_site) { nil } + subject do + described_class.new({ github_site: github_site }, [arg1, arg2]).call + end + + context "when option is invalid" do + it("should return the empty tuple") { expect(described_class.new({ github_site: nil }, ["blah", nil]).call).to eq [nil, nil] } + end + + context "when option is valid" do + let(:arg1) { "skywinder/ActionSheetPicker-3.0" } + let(:arg2) { nil } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + end + context "when option nil" do + let(:arg1) { nil } + let(:arg2) { nil } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array([nil, nil]) } + end + context "when site is nil" do + let(:arg1) { "skywinder/ActionSheetPicker-3.0" } + let(:arg2) { nil } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + end + + context "when site is valid" do + let(:arg1) { "skywinder/ActionSheetPicker-3.0" } + let(:arg2) { nil } + let(:github_site) { "https://codeclimate.com" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + end + + context "when second arg is not nil" do + let(:arg1) { "skywinder/ActionSheetPicker-3.0" } + let(:arg2) { "blah" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array([nil, nil]) } + end + + context "when all args is not nil" do + let(:arg1) { "skywinder/ActionSheetPicker-3.0" } + let(:arg2) { "blah" } + let(:github_site) { "https://codeclimate.com" } + + it { is_expected.to be_a(Array) } + it { is_expected.to match_array([nil, nil]) } + end + end +end From c8c86d5a879c453a307fb1a37766913eabb5bfe0 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Mon, 14 Nov 2016 23:53:34 +0100 Subject: [PATCH 2/6] ProjectNameFinder: Less proc --- lib/github_changelog_generator/project_name_finder.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/github_changelog_generator/project_name_finder.rb b/lib/github_changelog_generator/project_name_finder.rb index 06e279cc7..a98f284d0 100644 --- a/lib/github_changelog_generator/project_name_finder.rb +++ b/lib/github_changelog_generator/project_name_finder.rb @@ -25,10 +25,10 @@ def call -> { from_cli_option }, -> { FIXED_TEST_PROJECT if in_development? }, -> { from_git_remote } - ].find(-> { proc { [nil, nil] } }) do |strategy| + ].find(-> { [nil, nil] }) do |strategy| user, project = strategy.call - user && project - end.call + break [user, project] if user && project + end end def in_development? From 7793f726ab290ab7c826058e210d0d409d12022d Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Fri, 2 Dec 2016 22:48:53 +0100 Subject: [PATCH 3/6] Docs typo --- lib/github_changelog_generator/project_name_finder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/project_name_finder.rb b/lib/github_changelog_generator/project_name_finder.rb index a98f284d0..18f079407 100644 --- a/lib/github_changelog_generator/project_name_finder.rb +++ b/lib/github_changelog_generator/project_name_finder.rb @@ -9,7 +9,7 @@ class ProjectNameFinder # GitHub URL, or a 'username/projectname', or # simply a GitHub username # @option args [String] 1 If args[0] is given as a username, - # then args[1] can given as a projectname + # then args[1] can be given as a projectname def initialize(options, args) @options = options @args = args From 81851f9b42ae4c2ac78ffafd07e18f212950b81b Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Fri, 2 Dec 2016 23:21:32 +0100 Subject: [PATCH 4/6] Improve ProjectNameFinder tests, naming --- .../project_name_finder.rb | 57 ++++++++++--------- spec/unit/project_name_finder_spec.rb | 25 ++++---- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/lib/github_changelog_generator/project_name_finder.rb b/lib/github_changelog_generator/project_name_finder.rb index 18f079407..c84cca62f 100644 --- a/lib/github_changelog_generator/project_name_finder.rb +++ b/lib/github_changelog_generator/project_name_finder.rb @@ -4,12 +4,12 @@ class ProjectNameFinder attr_reader :options, :args # @param options [Options] - # @param args [Array] ARGV or the empty list - # @option args [String] 0 This parameter takes two forms: Either a full - # GitHub URL, or a 'username/projectname', or - # simply a GitHub username - # @option args [String] 1 If args[0] is given as a username, - # then args[1] can be given as a projectname + # @param args [Array] ARGV or the empty list. + # The first element of this argument takes two forms: + # Either a full GitHub URL, or a 'username/projectname', + # or simply a GitHub username. + # If the first element is given as a username, + # then the second element can be given as a projectname. def initialize(options, args) @options = options @args = args @@ -17,6 +17,8 @@ def initialize(options, args) FIXED_TEST_PROJECT = %w(skywinder changelog_test) + NO_MATCHING_USER_AND_PROJECT = [nil, nil] + # Returns a tuple of user and project from CLI arguments or git remote. # # @return [Array] user and project, or nil if unsuccessful @@ -25,7 +27,7 @@ def call -> { from_cli_option }, -> { FIXED_TEST_PROJECT if in_development? }, -> { from_git_remote } - ].find(-> { [nil, nil] }) do |strategy| + ].find(proc { NO_MATCHING_USER_AND_PROJECT }) do |strategy| user, project = strategy.call break [user, project] if user && project end @@ -39,27 +41,34 @@ def in_development? # # @return [Array, nil] user and project, or nil if unsuccessful def from_cli_option - user = nil - project = nil if args[0] && !args[1] - # this match should parse strings such "https://github.com/skywinder/Github-Changelog-Generator" or - # "skywinder/Github-Changelog-Generator" to user and name - match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(args[0]) + github_site_pattern =~ args.first begin - param = match[2].nil? + param = Regexp.last_match(2).nil? rescue - puts "Can't detect user and name from first parameter: '#{args[0]}' -> exit'" + puts "Can't detect user and name from first parameter: '#{args.first}' -> exit'" return end if param - return [nil, nil] + return NO_MATCHING_USER_AND_PROJECT else - user = match[1] - project = match[2] + [Regexp.last_match(:user), Regexp.last_match(:project)] end end - [user, project] + end + + # This pattern matches strings such as: + # + # "https://github.com/skywinder/Github-Changelog-Generator" + # + # or + # + # "skywinder/Github-Changelog-Generator" + # + # to user and name. + def github_site_pattern + /(?:.*#{Regexp.quote(github_site)}\/)?(?(.+))\/(?(.+))/ end # These patterns match these formats: @@ -84,19 +93,13 @@ def from_cli_option # # @return [Array] user and project def from_git_remote - user = nil - project = nil - GIT_REMOTE_PATTERNS.each do |git_remote_pattern| + GIT_REMOTE_PATTERNS.find(proc { NO_MATCHING_USER_AND_PROJECT }) do |git_remote_pattern| git_remote_pattern =~ git_remote_content if Regexp.last_match - user = Regexp.last_match(:user) - project = Regexp.last_match(:project) - break + break [Regexp.last_match(:user), Regexp.last_match(:project)] end end - - [user, project] end # @return [String] Output of git remote command @@ -107,7 +110,7 @@ def git_remote_content private def github_site - options[:github_site] || "github.com" + options[:github_site] || "https://github.com" end def git_remote diff --git a/spec/unit/project_name_finder_spec.rb b/spec/unit/project_name_finder_spec.rb index 3181f8056..1091b4756 100644 --- a/spec/unit/project_name_finder_spec.rb +++ b/spec/unit/project_name_finder_spec.rb @@ -57,7 +57,10 @@ end context "when option is invalid" do - it("should return the empty tuple") { expect(described_class.new({ github_site: nil }, ["blah", nil]).call).to eq [nil, nil] } + it("returns the empty tuple") do + result = described_class.new({ github_site: nil }, ["blah", nil]).call + expect(result).to eq(described_class::NO_MATCHING_USER_AND_PROJECT) + end end context "when option is valid" do @@ -65,30 +68,32 @@ let(:arg2) { nil } it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + it { is_expected.to match_array(%w(skywinder ActionSheetPicker-3.0)) } end - context "when option nil" do + + context "when option is nil" do let(:arg1) { nil } let(:arg2) { nil } it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } + it { is_expected.to match_array(described_class::NO_MATCHING_USER_AND_PROJECT) } end + context "when site is nil" do let(:arg1) { "skywinder/ActionSheetPicker-3.0" } let(:arg2) { nil } it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + it { is_expected.to match_array(%w(skywinder ActionSheetPicker-3.0)) } end context "when site is valid" do - let(:arg1) { "skywinder/ActionSheetPicker-3.0" } + let(:arg1) { "https://codeclimate.com/skywinder/ActionSheetPicker-3.0" } let(:arg2) { nil } let(:github_site) { "https://codeclimate.com" } it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } + it { is_expected.to match_array(%w(skywinder ActionSheetPicker-3.0)) } end context "when second arg is not nil" do @@ -96,16 +101,16 @@ let(:arg2) { "blah" } it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } + it { is_expected.to match_array(described_class::NO_MATCHING_USER_AND_PROJECT) } end context "when all args is not nil" do - let(:arg1) { "skywinder/ActionSheetPicker-3.0" } + let(:arg1) { "https://codeclimate.com/skywinder/ActionSheetPicker-3.0" } let(:arg2) { "blah" } let(:github_site) { "https://codeclimate.com" } it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } + it { is_expected.to match_array(described_class::NO_MATCHING_USER_AND_PROJECT) } end end end From 6d660ac938592ab526634e27d5fc308d248ba5a5 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Tue, 12 Sep 2017 17:43:50 +0200 Subject: [PATCH 5/6] Remove ProjectNameFinder class and adjunct code --- lib/github_changelog_generator/parser.rb | 7 +- .../project_name_finder.rb | 120 ------------------ lib/github_changelog_generator/task.rb | 6 +- spec/unit/project_name_finder_spec.rb | 116 ----------------- 4 files changed, 2 insertions(+), 247 deletions(-) delete mode 100644 lib/github_changelog_generator/project_name_finder.rb delete mode 100644 spec/unit/project_name_finder_spec.rb diff --git a/lib/github_changelog_generator/parser.rb b/lib/github_changelog_generator/parser.rb index 5b0479bfc..4f43b7a29 100755 --- a/lib/github_changelog_generator/parser.rb +++ b/lib/github_changelog_generator/parser.rb @@ -19,12 +19,7 @@ def self.parse_options abort [e, parser].join("\n") end - unless options[:user] && options[:project] - user, project = ProjectNameFinder.new(options, ARGV.dup).call - options[:user] = user - options[:project] = project - abort(parser.banner) unless options[:user] && options[:project] - end + abort(parser.banner) unless options[:user] && options[:project] print_options(options) diff --git a/lib/github_changelog_generator/project_name_finder.rb b/lib/github_changelog_generator/project_name_finder.rb deleted file mode 100644 index c84cca62f..000000000 --- a/lib/github_changelog_generator/project_name_finder.rb +++ /dev/null @@ -1,120 +0,0 @@ -# frozen_string_literal: true -module GitHubChangelogGenerator - class ProjectNameFinder - attr_reader :options, :args - - # @param options [Options] - # @param args [Array] ARGV or the empty list. - # The first element of this argument takes two forms: - # Either a full GitHub URL, or a 'username/projectname', - # or simply a GitHub username. - # If the first element is given as a username, - # then the second element can be given as a projectname. - def initialize(options, args) - @options = options - @args = args - end - - FIXED_TEST_PROJECT = %w(skywinder changelog_test) - - NO_MATCHING_USER_AND_PROJECT = [nil, nil] - - # Returns a tuple of user and project from CLI arguments or git remote. - # - # @return [Array] user and project, or nil if unsuccessful - def call - [ - -> { from_cli_option }, - -> { FIXED_TEST_PROJECT if in_development? }, - -> { from_git_remote } - ].find(proc { NO_MATCHING_USER_AND_PROJECT }) do |strategy| - user, project = strategy.call - break [user, project] if user && project - end - end - - def in_development? - ENV["RUBYLIB"] =~ /ruby-debug-ide/ - end - - # Returns GitHub username and project from CLI arguments - # - # @return [Array, nil] user and project, or nil if unsuccessful - def from_cli_option - if args[0] && !args[1] - github_site_pattern =~ args.first - - begin - param = Regexp.last_match(2).nil? - rescue - puts "Can't detect user and name from first parameter: '#{args.first}' -> exit'" - return - end - if param - return NO_MATCHING_USER_AND_PROJECT - else - [Regexp.last_match(:user), Regexp.last_match(:project)] - end - end - end - - # This pattern matches strings such as: - # - # "https://github.com/skywinder/Github-Changelog-Generator" - # - # or - # - # "skywinder/Github-Changelog-Generator" - # - # to user and name. - def github_site_pattern - /(?:.*#{Regexp.quote(github_site)}\/)?(?(.+))\/(?(.+))/ - end - - # These patterns match these formats: - # - # ``` - # origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch) - # git@github.com:skywinder/Github-Changelog-Generator.git - # ``` - # - # and - # - # ``` - # origin https://github.com/skywinder/ChangelogMerger (fetch) - # https://github.com/skywinder/ChangelogMerger - # ``` - GIT_REMOTE_PATTERNS = [ - /.*(?:[:\/])(?(?:-|\w|\.)*)\/(?(?:-|\w|\.)*)(?:\.git).*/, - /.*\/(?(?:-|\w|\.)*)\/(?(?:-|\w|\.)*).*/ - ] - - # Returns GitHub username and project from git remote output - # - # @return [Array] user and project - def from_git_remote - GIT_REMOTE_PATTERNS.find(proc { NO_MATCHING_USER_AND_PROJECT }) do |git_remote_pattern| - git_remote_pattern =~ git_remote_content - - if Regexp.last_match - break [Regexp.last_match(:user), Regexp.last_match(:project)] - end - end - end - - # @return [String] Output of git remote command - def git_remote_content - @git_remote_content ||= `git config --get remote.#{git_remote}.url` - end - - private - - def github_site - options[:github_site] || "https://github.com" - end - - def git_remote - options[:git_remote] - end - end -end diff --git a/lib/github_changelog_generator/task.rb b/lib/github_changelog_generator/task.rb index 373a21db9..cfaa756a1 100644 --- a/lib/github_changelog_generator/task.rb +++ b/lib/github_changelog_generator/task.rb @@ -48,15 +48,11 @@ def define(args, &task_block) # mimick parse_options options = Parser.default_options - user, project = ProjectNameFinder.new(options, []).call - options[:user] = user - options[:project] = project - OPTIONS.each do |o| v = instance_variable_get("@#{o}") options[o.to_sym] = v unless v.nil? end - + abort "user and project are required." unless options[:user] && options[:project] generator = Generator.new options log = generator.compound_changelog diff --git a/spec/unit/project_name_finder_spec.rb b/spec/unit/project_name_finder_spec.rb deleted file mode 100644 index 1091b4756..000000000 --- a/spec/unit/project_name_finder_spec.rb +++ /dev/null @@ -1,116 +0,0 @@ -# frozen_string_literal: true -require "github_changelog_generator/project_name_finder" -RSpec.describe GitHubChangelogGenerator::ProjectNameFinder do - context "with the empty options" do - it "finds nothing" do - expect(described_class.new({}, []).call).to eq [nil, nil] - end - end - - describe "#from_git_remote" do - subject do - finder = described_class.new({}, []) - allow(finder).to receive(:git_remote_content) { prepared_content } - finder.from_git_remote - end - - context "when remote is type 1" do - let(:prepared_content) { "origin https://github.com/skywinder/ActionSheetPicker-3.0 (fetch)" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - - context "when remote is type 2" do - let(:prepared_content) { "https://github.com/skywinder/ActionSheetPicker-3.0" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - - context "when remote is type 3" do - let(:prepared_content) { "https://github.com/skywinder/ActionSheetPicker-3.0" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - - context "when remote is type 4" do - let(:prepared_content) { "origin git@github.com:skywinder/ActionSheetPicker-3.0.git (fetch)" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) } - end - - context "when remote is invalid" do - let(:prepared_content) { "some invalid text" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array([nil, nil]) } - end - end - - describe "#from_cli_option" do - let(:github_site) { nil } - subject do - described_class.new({ github_site: github_site }, [arg1, arg2]).call - end - - context "when option is invalid" do - it("returns the empty tuple") do - result = described_class.new({ github_site: nil }, ["blah", nil]).call - expect(result).to eq(described_class::NO_MATCHING_USER_AND_PROJECT) - end - end - - context "when option is valid" do - let(:arg1) { "skywinder/ActionSheetPicker-3.0" } - let(:arg2) { nil } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(%w(skywinder ActionSheetPicker-3.0)) } - end - - context "when option is nil" do - let(:arg1) { nil } - let(:arg2) { nil } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(described_class::NO_MATCHING_USER_AND_PROJECT) } - end - - context "when site is nil" do - let(:arg1) { "skywinder/ActionSheetPicker-3.0" } - let(:arg2) { nil } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(%w(skywinder ActionSheetPicker-3.0)) } - end - - context "when site is valid" do - let(:arg1) { "https://codeclimate.com/skywinder/ActionSheetPicker-3.0" } - let(:arg2) { nil } - let(:github_site) { "https://codeclimate.com" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(%w(skywinder ActionSheetPicker-3.0)) } - end - - context "when second arg is not nil" do - let(:arg1) { "skywinder/ActionSheetPicker-3.0" } - let(:arg2) { "blah" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(described_class::NO_MATCHING_USER_AND_PROJECT) } - end - - context "when all args is not nil" do - let(:arg1) { "https://codeclimate.com/skywinder/ActionSheetPicker-3.0" } - let(:arg2) { "blah" } - let(:github_site) { "https://codeclimate.com" } - - it { is_expected.to be_a(Array) } - it { is_expected.to match_array(described_class::NO_MATCHING_USER_AND_PROJECT) } - end - end -end From 7ea75b6b4edffb70aea40c606d41cd975bc9b838 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Tue, 12 Sep 2017 17:51:51 +0200 Subject: [PATCH 6/6] Excise the github_remote option; update README --- README.md | 30 +++++++++++++++-------- lib/github_changelog_generator/options.rb | 1 - lib/github_changelog_generator/parser.rb | 1 - spec/unit/options_spec.rb | 8 +++--- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 87740cc25..3a1912a90 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,14 @@ GitHub Changelog Generator ![GitHub Logo](../master/images/logo.jpg) Since you don't have to fill your `CHANGELOG.md` manually now: just run the script, relax and take a cup of :coffee: before your next release! :tada: ### *What’s the point of a change log?* + To make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project. + ### *Why should I care?* -Because software tools are for people. If you don’t care, why are you contributing to open source? Surely, there must be a kernel (ha!) of care somewhere in that lovely little brain of yours. + +Because software tools are for _people_. "Changelogs make it easier for users and +contributors to see precisely what notable changes have been made between each +release (or version) of the project." :arrow_right: *[http://keepachangelog.com](http://keepachangelog.com)* @@ -71,32 +76,31 @@ See also Troubleshooting. ## Usage -**It's really simple!** -- If your **`git remote`** `origin` refers to your GitHub repo, just go to your project folder and run: +- Run this: - github_changelog_generator - -- Or, run this from anywhere: `github_changelog_generator -u github_username -p github_project` `github_changelog_generator github_username/github_project` -- If you are running it against a repository on a Github Enterprise install, you must specify *both* `--github-site` and `--github-api` command line options: +- For Github Enterprise repos, specify *both* `--github-site` and `--github-api` options: github_changelog_generator --github-site="https://github.yoursite.com" \ --github-api="https://github.yoursite.com/api/v3/" -This generates a changelog to the `CHANGELOG.md` file, with pretty markdown formatting. +This generates a `CHANGELOG.md`, with pretty Markdown formatting. ### Params + Type `github_changelog_generator --help` for details. For more details about params, read the Wiki page: [**Advanced change log generation examples**](https://github.com/skywinder/github-changelog-generator/wiki/Advanced-change-log-generation-examples) ### Params File + In your project root, you can put a params file named `.github_changelog_generator` to override default params: Example: + ``` unreleased=false future-release=5.0.0 @@ -106,6 +110,7 @@ since-tag=1.0.0 ### GitHub token GitHub only allows 50 unauthenticated requests per hour. + Therefore, it's recommended to run this script with authentication by using a **token**. Here's how: @@ -143,7 +148,7 @@ If you have a `HISTORY.md` file in your project, it will automatically be picked You love `rake`? We do, too! So, we've made it even easier for you: we've provided a `rake` task library for your changelog generation. -Just put something like this in your `Rakefile`: +Configure the task in your `Rakefile`: ```ruby require 'github_changelog_generator/task' @@ -154,11 +159,14 @@ GitHubChangelogGenerator::RakeTask.new :changelog do |config| end ``` -All command line options can be passed to the `rake` task as `config` parameters. And since you're naming the `rake` task yourself, you can create as many as you want. +All command-line options can be passed to the `rake` task as `config` +parameters. And since you're naming the `rake` task yourself, you can create +as many as you want. You can look for params names from the [parser source code (#setup_parser)](https://github.com/skywinder/github-changelog-generator/blob/master/lib/github_changelog_generator/parser.rb). For example, to translate the bugs label to Portuguese, instead of setting `config.bugs_label`, you have to set `config.bug_prefix`, and so on. ## Features and advantages of this project + - Generate canonical, neat change log file, followed by [basic change log guidelines](http://keepachangelog.com) :gem: - Optionally generate **Unreleased** changes (closed issues that have not released yet) :dizzy: - **GitHub Enterprise support** via command line options! :factory: @@ -179,12 +187,14 @@ You can look for params names from the [parser source code (#setup_parser)](http ### Alternatives + Here is a [wikipage list of alternatives](https://github.com/skywinder/Github-Changelog-Generator/wiki/Alternatives) that I found. But none satisfied my requirements. *If you know other projects, feel free to edit this Wiki page!* ### Projects using this library + Here's a [wikipage list of projects](https://github.com/skywinder/Github-Changelog-Generator/wiki/Projects-using-Github-Changelog-Generator). If you've used this project in a live app, please let me know! Nothing makes me happier than seeing someone else take my work and go wild with it. diff --git a/lib/github_changelog_generator/options.rb b/lib/github_changelog_generator/options.rb index 0370df5b0..493879369 100644 --- a/lib/github_changelog_generator/options.rb +++ b/lib/github_changelog_generator/options.rb @@ -26,7 +26,6 @@ class Options < SimpleDelegator filter_issues_by_milestone frontmatter future_release - git_remote github_endpoint github_site header diff --git a/lib/github_changelog_generator/parser.rb b/lib/github_changelog_generator/parser.rb index 4f43b7a29..0b363bb44 100755 --- a/lib/github_changelog_generator/parser.rb +++ b/lib/github_changelog_generator/parser.rb @@ -218,7 +218,6 @@ def self.default_options issue_prefix: "**Closed issues:**", bug_prefix: "**Fixed bugs:**", enhancement_prefix: "**Implemented enhancements:**", - git_remote: "origin", http_cache: true ) end diff --git a/spec/unit/options_spec.rb b/spec/unit/options_spec.rb index 56acde61f..c026b32ce 100644 --- a/spec/unit/options_spec.rb +++ b/spec/unit/options_spec.rb @@ -12,7 +12,7 @@ it "raises an error" do expect do described_class.new( - git_remote: "origin", + project: "rails", strength: "super-strength", wisdom: "deep" ) @@ -22,13 +22,13 @@ end describe "#[]=(key, value)" do - let(:options) { described_class.new(git_remote: "origin") } + let(:options) { described_class.new(project: "rails") } context "with known options" do it "sets the option value" do expect do - options[:git_remote] = "in the cloud" - end.to change { options[:git_remote] }.to "in the cloud" + options[:project] = "trails" + end.to change { options[:project] }.to "trails" end end pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy