Repository: twisted/towncrier

Issue:
The `title_format` configuration option assumes restructuredtext
The correct solution to my issue in #607 is to use the `title_format` configuration option.

The only problem is that this option [assumes](https://github.com/twisted/towncrier/blob/15d1e25d9dc676cd85fe1488341b272f6bb0e836/src/towncrier/build.py#L249) that you want:
```
<formatted_title>
<config.underscores[0] * formatted_title_length>
<rendered content>
```

But for markdown, we only want:
```
# <formatted_title>
<rendered content>
```

The builder however doesn't have a concept of markdown or restructuredtext.

I think the most compatible solution would be to use the markdown method if the template ends with '.md', otherwise use the current method, correct?

Discussion:
adiroiban: I think that in an ideal case, the builder should not care about makrdown or restructured format.

In the future we can also have AsciiDoc, or other formats.

It would be nice for `title_format` to export all the required variable to be able to build a multi-line jinja template.



Patch:
diff --git a/docs/configuration.rst b/docs/configuration.rst
index e7c8a99..0a35069 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -80,6 +80,8 @@ Top level keys
 
     ``""`` by default.
 
+    Formatted titles are appended a line of ``=`` on the following line (reStructuredText title format) unless the template has an ``.md`` suffix, in which case the title will instead be prefixed with ``#`` (markdown title format).
+
 ``issue_format``
     A format string for rendering the issue/ticket number in newsfiles.
 
diff --git a/src/towncrier/build.py b/src/towncrier/build.py
index b28606c..e0add37 100644
--- a/src/towncrier/build.py
+++ b/src/towncrier/build.py
@@ -170,8 +170,11 @@ def __main(
             .joinpath(config.template[1])
             .read_text(encoding="utf-8")
         )
+        template_extension = os.path.splitext(config.template[1])[1]
     else:
         template = Path(config.template).read_text(encoding="utf-8")
+        template_extension = os.path.splitext(config.template)[1]
+    is_markdown = template_extension.lower() == ".md"
 
     click.echo("Finding news fragments...", err=to_err)
 
@@ -215,22 +218,10 @@ def __main(
     if project_date is None:
         project_date = _get_date().strip()
 
-    if config.title_format:
-        top_line = config.title_format.format(
-            name=project_name, version=project_version, project_date=project_date
-        )
-        render_title_with_fragments = False
-        render_title_separately = True
-    elif config.title_format is False:
-        # This is an odd check but since we support both "" and False with
-        # different effects we have to do something a bit abnormal here.
-        top_line = ""
-        render_title_separately = False
-        render_title_with_fragments = False
-    else:
-        top_line = ""
-        render_title_separately = False
-        render_title_with_fragments = True
+    # Render the title in the template if the title format is set to "". It can
+    # alternatively be set to False or a string, in either case it shouldn't be rendered
+    # in the template.
+    render_title = config.title_format == ""
 
     rendered = render_fragments(
         # The 0th underline is used for the top line
@@ -243,18 +234,21 @@ def __main(
         {"name": project_name, "version": project_version, "date": project_date},
         top_underline=config.underlines[0],
         all_bullets=config.all_bullets,
-        render_title=render_title_with_fragments,
+        render_title=render_title,
     )
 
-    if render_title_separately:
-        content = "\n".join(
-            [
-                top_line,
-                config.underlines[0] * len(top_line),
-                rendered,
-            ]
+    if config.title_format:
+        top_line = config.title_format.format(
+            name=project_name, version=project_version, project_date=project_date
         )
+        if is_markdown:
+            parts = [f"# {top_line}"]
+        else:
+            parts = [top_line, config.underlines[0] * len(top_line)]
+        parts.append(rendered)
+        content = "\n".join(parts)
     else:
+        top_line = ""
         content = rendered
 
     if draft:
diff --git a/src/towncrier/newsfragments/610.feature.rst b/src/towncrier/newsfragments/610.feature.rst
new file mode 100644
index 0000000..5b76ab2
--- /dev/null
+++ b/src/towncrier/newsfragments/610.feature.rst
@@ -0,0 +1,1 @@
+The ``title_format`` configuration option now uses a markdown format for markdown templates.


Test patch:
diff --git a/src/towncrier/test/test_build.py b/src/towncrier/test/test_build.py
index 17ffad4..23cfb86 100644
--- a/src/towncrier/test/test_build.py
+++ b/src/towncrier/test/test_build.py
@@ -1032,6 +1032,56 @@ class TestCli(TestCase):
 
 
 
+        """
+        )
+
+        self.assertEqual(0, result.exit_code)
+        self.assertEqual(expected_output, result.output)
+
+    @with_project(
+        config="""
+        [tool.towncrier]
+        package = "foo"
+        filename = "NEWS.md"
+        title_format = "[{project_date}] CUSTOM RELEASE for {name} version {version}"
+        """
+    )
+    def test_title_format_custom_markdown(self, runner):
+        """
+        A non-empty title format adds the specified title, and if the target filename is
+        markdown then the title is added as a markdown header.
+        """
+        with open("foo/newsfragments/123.feature", "w") as f:
+            f.write("Adds levitation")
+        result = runner.invoke(
+            _main,
+            [
+                "--name",
+                "FooBarBaz",
+                "--version",
+                "7.8.9",
+                "--date",
+                "20-01-2001",
+                "--draft",
+            ],
+        )
+
+        expected_output = dedent(
+            """\
+            Loading template...
+            Finding news fragments...
+            Rendering news fragments...
+            Draft only -- nothing has been written.
+            What is seen below is what would be written.
+
+            # [20-01-2001] CUSTOM RELEASE for FooBarBaz version 7.8.9
+
+            ### Features
+
+            - Adds levitation (#123)
+
+
+
         """
         )