-
-
Notifications
You must be signed in to change notification settings - Fork 369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for specifying overrides in the command line for new projects #1552
Conversation
b1b1c7e
to
1eabe62
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is a work in progress, but it was just so tasty I couldn't help myself :-)
Broad strokes look good; a couple of suggestions inline.
One documentation related issue - the names of the configuration keys will be mostly undiscoverable by end users. Unless you actually open the template, you won't know that you can use app_name
to set the app name. However, I wonder if we should make this deliberate - i.e., provide the functionality, but not document the specific keys that can be used (and document them as being not documented), so that we don't have a backwards incompatibility problem if we need to change the template. The docs for the -Q option can then tell the user that the keys are template dependent, and we make specific guarantees of compatibility of keys between Briefcase versions.
src/briefcase/commands/new.py
Outdated
else: | ||
if not ((key := key.strip()) and (value := value.strip())): | ||
raise BriefcaseCommandError( | ||
f"Invalid Project configuration override '{override}'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f"Invalid Project configuration override '{override}'" | |
f"Invalid project configuration override {override!r}" |
src/briefcase/commands/new.py
Outdated
key, value = override.split("=", 1) | ||
except ValueError as e: | ||
raise BriefcaseCommandError( | ||
f"Unable to parse project configuration override '{override}'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f"Unable to parse project configuration override '{override}'" | |
f"Unable to parse project configuration override {override!r}" |
src/briefcase/commands/new.py
Outdated
) | ||
if key in overrides: | ||
raise BriefcaseCommandError( | ||
f"Project configuration override '{key}' specified multiple times" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f"Project configuration override '{key}' specified multiple times" | |
f"Project configuration override {key!r} specified multiple times" |
src/briefcase/commands/new.py
Outdated
# Use the override as the answer but clear it so if validation fails, | ||
# the user will be solicited for a value on the next loop | ||
self.input.prompt( | ||
f"Using override value '{override_value}' for {titlecase(variable)}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f"Using override value '{override_value}' for {titlecase(variable)}" | |
f"Using override value {override_value!r} for {titlecase(variable)}" |
src/briefcase/commands/new.py
Outdated
if override_value is not None: | ||
self.input.prompt() | ||
self.input.prompt( | ||
f"Using override value '{override_value}' for {choice_name}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f"Using override value '{override_value}' for {choice_name}" | |
f"Using override value {override_value!r} for {choice_name}" |
src/briefcase/commands/new.py
Outdated
**options, | ||
) | ||
|
||
|
||
0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray content?
src/briefcase/commands/new.py
Outdated
"Based on your formal name, we suggest an app name of" | ||
f"'{default_app_name}', but you can use another name if you want." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a missing space here:
"Based on your formal name, we suggest an app name of" | |
f"'{default_app_name}', but you can use another name if you want." | |
"Based on your formal name, we suggest an app name of " | |
f"{default_app_name!r}, but you can use another name if you want." |
src/briefcase/commands/new.py
Outdated
""" | ||
self.input.prompt(intro) | ||
self.prompt_intro(intro=intro) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to display the intro prompt if there's an override? Agreed we should display the "Using override" text, but the only reason I can see to display the text is so that in the case of bad input, the context for fixing it is known. I'd argue it's better to just raise an error on the basis that bad command line input can/should be fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thought process here was to only pivot the experience of creating a new project based on whether --no-input
was provided.
When --no-input
is absent (i.e. "normal"), everything is printed; however, when it is present, anything written with input.prompt()
is hidden.
If we try to tweak the "normal" experience based on whether an override value exists, I see two obstacles:
- Printing the new divider between questions becomes more difficult
- This divider is a part of
prompt_intro()
....so it'd have to be separated out - If we omit the divider for questions with overrides, then I think it's a little confusing because it will appear to be associated with the preceding question
- This divider is a part of
- If the override is invalid...then we're in a pickle
- When an override is considered validated, the loop runs again and now solicits the user
- But we'll need to now show the prompt....this means the prompt printing needs to be moved in to the loop....and we'll need to track already showing it
- Alternatively, I suppose the approach could be re-implemented like this...
def input_text(self, intro, variable, default, validator=None, override_value=None):
if override_value:
if self.validate_user_input(validator, override_value):
return override_value
self.prompt_intro(intro=intro)
while True:
self.input.prompt()
answer = self.input.text_input(
prompt=f"{titlecase(variable)} [{default}]: ",
default=default,
)
if self.validate_user_input(validator, answer):
return answer
Given that arguably may be a better implementation anyway...it gives more credence to this idea I suppose.
I would still like to print the divider; so, I can make that it's own method. I would also thinking of incorporating the essence of the question in to the divider.
Maybe something like this:
-- Formal Name ----------------------------------------------------------------
First, we need a formal name for your application.
This is the name that will be displayed to humans whenever the name of the
application is displayed. It can have spaces and punctuation if you like, and
any capitalization will be used as you type it.
Formal Name [Hello World]:
-- Package Name ---------------------------------------------------------------
Next, we need a name that can serve as a machine-readable Python package name
for your application.
This name must be PEP508-compliant - that means the name may only contain
letters, numbers, hyphens and underscores; it can't contain spaces or
punctuation, and it can't start with a hyphen or underscore.
Based on your formal name, we suggest an app name of 'helloworld', but you can
use another name if you want.
App Name [helloworld]:
-- Bundle Identifier ----------------------------------------------------------
Now we need a bundle identifier for your application. App stores need to
protect against having multiple applications with the same name; the bundle
identifier is the namespace they use to identify applications that come from
you. The bundle identifier is usually the domain name of your company or
project, in reverse order.
For example, if you are writing an application for Example Corp, whose website
is example.com, your bundle would be ``com.example``. The bundle will be
combined with your application's machine readable name to form a complete
application identifier (e.g., com.example.helloworld).
Bundle Identifier [com.example]:
It's a bit repetitive....but it also kinda nicely bookends the question...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thought process here was to only pivot the experience of creating a new project based on whether
--no-input
was provided.When
--no-input
is absent (i.e. "normal"), everything is printed; however, when it is present, anything written withinput.prompt()
is hidden.
That's fair - the use case for using -Q
and not using --no-input
is admittedly small.
If we try to tweak the "normal" experience based on whether an override value exists, I see two obstacles:
Printing the new divider between questions becomes more difficult
- This divider is a part of
prompt_intro()
....so it'd have to be separated out- If we omit the divider for questions with overrides, then I think it's a little confusing because it will appear to be associated with the preceding question
Agreed that there should still be a divider; implementation complexity notwithstanding, I was thinking we'd just avoid giving the impression that a question was actually asked.
If the override is invalid...then we're in a pickle
- When an override is considered validated, the loop runs again and now solicits the user
- But we'll need to now show the prompt....this means the prompt printing needs to be moved in to the loop....and we'll need to track already showing it
- Alternatively, I suppose the approach could be re-implemented like this...
...
Given that arguably may be a better implementation anyway...it gives more credence to this idea I suppose.
That was more in line with what I was thinking - that the core "question" loop itself wasn't quite right in the circumstances.
I would still like to print the divider; so, I can make that its own method. I would also thinking of incorporating the essence of the question in to the divider.
...
It's a bit repetitive....but it also kinda nicely bookends the question...
Agreed. Bonus points if the output in the override case is:
-- Package Name ----------------------------------------------------------
Using override value 'org.foobar' for Package Name.
-- Bundle Identifier ----------------------------------------------------------
...
The only additional thought I've got in that space is whether it's an opportunity to surface the actual variable name for -Q purposes (.e.g, --- Formal Name (formal_name) ------
) - but I'm not convinced that's worth it given that it's potentially confusing detail for a new user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is all in here now; I also tweaked the presentation of the actual generation of the new project. I did have an impulse to add some festive emojis....but I held back...
1eabe62
to
feebabb
Compare
feebabb
to
041430a
Compare
I agree with that....although, it also kinda makes me wonder if we shouldn't let this be a backdoor to putting extra key/value pairs in to the cookiecutter context without requiring a custom bootstrap... |
I suppose this also begs the question if these overrides should be exposed to third-party bootstraps....probably either via the bootstrap's constructor or passing it to Although, this is also really just letting scope creep take the reins.....and YAGNI, anyway. That said, I do think we should add |
041430a
to
14e8511
Compare
I updated the default behavior for all user prompts to be bold by default. Personally, I think this has better aesthetics to visually promote the actual essence of the prompt from any preamble as well as the user's own input. If you disagree, it'll be trivial to drop the commit. |
Example of this in action in CI: https://github.com/beeware/briefcase/actions/runs/6985434910/job/19009756602#step:12:185 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A minor tweak to the docs, but otherwise this looks good to me.
Changes
-Q
-Q "license=MIT license"
PR Checklist: