Extract GlotPress download retry logic and apply to all cases#667
Extract GlotPress download retry logic and apply to all cases#667
Conversation
GlotPressDownloader helper
AliSoftware
left a comment
There was a problem hiding this comment.
Preliminary review, I didn't check the whole code in details nor the unit tests just yet, but leaving my initial high-level thoughts still as a first pass.
lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_localize_helper.rb
Outdated
Show resolved
Hide resolved
lib/fastlane/plugin/wpmreleasetoolkit/helper/glotpress_download_helper.rb
Outdated
Show resolved
Hide resolved
lib/fastlane/plugin/wpmreleasetoolkit/helper/glotpress_downloader.rb
Outdated
Show resolved
Hide resolved
| # @return The result of the block if provided, or true/false indicating success | ||
| # | ||
| def download(url, locale) | ||
| @current_locale = locale # Store for error handling |
There was a problem hiding this comment.
Is there no way to propagate that locale to each method call that might need it, instead of using an instance variable, to avoid that risk altogether?
For example I think just passing it as a parameter to make_request (the only method which seems to reference that @current_locale apparently?) would be better?
Especially, I don't think we use Parallel.map right now to download multiple locales in parallel—but instead we just use a .each serial loop, right?—…but if we ever do, and thus one GlotPressDownloader might be reused for multiple loop iterations in parallel, storing that in an instance variable would lead to race condition issues.
There was a problem hiding this comment.
Also, the fact that we have to remember to reset the auto-retry counter once we get a 200 response (line 64) so that we're ready to handle the next call to download in the next caller loop iteration… makes it brittle too as well given the reuse context.
So I wonder if we shouldn't instead require to create a new instance for every single download, i.e. not have a single GlotpressDownloader instance created outside of the caller's loop and calling downloader.download(url, locale) on the same instance inside each loop iteration, but instead have the url + locale be on the constructor (def initialize) and download take no argument at all, to enfoce each loop iteration having to create a separate instance, so that we're guaranteed that there won't be any instance variables with old values we forgot to reset between loops, and that we wouldn't risk having race conditions from reuse (especially if we migrate to Parallel.map one day…)
There was a problem hiding this comment.
Great suggestion, thanks! 👍 it makes the state management easier to follow. Updated on 19fc711.
lib/fastlane/plugin/wpmreleasetoolkit/helper/glotpress_download_helper.rb
Outdated
Show resolved
Hide resolved
| # Unexpected status code (including 404, 500, etc.) | ||
| status_line = "#{response.code} #{response.message}" | ||
| UI.error("Error downloading locale `#{locale}` — #{status_line} (#{original_uri})") | ||
| if !FastlaneCore::Helper.is_ci? && UI.confirm("Retry downloading `#{locale}`?") |
There was a problem hiding this comment.
I wonder if is_ci? is the right condition for it here, as opposed to "is the current terminal interactive"… which I think Fastlane's UI module already have a helper for?
[EDIT] Yes, UI.interactive? is the one I had in mind 💭
Co-authored-by: Olivier Halligon <olivier.halligon@automattic.com>
…ress_downloader`
AliSoftware
left a comment
There was a problem hiding this comment.
Approving to unblock, as the 2 comments are nitpicks not blockers.
PS: Probably worth testing this on a real project (e.g. making wordpress-android point its Gemfile to this PR's branch and run a test lane calling this to validate it works in real world use case) before merging, just to be extra sure.
lib/fastlane/plugin/wpmreleasetoolkit/helper/glotpress_downloader.rb
Outdated
Show resolved
Hide resolved
| # @yield [String] The response body if the download was successful | ||
| # @return The result of the block if provided, or true/false indicating success if no block provided | ||
| # | ||
| def download(&) |
There was a problem hiding this comment.
Given that all call sites will now basically always call downloader = GlotpressDownloader.new(…) followed immediately by downloader.download, I wonder if it would be worth adding a convenience class method too to make all the call sites that use that pattern even simpler?
def self.download(url:, locale:, auto_retry: false, &)
downloader = self.new(url: url, locale: locale, auto_retry: auto_retry)
downloader.download(&)
end…der.rb Co-authored-by: Olivier Halligon <olivier.halligon@automattic.com>
Done, updated the description with a reference to a draft PR. |
Related to p1760606044394999-slack-CC7L49W13
Fixes AINFRA-1419
What does it do?
This PR extracts the GlotPress download code with retry logic originally from
metadata_download_helper.rbto a separate helperGlotPressDownloader, and use that same helper also inandroid_localize_helper.rbandios_l10n_helper.rb.Testing
I've created a draft PR where I ran lanes that download GlotPress: wordpress-mobile/WordPress-Android#22291 and triggered two builds:
Checklist before requesting a review
bundle exec rubocopto test for code style violations and recommendations.specs/*_spec.rb) if applicable.bundle exec rspecto run the whole test suite and ensure all your tests pass.CHANGELOG.mdfile to describe your changes under the appropriate existing###subsection of the existing## Trunksection.MIGRATION.mdfile to describe how the changes will affect the migration from the previous major version and what the clients will need to change and consider.