AlexWayfer/gem_toys

View on GitHub
lib/gem_toys/template/release.rb

Summary

Maintainability
A
25 mins
Test Coverage
# frozen_string_literal: true

require 'highline'

require_relative 'release/changelog'
require_relative 'release/git'

module GemToys
    class Template
        ## Template with gem release tool, expanding internally
        class Release
            include Toys::Template

            attr_reader :changelog_file_name, :version_file_path, :version_tag_prefix, :unreleased_title

            def initialize(
                changelog_file_name:,
                version_file_path:,
                version_tag_prefix:,
                unreleased_title:
            )
                @changelog_file_name = changelog_file_name
                @version_file_path = version_file_path
                @version_tag_prefix = version_tag_prefix
                @unreleased_title = unreleased_title
            end

            on_expand do |template|
                tool :release do
                    include Release::Changelog
                    include Release::Git

                    required_arg :new_version

                    to_run do
                        require 'date'

                        @template = template

                        @today = Date.today.to_s

                        handle_new_version

                        update_version_file

                        ## Update CHANGELOG
                        update_changelog_file

                        wait_for_manual_check

                        ## Build new gem file
                        exec_tool 'gem build'

                        ## Commit, tag, push
                        process_git

                        puts 'Pushing gem...'
                        sh "gem push #{current_gem_file}"
                    end

                    private

                    self::VERSION_TYPES = %w[
                        major
                        minor
                        patch
                    ].freeze

                    def handle_new_version
                        ## https://github.com/dazuma/toys/issues/103
                        @new_version =
                            if (version_index = self.class::VERSION_TYPES.index(new_version))
                                current_version_parts =
                                    current_version.split('.')[0..(self.class::VERSION_TYPES.count - 1)]
                                handle_keyword_in_new_version current_version_parts, version_index
                            else
                                new_version
                            end
                    end

                    def handle_keyword_in_new_version(current_version_parts, version_index)
                        current_version_parts.map.with_index do |version_part, index|
                            if index < version_index
                                version_part
                            elsif index == version_index
                                Integer(version_part) + 1
                            else
                                0
                            end
                        end.join('.')
                    end

                    def update_version_file
                        puts 'Updating version file...'

                        File.write(
                            version_file_path,
                            version_file_content.sub(/(VERSION = )'.+'/, "\\1'#{@new_version}'")
                        )
                    end

                    self::MANUAL_CHECK_MENU = {
                        yes: (proc do
                            ## `current_version` is using in `git` and `gem` commands
                            clear_memery_cache! :current_version
                        end),
                        no: (proc do
                            handle_refusing_to_continue
                        end),
                        refresh: (proc do
                            wait_for_manual_check
                        end)
                    }.freeze

                    def wait_for_manual_check
                        print_files_diff

                        HighLine.new.choose do |menu|
                            menu.layout = :one_line

                            menu.prompt = 'Are these changes correct? '

                            self.class::MANUAL_CHECK_MENU.each do |choice, block|
                                menu.choice(choice) { instance_exec(&block) }
                            end
                        end
                    end

                    def print_files_diff
                        $stdout.puts
                        sh "git diff #{version_file_path} #{changelog_file_path}"
                        $stdout.puts
                    end

                    def handle_refusing_to_continue
                        [version_file_path, changelog_file_path].each do |file_path|
                            sh "git restore --worktree #{file_path}"
                        end
                        abort
                    end
                end
            end
        end
    end
end