Repository: twisted/towncrier

Issue:
Rendering issue with release candidate (19.9.0rc1)
When using the release candidate (19.9.0rc1), the title of the new entry in the Changelog is poorly formatted.

_This is using the same config as for 19.2.0._

There is duplication of the line:
```
"version" (2020-04-02)
====================
"package-name" "version" (2020-04-02)
=======================
```

Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/twisted/towncrier/pull/276?src=pr&el=h1) Report
> Merging [#276](https://codecov.io/gh/twisted/towncrier/pull/276?src=pr&el=desc) (b5cdf25) into [master](https://codecov.io/gh/twisted/towncrier/commit/3c2f9fdfc81299c354662e53884ae9b90ce8ec52?el=desc) (3c2f9fd) will **increase** coverage by `0.26%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/twisted/towncrier/pull/276/graphs/tree.svg?width=650&height=150&src=pr&token=9npndnog2E)](https://codecov.io/gh/twisted/towncrier/pull/276?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #276      +/-   ##
==========================================
+ Coverage   89.84%   90.11%   +0.26%     
==========================================
  Files          11       11              
  Lines         453      435      -18     
  Branches       91       86       -5     
==========================================
- Hits          407      392      -15     
+ Misses         25       23       -2     
+ Partials       21       20       -1     
```


| [Impacted Files](https://codecov.io/gh/twisted/towncrier/pull/276?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/towncrier/\_builder.py](https://codecov.io/gh/twisted/towncrier/pull/276/diff?src=pr&el=tree#diff-c3JjL3Rvd25jcmllci9fYnVpbGRlci5weQ==) | `93.49% <ø> (ø)` | |
| [src/towncrier/build.py](https://codecov.io/gh/twisted/towncrier/pull/276/diff?src=pr&el=tree#diff-c3JjL3Rvd25jcmllci9idWlsZC5weQ==) | `93.05% <100.00%> (+3.72%)` | :arrow_up: |
| [src/towncrier/create.py](https://codecov.io/gh/twisted/towncrier/pull/276/diff?src=pr&el=tree#diff-c3JjL3Rvd25jcmllci9jcmVhdGUucHk=) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/twisted/towncrier/pull/276?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/twisted/towncrier/pull/276?src=pr&el=footer). Last update [3c2f9fd...5417caf](https://codecov.io/gh/twisted/towncrier/pull/276?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/towncrier/_builder.py b/src/towncrier/_builder.py
index 90988ac..e8ca9ba 100644
--- a/src/towncrier/_builder.py
+++ b/src/towncrier/_builder.py
@@ -224,6 +224,7 @@ def render_issue(issue_format, issue):
 def render_fragments(
     template,
     issue_format,
+    top_line,
     fragments,
     definitions,
     underlines,
@@ -288,6 +289,7 @@ def render_fragments(
         return u""
 
     res = jinja_template.render(
+        top_line=top_line,
         sections=data,
         definitions=definitions,
         underlines=underlines,
diff --git a/src/towncrier/build.py b/src/towncrier/build.py
index e4952ed..18b606c 100644
--- a/src/towncrier/build.py
+++ b/src/towncrier/build.py
@@ -141,7 +141,6 @@ def __main(
         top_line = config["title_format"].format(
             name=project_name, version=project_version, project_date=project_date
         )
-        top_line += u"\n" + (config["underlines"][0] * len(top_line)) + u"\n"
     else:
         top_line = ""
 
@@ -149,6 +148,7 @@ def __main(
         # The 0th underline is used for the top line
         template,
         config["issue_format"],
+        top_line,
         fragments,
         definitions,
         config["underlines"][1:],
@@ -164,10 +164,7 @@ def __main(
             "What is seen below is what would be written.\n",
             err=to_err,
         )
-        if top_line:
-            click.echo("\n%s\n%s" % (top_line, rendered))
-        else:
-            click.echo(rendered)
+        click.echo(rendered)
     else:
         click.echo("Writing to newsfile...", err=to_err)
         start_line = config["start_line"]
diff --git a/src/towncrier/newsfragments/180.bugfix b/src/towncrier/newsfragments/180.bugfix
new file mode 100644
index 0000000..580d960
--- /dev/null
+++ b/src/towncrier/newsfragments/180.bugfix
@@ -0,0 +1,1 @@
+Specifying ``title_format`` disables default top line creation to avoid duplication.
diff --git a/src/towncrier/templates/default.rst b/src/towncrier/templates/default.rst
index 362d8e1..ac551df 100644
--- a/src/towncrier/templates/default.rst
+++ b/src/towncrier/templates/default.rst
@@ -1,4 +1,7 @@
-{% if versiondata.name %}
+{% if top_line %}
+{{ top_line }}
+{{ top_underline * ((top_line)|length)}}
+{% elif versiondata.name %}
 {{ versiondata.name }} {{ versiondata.version }} ({{ versiondata.date }})
 {{ top_underline * ((versiondata.name + versiondata.version + versiondata.date)|length + 4)}}
 {% else %}


Test patch:
diff --git a/src/towncrier/test/test_format.py b/src/towncrier/test/test_format.py
index 26caa05..c0bbf7f 100644
--- a/src/towncrier/test/test_format.py
+++ b/src/towncrier/test/test_format.py
@@ -3,6 +3,8 @@
 
 from __future__ import absolute_import, division
 
+from textwrap import dedent
+
 import pkg_resources
 
 from twisted.trial.unittest import TestCase
@@ -129,6 +131,7 @@ Bugfixes
         output = render_fragments(
             template,
             None,
+            "",
             fragments,
             definitions,
             ["-", "~"],
@@ -173,6 +176,7 @@ Bugfixes
         output = render_fragments(
             template,
             None,
+            "",
             fragments,
             definitions,
             ["*", "^"],
@@ -218,6 +222,7 @@ Misc
         output = render_fragments(
             template,
             u"xx{issue}",
+            "",
             fragments,
             definitions,
             ["-", "~"],
@@ -276,6 +281,7 @@ Features
         output = render_fragments(
             template,
             None,
+            "",
             fragments,
             definitions,
             ["-", "~"],
@@ -327,6 +333,7 @@ Features
         output = render_fragments(
             template,
             None,
+            "",
             fragments,
             definitions,
             ["-", "~"],
@@ -334,3 +341,30 @@ Features
             versiondata={"name": "MyProject", "version": "1.0", "date": "never"},
         )
         self.assertEqual(output, expected_output)
+
+    def test_title_format_specified(self):
+        """
+        Title format replaces default top line rendering.
+        """
+        template = pkg_resources.resource_string(
+            "towncrier", "templates/default.rst"
+        ).decode("utf8")
+
+        fragments = split_fragments(fragments={}, definitions=OrderedDict())
+        output = render_fragments(
+            template=template,
+            issue_format=None,
+            top_line="A custom top line",
+            fragments=fragments,
+            definitions=OrderedDict(),
+            underlines=["-", "~"],
+            wrap=False,
+            versiondata={"name": "MyProject", "version": "1.0", "date": "never"},
+        )
+
+        expected_output = dedent(u"""\
+        A custom top line
+        =================
+        """)
+
+        self.assertEqual(expected_output, output)
diff --git a/src/towncrier/test/test_write.py b/src/towncrier/test/test_write.py
index 37bbd10..7ad4853 100644
--- a/src/towncrier/test/test_write.py
+++ b/src/towncrier/test/test_write.py
@@ -98,6 +98,7 @@ Old text.
             render_fragments(
                 template,
                 None,
+                "",
                 fragments,
                 definitions,
                 ["-", "~"],
@@ -212,6 +213,7 @@ Old text.
             render_fragments(
                 template,
                 None,
+                "",
                 fragments,
                 definitions,
                 ["-", "~"],