Skip to content

#2165: interactive template variables - #2179

Open
Paras14 wants to merge 2 commits into
devonfw:mainfrom
Paras14:feature/2165-interactive-template-variables
Open

#2165: interactive template variables#2179
Paras14 wants to merge 2 commits into
devonfw:mainfrom
Paras14:feature/2165-interactive-template-variables

Conversation

@Paras14

@Paras14 Paras14 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

This PR fixes #2165

Implemented changes:

  • Extended VariableSyntax.SQUARE to additionally match $[ask:VARIABLE] and $[secret:VARIABLE] expressions.
  • Added IdeContext.askForSecret for masked console input via Console.readPassword.
  • During variable resolution, ask expressions for undefined variables prompt the user and persist the entered value to the user's conf/ide.properties so they are not asked again.
  • Ask expressions for already defined variables resolve like plain variables without any interaction.
  • In batch mode ask expressions are skipped with a warning.
  • Updated configurator.adoc and added tests in EnvironmentVariablesTest.

As discussed with @hohwille, the prefix syntax encodes secret vs. plain in the name instead of using function arguments, which aligns it with the $[secret:name] syntax introduced by #2162. A future @function(args) syntax as proposed in #989 can be added as a further alternative in the same regex. Motivated by #412 to preconfigure AI assistant plugins with a backend URL and API key that must not be committed to the settings repository. See also #987.


Testing instructions

Tested on Windows using PowerShell. Strongly recommended to run an actual PowerShell window, as masking needs System.console(), which is null in IDE consoles and when input is piped, and there the input stays visible
with a warning by design.

  1. Build the branch and dump the classpath, from the repository root:

    mvn -pl cli -am install -DskipTests
    mvn -pl cli dependency:build-classpath "-Dmdep.outputFile=C:\temp\cp.txt"
    $CP = (Get-Content C:\temp\cp.txt -Raw).Trim()
    $CLASSES = "C:\projects\IDEasy\workspaces\main\IDEasy\cli\target\classes"
    
    _(make sure you add your path correctly for the cli target classes: "<repo>\cli\target\classes")_
    
  2. In a test project create IDEasy\settings\workspace\update\ai-test.properties:

    ai.backend.url=$[ask:MY_URL]
    ai.api.key=$[secret:MY_TOKEN]
    

    Make sure MY_URL and MY_TOKEN are not yet defined in IDEasy\conf\ide.properties.

  3. From the project directory, run the local build:

    cd C:\projects\IDEasy
    java -cp "$CLASSES;$CP" com.devonfw.tools.ide.cli.Ideasy update
    

    You are prompted before any downloads: MY_URL with visible input,
    MY_TOKEN with hidden(masked) input. Each prompt names the file requiring it.

    1. Check that IDEasy\workspaces\main\ai-test.properties contains the entered values
      with no $[...] left, and that both variables were written to
      conf\ide.properties exactly as typed.
  4. Run the same command again. You won't get any input prompts this time, the persisted values are reused.

  5. Add MY_OTHER=https://example.com to conf\ide.properties, then add the line
    ai.other=$[ask:MY_OTHER] to IDEasy\settings\workspace\update\ai-test.properties.
    Run again. There is no prompt, and IDEasy\workspaces\main\ai-test.properties
    contains ai.other=https://example.com.

  6. Remove MY_URL and MY_TOKEN from IDEasy\conf\ide.properties and run with --batch.
    No input prompts, but one warning per expression, and the $[ask:...] and $[secret:...]
    text is left untouched in the output.


Checklist for this PR

  • When running mvn clean test locally all tests pass and build is successful
  • PR title is of the form #«issue-id»: «brief summary»
  • PR top-level comment summarizes what has been done and contains link to addressed issue(s)
  • PR and issue(s) have suitable labels
  • Issue is set to In Progress and assigned to you
  • You followed all coding conventions
  • You have added the issue implemented by your PR in CHANGELOG.adoc
  • You have formulated clear instructions on how to test your contribution under "Testing instructions"

@github-project-automation github-project-automation Bot moved this to 🆕 New in IDEasy board Jul 21, 2026
@Paras14 Paras14 self-assigned this Jul 21, 2026
@Paras14 Paras14 moved this from 🆕 New to Team Review in IDEasy board Jul 21, 2026
@coveralls

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 29803149984

Coverage decreased (-0.04%) to 72.439%

Details

  • Coverage decreased (-0.04%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 144 coverage regressions across 6 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

144 previously-covered lines in 6 files lost coverage.

File Lines Losing Coverage Coverage
com/devonfw/tools/ide/context/AbstractIdeContext.java 99 70.06%
com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java 19 81.65%
com/devonfw/tools/ide/context/IdeContext.java 16 78.57%
com/devonfw/tools/ide/context/IdeContextConsole.java 7 0.0%
com/devonfw/tools/ide/variable/VariableSyntax.java 2 85.71%
com/devonfw/tools/ide/version/VersionSegment.java 1 90.03%

Coverage Stats

Coverage Status
Relevant Lines: 16950
Covered Lines: 12804
Line Coverage: 75.54%
Relevant Branches: 7574
Covered Branches: 4961
Branch Coverage: 65.5%
Branches in Coverage %: Yes
Coverage Strength: 3.2 hits per line

💛 - Coveralls

@QuangAnhLe QuangAnhLe moved this from Team Review to 🏗 In progress in IDEasy board Jul 22, 2026
@QuangAnhLe QuangAnhLe moved this from 🏗 In progress to Team Review in IDEasy board Jul 22, 2026
*/
private String resolveAskExpression(String variableName, boolean secret, AbstractEnvironmentVariables resolvedVars, Object src) {

if ((this.context == null) || this.context.isBatchMode()) {

@QuangAnhLe QuangAnhLe Jul 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.context is set in the constructor: ( line 63)

if (context == null) {
this.context = parent.context; // can be null if the root parent is also null
}

If parent is null and context is null → throw IllegalArgumentException. If parent is not null but parent.context is null... the chain might be null. However, in practice, AbstractEnvironmentVariables is always created via AbstractIdeContext.createVariables() → so a context always exists. While this scenario is unlikely, it is best to include a guard. 👍

}

@Override
public String askForSecret(String message) {

@QuangAnhLe QuangAnhLe Jul 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In AbstractIdeContext.askForSecret(), an empty input repeats the loop with no feedback. askForInput() has the same behaviour but at least the user can see what they typed. For masked input the user has no idea if they typed nothing or if the terminal swallowed their input. Consider logging a hint like "Empty input — please enter a value" before the next iteration. :)

@QuangAnhLe QuangAnhLe moved this from Team Review to 🏗 In progress in IDEasy board Jul 24, 2026
@hohwille hohwille moved this from 🏗 In progress to Team Review in IDEasy board Jul 24, 2026
@hohwille hohwille changed the title Feature/2165 interactive template variables #2165: interactive template variables Jul 27, 2026

@hohwille hohwille left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Paras14 and @QuangAnhLe this issue seems to be in team-review for quite a while.
As you already commented in the PR and we discussed in the daily your idea to implement this via #989 as according functions would make more sense and allow a generic and more future-proof solution.
I would really like to avoid that we and potential IDEasy projects start using this ask and secret syntax and we later deprecated it replacing it with @ask function as already suggested and fully making sense.
Maybe it was a misunderstanding in the daily but I thought that someone had already implemented the function syntax and we should therefore use that... I could not find any PR implementing #989 so it seems I got something wrong.
Would you agree that it makes sense to update your PR to implement #989 and using the function syntax @ask (or @ask-variable / @ask-secret if we want to simplify usage and prevent that the secret flag needs to be passed as parameter).
This way almost no work in this PR would be in vain. Most changes are needed anyway.

UPDATE: I have refined #989 and set it as read-to-implement.

Comment on lines +1061 to +1076
while (true) {
if (!message.isBlank()) {
IdeLogLevel.INTERACTION.log(LOG, message);
}
if (isBatchMode()) {
if (isForceMode()) {
return null;
} else {
throw new CliAbortException();
}
}
String input = readSecretLine().trim();
if (!input.isEmpty()) {
return input;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if a secret value MUST NOT be empty.
It may be odd but in some test/dev scenarios empty passwords may be used for convenience.

* "$[ask:MY_VARIABLE]" or "$[secret:MY_VARIABLE]" that will interactively ask the user for the value if the variable is undefined.
*/
SQUARE("\\$\\[([a-zA-Z0-9_-]+)\\]") {
SQUARE("\\$\\[(?:([a-zA-Z0-9_-]+)|(ask|secret):([a-zA-Z0-9_-]+))\\]") {

@hohwille hohwille Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW: It is better to have the prefix/function as an optional group without duplicating the variable group like this (and our convention is also to mark opening parenthesis with the according group index for readability):

  // ...........1.2...........3
  SQUARE("\\$\\[(@([a-z-]+):)?([a-zA-Z0-9_-]+)\\]") {

@QuangAnhLe

Copy link
Copy Markdown

@hohwille could this ticket close and move to #989 ?

@Paras14

Paras14 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

@hohwille Agreed, reworking this around the generic function syntax is the better, more future-proof approach. Most of the existing implementation can still be reused, so the work will not be lost.

@QuangAnhLe My suggestion would be to keep #2165 open for now and revisit it once the generic function syntax in #989 is implemented.

@hohwille

hohwille commented Jul 30, 2026

Copy link
Copy Markdown
Member

My suggestion would be to keep #2165 open for now and revisit it once the generic function syntax in #989 is implemented.

I would implement both #2164 and #989 in this PR so merging it, will close both stories.
When you create the infrastructure to allow functions, you need to implement a function anyhow as otherwise it is impossible to test the feature. Then you can already implement ask-variable and ask-secret in the same way as you did here with the now obsolete syntax. If you want to keep the PR small, you can keep the persisting of user entered values out of this PR and implement it in a separate PR. If you do so, please create an issue so it will not be forgotten since technically it would be part of implementing #989.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Team Review

Development

Successfully merging this pull request may close these issues.

prompt user for template variables at apply time

4 participants