Repository: beeware/briefcase (python)

Issue:
License selection mentions ambiguous "BSD license"
### Describe the bug

During `briefcase new`, the license selection looks like this:

```text
-- Project License -----------------------------------------------------------

What license do you want to use for this project's code?

  1) BSD license
  2) MIT license
  3) Apache Software License
  4) GNU General Public License v2 (GPLv2)
  5) GNU General Public License v2 or later (GPLv2+)
  6) GNU General Public License v3 (GPLv3)
  7) GNU General Public License v3 or later (GPLv3+)
  8) Proprietary
  9) Other

Project License [1]:
```

The term "BSD license" [is ambiguous](https://en.wikipedia.org/wiki/BSD_licenses#Terms). When selected, briefcase will create a 3-clause BSD license.

### Steps to reproduce

* Run `briefcase new`
* Enter the Formal Name
* Enter the App Name
* Enter the Bundle Identifier
* Enter the Project Name
* Enter the Author
* Enter the Author's Email
* Enter the Application URL

### Expected behavior

I think `BSD 3-Clause "New" or "Revised" License` (following the [SPDX License List](https://spdx.org/licenses/)) would be a better term.

### Screenshots

_No response_

### Environment

- Operating System: Linux (Ubuntu 25.04)
- Python version: 3.13.3
- Software versions:
  - Briefcase: 0.3.22
  - Toga: -


### Logs




### Additional context

I'm not deeply familiar with software licensing. Maybe "BSD license" is generally understood by most users.

If you are interested, I could create a PR.

Pull request description:
Align license terminology with SPDX License List
## Summary
Align license names with the [SPDX License List](https://spdx.org/licenses/).

Fixes #2270
Accompanies https://github.com/beeware/briefcase-template/pull/191

## Details
I changed the non-standard license names listed in the cookiecutter template to SPDX terms (formatted as `"[Full Name] (Identifier)"`):

* BSD-3-Clause
    * old: `BSD license`
    * new: `BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)`
* MIT
    * old: `MIT license`
    * new: `MIT License (MIT)`
* Apache-2.0
    * old: `Apache Software License`
    * new: `Apache License 2.0 (Apache-2.0)`
* GPL-2.0
    * old: `GNU General Public License v2 (GPLv2)`
    * new: `GNU General Public License v2.0 only (GPL-2.0)`
* GPL-2.0+
    * old: `GNU General Public License v2 or later (GPLv2+)`
    * new: `GNU General Public License v2.0 or later (GPL-2.0+)`
* GPL-3.0
    * old: `GNU General Public License v3 (GPLv3)`
    * new: `GNU General Public License v3.0 only (GPL-3.0)`
* GPL-3.0+
    * old: `GNU General Public License v3 or later (GPLv3+)`
    * new: `GNU General Public License v3.0 or later (GPL-3.0+)`

## PR Checklist:
- [x] All new features have been tested
- [x] All new features have been documented
- [x] I have read the **CONTRIBUTING.md** file
- [x] will abide by the code of conduct

briefcase-template-repo: https://github.com/NMertsch/briefcase-template
briefcase-template-ref: 2270-improve-license-selection-wording

Patch:
diff --git a/changes/2269.feature.rst b/changes/2269.feature.rst
new file mode 100644
index 00000000..364ca7ad
--- /dev/null
+++ b/changes/2269.feature.rst
@@ -0,0 +1,1 @@
+When creating a new project with ``briefcase new``, or converting an existing project with ``briefcase convert``, Briefcase will now try to infer the author's name and email address from the git configuration.
diff --git a/changes/2270.bugfix.rst b/changes/2270.bugfix.rst
new file mode 100644
index 00000000..b10e96fb
--- /dev/null
+++ b/changes/2270.bugfix.rst
@@ -0,0 +1,1 @@
+License names now follow the `SPDX License List <https://spdx.org/licenses/>`_.
diff --git a/changes/2270.removal.rst b/changes/2270.removal.rst
new file mode 100644
index 00000000..847027c0
--- /dev/null
+++ b/changes/2270.removal.rst
@@ -0,0 +1,1 @@
+The ``new`` command now uses `SPDX identifiers <https://spdx.org/licenses/>`_ when referring to licenses. If you have been using the ``-Q license=XXX`` option to automate application creation, you will need to modify the value provided to match the SPDX specifier for that license (e.g., ``MIT`` instead of ``MIT license``, and ``BSD-3-Clause`` instead of ``BSD``).
diff --git a/docs/reference/commands/convert.rst b/docs/reference/commands/convert.rst
index d3606372..695b1ee5 100644
--- a/docs/reference/commands/convert.rst
+++ b/docs/reference/commands/convert.rst
@@ -44,8 +44,8 @@ branch of the template.
 
 Override the answer to a prompt with the provided value.
 
-For instance, if ``-Q "license=MIT license"`` is specified, then the question
-to choose a license will not be presented to the user and the MIT license will
+For instance, if ``-Q "license=MIT"`` is specified, then the question
+to choose a license will not be presented to the user and the MIT License will
 be automatically used for the project. When used in conjunction with
 ``--no-input``, the provided value overrides the default answer.
 
diff --git a/docs/reference/commands/new.rst b/docs/reference/commands/new.rst
index 776c9c14..298cf97f 100644
--- a/docs/reference/commands/new.rst
+++ b/docs/reference/commands/new.rst
@@ -40,8 +40,8 @@ branch of the template.
 
 Override the answer to a new project prompt with the provided value.
 
-For instance, if ``-Q "license=MIT license"`` is specified, then the question to
-choose a license will not be presented to the user and the MIT license will be
+For instance, if ``-Q "license=MIT"`` is specified, then the question to
+choose a license will not be presented to the user and the MIT License will be
 automatically used for the project. When used in conjunction with ``--no-input``,
 the provided value overrides the default answer.
 
diff --git a/src/briefcase/commands/base.py b/src/briefcase/commands/base.py
index 7bb4998f..4a19b58c 100644
--- a/src/briefcase/commands/base.py
+++ b/src/briefcase/commands/base.py
@@ -1156,3 +1156,23 @@ Did you run Briefcase in a project directory that contains {filename.name!r}?"""
                 output_path=output_path,
                 extra_context=extra_context,
             )
+
+    def get_git_config_value(self, section: str, option: str) -> str | None:
+        """Get the requested git config value, if available.
+
+        :param section: The configuration section.
+        :param option: The configuration option.
+        :returns: The configuration value, or None.
+        """
+        git_config_paths = [
+            self.tools.git.config.get_config_path("system"),
+            self.tools.git.config.get_config_path("global"),
+            self.tools.git.config.get_config_path("user"),
+            ".git/config",
+        ]
+
+        with self.tools.git.config.GitConfigParser(git_config_paths) as git_config:
+            if git_config.has_option(section, option):
+                return git_config.get_value(section, option)
+
+        return None
diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py
index 3b35d916..0a61ab8d 100644
--- a/src/briefcase/commands/convert.py
+++ b/src/briefcase/commands/convert.py
@@ -11,7 +11,7 @@ from urllib.parse import urlparse
 from packaging.utils import canonicalize_name
 
 from ..config import is_valid_app_name
-from .new import NewCommand, parse_project_overrides
+from .new import LICENSE_OPTIONS, NewCommand, parse_project_overrides
 
 if sys.version_info >= (3, 11):  # pragma: no-cover-if-lt-py311
     import tomllib
@@ -387,11 +387,20 @@ class ConvertCommand(NewCommand):
             if "name" in author
         ]
 
+        default_author = "Jane Developer"
         if not options or override_value is not None:
+            git_username = self.get_git_config_value("user", "name")
+            if git_username is not None:
+                default_author = git_username
+                intro = (
+                    f"{intro}\n\n"
+                    + f"Based on your git configuration, we believe it could be '{git_username}'."
+                )
+
             return self.console.text_question(
                 intro=intro,
                 description="Author",
-                default="Jane Developer",
+                default=default_author,
                 override_value=override_value,
             )
         elif len(options) > 1:
@@ -421,7 +430,7 @@ class ConvertCommand(NewCommand):
             author = self.console.text_question(
                 intro="Who do you want to be credited as the author of this application?",
                 description="Author",
-                default="Jane Developer",
+                default=default_author,
                 override_value=None,
             )
 
@@ -433,8 +442,14 @@ class ConvertCommand(NewCommand):
 
         :returns: author email
         """
-        default = self.make_author_email(author, bundle)
-        default_source = "the author name and bundle"
+        git_email = self.get_git_config_value("user", "email")
+        if git_email is None:
+            default = self.make_author_email(author, bundle)
+            default_source = "the author name and bundle"
+        else:
+            default = git_email
+            default_source = "your git configuration"
+
         for author_info in self.pep621_data.get("authors", []):
             if author_info.get("name") == author and author_info.get("email"):
                 default = author_info["email"]
@@ -465,36 +480,36 @@ class ConvertCommand(NewCommand):
         # LIMITED will generate a false match.
 
         hint_patterns = {
-            "Apache Software License": ["Apache"],
-            "BSD license": [
+            "Apache-2.0": ["Apache"],
+            "BSD-3-Clause": [
                 "Redistribution and use in source and binary forms",
                 "BSD",
             ],
-            "GNU General Public License v2 or later (GPLv2+)": [
+            "GPL-2.0+": [
                 "Free Software Foundation, either version 2 of the License",
                 "GPLv2+",
             ],
-            "GNU General Public License v2 (GPLv2)": [
+            "GPL-2.0": [
                 "version 2 of the GNU General Public License",
                 "GPLv2",
             ],
-            "GNU General Public License v3 or later (GPLv3+)": [
+            "GPL-3.0+": [
                 "either version 3 of the License",
                 "GPLv3+",
             ],
-            "GNU General Public License v3 (GPLv3)": [
+            "GPL-3.0": [
                 "version 3 of the GNU General Public License",
                 "GPLv3",
             ],
-            "MIT license": [
+            "MIT": [
                 "Permission is hereby granted, free of charge",
                 "MIT",
             ],
         }
-        for hint, license_patterns in hint_patterns.items():
+        for license_id, license_patterns in hint_patterns.items():
             for license_pattern in license_patterns:
                 if license_pattern.lower() in license_text.lower():
-                    return hint
+                    return license_id
 
         return "Other"
 
@@ -539,22 +554,11 @@ class ConvertCommand(NewCommand):
         :returns: The project
         """
         default, intro = self.get_license_hint()
-        options = [
-            "BSD license",
-            "MIT license",
-            "Apache Software License",
-            "GNU General Public License v2 (GPLv2)",
-            "GNU General Public License v2 or later (GPLv2+)",
-            "GNU General Public License v3 (GPLv3)",
-            "GNU General Public License v3 or later (GPLv3+)",
-            "Proprietary",
-            "Other",
-        ]
 
         return self.console.selection_question(
             intro=intro,
             description="Project License",
-            options=options,
+            options=LICENSE_OPTIONS,
             default=default,
             override_value=override_value,
         )
diff --git a/src/briefcase/commands/new.py b/src/briefcase/commands/new.py
index 21b89c23..550f7af4 100644
--- a/src/briefcase/commands/new.py
+++ b/src/briefcase/commands/new.py
@@ -25,6 +25,19 @@ from briefcase.integrations.git import Git
 
 from .base import BaseCommand
 
+LICENSE_OPTIONS = {
+    "BSD-3-Clause": 'BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)',
+    "MIT": "MIT License (MIT)",
+    "Apache-2.0": "Apache License 2.0 (Apache-2.0)",
+    "GPL-2.0": "GNU General Public License v2.0 only (GPL-2.0)",
+    "GPL-2.0+": "GNU General Public License v2.0 or later (GPL-2.0+)",
+    "GPL-3.0": "GNU General Public License v3.0 only (GPL-3.0)",
+    "GPL-3.0+": "GNU General Public License v3.0 or later (GPL-3.0+)",
+    "Proprietary": "Proprietary",
+    "Other": "Other",
+}
+DEFAULT_LICENSE = "BSD-3-Clause"
+
 
 def get_gui_bootstraps() -> dict[str, type[BaseGuiBootstrap]]:
     """Loads built-in and third-party GUI bootstraps."""
@@ -266,23 +279,12 @@ class NewCommand(BaseCommand):
             override_value=override_value,
         )
 
-    def input_license(self, override_value: str | None):
-        licenses = [
-            "BSD license",
-            "MIT license",
-            "Apache Software License",
-            "GNU General Public License v2 (GPLv2)",
-            "GNU General Public License v2 or later (GPLv2+)",
-            "GNU General Public License v3 (GPLv3)",
-            "GNU General Public License v3 or later (GPLv3+)",
-            "Proprietary",
-            "Other",
-        ]
+    def input_license(self, override_value: str | None) -> str:
         return self.console.selection_question(
             intro="What license do you want to use for this project's code?",
             description="Project License",
-            options=licenses,
-            default=licenses[0],
+            options=LICENSE_OPTIONS,
+            default=DEFAULT_LICENSE,
             override_value=override_value,
         )
 
@@ -365,27 +367,46 @@ class NewCommand(BaseCommand):
             override_value=project_overrides.pop("description", None),
         )
 
+        author_intro = (
+            "Who do you want to be credited as the author of this application?\n"
+            "\n"
+            "This could be your own name, or the name of your company you work for."
+        )
+        default_author = "Jane Developer"
+        git_username = self.get_git_config_value("user", "name")
+        if git_username is not None:
+            default_author = git_username
+            author_intro = (
+                f"{author_intro}\n\n"
+                f"Based on your git configuration, we believe it could be '{git_username}'."
+            )
         author = self.console.text_question(
-            intro=(
-                "Who do you want to be credited as the author of this application?\n"
-                "\n"
-                "This could be your own name, or the name of your company you work for."
-            ),
+            intro=author_intro,
             description="Author",
-            default="Jane Developer",
+            default=default_author,
             override_value=project_overrides.pop("author", None),
         )
 
+        author_email_intro = (
+            "What email address should people use to contact the developers of "
+            "this application?\n"
+            "\n"
+            "This might be your own email address, or a generic contact address "
+            "you set up specifically for this application."
+        )
+        git_email = self.get_git_config_value("user", "email")
+        if git_email is None:
+            default_author_email = self.make_author_email(author, bundle)
+        else:
+            default_author_email = git_email
+            author_email_intro = (
+                f"{author_email_intro}\n\n"
+                f"Based on your git configuration, we believe it could be '{git_email}'."
+            )
         author_email = self.console.text_question(
-            intro=(
-                "What email address should people use to contact the developers of "
-                "this application?\n"
-                "\n"
-                "This might be your own email address, or a generic contact address "
-                "you set up specifically for this application."
-            ),
+            intro=author_email_intro,
             description="Author's Email",
-            default=self.make_author_email(author, bundle),
+            default=default_author_email,
             validator=self.validate_email,
             override_value=project_overrides.pop("author_email", None),
         )


Test patch:
diff --git a/tests/commands/base/test_get_git_config_value.py b/tests/commands/base/test_get_git_config_value.py
new file mode 100644
index 00000000..d20752b8
--- /dev/null
+++ b/tests/commands/base/test_get_git_config_value.py
@@ -0,0 +1,48 @@
+import itertools
+from unittest import mock
+from unittest.mock import MagicMock
+
+
+def test_all_config_files_are_read(base_command, mock_git):
+    """All git config files are read (system, global, user, repo)."""
+    base_command.tools.git = mock_git
+    mock_git.config.get_config_path.side_effect = ["file1", "file2", "file3"]
+
+    base_command.get_git_config_value("test-section", "test-option")
+
+    assert mock_git.config.get_config_path.call_args_list == [
+        mock.call("system"),
+        mock.call("global"),
+        mock.call("user"),
+    ]
+    expected_config_files = ["file1", "file2", "file3", ".git/config"]
+    mock_git.config.GitConfigParser.assert_called_once_with(expected_config_files)
+
+
+def test_config_values_are_parsed(base_command, tmp_path, monkeypatch):
+    """If the requested value exists in one of the config files, it shall be returned."""
+    import git
+
+    # use 'real' gitpython library (no mock)
+    base_command.tools.git = git
+
+    # mock `git.config.get_config_path` to always provide the same three local files
+    mock_config_paths = ["missing-file-1", "config-1", "missing-file-2"]
+    git.config.get_config_path = MagicMock()
+    git.config.get_config_path.side_effect = itertools.cycle(mock_config_paths)
+
+    # create local two config files
+    monkeypatch.chdir(tmp_path)
+    (tmp_path / "config-1").write_text("[user]\n\tname = Some User\n", encoding="utf-8")
+    (tmp_path / ".git").mkdir()
+    (tmp_path / ".git" / "config").write_text(
+        "[user]\n\temail = my@email.com\n", encoding="utf-8"
+    )
+
+    # expect values are parsed from all existing config files
+    assert base_command.get_git_config_value("user", "name") == "Some User"
+    assert base_command.get_git_config_value("user", "email") == "my@email.com"
+
+    # expect that missing sections and options are handled
+    assert base_command.get_git_config_value("user", "something") is None
+    assert base_command.get_git_config_value("something", "something") is None
diff --git a/tests/commands/convert/conftest.py b/tests/commands/convert/conftest.py
index 9551bf47..d48129c8 100644
--- a/tests/commands/convert/conftest.py
+++ b/tests/commands/convert/conftest.py
@@ -1,4 +1,5 @@
 from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
 
 import pytest
 
@@ -54,7 +55,9 @@ class DummyConvertCommand(ConvertCommand):
 @pytest.fixture
 def convert_command(tmp_path):
     (tmp_path / "project").mkdir()
-    return DummyConvertCommand(base_path=tmp_path / "project")
+    command = DummyConvertCommand(base_path=tmp_path / "project")
+    command.get_git_config_value = MagicMock(return_value=None)
+    return command
 
 
 @pytest.fixture
diff --git a/tests/commands/convert/test_input_author.py b/tests/commands/convert/test_input_author.py
index 717e16e0..39b40e25 100644
--- a/tests/commands/convert/test_input_author.py
+++ b/tests/commands/convert/test_input_author.py
@@ -209,3 +209,18 @@ def test_prompted_author_with_pyproject_other(convert_command):
     )
     convert_command.console.values = ["5", "Some Author"]
     assert convert_command.input_author(None) == "Some Author"
+
+
+def test_default_author_from_git_config(convert_command, monkeypatch, capsys):
+    """If git integration is configured, and a config value 'user.name' is available,
+    use that value as default."""
+    convert_command.tools.git = object()
+    convert_command.get_git_config_value = MagicMock(return_value="Some Author")
+    convert_command.console.values = [""]
+
+    assert convert_command.input_author(None) == "Some Author"
+    convert_command.get_git_config_value.assert_called_once_with("user", "name")
+
+    # RichConsole wraps long lines, so we have to unwrap before we check
+    stdout = capsys.readouterr().out.replace("\n", " ")
+    assert stdout == PartialMatchString("Based on your git configuration")
diff --git a/tests/commands/convert/test_input_email.py b/tests/commands/convert/test_input_email.py
index c441a01e..a25ba148 100644
--- a/tests/commands/convert/test_input_email.py
+++ b/tests/commands/convert/test_input_email.py
@@ -94,3 +94,21 @@ def test_prompted_email(convert_command):
         convert_command.input_email("Some name", "com.some.bundle", None)
         == "my@email.com"
     )
+
+
+def test_default_email_from_git_config(convert_command, monkeypatch, capsys):
+    """If git integration is configured, and a config value 'user.email' is available,
+    use that value as default."""
+    convert_command.tools.git = object()
+    convert_command.get_git_config_value = MagicMock(return_value="my@email.com")
+    convert_command.console.values = [""]
+
+    assert (
+        convert_command.input_email("Some name", "com.some.bundle", None)
+        == "my@email.com"
+    )
+    convert_command.get_git_config_value.assert_called_once_with("user", "email")
+
+    # RichConsole wraps long lines, so we have to unwrap before we check
+    stdout = capsys.readouterr().out.replace("\n", " ")
+    assert stdout == PartialMatchString("Based on your git configuration")
diff --git a/tests/commands/convert/test_input_license.py b/tests/commands/convert/test_input_license.py
index ba03a8e6..61dbae90 100644
--- a/tests/commands/convert/test_input_license.py
+++ b/tests/commands/convert/test_input_license.py
@@ -2,50 +2,44 @@ from unittest.mock import MagicMock
 
 import pytest
 
+from briefcase.commands.new import LICENSE_OPTIONS
+
 from ...utils import PartialMatchString
 
 
 @pytest.mark.parametrize("license_file_name", ["LICENSE", "LICENCE"])
 @pytest.mark.parametrize(
-    "license_text, license",
+    "license_text, license_id",
     [
-        ("MIT", "MIT license"),
-        ("MIT license", "MIT license"),
-        ("Permission is hereby granted, free of charge", "MIT license"),
-        ("Apache license", "Apache Software License"),
-        ("BSD", "BSD license"),
-        ("BSD license", "BSD license"),
+        ("MIT", "MIT"),
+        ("MIT license", "MIT"),
+        ("Permission is hereby granted, free of charge", "MIT"),
+        ("Apache license", "Apache-2.0"),
+        ("BSD", "BSD-3-Clause"),
+        ("BSD license", "BSD-3-Clause"),
         # Includes some extra text to ensure it doesn't get caught as MIT because of
         # perMITted
         (
             "Redistribution and use in source and binary forms, with or without modification, are permitted",
-            "BSD license",
-        ),
-        ("GPLv2", "GNU General Public License v2 (GPLv2)"),
-        (
-            "version 2 of the GNU General Public License",
-            "GNU General Public License v2 (GPLv2)",
-        ),
-        ("GPLv2+", "GNU General Public License v2 or later (GPLv2+)"),
-        (
-            "Free Software Foundation, either version 2 of the License",
-            "GNU General Public License v2 or later (GPLv2+)",
-        ),
-        ("GPLv3", "GNU General Public License v3 (GPLv3)"),
-        (
-            "version 3 of the GNU General Public License",
-            "GNU General Public License v3 (GPLv3)",
-        ),
-        ("GPLv3+", "GNU General Public License v3 or later (GPLv3+)"),
-        (
-            "either version 3 of the License",
-            "GNU General Public License v3 or later (GPLv3+)",
+            "BSD-3-Clause",
         ),
+        ("GPLv2", "GPL-2.0"),
+        ("version 2 of the GNU General Public License", "GPL-2.0"),
+        ("GPLv2+", "GPL-2.0+"),
+        ("Free Software Foundation, either version 2 of the License", "GPL-2.0+"),
+        ("GPLv3", "GPL-3.0"),
+        ("version 3 of the GNU General Public License", "GPL-3.0"),
+        ("GPLv3+", "GPL-3.0+"),
+        ("either version 3 of the License", "GPL-3.0+"),
         ("Some text", "Other"),
     ],
 )
 def test_get_license_from_file(
-    convert_command, license_text, license, license_file_name, monkeypatch
+    convert_command,
+    license_text,
+    license_id,
+    license_file_name,
+    monkeypatch,
 ):
     mock_selection_question = MagicMock()
     monkeypatch.setattr(
@@ -60,67 +54,46 @@ def test_get_license_from_file(
     )
 
     convert_command.input_license(None)
+
     mock_selection_question.assert_called_once_with(
-        intro=PartialMatchString("the license file"),
+        intro=PartialMatchString("Based on the license file"),
         description="Project License",
-        options=[
-            "BSD license",
-            "MIT license",
-            "Apache Software License",
-            "GNU General Public License v2 (GPLv2)",
-            "GNU General Public License v2 or later (GPLv2+)",
-            "GNU General Public License v3 (GPLv3)",
-            "GNU General Public License v3 or later (GPLv3+)",
-            "Proprietary",
-            "Other",
-        ],
-        default=license,
+        options=LICENSE_OPTIONS,
+        default=license_id,
         override_value=None,
     )
 
 
 @pytest.mark.parametrize(
-    "license_text, license",
+    "license_text, license_id",
     [
-        ("MIT", "MIT license"),
-        ("MIT license", "MIT license"),
-        ("Permission is hereby granted, free of charge", "MIT license"),
-        ("Apache license", "Apache Software License"),
-        ("BSD", "BSD license"),
-        ("BSD license", "BSD license"),
+        ("MIT", "MIT"),
+        ("MIT license", "MIT"),
+        ("Permission is hereby granted, free of charge", "MIT"),
+        ("Apache license", "Apache-2.0"),
+        ("BSD", "BSD-3-Clause"),
+        ("BSD license", "BSD-3-Clause"),
         # Includes some extra text to ensure it doesn't get caught as MIT because of
         # perMITted
         (
             "Redistribution and use in source and binary forms, with or without modification, are permitted",
-            "BSD license",
-        ),
-        ("GPLv2", "GNU General Public License v2 (GPLv2)"),
-        (
-            "version 2 of the GNU General Public License",
-            "GNU General Public License v2 (GPLv2)",
-        ),
-        ("GPLv2+", "GNU General Public License v2 or later (GPLv2+)"),
-        (
-            "Free Software Foundation, either version 2 of the License",
-            "GNU General Public License v2 or later (GPLv2+)",
-        ),
-        ("GPLv3", "GNU General Public License v3 (GPLv3)"),
-        (
-            "version 3 of the GNU General Public License",
-            "GNU General Public License v3 (GPLv3)",
-        ),
-        ("GPLv3+", "GNU General Public License v3 or later (GPLv3+)"),
-        (
-            "either version 3 of the License",
-            "GNU General Public License v3 or later (GPLv3+)",
+            "BSD-3-Clause",
         ),
+        ("GPLv2", "GPL-2.0"),
+        ("version 2 of the GNU General Public License", "GPL-2.0"),
+        ("GPLv2+", "GPL-2.0+"),
+        ("Free Software Foundation, either version 2 of the License", "GPL-2.0+"),
+        ("GPLv3", "GPL-3.0"),
+        ("version 3 of the GNU General Public License", "GPL-3.0"),
+        ("GPLv3+", "GPL-3.0+"),
+        ("either version 3 of the License", "GPL-3.0+"),
         ("Some text", "Other"),
     ],
 )
 def test_get_license_from_pep621_license_file(
     convert_command,
     license_text,
-    license,
+    license_id,
     monkeypatch,
 ):
     mock_selection_question = MagicMock()
@@ -141,42 +114,32 @@ def test_get_license_from_pep621_license_file(
     convert_command.input_license(None)
 
     mock_selection_question.assert_called_once_with(
-        intro=PartialMatchString("the license file"),
+        intro=PartialMatchString("Based on the license file"),
         description="Project License",
-        options=[
-            "BSD license",
-            "MIT license",
-            "Apache Software License",
-            "GNU General Public License v2 (GPLv2)",
-            "GNU General Public License v2 or later (GPLv2+)",
-            "GNU General Public License v3 (GPLv3)",
-            "GNU General Public License v3 or later (GPLv3+)",
-            "Proprietary",
-            "Other",
-        ],
-        default=license,
+        options=LICENSE_OPTIONS,
+        default=license_id,
         override_value=None,
     )
 
 
 @pytest.mark.parametrize(
-    "license_text, license",
+    "license_text, license_id",
     [
-        ("MIT", "MIT license"),
-        ("MIT license", "MIT license"),
-        ("BSD", "BSD license"),
-        ("BSD license", "BSD license"),
-        ("GPLv2", "GNU General Public License v2 (GPLv2)"),
-        ("GPLv2+", "GNU General Public License v2 or later (GPLv2+)"),
-        ("GPLv3", "GNU General Public License v3 (GPLv3)"),
-        ("GPLv3+", "GNU General Public License v3 or later (GPLv3+)"),
+        ("MIT", "MIT"),
+        ("MIT license", "MIT"),
+        ("BSD", "BSD-3-Clause"),
+        ("BSD license", "BSD-3-Clause"),
+        ("GPLv2", "GPL-2.0"),
+        ("GPLv2+", "GPL-2.0+"),
+        ("GPLv3", "GPL-3.0"),
+        ("GPLv3+", "GPL-3.0+"),
         ("Some text", "Other"),
     ],
 )
 def test_get_license_from_pyproject(
     convert_command,
     license_text,
-    license,
+    license_id,
     monkeypatch,
 ):
     mock_selection_question = MagicMock()
@@ -188,21 +151,12 @@ def test_get_license_from_pyproject(
     )
 
     convert_command.input_license(None)
+
     mock_selection_question.assert_called_once_with(
-        intro=PartialMatchString("the PEP621 formatted pyproject.toml"),
+        intro=PartialMatchString("Based on the PEP621 formatted pyproject.toml"),
         description="Project License",
-        options=[
-            "BSD license",
-            "MIT license",
-            "Apache Software License",
-            "GNU General Public License v2 (GPLv2)",
-            "GNU General Public License v2 or later (GPLv2+)",
-            "GNU General Public License v3 (GPLv3)",
-            "GNU General Public License v3 or later (GPLv3+)",
-            "Proprietary",
-            "Other",
-        ],
-        default=license,
+        options=LICENSE_OPTIONS,
+        default=license_id,
         override_value=None,
     )
 
@@ -214,23 +168,10 @@ def test_no_license_hint(convert_command, monkeypatch):
     )
 
     convert_command.input_license(None)
-    mock_selection_question.assert_called_once_with(
-        intro="What license do you want to use for this project's code? ",
-        description="Project License",
-        options=[
-            "BSD license",
-            "MIT license",
-            "Apache Software License",
-            "GNU General Public License v2 (GPLv2)",
-            "GNU General Public License v2 or later (GPLv2+)",
-            "GNU General Public License v3 (GPLv3)",
-            "GNU General Public License v3 or later (GPLv3+)",
-            "Proprietary",
-            "Other",
-        ],
-        default=None,
-        override_value=None,
-    )
+
+    assert mock_selection_question.call_count == 1
+    assert mock_selection_question.call_args.kwargs["default"] is None
+    assert "Based on" not in mock_selection_question.call_args.kwargs["intro"]
 
 
 def test_override_is_used(convert_command):
diff --git a/tests/commands/new/conftest.py b/tests/commands/new/conftest.py
index 30160261..a2daaab5 100644
--- a/tests/commands/new/conftest.py
+++ b/tests/commands/new/conftest.py
@@ -1,3 +1,5 @@
+from unittest.mock import MagicMock
+
 import pytest
 
 from briefcase.commands import NewCommand
@@ -42,4 +44,6 @@ class DummyNewCommand(NewCommand):
 
 @pytest.fixture
 def new_command(tmp_path):
-    return DummyNewCommand(base_path=tmp_path)
+    command = DummyNewCommand(base_path=tmp_path)
+    command.get_git_config_value = MagicMock(return_value=None)
+    return command
diff --git a/tests/commands/new/test_build_app_context.py b/tests/commands/new/test_build_app_context.py
index c370f707..1124c6a0 100644
--- a/tests/commands/new/test_build_app_context.py
+++ b/tests/commands/new/test_build_app_context.py
@@ -1,3 +1,8 @@
+from unittest import mock
+
+from ...utils import PartialMatchString
+
+
 def test_question_sequence(new_command):
     """Questions are asked, a context is constructed."""
 
@@ -26,7 +31,7 @@ def test_question_sequence(new_command):
         class_name="MyApplication",
         description="Cool stuff",
         formal_name="My Application",
-        license="GNU General Public License v2 (GPLv2)",
+        license="GPL-2.0",
         module_name="myapplication",
         source_dir="src/myapplication",
         test_source_dir="tests",
@@ -51,7 +56,7 @@ def test_question_sequence_with_overrides(new_command):
             author="override, author",
             author_email="author@override.tld",
             url="https://override.example.com",
-            license="MIT license",
+            license="MIT",
         ),
     )
 
@@ -63,7 +68,7 @@ def test_question_sequence_with_overrides(new_command):
         class_name="MyOverrideApp",
         description="My override description",
         formal_name="My Override App",
-        license="MIT license",
+        license="MIT",
         module_name="myoverrideapp",
         source_dir="src/myoverrideapp",
         test_source_dir="tests",
@@ -102,7 +107,7 @@ def test_question_sequence_with_bad_license_override(new_command):
         class_name="MyOverrideApp",
         description="My override description",
         formal_name="My Override App",
-        license="GNU General Public License v2 (GPLv2)",
+        license="GPL-2.0",
         module_name="myoverrideapp",
         source_dir="src/myoverrideapp",
         test_source_dir="tests",
@@ -126,10 +131,65 @@ def test_question_sequence_with_no_user_input(new_command):
         class_name="HelloWorld",
         description="My first application",
         formal_name="Hello World",
-        license="BSD license",
+        license="BSD-3-Clause",
         module_name="helloworld",
         source_dir="src/helloworld",
         test_source_dir="tests",
         project_name="Hello World",
         url="https://example.com/helloworld",
     )
+
+
+def test_author_and_email_use_git_config_as_fallback(new_command):
+    """If no user input is provided, git config values 'git.user' and 'git.email' are used if
+    available."""
+    new_command.tools.git = object()
+    new_command.get_git_config_value = mock.MagicMock()
+    new_command.get_git_config_value.side_effect = ["Some Author", "my@email.com"]
+
+    new_command.console.input_enabled = False
+
+    context = new_command.build_app_context(project_overrides={})
+
+    assert context["author"] == "Some Author"
+    assert context["author_email"] == "my@email.com"
+    assert new_command.get_git_config_value.call_args_list == [
+        mock.call("user", "name"),
+        mock.call("user", "email"),
+    ]
+
+
+def test_git_config_is_mentioned_as_source(new_command, monkeypatch):
+    """If git config is used as default value, this shall be mentioned to the user."""
+    new_command.tools.git = object()
+    new_command.get_git_config_value = mock.MagicMock()
+    new_command.get_git_config_value.side_effect = ["Some Author", "my@email.com"]
+
+    new_command.console.input_enabled = False
+
+    mock_text_question = mock.MagicMock()
+    mock_text_question.side_effect = lambda *args, **kwargs: kwargs["default"]
+    monkeypatch.setattr(new_command.console, "text_question", mock_text_question)
+
+    new_command.build_app_context(project_overrides={})
+
+    assert (
+        mock.call(
+            intro=PartialMatchString("Based on your git configuration"),
+            description="Author",
+            default="Some Author",
+            override_value=None,
+        )
+        in mock_text_question.call_args_list
+    )
+
+    assert (
+        mock.call(
+            intro=PartialMatchString("Based on your git configuration"),
+            description="Author's Email",
+            default="my@email.com",
+            override_value=None,
+            validator=new_command.validate_email,
+        )
+        in mock_text_question.call_args_list
+    )