pr-with-params 1.3.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4374906553024e9cff3a612ac9baa8aed678fe6a025f6a7ad15c060b145a11c8
4
- data.tar.gz: 2f0b0cc426dbbd284a19e74ae1ee197a12941957ae1bf95cbcd13517e0578dcc
3
+ metadata.gz: fb5d7b7436528a5a08568892974cc238bd5ffc1dd6fb40e2157be5600b72dd04
4
+ data.tar.gz: 8904c400e3fc49e1ac68f1434279af3d2b2a92f8c39fddc0b0b4b35c825d3b5f
5
5
  SHA512:
6
- metadata.gz: 0cfa3f9578b19dfcd1ba614cfa4dfc7b8b7001f057f00e3678ba3f42e2d3345c0d7a85886adb6e5891f622c5d35de10b9ab565643df973c30d10e5e58341e9ee
7
- data.tar.gz: b92649ee79ff701cd40c20a78ecdf869700a6aa13f38dbb1fadc1628be66a7dc94248a8e7fc05ff1dd27f838c237e7d935378a275f4c1112d0aafddf45441e9e
6
+ metadata.gz: 00473d4b542df9d312c51f8e6801b2050109108780f914fe49cd5f5048493792d002ab3bfb5b992461644352612e9cfc2c70ff80d010fcfc32d71596d14f7379
7
+ data.tar.gz: 4b86fc3db6832b59830123dce6a2b1077b90fc312d28cd360bcc648a931692680c03059c218e7acd2d8da3ccdbc3886541dd98ee677ed699347d3a5d1d523fbe
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ ## [2.0.0] - 2023-06-05
2
+
3
+ To migrate to this version, simply run `gem update pr-with-params`
4
+
5
+ ### Added
6
+ - Ability to specify a list of validators to run as an option
7
+ - Ability to set `ignore_conventional_commits` in config file
8
+ - Default path for config file at `~/.pwp/config.yml`
9
+
10
+ ### Changed
11
+ - `--conf` is now `--config_path` or `-path` or `-p`
12
+ - Make specifying config path optional - was previously a required
13
+ - CLI name is now `pr-wip` - was previously `pr-with-params`
14
+ - `--validate-conventional-commits` is now `--ignore-conventional-commits` - optionally turn off versus optionally turn on
15
+
16
+ ### Fixed
17
+ - Occasional failures related to `launchy` not being loaded properly
data/Gemfile CHANGED
@@ -4,7 +4,3 @@ source "https://rubygems.org"
4
4
 
5
5
  # Specify your gem's dependencies in pr-with-params.gemspec
6
6
  gemspec
7
-
8
- gem "rake", "~> 13.0"
9
- gem "minitest", "~> 5.0"
10
- gem "rubocop", "~> 0.80"
data/Gemfile.lock CHANGED
@@ -1,18 +1,29 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pr-with-params (1.3.1)
4
+ pr-with-params (2.0.0)
5
+ activesupport
5
6
  launchy (~> 2.5)
7
+ rake (~> 13.0)
8
+ thor
6
9
 
7
10
  GEM
8
11
  remote: https://rubygems.org/
9
12
  specs:
10
- addressable (2.8.1)
13
+ activesupport (7.0.5)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (>= 1.6, < 2)
16
+ minitest (>= 5.1)
17
+ tzinfo (~> 2.0)
18
+ addressable (2.8.4)
11
19
  public_suffix (>= 2.0.2, < 6.0)
12
20
  ast (2.4.2)
13
- launchy (2.5.0)
14
- addressable (~> 2.7)
15
- minitest (5.16.2)
21
+ concurrent-ruby (1.2.2)
22
+ i18n (1.14.1)
23
+ concurrent-ruby (~> 1.0)
24
+ launchy (2.5.2)
25
+ addressable (~> 2.8)
26
+ minitest (5.18.0)
16
27
  parallel (1.22.1)
17
28
  parser (3.1.2.0)
18
29
  ast (~> 2.4.1)
@@ -33,15 +44,18 @@ GEM
33
44
  rubocop-ast (1.19.1)
34
45
  parser (>= 3.1.1.0)
35
46
  ruby-progressbar (1.11.0)
47
+ thor (1.2.2)
48
+ tzinfo (2.0.6)
49
+ concurrent-ruby (~> 1.0)
36
50
  unicode-display_width (1.8.0)
37
51
 
38
52
  PLATFORMS
39
53
  arm64-darwin-21
54
+ arm64-darwin-22
40
55
 
41
56
  DEPENDENCIES
42
- minitest (~> 5.0)
57
+ minitest (~> 5.18.0)
43
58
  pr-with-params!
44
- rake (~> 13.0)
45
59
  rubocop (~> 0.80)
46
60
 
47
61
  BUNDLED WITH
data/README.md CHANGED
@@ -1,11 +1,8 @@
1
- # PR::With::Params [![Gem Version](https://badge.fury.io/rb/pr-with-params.svg)](https://badge.fury.io/rb/pr-with-params)
1
+ # PRWithParams [![Gem Version](https://badge.fury.io/rb/pr-with-params.svg)](https://badge.fury.io/rb/pr-with-params)
2
2
  A lightweight ruby gem that pushes current local branch to remote with upstream at origin/[local-branch-name]. It also opens a new pull request browser window at a URL with customized query params, based on specified options, which pre-populates certain fields in the pull request. This is especially useful when supporting multiple PR templates within a code base.
3
3
 
4
4
  Inspired by GitHub's documentation on [using query params to create pull requets](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/using-query-parameters-to-create-a-pull-request)
5
5
 
6
- ## Prerequisites
7
- This gem currently uses the [launchy gem](https://github.com/copiousfreetime/launchy) to open URLs. Ensure your code base satisfies launchy's dependencies.
8
-
9
6
  ## Installation
10
7
 
11
8
  Add this line to your application's Gemfile:
@@ -24,13 +21,15 @@ Or install it yourself as:
24
21
 
25
22
  ## Usage
26
23
 
24
+ NOTE: CLI name has changed as of v2.0.0 from `pr-with-params` to `pr-wip` along with some other breaking changes. See changelog for more details
25
+
27
26
  Assuming you've committed your changes and your local branch is ready to be pushed, run:
28
27
 
29
28
  ```
30
- $ pr-with-params -t new_feature_template.md -l 'work in progress'
29
+ $ pr-wip -t new_feature_template.md -l 'work in progress'
31
30
  ```
32
31
 
33
- For a full list of options, run `$ pr-with-params -h`
32
+ For a full list of options, run `$ pr-wip -h`
34
33
 
35
34
  #### Using Config File
36
35
 
@@ -49,8 +48,8 @@ bug_fix:
49
48
  labels: bug,urgent
50
49
  ```
51
50
 
52
- * To run with config file, use `$ pr-with-params --conf='path/to/file.yml' --scope=bug_fix`. If `--scope` option is not specified, only `:default` scope will apply.
53
- * If you specify a config file (`--conf`) and also pass options by flag (e.g `--base-branch=develop`), the flag value will override the config value.
51
+ * To run with config file, use `$ pr-wip --config_path='path/to/file.yml' --scope=bug_fix`. If `--scope` option is not specified, only `:default` scope will apply.
52
+ * If you specify a config file (`--config_path`) and also pass options by flag (e.g `--base-branch=develop`), the flag value will override the config value.
54
53
  * All your defaults go in the `:default` scope.
55
54
  * Only fields defined in another scope will override the defaults. In the example above, the final list of configs will be:
56
55
 
@@ -62,6 +61,7 @@ bug_fix:
62
61
  | Config | Type | Example |
63
62
  | :--- | :---: | :--- |
64
63
  | validators | `Array` | \`[conventional_commits]\` |
64
+ | ignore_conventional_commits | `Boolean` | true |
65
65
  | base_branch | `String` | develop |
66
66
  | template | `String` | new_feature_template.md |
67
67
  | title | `String` | 'Update login screen' |
@@ -69,7 +69,7 @@ bug_fix:
69
69
  | assignees | `String` | 2k-joker |
70
70
 
71
71
  #### Validators
72
- * **Conventional Commits**: Gem supports validation of conventional commits for PR `:title`. If you want to follow [conventional commit specs](https://www.conventionalcommits.org/en/v1.0.0/#specification) in your codebase, you may want to use this validation. To turn it on, set the `--validate-conventional-commits` flag or add `conventional_commits` to the list of validators in your config file.
72
+ * **Conventional Commits**: Gem supports validation of conventional commits for PR `:title` by default. This is to encourage users to follow [conventional commit specs](https://www.conventionalcommits.org/en/v1.0.0/#specification). To turn it off, pass the `--ignore-conventional-commits` flag or add `conventional_commits` to the list of validators in your config file or set `ignore_conventional_commits: true` in your config file.
73
73
 
74
74
  ## Development
75
75
 
data/bin/console CHANGED
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "bundler/setup"
5
- require "pr/with/params"
5
+ require "pr_with_params"
6
6
 
7
7
  # You can add fixtures and/or initialization code here to make experimenting
8
8
  # with your gem easier. You can also use a different console, if you like.
data/bin/setup CHANGED
@@ -4,5 +4,5 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
-
7
+ Bundler.require(:default, :development)
8
8
  # Do any other automated setup that you need to do here
data/exe/pr-wip ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup' # support local testing/development
4
+ require_relative '../lib/pr_with_params'
5
+
6
+ PRWithParams::CLI.start
@@ -0,0 +1,77 @@
1
+ require 'active_support/core_ext/hash'
2
+ require 'thor'
3
+ require 'launchy'
4
+ require 'uri'
5
+
6
+ module PRWithParams
7
+ class CLI < Thor
8
+ desc "open", "Open a new pull request for local branch"
9
+ long_desc <<-LONGDESC
10
+ `pr-wip open [options]` will open a new browser window with a pull request at a URL with customized query params from <options>.
11
+
12
+ The pull request will be pre-populated with certain fields based on custom query params
13
+ LONGDESC
14
+ option :template, type: :string, aliases: ['-t'], desc: 'Specify the filename of the target custom PR template (e.g: bug_squash_template.md). Will use default template otherwise.'
15
+ option :config_path, type: :string, aliases: ['-path', '-p'], default: '/.pwp/config.yml', desc: 'Path to yaml file where configs are defined. Defaults to: ~/.pwp/config.yml'
16
+ option :scope, type: :string, aliases: ['-s'], default: 'default', desc: 'Specify the scope name under which options are defined.'
17
+ option :base_branch, type: :string, aliases: ['-b'], desc: 'Specify the base branch for your PR (e.g: develop). Will use default branch otherwise.'
18
+ option :title, type: :string, aliases: ['-d', '-desc', '--description'], desc: 'Specify a custom PR title. Will use the first branch commit message otherwise.'
19
+ option :labels, type: :string, aliases: ['-l'], desc: "Specify a list of labels (e.g: 'help+wanted,bug,urgent,work+in+progress')."
20
+ option :assignees, type: :string, aliases: ['-a'], desc: "Specify a list of assignees (e.g: 'octocat,codedog')."
21
+ option :validators, type: :array, default: [:conventional_commits], desc: 'Specify a list of validations to run against local branch before opening pull request'
22
+ option :ignore_conventional_commits, type: :boolean, desc: 'Allow PR titles that do not conform to conventional commits spec.'
23
+ def open
24
+ home_dir_path = `echo $HOME`.chomp
25
+ config_file_path = "#{home_dir_path}/#{options[:config_path].delete_prefix('/')}"
26
+ config_options = (options[:config_path].empty? ? {} : PRWithParams::ConfigParser.new(config_file_path: config_file_path, scope: options[:scope]).parse!).with_indifferent_access
27
+ options[:validators].delete(:conventional_commits) if options[:ignore_conventional_commits]
28
+ all_options = config_options.merge(options)
29
+
30
+ branch_name = `git rev-parse --abbrev-ref HEAD`.chomp
31
+ base_branch = all_options.delete(:base_branch) || `git remote show origin | grep "HEAD branch" | sed 's/.*: //'`.chomp
32
+
33
+ default_title = `git show-branch --no-name $(git log #{base_branch}..#{branch_name} --pretty=format:"%h" | tail -1)`.chomp
34
+ all_options[:title] ||= default_title
35
+
36
+ puts all_options
37
+ puts options
38
+ PRWithParams::OptionsValidator.validate!(all_options.except(:validators), validators: all_options[:validators])
39
+
40
+ remote_git_uri = `git config --get remote.origin.url`.sub('git@github.com:', '').sub('.git', '').chomp
41
+ uri_path = "/#{remote_git_uri}/compare/#{base_branch}...#{branch_name}"
42
+
43
+ push_local_to_remote(branch_name, base_branch, remote_git_uri)
44
+ open_pr_in_browser(uri_path, all_options)
45
+ rescue StandardError => e
46
+ message = "\e[31mERROR\e[0m: An error occurred while building or opening your custom pull request URL"
47
+ reason = "reason: #{e.message}"
48
+ backtrace = "backtrace: #{e.backtrace&.last(10)&.join("\n")}"
49
+ error_message = [message, reason, backtrace, "\n\n"].join("\n")
50
+
51
+ warn error_message
52
+ exit 1
53
+ end
54
+
55
+ private
56
+
57
+ def push_local_to_remote(branch_name, base_branch, remote_git_uri)
58
+ puts "current branch: \e[36m#{branch_name}\e[0m"
59
+ puts "base branch: \e[36m#{base_branch}\e[0m"
60
+ puts "repo path: \e[36m#{remote_git_uri}\e[0m"
61
+
62
+ push_message = "\nPushing your local branch to origin/#{branch_name}..."
63
+ puts "\e[32m#{push_message}\e[0m"
64
+ `sleep 1`
65
+
66
+ system("git push -u origin #{branch_name}", exception: true)
67
+ end
68
+
69
+ def open_pr_in_browser(uri_path, options)
70
+ uri_host = 'www.github.com'
71
+ uri_query = URI.encode_www_form(options)
72
+ url_string = URI::HTTPS.build(host: uri_host, path: uri_path, query: uri_query).to_s
73
+
74
+ Launchy.open(url_string)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,51 @@
1
+ require 'yaml'
2
+
3
+ module PRWithParams
4
+ class ConfigParser
5
+ class ParserError < StandardError; end
6
+
7
+ # Attributes
8
+ attr_reader :config_file_path, :scope, :parsed_config, :filtered_config
9
+
10
+ # Constants
11
+ VALID_CONFIG_KEYS = %i[validators base_branch template title labels assignees].freeze
12
+
13
+ def initialize(config_file_path:, scope: nil)
14
+ @config_file_path = config_file_path
15
+ @scope = scope&.to_sym || :default
16
+ end
17
+
18
+ def parse!
19
+ validate_file_type!
20
+ parse_yaml_config
21
+ end
22
+
23
+ private
24
+
25
+ def parse_yaml_config
26
+ @parsed_config = YAML.safe_load(IO.read(config_file_path)).to_h.transform_keys(&:to_sym)
27
+ @filtered_config = scoped_config.transform_keys(&:to_sym).slice(*VALID_CONFIG_KEYS)
28
+ end
29
+
30
+ def scoped_config
31
+ if scope == :default || parsed_config.fetch(scope, {}).empty?
32
+ parsed_config.fetch(:default, {})
33
+ else
34
+ parsed_config.fetch(:default, {}).merge(parsed_config[scope])
35
+ end
36
+ end
37
+
38
+ def validate_file_type!
39
+ raise ParserError, 'Config file type must be YAML (.yaml or .yml)' unless yaml_file?
40
+ raise ParserError, "Config file path is invalid or file does not exist: #{config_file_path}" unless file_exists?
41
+ end
42
+
43
+ def yaml_file?
44
+ config_file_path.end_with?('.yaml') || config_file_path.end_with?('yml')
45
+ end
46
+
47
+ def file_exists?
48
+ File.file?(config_file_path)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ module PRWithParams
2
+ class OptionsValidator
3
+ class ValidatorError < StandardError; end
4
+
5
+ VALIDATOR_CLASS_MAP = {
6
+ conventional_commits: 'PRWithParams::ConventionalCommitValidator'
7
+ }.freeze
8
+
9
+ class << self
10
+ def validate!(options, validators: [])
11
+ validators.each do |validator|
12
+ validate_options(validator, options)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def validate_options(validator, options)
19
+ class_name = VALIDATOR_CLASS_MAP[validator.to_sym]
20
+ raise(ValidatorError, "Invalid or undefined validator: #{validator}") if class_name.nil?
21
+
22
+ Object.const_get(class_name).new(options).validate!
23
+ end
24
+ end
25
+ end
26
+
27
+ class ConventionalCommitValidator
28
+ CONVENTIONAL_COMMIT_REGEX = /^((build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.*\))?(!)?(: (.*\s*)*))|(^Merge (.*\s*)*)|(^Initial commit$)/.freeze
29
+
30
+ def initialize(options)
31
+ @commit_message = options[:title]
32
+ end
33
+
34
+ def validate!
35
+ raise OptionsValidator::ValidatorError, "Conventional commit specifications not met for commit message: '#{@commit_message}'" unless valid_commit?
36
+ end
37
+
38
+ private
39
+
40
+ def valid_commit?
41
+ CONVENTIONAL_COMMIT_REGEX.match?(@commit_message)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PRWithParams
4
+ VERSION = "2.0.0"
5
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'pr_with_params/config_parser'
2
+ require_relative 'pr_with_params/options_validator'
3
+ require_relative 'pr_with_params/version'
4
+ require_relative 'pr_with_params/cli'
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/pr/with/params/version"
3
+ require_relative "lib/pr_with_params/version"
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "pr-with-params"
7
- spec.version = PR::With::Params::VERSION
7
+ spec.version = PRWithParams::VERSION
8
8
  spec.authors = ["2k-joker"]
9
9
  spec.email = ["kum.vanjunior@gmail.com"]
10
10
 
11
11
  spec.summary = "Pushes current local branch to remote with upstream at origin/[local-branch-name]. It also opens a new pull request browser window at a URL with customized query params, based on specified options, which pre-populates certain fields in the pull request. This is especially useful when supporting multiple PR templates within a code base."
12
12
  spec.homepage = "https://github.com/2k-joker/pr-with-params"
13
13
  spec.licenses = ["MIT"]
14
- spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
14
+ spec.required_ruby_version = Gem::Requirement.new("~> 3.1.0")
15
15
 
16
16
  spec.metadata["homepage_uri"] = spec.homepage
17
17
 
@@ -24,9 +24,11 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- # Uncomment to register a new dependency of your gem
28
27
  spec.add_dependency "launchy", "~> 2.5"
28
+ spec.add_dependency "rake", "~> 13.0"
29
+ spec.add_dependency "thor"
30
+ spec.add_dependency "activesupport"
29
31
 
30
- # For more information and examples about making a new gem, checkout our
31
- # guide at: https://bundler.io/guides/creating_gem.html
32
+ spec.add_development_dependency "minitest", "~> 5.18.0"
33
+ spec.add_development_dependency "rubocop", "~> 0.80"
32
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pr-with-params
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 2k-joker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-16 00:00:00.000000000 Z
11
+ date: 2023-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: launchy
@@ -24,11 +24,81 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 5.18.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 5.18.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.80'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.80'
27
97
  description:
28
98
  email:
29
99
  - kum.vanjunior@gmail.com
30
100
  executables:
31
- - pr-with-params
101
+ - pr-wip
32
102
  extensions: []
33
103
  extra_rdoc_files: []
34
104
  files:
@@ -36,6 +106,7 @@ files:
36
106
  - ".github/PULL_REQUEST_TEMPLATE/new_feature_template.md"
37
107
  - ".gitignore"
38
108
  - ".rubocop.yml"
109
+ - CHANGELOG.md
39
110
  - Gemfile
40
111
  - Gemfile.lock
41
112
  - LICENSE
@@ -43,11 +114,12 @@ files:
43
114
  - Rakefile
44
115
  - bin/console
45
116
  - bin/setup
46
- - exe/pr-with-params
47
- - lib/pr/with/params.rb
48
- - lib/pr/with/params/config_parser.rb
49
- - lib/pr/with/params/options_validator.rb
50
- - lib/pr/with/params/version.rb
117
+ - exe/pr-wip
118
+ - lib/pr_with_params.rb
119
+ - lib/pr_with_params/cli.rb
120
+ - lib/pr_with_params/config_parser.rb
121
+ - lib/pr_with_params/options_validator.rb
122
+ - lib/pr_with_params/version.rb
51
123
  - pr-with-params.gemspec
52
124
  homepage: https://github.com/2k-joker/pr-with-params
53
125
  licenses:
@@ -60,7 +132,7 @@ require_paths:
60
132
  - lib
61
133
  required_ruby_version: !ruby/object:Gem::Requirement
62
134
  requirements:
63
- - - ">="
135
+ - - "~>"
64
136
  - !ruby/object:Gem::Version
65
137
  version: 3.1.0
66
138
  required_rubygems_version: !ruby/object:Gem::Requirement
data/exe/pr-with-params DELETED
@@ -1,107 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup' # support local testing/development
4
- require_relative '../lib/pr/with/params'
5
- require 'json'
6
- require 'optparse'
7
- require 'open3'
8
-
9
- options = { expand: 1, validators: [] }
10
- config_file_path = ''
11
- config_scope = nil
12
-
13
- # rubocop:disable Metrics/BlockLength
14
- parser = OptionParser.new do |opt|
15
- opt.banner = "Usage pr-with-params [options]"
16
-
17
- opt.separator ''
18
-
19
- opt.on('-h', '--help', 'Show this usage help menu') do |_h|
20
- puts opt
21
- exit
22
- end
23
-
24
- opt.separator ''
25
-
26
- opt.on('-v', '--version', 'Returns your current local gem version') do |_h|
27
- puts 'pr-with-params ' + PR::With::Params::VERSION
28
- exit
29
- end
30
-
31
- opt.separator ''
32
-
33
- opt.on('--conf FILE_PATH', 'Path to yaml file where options are defined. NOTE that relative paths are not supported.') do |conf_file|
34
- stdout, _stderr, _status = Open3.capture3("echo #{conf_file}")
35
- config_file_path = stdout.chomp
36
- end
37
-
38
- opt.on('--scope CONFIG_SCOPE', 'Specify the scope name under which options are defined.') do |scope|
39
- config_scope = scope
40
- end
41
-
42
- opt.on('-b', '--base-branch BRANCH', "Specify the base branch for your PR (e.g: 'develop'). Will use default branch otherwise.") do |pr_base_branch|
43
- options[:base_branch] = pr_base_branch
44
- end
45
-
46
- opt.on('-t', '--template TEMPLATE', "Specify the filename of the target custom PR template (e.g: 'bug_squash_template.md'). Will use default template otherwise.") do |pr_template|
47
- options[:template] = pr_template
48
- end
49
-
50
- opt.on('-d', '--description DESC', 'Specify a custom PR title. Will use the first branch commit message otherwise.') do |pr_description|
51
- options[:title] = pr_description
52
- end
53
-
54
- opt.on('-l', '--labels LABELS', "Specify a list of labels (e.g: 'help+wanted,bug,urgent,work+in+progress').") do |pr_labels|
55
- options[:labels] = pr_labels
56
- end
57
-
58
- opt.on('-a', '--assignees ASSIGNEES', "Specify a list of assignees (e.g: 'octocat,codedog').") do |pr_assignees|
59
- options[:assignees] = pr_assignees
60
- end
61
-
62
- opt.on('--validate-conventional-commits', 'Validates that your PR title conforms to conventional commits specs.') do |_|
63
- options[:validators] = options[:validators].to_a << :conventional_commits
64
- end
65
- end
66
- # rubocop:enable Metrics/BlockLength
67
-
68
- begin
69
- parser.parse!
70
-
71
- config_options = PR::With::Params.parse_config(config_file_path, config_scope)
72
- options = config_options.merge(options)
73
-
74
- branch_name = `git rev-parse --abbrev-ref HEAD`.chomp
75
- base_branch = options.delete(:base_branch) || `git remote show origin | grep "HEAD branch" | sed 's/.*: //'`.chomp
76
-
77
- default_title = `git show-branch --no-name $(git log #{base_branch}..#{branch_name} --pretty=format:"%h" | tail -1)`.chomp
78
- options[:title] ||= default_title
79
-
80
- PR::With::Params.validate_options!(options)
81
-
82
- remote_git_uri = `git config --get remote.origin.url`.sub('git@github.com:', '').sub('.git', '').chomp
83
- uri_host = 'www.github.com'
84
- uri_path = "/#{remote_git_uri}/compare/#{base_branch}...#{branch_name}"
85
-
86
- puts "current branch: \e[36m#{branch_name}\e[0m"
87
- puts "base branch: \e[36m#{base_branch}\e[0m"
88
- puts "repo path: \e[36m#{remote_git_uri}\e[0m"
89
-
90
- push_message = "\nPushing your local branch to origin/#{branch_name}..."
91
- puts "\e[32m#{push_message}\e[0m"
92
- `sleep 1`
93
- system("git push -u origin #{branch_name}", exception: true)
94
-
95
- open_url_message = "\nOpening pull request browser window..."
96
- puts "\e[32m#{open_url_message}\e[0m"
97
- `sleep 1`
98
- PR::With::Params.open(host: uri_host, path: uri_path, query: options)
99
- rescue StandardError => e
100
- message = "\e[31mERROR\e[0m: An error occurred while building or opening your custom pull request URL"
101
- reason = "reason: #{e.message}"
102
- backtrace = "backtrace: #{e.backtrace&.last(10)&.join("\n")}"
103
- error_message = [message, reason, backtrace, "\n\n"].join("\n")
104
-
105
- warn error_message
106
- exit 1
107
- end
@@ -1,53 +0,0 @@
1
- require 'yaml'
2
-
3
- module PR
4
- module With
5
- module Params
6
- class ConfigParser
7
- # Attributes
8
- attr_reader :config_file_path, :scope, :parsed_config, :filtered_config
9
-
10
- # Constants
11
- VALID_CONFIG_KEYS = %i[validators base_branch template title labels assignees].freeze
12
-
13
- def initialize(config_file_path:, scope: nil)
14
- @config_file_path = config_file_path
15
- @scope = scope&.to_sym || :default
16
- end
17
-
18
- def parse!
19
- validate_file_type!
20
- parse_yaml_config
21
- end
22
-
23
- private
24
-
25
- def parse_yaml_config
26
- @parsed_config = YAML.safe_load(IO.read(config_file_path)).transform_keys(&:to_sym)
27
- @filtered_config = scoped_config.transform_keys(&:to_sym).slice(*VALID_CONFIG_KEYS)
28
- end
29
-
30
- def scoped_config
31
- if scope == :default || parsed_config.fetch(scope, {}).empty?
32
- parsed_config.fetch(:default, {})
33
- else
34
- parsed_config.fetch(:default, {}).merge(parsed_config[scope])
35
- end
36
- end
37
-
38
- def validate_file_type!
39
- raise(TypeError, 'Config file type must be YAML (.yaml or .yml)') unless yaml_file?
40
- raise(ArgumentError, "Config file path is invalid or file does not exist: #{config_file_path}") unless file_exists?
41
- end
42
-
43
- def yaml_file?
44
- config_file_path.end_with?('.yaml') || config_file_path.end_with?('yml')
45
- end
46
-
47
- def file_exists?
48
- File.file?(config_file_path)
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,46 +0,0 @@
1
- module PR
2
- module With
3
- module Params
4
- class OptionsValidator
5
- VALIDATOR_CLASS_MAP = {
6
- conventional_commits: 'PR::With::Params::ConventionalCommitValidator'
7
- }.freeze
8
-
9
- class << self
10
- def validate!(options, validators: [])
11
- validators.each do |validator|
12
- validate_options(validator, options)
13
- end
14
- end
15
-
16
- private
17
-
18
- def validate_options(validator, options)
19
- class_name = VALIDATOR_CLASS_MAP[validator.to_sym]
20
- raise(ArgumentError, "Invalid or undefined validator: #{validator}") if class_name.nil?
21
-
22
- Object.const_get(class_name).new(options).validate!
23
- end
24
- end
25
- end
26
-
27
- class ConventionalCommitValidator
28
- CONVENTIONAL_COMMIT_REGEX = /^((build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.*\))?(!)?(: (.*\s*)*))|(^Merge (.*\s*)*)|(^Initial commit$)/.freeze
29
-
30
- def initialize(options)
31
- @commit_message = options[:title]
32
- end
33
-
34
- def validate!
35
- raise("Conventional commit specifications not met for commit message: '#{@commit_message}'") unless valid_commit?
36
- end
37
-
38
- private
39
-
40
- def valid_commit?
41
- CONVENTIONAL_COMMIT_REGEX.match?(@commit_message)
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PR
4
- module With
5
- module Params
6
- VERSION = "1.3.1"
7
- end
8
- end
9
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'params/version'
4
- require_relative 'params/config_parser'
5
- require_relative 'params/options_validator'
6
- require 'uri'
7
- require 'json'
8
- require 'launchy'
9
-
10
- module PR
11
- module With
12
- module Params
13
- class Error < StandardError; end
14
-
15
- extend self
16
-
17
- def open(host:, path:, query:)
18
- uri_query = URI.encode_www_form(query)
19
- url_string = URI::HTTPS.build(host: host, path: path, query: uri_query).to_s
20
- Launchy.open(url_string)
21
- end
22
-
23
- def parse_config(file_path, scope)
24
- file_path.empty? ? {} : ConfigParser.new(config_file_path: file_path, scope: scope).parse!
25
- rescue StandardError => e
26
- message = "\e[35mWARNING\e[0m: Error parsing config file. Using defaults"
27
- reason = "reason: #{e.message}"
28
- backtrace = "backtrace: #{e.backtrace&.last(10)&.join("\n")}"
29
- error_message = [message, reason, backtrace, "\n\n"].join("\n")
30
-
31
- warn error_message
32
-
33
- {}
34
- end
35
-
36
- def validate_options!(options)
37
- validators = options.delete(:validators)
38
- OptionsValidator.validate!(options, validators: validators)
39
- end
40
- end
41
- end
42
- end