sashimi_tanpopo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '099fb0f8aa9d08ee9e8c1bf6c005b7df432489e959cd8e5cf29b621952fa3ab3'
4
+ data.tar.gz: 846da9967f9288a7cd0d3bc0aea82d8e958941a048188e7e4dc4bbc0aa05e3b8
5
+ SHA512:
6
+ metadata.gz: ec54ab868a5855195d011bec93a2c4c62f1255fe9a75bda3a4677dec4a08de20da100407284e6fdd823c7cbc0883bb713a7b24f4c127cc700fb731bc791ef2de
7
+ data.tar.gz: 6c26659eb9d35b2cf79260f1f3dff44e30d310bf0008ccbbba3811d988984e9d7ad78ad3f7b84e62654251fe4bb6fa584bffdd99d3cce86049a781ac1aa3c1bf
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
data/.yardopts ADDED
@@ -0,0 +1,8 @@
1
+ --markup markdown
2
+ --no-private
3
+ --hide-void-return
4
+ --output-dir ./.doc
5
+ -
6
+ CHANGELOG.md
7
+ LICENSE.txt
8
+ docs/*
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-10-07
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 sue445
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # SashimiTanpopo :sushi: :blossom:
2
+ Change files and create patches
3
+
4
+ [![test](https://github.com/sue445/sashimi_tanpopo/actions/workflows/test.yml/badge.svg)](https://github.com/sue445/sashimi_tanpopo/actions/workflows/test.yml)
5
+
6
+ ## Example
7
+ ```ruby
8
+ # recipe.rb
9
+
10
+ update_file ".ruby-version" do |content|
11
+ content.gsub!(/^[\d.]+$/, params[:ruby_version])
12
+ end
13
+
14
+ update_file "Dockerfile" do |content|
15
+ content.gsub!(/^FROM ruby:([\d.]+)$/, %Q{FROM ruby:#{params[:ruby_version]}})
16
+ end
17
+
18
+ @ruby_minor_version = params[:ruby_version].to_f
19
+
20
+ update_file ".rubocop.yml" do |content|
21
+ content.gsub!(/TargetRubyVersion: ([\d.]+)/, "TargetRubyVersion: #{@ruby_minor_version}")
22
+ end
23
+
24
+ update_file ".github/workflows/*.yml" do |content|
25
+ content.gsub!(/ruby-version: "(.+)"/, %Q{ruby-version: "#{params[:ruby_version]}"})
26
+ end
27
+ ```
28
+
29
+ ```bash
30
+ # Update local app files using recipe.rb
31
+ $ sashimi_tanpopo local --target-dir=/path/to/app --params=ruby_version:3.4.5 /path/to/recipe.rb
32
+
33
+ # Update local app files using recipe.rb and create Pull Request
34
+ $ sashimi_tanpopo github --target-dir=/path/to/app --params=ruby_version:3.4.5 \
35
+ --message="Upgrade to Ruby 3.4.5" --github-repository=yourname/yourrepo --pr-title="Upgrade to Ruby 3.4.5" \
36
+ --pr-source-branch=ruby_3.4.5 --pr-target-branch=main --pr-draft /path/to/recipe.rb
37
+ ```
38
+
39
+ ## Installation
40
+ ```bash
41
+ gem install sashimi_tanpopo
42
+ ```
43
+
44
+ ## Usage
45
+ ### sashimi_tanpopo local
46
+ Change local files using recipe files
47
+
48
+ ```bash
49
+ $ sashimi_tanpopo help local
50
+
51
+ Usage:
52
+ sashimi_tanpopo local RECIPE [RECIPE...]
53
+
54
+ Options:
55
+ -d, [--target-dir=TARGET_DIR] # Target directory. Default: current directory
56
+ -p, [--params=key:value] # Params passed to recipe file
57
+ [--dry-run], [--no-dry-run], [--skip-dry-run] # Whether to run dry run
58
+ # Default: false
59
+ [--color], [--no-color], [--skip-color] # Whether to colorize output
60
+ # Default: true
61
+ ```
62
+
63
+ ### sashimi_tanpopo github
64
+ Change local files using recipe files and create Pull Request
65
+
66
+ ```bash
67
+ $ sashimi_tanpopo help github
68
+
69
+ Usage:
70
+ sashimi_tanpopo github RECIPE [RECIPE...] --pr-source-branch=pr_branch --pr-title=PR_TITLE -m, --message=COMMIT_MESSAGE
71
+
72
+ Options:
73
+ -d, [--target-dir=TARGET_DIR] # Target directory. Default: current directory
74
+ -p, [--params=key:value] # Params passed to recipe file
75
+ [--dry-run], [--no-dry-run], [--skip-dry-run] # Whether to run dry run
76
+ # Default: false
77
+ [--color], [--no-color], [--skip-color] # Whether to colorize output
78
+ # Default: true
79
+ [--git-user-name=GIT_USER_NAME] # user name for git commit. Default: username of user authenticated with token
80
+ [--git-email=GIT_EMAIL] # email for git commit. Default: <git_user_name>@users.noreply.<github_host>
81
+ -m, --message=COMMIT_MESSAGE # commit message
82
+ [--github-repository=user/repo] # GitHub repository for Pull Request. One of --github--repository or $GITHUB_REPOSITORY is required [$GITHUB_REPOSITORY]
83
+ [--github-api-url=GITHUB_API_URL] # GitHub API endpoint. One of --github-api-url or $GITHUB_API_URL is required [$GITHUB_API_URL]
84
+ # Default: https://api.github.com
85
+ [--github-token=GITHUB_TOKEN] # GitHub access token. One of --github-token or $GITHUB_TOKEN is required [$GITHUB_TOKEN]
86
+ --pr-title=PR_TITLE # Pull Request title
87
+ [--pr-body=PR_BODY] # Pull Request body
88
+ --pr-source-branch=pr_branch # Pull Request source branch (a.k.a. head branch)
89
+ [--pr-target-branch=main] # Pull Request target branch (a.k.a. base branch). One of --pr-target-branch or $GITHUB_REF_NAME is required [$GITHUB_REF_NAME]
90
+ [--pr-assignees=one two three] # Pull Request assignees
91
+ [--pr-reviewers=one two three] # Pull Request reviewers
92
+ [--pr-labels=one two three] # Pull Request labels
93
+ [--pr-draft], [--no-pr-draft], [--skip-pr-draft] # Whether to create draft Pull Request
94
+ # Default: false
95
+ ```
96
+
97
+ ### sashimi_tanpopo gitlab
98
+ Change local files using recipe files and create Merge Request
99
+
100
+ ```bash
101
+ $ sashimi_tanpopo help gitlab
102
+
103
+ Usage:
104
+ sashimi_tanpopo gitlab RECIPE [RECIPE...] --mr-source-branch=mr_branch --mr-title=MR_TITLE -m, --message=COMMIT_MESSAGE
105
+
106
+ Options:
107
+ -d, [--target-dir=TARGET_DIR] # Target directory. Default: current directory
108
+ -p, [--params=key:value] # Params passed to recipe file
109
+ [--dry-run], [--no-dry-run], [--skip-dry-run] # Whether to run dry run
110
+ # Default: false
111
+ [--color], [--no-color], [--skip-color] # Whether to colorize output
112
+ # Default: true
113
+ [--git-user-name=GIT_USER_NAME] # user name for git commit. Default: username of user authenticated with token
114
+ [--git-email=GIT_EMAIL] # email for git commit. Default: <git_user_name>@noreply.<gitlab_host>
115
+ -m, --message=COMMIT_MESSAGE # commit message
116
+ [--gitlab-project=user/repo] # GitLab project for Merge Request. One of --gitlab-project, $GITLAB_PROJECT or $CI_PROJECT_PATH is required [$GITLAB_PROJECT, $CI_PROJECT_PATH]
117
+ [--gitlab-api-url=GITLAB_API_URL] # GitLab API endpoint. One of --gitlab-api-url, $GITLAB_API_URL or $CI_API_V4_URL is required [$GITLAB_API_URL, $CI_API_V4_URL]
118
+ # Default: https://gitlab.com/api/v4
119
+ [--gitlab-token=GITLAB_TOKEN] # GitLab access token. One of --gitlab-token or $GITLAB_TOKEN is required [$GITLAB_TOKEN]
120
+ --mr-title=MR_TITLE # Merge Request title
121
+ [--mr-body=MR_BODY] # Merge Request body
122
+ --mr-source-branch=mr_branch # Merge Request source branch
123
+ [--mr-target-branch=main] # Merge Request target branch). One of --mr-target-branch, $MR_TARGET_BRANCH or $CI_DEFAULT_BRANCH is required [$MR_TARGET_BRANCH, $CI_DEFAULT_BRANCH]
124
+ [--mr-assignees=one two three] # Merge Request assignees
125
+ [--mr-reviewers=one two three] # Merge Request reviewers
126
+ [--mr-labels=one two three] # Merge Request labels
127
+ [--mr-draft], [--no-mr-draft], [--skip-mr-draft] # Whether to create draft Merge Request
128
+ # Default: false
129
+ [--mr-auto-merge], [--no-mr-auto-merge], [--skip-mr-auto-merge] # Whether to set auto-merge to Merge Request
130
+ # Default: false
131
+ ```
132
+
133
+ ## Recipe file specification
134
+ See [docs/RECIPE.md](docs/RECIPE.md)
135
+
136
+ ## Development
137
+
138
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
139
+
140
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
141
+
142
+ ## Contributing
143
+
144
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sue445/sashimi_tanpopo.
145
+
146
+ ## License
147
+
148
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ namespace :rbs do
9
+ desc "`rbs collection install` and `git commit`"
10
+ task :install do
11
+ sh "rbs collection install"
12
+ sh "git add rbs_collection.lock.yaml"
13
+ sh "git commit -m 'rbs collection install' || true"
14
+ end
15
+ end
16
+
17
+ desc "Check rbs"
18
+ task :rbs do
19
+ sh "rbs validate"
20
+ sh "steep check"
21
+ end
22
+
23
+ task default: %i[spec rbs]
data/Steepfile ADDED
@@ -0,0 +1,32 @@
1
+ # D = Steep::Diagnostic
2
+
3
+ target :lib do
4
+ signature "sig"
5
+ ignore_signature "sig/test"
6
+
7
+ check "lib" # Directory name
8
+ # check "path/to/source.rb" # File name
9
+ # check "app/models/**/*.rb" # Glob
10
+ # ignore "lib/templates/*.rb"
11
+
12
+ # library "pathname" # Standard libraries
13
+ # library "strong_json" # Gems
14
+
15
+ # configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
16
+ # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
17
+ # configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
18
+ # configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
19
+ # configure_code_diagnostics do |hash| # You can setup everything yourself
20
+ # hash[D::Ruby::NoMethod] = :information
21
+ # end
22
+ end
23
+
24
+ # target :test do
25
+ # unreferenced! # Skip type checking the `lib` code when types in `test` target is changed
26
+ # signature "sig/test" # Put RBS files for tests under `sig/test`
27
+ # check "test" # Type check Ruby scripts under `test`
28
+ #
29
+ # configure_code_diagnostics(D::Ruby.lenient) # Weak type checking for test code
30
+ #
31
+ # # library "pathname" # Standard libraries
32
+ # end
data/docs/RECIPE.md ADDED
@@ -0,0 +1,63 @@
1
+ # Recipe file specification
2
+ Recipe file is simple Ruby script, with some exceptions.
3
+
4
+ By avoiding the DSL methods listed below, you are free to write code as you like.
5
+
6
+ ## Special methods available within recipe file
7
+ The following methods are special variables available within recipe file.
8
+
9
+ ### `dry_run?`
10
+ Whether dry run
11
+
12
+ e.g.
13
+
14
+ ```rb
15
+ unless dry_run?
16
+ puts "This will be called when apply mode"
17
+ end
18
+ ```
19
+
20
+ ### `params`
21
+ Passed from `--params`
22
+
23
+ Returns:
24
+
25
+ * `Hash<Symbol, String>`
26
+
27
+ e.g.
28
+
29
+ ```bash
30
+ sashimi_tanpopo local --params name:sue445 --params lang:ja recipe.rb
31
+ ```
32
+
33
+ within `recipe.rb`
34
+
35
+ ```rb
36
+ # recipe.rb
37
+
38
+ params
39
+ #=> {name: "sue445", lang: "ja"}
40
+ ```
41
+
42
+ ### `update_file`
43
+ Update files if exists
44
+
45
+ ```ruby
46
+ # Update single file
47
+ update_file "test.txt" do |content|
48
+ content.gsub!("name", params[:name])
49
+ end
50
+
51
+ # Update multiple files
52
+ update_file ".github/workflows/*.yml" do |content|
53
+ content.gsub!(/ruby-version: "(.+)"/, %Q{ruby-version: "#{params[:ruby_version]}"})
54
+ end
55
+ ```
56
+
57
+ Parameters:
58
+
59
+ * `pattern`: Path to target file (relative path from `--target-dir`). This supports [`Dir.glob`](https://ruby-doc.org/current/Dir.html#method-c-glob) pattern. (e.g. `.github/workflows/*.yml`)
60
+
61
+ Yield Parameters:
62
+
63
+ * `content`: Content of file. If `content` is changed in block, file will be changed.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "sashimi_tanpopo/cli"
4
+
5
+ SashimiTanpopo::CLI.start(ARGV)
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "sashimi_tanpopo"
5
+
6
+ module SashimiTanpopo
7
+ class CLI < Thor
8
+ desc "version", "Show sashimi_tanpopo version"
9
+ def version
10
+ puts "sashimi_tanpopo v#{SashimiTanpopo::VERSION}"
11
+ end
12
+
13
+ def self.exit_on_failure?
14
+ true
15
+ end
16
+
17
+ def self.define_exec_common_options
18
+ option :target_dir, type: :string, aliases: "-d", desc: "Target directory. Default: current directory"
19
+ option :params, type: :hash, aliases: "-p", default: {}, desc: "Params passed to recipe file", repeatable: true
20
+ option :dry_run, type: :boolean, default: false, desc: "Whether to run dry run"
21
+ option :color, type: :boolean, default: true, desc: "Whether to colorize output"
22
+ end
23
+
24
+ desc "local RECIPE [RECIPE...]", "Change local files using recipe files"
25
+ define_exec_common_options
26
+ def local(*recipe_files)
27
+ Provider::Local.new(
28
+ recipe_paths: recipe_files,
29
+ target_dir: options[:target_dir],
30
+ params: self.class.normalize_params(options[:params]),
31
+ dry_run: options[:dry_run],
32
+ is_colored: options[:color],
33
+ ).perform
34
+ end
35
+
36
+ desc "github RECIPE [RECIPE...]", "Change local files using recipe files and create Pull Request"
37
+ define_exec_common_options
38
+ option :git_user_name, type: :string, desc: "user name for git commit. Default: username of user authenticated with token"
39
+ option :git_email, type: :string, desc: "email for git commit. Default: <git_user_name>@users.noreply.<github_host>"
40
+ option :message, type: :string, desc: "commit message", required: true, aliases: "-m", banner: "COMMIT_MESSAGE"
41
+ option :github_repository, type: :string, desc: "GitHub repository for Pull Request. One of --github--repository or $GITHUB_REPOSITORY is required [$GITHUB_REPOSITORY]", banner: "user/repo"
42
+ option :github_api_url, type: :string, desc: "GitHub API endpoint. One of --github-api-url or $GITHUB_API_URL is required [$GITHUB_API_URL]", default: "https://api.github.com"
43
+ option :github_token, type: :string, desc: "GitHub access token. One of --github-token or $GITHUB_TOKEN is required [$GITHUB_TOKEN]"
44
+ option :pr_title, type: :string, desc: "Pull Request title", required: true
45
+ option :pr_body, type: :string, desc: "Pull Request body"
46
+ option :pr_source_branch, type: :string, desc: "Pull Request source branch (a.k.a. head branch)", required: true, banner: "pr_branch"
47
+ option :pr_target_branch, type: :string, desc: "Pull Request target branch (a.k.a. base branch). One of --pr-target-branch or $GITHUB_REF_NAME is required [$GITHUB_REF_NAME]", banner: "main"
48
+ option :pr_assignees, type: :array, desc: "Pull Request assignees", default: []
49
+ option :pr_reviewers, type: :array, desc: "Pull Request reviewers", default: []
50
+ option :pr_labels, type: :array, desc: "Pull Request labels", default: []
51
+ option :pr_draft, type: :boolean, desc: "Whether to create draft Pull Request", default: false
52
+ def github(*recipe_files)
53
+ repository = option_or_env!(option_name: :github_repository, env_name: "GITHUB_REPOSITORY")
54
+ api_endpoint = option_or_env!(option_name: :github_api_url, env_name: "GITHUB_API_URL")
55
+ access_token = option_or_env!(option_name: :github_token, env_name: "GITHUB_TOKEN")
56
+ pr_target_branch = option_or_env!(option_name: :pr_target_branch, env_name: "GITHUB_REF_NAME")
57
+
58
+ Provider::GitHub.new(
59
+ recipe_paths: recipe_files,
60
+ target_dir: options[:target_dir],
61
+ params: self.class.normalize_params(options[:params]),
62
+ dry_run: options[:dry_run],
63
+ is_colored: options[:color],
64
+ git_username: options[:git_user_name],
65
+ git_email: options[:git_email],
66
+ commit_message: options[:message],
67
+ repository: repository,
68
+ api_endpoint: api_endpoint,
69
+ access_token: access_token,
70
+ pr_title: options[:pr_title],
71
+ pr_body: options[:pr_body],
72
+ pr_source_branch: options[:pr_source_branch],
73
+ pr_target_branch: pr_target_branch,
74
+ pr_assignees: options[:pr_assignees],
75
+ pr_reviewers: options[:pr_reviewers],
76
+ pr_labels: options[:pr_labels],
77
+ is_draft_pr: options[:pr_draft],
78
+ ).perform
79
+ end
80
+
81
+ desc "gitlab RECIPE [RECIPE...]", "Change local files using recipe files and create Merge Request"
82
+ define_exec_common_options
83
+ option :git_user_name, type: :string, desc: "user name for git commit. Default: username of user authenticated with token"
84
+ option :git_email, type: :string, desc: "email for git commit. Default: <git_user_name>@noreply.<gitlab_host>"
85
+ option :message, type: :string, desc: "commit message", required: true, aliases: "-m", banner: "COMMIT_MESSAGE"
86
+ option :gitlab_project, type: :string, desc: "GitLab project for Merge Request. One of --gitlab-project, $GITLAB_PROJECT or $CI_PROJECT_PATH is required [$GITLAB_PROJECT, $CI_PROJECT_PATH]", banner: "user/repo"
87
+ option :gitlab_api_url, type: :string, desc: "GitLab API endpoint. One of --gitlab-api-url, $GITLAB_API_URL or $CI_API_V4_URL is required [$GITLAB_API_URL, $CI_API_V4_URL]", default: "https://gitlab.com/api/v4"
88
+ option :gitlab_token, type: :string, desc: "GitLab access token. One of --gitlab-token or $GITLAB_TOKEN is required [$GITLAB_TOKEN]"
89
+ option :mr_title, type: :string, desc: "Merge Request title", required: true
90
+ option :mr_body, type: :string, desc: "Merge Request body"
91
+ option :mr_source_branch, type: :string, desc: "Merge Request source branch", required: true, banner: "mr_branch"
92
+ option :mr_target_branch, type: :string, desc: "Merge Request target branch). One of --mr-target-branch, $MR_TARGET_BRANCH or $CI_DEFAULT_BRANCH is required [$MR_TARGET_BRANCH, $CI_DEFAULT_BRANCH]", banner: "main"
93
+ option :mr_assignees, type: :array, desc: "Merge Request assignees", default: []
94
+ option :mr_reviewers, type: :array, desc: "Merge Request reviewers", default: []
95
+ option :mr_labels, type: :array, desc: "Merge Request labels", default: []
96
+ option :mr_draft, type: :boolean, desc: "Whether to create draft Merge Request", default: false
97
+ option :mr_auto_merge, type: :boolean, desc: "Whether to set auto-merge to Merge Request", default: false
98
+ def gitlab(*recipe_files)
99
+ repository = option_or_env!(option_name: :gitlab_project, env_name: %w[GITLAB_PROJECT CI_PROJECT_PATH])
100
+ api_endpoint = option_or_env!(option_name: :gitlab_api_url, env_name: %w[GITLAB_API_URL CI_API_V4_URL])
101
+ access_token = option_or_env!(option_name: :gitlab_token, env_name: "GITLAB_TOKEN")
102
+ mr_target_branch = option_or_env!(option_name: :mr_target_branch, env_name: %w[MR_TARGET_BRANCH CI_DEFAULT_BRANCH])
103
+
104
+ Provider::GitLab.new(
105
+ recipe_paths: recipe_files,
106
+ target_dir: options[:target_dir],
107
+ params: self.class.normalize_params(options[:params]),
108
+ dry_run: options[:dry_run],
109
+ is_colored: options[:color],
110
+ git_username: options[:git_user_name],
111
+ git_email: options[:git_email],
112
+ commit_message: options[:message],
113
+ repository: repository,
114
+ api_endpoint: api_endpoint,
115
+ access_token: access_token,
116
+ mr_title: options[:mr_title],
117
+ mr_body: options[:mr_body],
118
+ mr_source_branch: options[:mr_source_branch],
119
+ mr_target_branch: mr_target_branch,
120
+ mr_assignees: options[:mr_assignees],
121
+ mr_reviewers: options[:mr_reviewers],
122
+ mr_labels: options[:mr_labels],
123
+ is_draft_mr: options[:mr_draft],
124
+ is_auto_merge: options[:mr_auto_merge],
125
+ ).perform
126
+ end
127
+
128
+ # @param params [Hash<String, String>]
129
+ # @return [Hash<Symbol,String>]
130
+ #
131
+ # @example
132
+ # normalize_params({"k1"=>"v1", "k2"=>"v2"})
133
+ # #=> {k1: "v1", k2: "v2"}
134
+ def self.normalize_params(params)
135
+ params.transform_keys(&:to_sym)
136
+ end
137
+
138
+ private
139
+
140
+ no_commands do
141
+ # @param option_name [String,Symbol]
142
+ # @param env_name [String, Array<String>]
143
+ # @param default [String,nil]
144
+ # @return [String,nil]
145
+ def option_or_env(option_name:, env_name:, default: nil)
146
+ return options[option_name] if options[option_name] && !options[option_name].empty?
147
+
148
+ env_names = Array(env_name) #: Array[String]
149
+ env_names.each do |name|
150
+ return ENV[name] unless ENV.fetch(name, "") == ""
151
+ end
152
+
153
+ default
154
+ end
155
+
156
+ # @param option_name [String,Symbol]
157
+ # @param env_name [String, Array<String>]
158
+ # @return [String]
159
+ def option_or_env!(option_name:, env_name:)
160
+ value = option_or_env(option_name: option_name, env_name: env_name)
161
+ return value if value
162
+
163
+ env_names = Array(env_name)
164
+ SashimiTanpopo.logger.error "Error: One of --#{option_name.to_s.gsub("_", "-")}, #{env_names.join(", ")} is required!"
165
+ exit!
166
+ end
167
+ end
168
+ end
169
+ end