From 1fb6caaca535579009681a9b7e4a452625605029 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 14 Oct 2021 17:07:02 +0200 Subject: [PATCH 1/4] Introduce interactive_prompt_reminder helper --- .../helper/interactive_prompt_reminder.rb | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb b/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb new file mode 100644 index 000000000..81720161b --- /dev/null +++ b/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb @@ -0,0 +1,62 @@ +require 'fastlane_core' + +module FastlaneCore + # NOTE: FastlaneCore::UI delegates to the FastlaneCore::Shell implementation when output is the terminal + class Shell + # Calls the block given and remind the user with a vocal message if the block does not return after specific delays. + # + # Especially useful when using a block which calls methods that are interactive, in order to remind the user + # to answer the interactive prompt if they forgot about it after some delays. + # + # Example usage: + # + # text = with_reminder do + # puts "Enter some text:" + # $stdout.getch + # end + # + # @param [Double,Array] after + # Delay or list of delays to wait for before pronouncing the reminder message. + # If an array of values is passed, the message will be pronounced multiple times, after having waited for the subsequent delays in turn. + # Defaults to reminding after 30s, then 3mn, then 10mn. + # @param [String] message + # The message to pronounce out loud after the delay has passed, if the block hasn't returned beforehand. + # @return The same value that the blocks might return + # + def self.with_reminder(after: [30, 180, 600], message: 'An interactive prompt is waiting for you in the Terminal!') + delays_list = Array(after) + thread = Thread.new do + until delays_list.empty? + sleep(delays_list.shift) + $stdout.beep + system('say', message) + end + end + # execute the interactive code + res = yield + # if we replied before the timeout, kill the thread so message won't be triggered + thread.kill + # If the block given returned a value, pass it + return res + end + + # Monkey-Patch fastlane's `UI.input`, `UI.confirm`, `UI.select` and `UI.password` interactive methods + # (which delegate to `FastlaneCore::Shell` when output is the terminal) + # + # Once you call this method, any invocation of `UI.input`, `UI.confirm`, `UI.select` or `UI.password` + # anywhere in Fastlane (by your Fastfile, an action, …) will be wrapped in a call to with_reminder automatically. + # + def self.monkey_patch_interactive_prompts_with_reminder + %i[input confirm select password].each do |method_name| + old_method = instance_method(method_name) + + define_method(method_name) do |*args| + FastlaneCore::Shell.with_reminder { old_method.bind(self).call(*args) } + end + end + end + end +end + +# Apply Monkey patch +FastlaneCore::Shell.monkey_patch_interactive_prompts_with_reminder From 7e16f90aeefaddb8401458e1a04ba032f85ebfd2 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 14 Oct 2021 21:15:38 +0200 Subject: [PATCH 2/4] Add env vars to allow control of the features + Add doc --- .../helper/interactive_prompt_reminder.rb | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb b/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb index 81720161b..a86bebb7c 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb @@ -1,8 +1,32 @@ require 'fastlane_core' +# The feature in this file are controlled by the following ENV vars: +# +# @env `FASTLANE_PROMPT_REMINDER_DISABLE_AUTO_PATCH` +# If this variable is set, it will disable the auto-application of the monkey patch. In such case, +# `UI.input`, `UI.confirm`, `UI.select` and `UI.password` methods won't be automatically patched +# unless you explicitly call `monkey_patch_interactive_prompts_with_reminder` yourself. +# +# @env `FASTLANE_PROMPT_REMINDER_MESSAGE` +# - If not set, then while auto-patching the `UI.…` methods, it will NOT make the patched methods +# speak any vocal message – and instead will only emit a beep and make your Terminal icon jump in the Dock. +# - If set to `default`, `true`, `yes` or `1`, then while auto-patching the `UI.…` methods, it will +# make the patched methods announce the default message. +# - If set to any other string, it will make the patched methods use that string as the message to announce +# during the reminders +# - NOTE: This env var only has an effect if the other `FASTLANE_PROMPT_REMINDER_DISABLE_AUTO_PATCH` env var +# is _not_ set (and thus the `UI.…` methods _are_ auto-patched), because it only affects how auto-patching is done. +# +# @env `FASTLANE_PROMPT_REMINDER_DELAYS` +# The delays (in seconds) to use when monkey-patching the `UI.…` methods to wrap them around `with_reminder`, +# separated by a comma (e.g. `60,300,900`). If unset, will use the default delays of `30,180,600`. + module FastlaneCore # NOTE: FastlaneCore::UI delegates to the FastlaneCore::Shell implementation when output is the terminal class Shell + DEFAULT_PROMPT_REMINDER_MESSAGE = 'An interactive prompt is waiting for you in the Terminal!'.freeze + DEFAULT_PROMPT_REMINDER_DELAYS = [30, 180, 600].freeze + # Calls the block given and remind the user with a vocal message if the block does not return after specific delays. # # Especially useful when using a block which calls methods that are interactive, in order to remind the user @@ -23,13 +47,13 @@ class Shell # The message to pronounce out loud after the delay has passed, if the block hasn't returned beforehand. # @return The same value that the blocks might return # - def self.with_reminder(after: [30, 180, 600], message: 'An interactive prompt is waiting for you in the Terminal!') - delays_list = Array(after) + def self.with_reminder(after: DEFAULT_PROMPT_REMINDER_DELAYS, message: DEFAULT_PROMPT_REMINDER_MESSAGE) + delays_list = Array(after.dup) thread = Thread.new do until delays_list.empty? sleep(delays_list.shift) $stdout.beep - system('say', message) + system('say', message) unless message.nil? end end # execute the interactive code @@ -46,12 +70,12 @@ def self.with_reminder(after: [30, 180, 600], message: 'An interactive prompt is # Once you call this method, any invocation of `UI.input`, `UI.confirm`, `UI.select` or `UI.password` # anywhere in Fastlane (by your Fastfile, an action, …) will be wrapped in a call to with_reminder automatically. # - def self.monkey_patch_interactive_prompts_with_reminder + def self.monkey_patch_interactive_prompts_with_reminder(after: DEFAULT_PROMPT_REMINDER_DELAYS, message: DEFAULT_PROMPT_REMINDER_MESSAGE) %i[input confirm select password].each do |method_name| old_method = instance_method(method_name) define_method(method_name) do |*args| - FastlaneCore::Shell.with_reminder { old_method.bind(self).call(*args) } + FastlaneCore::Shell.with_reminder(after: after, message: message) { old_method.bind(self).call(*args) } end end end @@ -59,4 +83,11 @@ def self.monkey_patch_interactive_prompts_with_reminder end # Apply Monkey patch -FastlaneCore::Shell.monkey_patch_interactive_prompts_with_reminder +unless ENV['FASTLANE_PROMPT_REMINDER_DISABLE_AUTO_PATCH'] + message = ENV['FASTLANE_PROMPT_REMINDER_MESSAGE'] + message = FastlaneCore::Shell::DEFAULT_PROMPT_REMINDER_MESSAGE if %w[default true yes 1].include?(message&.downcase) + delays = ENV['FASTLANE_PROMPT_REMINDER_DELAYS']&.split(',')&.map(&:to_i) || FastlaneCore::Shell::DEFAULT_PROMPT_REMINDER_DELAYS + + FastlaneCore::UI.verbose("Monkey-patching the UI interactive methods to add a reminder (#{delays.inspect}, #{message.inspect})") + FastlaneCore::Shell.monkey_patch_interactive_prompts_with_reminder(after: delays, message: message) +end From a281b8718b002fb8c2f1be6ddd090efe8ec44848 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Wed, 20 Oct 2021 18:52:05 +0200 Subject: [PATCH 3/4] Add CHANGELOG entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa3db3f51..63accc644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ _None_ ### New Features -_None_ +* Added a reminder mechanism for when you forgot a prompt was waiting for you in the Terminal. This reminder is [configurable via environment variables](https://github.com/wordpress-mobile/release-toolkit/blob/5c9b79db4bfcb298376fe3e81bc53881795922a5/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb#L3-L22) to change the default delays and optionally opt-in for speaking a voice message in addition to the default beep + dock icon badge. [#302] ### Bug Fixes From 8a756a64629b8e8c13278702488cb0dab1d13ed6 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Fri, 22 Oct 2021 13:42:08 +0200 Subject: [PATCH 4/4] Fix typo in comment h/t @mokagio Co-authored-by: Gio Lodi --- .../wpmreleasetoolkit/helper/interactive_prompt_reminder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb b/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb index a86bebb7c..93c6bfbfb 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb @@ -1,6 +1,6 @@ require 'fastlane_core' -# The feature in this file are controlled by the following ENV vars: +# The features in this file are controlled by the following ENV vars: # # @env `FASTLANE_PROMPT_REMINDER_DISABLE_AUTO_PATCH` # If this variable is set, it will disable the auto-application of the monkey patch. In such case,