Validate on shopify theme share/pull/push/serve if users are running the command in a theme/empty directory#2680
Conversation
| @@ -0,0 +1,31 @@ | |||
| # frozen_string_literal: true | |||
There was a problem hiding this comment.
Got influenced by lib/project_types/extension/models/specification_handlers/theme_app_extension.rb,
especially the concept of SpecificationHandlers
| @ctx, | ||
| [], | ||
| title: @ctx.message("theme.confirm_current_directory"), | ||
| force: options.flags[:force], |
There was a problem hiding this comment.
The two existing ConfirmStore.asks support the --force flag.
As it also makes sense for this PR's confirmation dialogue, I added it to all commands.
There was a problem hiding this comment.
we should definitely get input from UX before making an overarching change like this 😌
functionally i think this makes sense, we're giving developers the previous behavior where you can run theme commands in any directory and allowing them to do that with --force
also after poking around in the repo a bit, i don't see any classes that try to directly look at this flag. so unexpected downstream effects seem unlikely 😄
There was a problem hiding this comment.
alternatively maybe a new flag make sense? --ignore-theme-validation or something of that kind? --force comes off as pretty destructive of a flag (cc: git push --force lol)
There was a problem hiding this comment.
we should definitely get input from UX before making an overarching change like this 😌
Agreed. I will wait for Guilherme's input, before asking Meredith then :)
also after poking around in the repo a bit, i don't see any classes that try to directly look at this flag. so unexpected downstream effects seem unlikely 😄
Thanks for checking too :)
maybe a new flag make sense? --ignore-theme-validation or something of that kind? --force comes off as pretty destructive of a flag (cc: git push --force lol)
Contouring the validation with --force is kinda destructive: It can overwrite a potentially valid theme with some irrelevant/invalid files 😄.
There was a problem hiding this comment.
Guilherme suggests to hide the --force flag (hide it from help message in 2.x / hidden: true in 3.x) and to create a an issue (enhancement) at shopify/cli suggesting the introduction of the -f/--force as a pattern in the whole CLI. Then Meredith can give her input.
I agree with this suggestion.
|
|
||
| def setup | ||
| super | ||
| project_context("theme") |
There was a problem hiding this comment.
By default, cwd is test/fixtures/project.
In order to work in a valid theme directory, I have used project_context, which includes a cd into test/fixtures/theme.
I initially tried with setting the root parameter (at multiple places), but it kept staying in test/fixtures/project.
Even though I don't like changing dirs during tests, I think it's a cleaner solution than explicitly setting root everywhere (provided that this would work).
There was a problem hiding this comment.
very cool find! TIL about project_context
Even though I don't like changing dirs during tests, I think it's a cleaner solution than explicitly setting root everywhere (provided that this would work).
i don't know if i agree? it's cleaner in the sense it has less lines of code but project_context("theme") doesn't have a clear function without exploring the meaning of that method. "theme" means the theme fixture but will that be super clear when we come back to this code in 6 months?
regardless of what we pick, i see we're setting root in some tests still while also using project_context. setting root seems to be a convention in theme tests and using project_context is a convention for app tests. i don't think conventions are sacred. they should be should be challenged but we can still be intentional and consistent when deciding to make a change 😄
There was a problem hiding this comment.
project_context("theme") doesn't have a clear function without exploring the meaning of that method
That's true. I trusted/got inspired by the existing code where project_context was used at least 7 times 😄.
will that be super clear when we come back to this code in 6 months?
Probably not, but I accepted this, as I thought we would migrate the existing theme code to 3.x sooner 😄. But I will try again with setting root in the tests then :)
| end | ||
|
|
||
| def test_push_with_root | ||
| specified_root = ShopifyCLI::ROOT + "/test/fixtures/theme" |
There was a problem hiding this comment.
I had to use a different root, because theme serve push in a non-existing folder like dist does not make sense.
shopify theme share/pull/push/serve/dev if users are running the command in a theme/empty directoryshopify theme share/pull/push/serve if users are running the command in a theme/empty directory
|
|
||
| def call(_args, name) | ||
| root = root_value(options, name) | ||
| return unless exist_and_empty?(root) || |
There was a problem hiding this comment.
When the user answers with No, the exit code is 0.
I think that's okay, as the program finished successfully.
mgmanzella
left a comment
There was a problem hiding this comment.
thank you @Poitrin! this look great 🔥 dropped some questions 🙏
|
|
||
| def setup | ||
| super | ||
| project_context("theme") |
There was a problem hiding this comment.
very cool find! TIL about project_context
Even though I don't like changing dirs during tests, I think it's a cleaner solution than explicitly setting root everywhere (provided that this would work).
i don't know if i agree? it's cleaner in the sense it has less lines of code but project_context("theme") doesn't have a clear function without exploring the meaning of that method. "theme" means the theme fixture but will that be super clear when we come back to this code in 6 months?
regardless of what we pick, i see we're setting root in some tests still while also using project_context. setting root seems to be a convention in theme tests and using project_context is a convention for app tests. i don't think conventions are sacred. they should be should be challenged but we can still be intentional and consistent when deciding to make a change 😄
| @ctx, | ||
| [], | ||
| title: @ctx.message("theme.confirm_current_directory"), | ||
| force: options.flags[:force], |
There was a problem hiding this comment.
we should definitely get input from UX before making an overarching change like this 😌
functionally i think this makes sense, we're giving developers the previous behavior where you can run theme commands in any directory and allowing them to do that with --force
also after poking around in the repo a bit, i don't see any classes that try to directly look at this flag. so unexpected downstream effects seem unlikely 😄
| end | ||
|
|
||
| def current_directory_confirmed? | ||
| return false if @ctx.testing? |
There was a problem hiding this comment.
why do we need this override when testing?
There was a problem hiding this comment.
I don't remember if it was with dev test or rake test, but after adding the current_directory_confirmed? method, running the tests and the current working directory initially was no valid theme directory, the confirmation dialogue kicked in and the tests instantly finished with Interrupted. So suddenly only 800+ tests run, instead of 1500+.
In order to avoid confirmation dialogues leading to less tests being executed, I prefer failing tests – therefore the return false.
I'm open to better ideas here 😄
I could also raise an error saying something like "You can't open a confirmation dialogue during tests".
There was a problem hiding this comment.
I prefer failing tests – therefore the return false.
ah yeh i see! and agreed 😄
I could also raise an error saying something like "You can't open a confirmation dialogue during tests".
+1 for this approach
There was a problem hiding this comment.
+1 for this approach
Great, CLI will raise an error now for this case :)
|
|
||
| def call(_args, name) | ||
| root = root_value(options, name) | ||
| return unless theme_directory?(root) || current_directory_confirmed? |
There was a problem hiding this comment.
/nit doing the check in your head of unless plus an || statement feels is a little hard to read
There was a problem hiding this comment.
I agree, I will change it :)
|
one more thing! i noticed when testing i select |
| REQUIRED_FOLDERS = %w(config layout sections templates).map { |folder| "#{folder}/" } | ||
|
|
||
| def initialize(root) | ||
| Dir.chdir(root) do |
There was a problem hiding this comment.
Nitpick: Would it work if instead of choosing a directory through Dir.chdir, we do:
self.folders = Dir[File.join(root, "*/")] + Dir[File.join(root, "templates/*/")]There was a problem hiding this comment.
The code you posted does not work out-of-the-box, because Dir[…] would return the complete absolute paths and I couldn't subtract these from REQUIRED_FOLDERS so easily.
However, the more I understood the issue's actual requirements, the more I could simplify code to…
def valid?
REQUIRED_FOLDERS.all? { |required_folder| Dir.exist?(File.join(root, required_folder)) }
end… so thanks for leading me to a better solution ✨
| attr_accessor :folders | ||
| attr_accessor :missing_folders | ||
|
|
||
| def validate |
There was a problem hiding this comment.
How many directories do templates have? If it's not too many, I'd refrain from having an internal state, missing_folders, that's internally mutated as a side effect of doing a validation. What about?
def missing_folders
REQUIRED_FOLDERS - folders
endThere was a problem hiding this comment.
I was inspired by Rails (calling .valid? to get the correct results for .errors) for this solution, but I agree, so I changed it :)
|
|
||
| def call(_args, name) | ||
| root = root_value(options, name) | ||
| return unless theme_directory?(root) || current_directory_confirmed? |
There was a problem hiding this comment.
I see this logic duplicated across theme commands. Have you considered extracting it into a base class or a module that's included in all the theme classes? We mitigate thus the risk of potential inconsistencies in the future.
There was a problem hiding this comment.
I have introduced another method so that the lines look now like…
return unless valid_theme_directory?(root)| @command.options.flags[:theme] = 1234 | ||
| @command.call([], "pull") | ||
|
|
||
| FileUtils.rmdir(specified_root) |
There was a problem hiding this comment.
What happens if the test fails before reaching this line? Who will remove the specified_root directory?
There was a problem hiding this comment.
Good catch! 👍🏻
I have added an ensure before this line, which should hopefully fix the issue :)
Thanks for highlighting this! |
| @command.call([], "push") | ||
| end | ||
|
|
||
| def test_push_with_root |
There was a problem hiding this comment.
As I had to set root in every test to point to a valid theme directory, the test_push_with_root made no sense anymore, so I deleted it.
| end | ||
| end | ||
|
|
||
| def test_can_specify_root |
There was a problem hiding this comment.
See comment above about why this kind of tests could be deleted.
New changes, requesting a new review
mgmanzella
left a comment
There was a problem hiding this comment.
amazing 🚀 this was a change that affected all theme commands, required almost every theme command test to be modified, and you were still able to keep it at ~300 lines. well done!!
| end | ||
|
|
||
| def current_directory_confirmed? | ||
| return false if @ctx.testing? |
There was a problem hiding this comment.
I prefer failing tests – therefore the return false.
ah yeh i see! and agreed 😄
I could also raise an error saying something like "You can't open a confirmation dialogue during tests".
+1 for this approach
…ed? is called during tests
…re` if users are running the command in a theme directory (#751) ### WHY are these changes introduced? - Fixes Shopify/shopify-cli#2669 - Addition to Shopify/shopify-cli#2680 We want to… - ensure that partners execute `shopify theme serve/dev` in the _correct_ directory - enforce the usage of the theme [architecture](https://shopify.dev/themes/architecture#directory-structure-and-component-types) directory structure ### WHAT is this pull request doing? Add `--force` flag for validation on `shopify theme dev` if users are running the command in a theme directory. ### How to test your changes? See Shopify/shopify-cli#2680, but test with `shopify theme dev`, instead of `shopify theme serve`. Make sure that your CLI 2.x version has the correct changes.
…n dialogue respect `SHOPIFY_CLI_TTY` (#1369) ### WHY are these changes introduced? Fixes #1136 #### Issue 1: `@ctx.testing?` is `true` even outside a testing environment CLI was hanging when `theme pull` was executed in a directory with already existing files. CLI should have shown question > It doesn’t seem like you’re running this command in a theme directory. Are you sure you want to proceed? but was never shown, due to ```ruby raise "Current theme directory can't be confirmed during tests" if @ctx.testing? ``` Discussion why `raise … if @ctx.testing?` was introduced can be found [here](Shopify/shopify-cli#2680 (comment)). Because `@ctx.testing?` returns `true` based on `ENV["CI"]`, we can’t differentiate between CLI running in partner’s pipeline or in CLI tests. #### Issue 2: Confirmation dialogue ignores environment variable `SHOPIFY_CLI_TTY` Moreover, even after having removed `raise … if @ctx.testing?` and `SHOPIFY_CLI_TTY` being set to `0`, CLI would still hang at the “It doesn’t seem like …” question. ### WHAT is this pull request doing? 1. Removes `raise … if @ctx.testing?`. 2. Shows a warning `It doesn’t seem like you’re running this command in a theme directory.` when `SHOPIFY_CLI_TTY` is set to `0`, but continues to `pull`. * `--force` flag as a way to confirm the command will not be included in the error message yet. * → Shopify/shopify-cli#2680 (comment) * → #760
WHY are these changes introduced?
shopify theme share/pull/push/serve/devif users are running the command in a theme/empty directory #2669shopify theme push/servefrom deleting a theme when developers run it in an invalid directory #2623--forceflag for validation onshopify theme dev/pull/push/shareif users are running the command in a theme directory cli#751We want to…
shopify theme share/pull/push/serve/devin the correct directoryWHAT is this pull request doing?
Requests a confirmation from the user to the following question …
… if users …
shopify theme sharein a directory that doesn't seem [1] like a theme directoryshopify theme serve/devin a directory that doesn't seem [1] like a theme directoryshopify theme pushin a directory that doesn't seem [1] like a theme directoryshopify theme pullin a directory that doesn't seem [1] like a theme directory or it's not empty[1] https://shopify.dev/themes/architecture#directory-structure-and-component-types
How to test your changes?
No confirmation needed
Part 1:
cdinto a directory that seems like a theme directory and contains some theme files.shopify theme share/serve/pushPart 2:
cdinto an empty directory, or a directory with at least the folders%w(config layout sections templates)shopify theme pullConfirmation needed
Part 1:
cdinto an empty directory, or a directory that does not contain all the folders%w(config layout sections templates)shopify theme share/serve/pushPart 2:
cdinto a non-empty directory that does not contain all the folders%w(config layout sections templates)shopify theme pullPost-release steps
Update shopify.dev docs, as– flag is hidden, so no need to update docs f.t.m.--forceflag has been added.Update checklist