Repository: quarkusio/quarkus
Issue: CLI allows to create project with comma in the name

### Describe the bug

CLI allows to create project with comma in the name. Comma is not allowed character for `artifactId` in maven projects

```
quarkus create app app,with,comma
cd app,with,comma
quarkus build

...

[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] 'artifactId' with value 'app,with,comma' does not match a valid id pattern. @ line 6, column 15
 @
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR]   The project org.acme:app,with,comma:1.0.0-SNAPSHOT (/Users/rsvoboda/tmp/app,with,comma/pom.xml) has 1 error
[ERROR]     'artifactId' with value 'app,with,comma' does not match a valid id pattern. @ line 6, column 15
```

### Expected behavior

CLI complains about problematic character

### Actual behavior

CLI allows to create project with comma in the name. Comma is not allowed character for `artifactId` in maven projects

### How to Reproduce?

```
quarkus create app app,with,comma
cd app,with,comma
quarkus build
```

### Output of `uname -a` or `ver`

_No response_

### Output of `java -version`

Java 11

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.9.2.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

_No response_

### Additional information

_No response_

Changed files:
devtools/cli/src/test/java/io/quarkus/cli/create/TargetGAVGroupTest.java
devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java
integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java
devtools/cli/src/main/java/io/quarkus/cli/create/TargetGAVGroup.java

diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetGAVGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetGAVGroup.java
index 5c45c4a6425..04d083d811f 100644
--- a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetGAVGroup.java
+++ b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetGAVGroup.java
@@ -1,9 +1,15 @@
 package io.quarkus.cli.create;
 
+import java.util.regex.Pattern;
+
 import io.quarkus.devtools.commands.CreateProjectHelper;
 import picocli.CommandLine;
+import picocli.CommandLine.TypeConversionException;
 
 public class TargetGAVGroup {
+    static final String BAD_IDENTIFIER = "The specified %s identifier (%s) contains invalid characters. Valid characters are alphanumeric (A-Za-z), underscore, dash and dot.";
+    static final Pattern OK_ID = Pattern.compile("[0-9A-Za-z_.-]+");
+
     static final String DEFAULT_GAV = CreateProjectHelper.DEFAULT_GROUP_ID + ":"
             + CreateProjectHelper.DEFAULT_ARTIFACT_ID + ":"
             + CreateProjectHelper.DEFAULT_VERSION;
@@ -52,6 +58,13 @@ void projectGav() {
                     }
                 }
             }
+            if (artifactId != CreateProjectHelper.DEFAULT_ARTIFACT_ID && !OK_ID.matcher(artifactId).matches()) {
+                throw new TypeConversionException(String.format(BAD_IDENTIFIER, "artifactId", artifactId));
+            }
+            if (groupId != CreateProjectHelper.DEFAULT_GROUP_ID && !OK_ID.matcher(groupId).matches()) {
+                throw new TypeConversionException(String.format(BAD_IDENTIFIER, "groupId", groupId));
+            }
+
             initialized = true;
         }
     }
diff --git a/devtools/cli/src/test/java/io/quarkus/cli/create/TargetGAVGroupTest.java b/devtools/cli/src/test/java/io/quarkus/cli/create/TargetGAVGroupTest.java
index a8714b43e23..1602c0d7ac0 100644
--- a/devtools/cli/src/test/java/io/quarkus/cli/create/TargetGAVGroupTest.java
+++ b/devtools/cli/src/test/java/io/quarkus/cli/create/TargetGAVGroupTest.java
@@ -4,6 +4,7 @@
 import org.junit.jupiter.api.Test;
 
 import io.quarkus.devtools.commands.CreateProjectHelper;
+import picocli.CommandLine.TypeConversionException;
 
 public class TargetGAVGroupTest {
 
@@ -92,4 +93,16 @@ void testOldParameters() {
         Assertions.assertEquals("a", gav.getArtifactId());
         Assertions.assertEquals("v", gav.getVersion());
     }
+
+    @Test
+    void testBadArtifactId() {
+        gav.gav = "g:a/x:v";
+        Assertions.assertThrows(TypeConversionException.class, () -> gav.getArtifactId());
+    }
+
+    @Test
+    void testBadGroupId() {
+        gav.gav = "g,x:a:v";
+        Assertions.assertThrows(TypeConversionException.class, () -> gav.getGroupId());
+    }
 }
diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java
index 44a5e3f8216..21932062a48 100644
--- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java
+++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java
@@ -12,6 +12,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.function.Predicate;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import org.apache.maven.execution.MavenSession;
@@ -55,6 +56,8 @@
  */
 @Mojo(name = "create", requiresProject = false)
 public class CreateProjectMojo extends AbstractMojo {
+    static final String BAD_IDENTIFIER = "The specified %s identifier (%s) contains invalid characters. Valid characters are alphanumeric (A-Za-z), underscore, dash and dot.";
+    static final Pattern OK_ID = Pattern.compile("[0-9A-Za-z_.-]+");
 
     private static final String DEFAULT_GROUP_ID = "org.acme";
     private static final String DEFAULT_ARTIFACT_ID = "code-with-quarkus";
@@ -262,6 +265,13 @@ public void execute() throws MojoExecutionException {
         }
 
         askTheUserForMissingValues();
+        if (projectArtifactId != DEFAULT_ARTIFACT_ID && !OK_ID.matcher(projectArtifactId).matches()) {
+            throw new MojoExecutionException(String.format(BAD_IDENTIFIER, "artifactId", projectArtifactId));
+        }
+        if (projectGroupId != DEFAULT_GROUP_ID && !OK_ID.matcher(projectGroupId).matches()) {
+            throw new MojoExecutionException(String.format(BAD_IDENTIFIER, "groupId", projectGroupId));
+        }
+
         projectRoot = new File(outputDirectory, projectArtifactId);
         if (projectRoot.exists()) {
             throw new MojoExecutionException("Unable to create the project, " +
diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java
index ef5d03e70a7..16b859cf388 100644
--- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java
+++ b/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java
@@ -396,6 +396,34 @@ public void testProjectGenerationFromScratchWithCustomDependencies() throws Exce
                 && d.getVersion().equalsIgnoreCase("2.5"))).isTrue();
     }
 
+    @Test
+    public void testBadArtifactId() throws Exception {
+        testDir = initEmptyProject("projects/project-generation-with-bad-artifact-id");
+        assertThat(testDir).isDirectory();
+        invoker = initInvoker(testDir);
+
+        Properties properties = new Properties();
+        properties.put("projectArtifactId", "acme,fail");
+        properties.put("extensions", "resteasy,commons-io:commons-io:2.5");
+        InvocationResult result = setup(properties);
+
+        assertThat(result.getExitCode()).isNotZero();
+    }
+
+    @Test
+    public void testBadGroupId() throws Exception {
+        testDir = initEmptyProject("projects/project-generation-with-bad-group-id");
+        assertThat(testDir).isDirectory();
+        invoker = initInvoker(testDir);
+
+        Properties properties = new Properties();
+        properties.put("projectGroupId", "acme,fail");
+        properties.put("extensions", "resteasy,commons-io:commons-io:2.5");
+        InvocationResult result = setup(properties);
+
+        assertThat(result.getExitCode()).isNotZero();
+    }
+
     @Test
     public void testProjectGenerationFromScratchWithAppConfigParameter() throws MavenInvocationException, IOException {
         testDir = initEmptyProject("projects/project-generation-with-config-param");