Module: Request::InteractiveCloudflareRecovery
- Defined in:
- lib/Request.rb
Overview
Interactive Cloudflare recovery: when running on a developer’s own machine (i.e. there is a real TTY and no CI marker env var), instead of just raising CloudflareBlockedError we can open Medium in the user’s default browser, let them clear the challenge by hand, and retry the request once. CI environments still raise immediately.
Constant Summary collapse
- CI_ENV_VARS =
Common CI env vars. If any of these is set to a non-empty, non-“false” value, we assume non-interactive.
%w[CI GITHUB_ACTIONS GITLAB_CI CIRCLECI JENKINS_URL BUILDKITE TF_BUILD TRAVIS APPVEYOR].freeze
- DISABLE_ENV_VAR =
Explicit opt-out for users who want the old raise-and-exit behavior even on a TTY.
'MEDIUM_NO_AUTO_BROWSER'.freeze
Class Method Summary collapse
- .available?(env: ENV, stdin: $stdin, stdout: $stdout) ⇒ Boolean
- .inCIEnvironment?(env = ENV) ⇒ Boolean
-
.openCommand(url, hostOS: RbConfig::CONFIG['host_os']) ⇒ Object
Build the platform-appropriate command for opening a URL in the default browser.
- .openInBrowser(url, errput: $stderr) ⇒ Object
-
.run(url, errput: $stderr, input: $stdin, autoOpen: true) ⇒ Object
Run the interactive recovery flow.
Class Method Details
.available?(env: ENV, stdin: $stdin, stdout: $stdout) ⇒ Boolean
69 70 71 72 73 74 75 76 |
# File 'lib/Request.rb', line 69 def available?(env: ENV, stdin: $stdin, stdout: $stdout) return false if env[DISABLE_ENV_VAR].to_s == '1' return false if inCIEnvironment?(env) stdin.tty? && stdout.tty? rescue StandardError # Some test stdio doubles don't implement .tty? — treat as non-interactive. false end |
.inCIEnvironment?(env = ENV) ⇒ Boolean
78 79 80 81 82 83 |
# File 'lib/Request.rb', line 78 def inCIEnvironment?(env = ENV) CI_ENV_VARS.any? do |key| value = env[key].to_s !value.empty? && value.downcase != 'false' && value != '0' end end |
.openCommand(url, hostOS: RbConfig::CONFIG['host_os']) ⇒ Object
Build the platform-appropriate command for opening a URL in the default browser. Returned as an array so callers can spawn / system without going through a shell.
88 89 90 91 92 93 94 |
# File 'lib/Request.rb', line 88 def openCommand(url, hostOS: RbConfig::CONFIG['host_os']) case hostOS when /darwin/ then ['open', url] when /mswin|mingw|cygwin/ then ['cmd', '/c', 'start', '', url] else ['xdg-open', url] end end |
.openInBrowser(url, errput: $stderr) ⇒ Object
96 97 98 99 100 |
# File 'lib/Request.rb', line 96 def openInBrowser(url, errput: $stderr) spawn(*openCommand(url), out: File::NULL, err: File::NULL) rescue Errno::ENOENT, StandardError => e errput.puts "(Couldn't auto-open browser — #{e.class}: #{e.}. Open #{url} manually.)" end |
.run(url, errput: $stderr, input: $stdin, autoOpen: true) ⇒ Object
Run the interactive recovery flow. Returns true if the user confirmed they cleared the challenge, false if they pressed Ctrl-D (EOF) or otherwise gave up.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/Request.rb', line 105 def run(url, errput: $stderr, input: $stdin, autoOpen: true) errput.puts <<~MSG ────────────────────────────────────────────────────────────────────── ⚠ Cloudflare bot challenge detected at #{url}. Since this looks like an interactive run, you can clear the challenge in your browser: 1. A browser window will open at https://medium.com. 2. Complete the "Just a moment…" / CAPTCHA challenge there. 3. Come back here and press Enter to retry. (To disable this prompt and just fail fast, set #{DISABLE_ENV_VAR}=1.) ────────────────────────────────────────────────────────────────────── MSG openInBrowser('https://medium.com', errput: errput) if autoOpen errput.print 'Press Enter once the challenge is cleared (Ctrl-D to give up)… ' line = input.gets errput.puts !line.nil? end |