Update the release plan as appropriate
This commit is contained in:
Brett Cannon 2022-06-14 13:30:14 -07:00 коммит произвёл GitHub
Родитель bd54252831
Коммит aadb2c4daa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 45 добавлений и 594 удалений

100
.github/release_plan.md поставляемый
Просмотреть файл

@ -2,73 +2,63 @@ All dates should align with VS Code's [iteration](https://github.com/microsoft/v
# Feature freeze (Monday @ 17:00 America/Vancouver, XXX XX)
- [ ] Announce the feature freeze on both Teams and e-mail, leave enough time for teams to surface any last minute issues that need to get in before freeze. Make sure debugger and Language Server teams are looped in as well.
- [ ] Announce the feature freeze on both Teams and e-mail, leave enough time for teams to surface any last minute issues that need to get in before freeze. Make sure debugger and Language Server teams are looped in as well.
# Release candidate (Monday, XXX XX)
- [ ] Update `main` for the release
- [ ] Change the version in [`package.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) to the next **even** number (🤖)
- [ ] Run `npm install` to make sure [`package-lock.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) is up-to-date (🤖)
- [ ] Check `pypi.org` and update the version of `debugpy` in `install_debugpy.py` if necessary.
- [ ] Update `languageServerVersion` in `package.json` to point to the latest version of the [Language Server](https://github.com/Microsoft/python-language-server). Check with the language server team if this needs updating (🤖)
- [ ] Update [`CHANGELOG.md`](https://github.com/Microsoft/vscode-python/blob/main/CHANGELOG.md) (🤖)
- [ ] Run [`news`](https://github.com/Microsoft/vscode-python/tree/main/news) (typically `python news --final --update CHANGELOG.md | code-insiders -`)
- [ ] Copy over the "Thanks" section from the previous release into the "Thanks" section for the new release
- [ ] Make sure the "Thanks" section is up-to-date (e.g. compare to versions in [`requirements.txt`](https://github.com/microsoft/vscode-python/blob/main/requirements.txt))
- [ ] Touch up news entries (e.g. add missing periods)
- [ ] Check the Markdown rendering to make sure everything looks good
- [ ] Add any relevant news entries for `debugpy` and the language server if they were updated
- [ ] Update [`ThirdPartyNotices-Distribution.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Distribution.txt) by using https://tools.opensource.microsoft.com/notice (Notes for this process are in the Team OneNote under Python VS Code → Dev Process → Third-Party Notices / TPN file)
- [ ] Update [`ThirdPartyNotices-Repository.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Repository.txt) as appropriate. This file is manually edited so you can check with the teams if anything needs to be added here.
- [ ] Merge pull request into `main`
- [ ] Create the [`release` branch](https://github.com/microsoft/vscode-python/branches)
- [ ] If there are `release` branches that are two versions old (e.g. release-2020.[minor - 2]) you can delete them at this time
- [ ] Create a new `release/YYYY.minor` branch from `main`
- [ ] Update `main` post-release (🤖)
- [ ] Bump the minor version number to the next ("YYYY.[minor+1]") release in the `main` branch to an **odd** number (🤖)
- [ ] `package.json`
- [ ] `package-lock.json`
- [ ] Create a pull request against `main`
- [ ] Merge pull request into `main`
- [ ] Announce the code freeze is over on the same channels
- [ ] Update Component Governance (Notes are in the team OneNote under Python VS Code → Dev Process → Component Governance).
- [ ] Make sure there are no active alerts
- [ ] Manually add any repository/embedded/CG-incompatible dependencies
- [ ] Open appropriate [documentation issues](https://github.com/microsoft/vscode-docs/issues?q=is%3Aissue+is%3Aopen+label%3Apython)
- [ ] Begin drafting a [blog](http://aka.ms/pythonblog) post. Contact the PM team for this.
- [ ] Update `main` for the release
- [ ] Change the version in [`package.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) to the next **even** number (🤖)
- [ ] Run `npm install` to make sure [`package-lock.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) is up-to-date (🤖)
- [ ] Check `pypi.org` and update the version of `debugpy` in `install_debugpy.py` if necessary.
- [ ] Update `languageServerVersion` in `package.json` to point to the latest version of the [Language Server](https://github.com/Microsoft/python-language-server). Check with the language server team if this needs updating (🤖)
- [ ] Update [`ThirdPartyNotices-Distribution.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Distribution.txt) by using https://tools.opensource.microsoft.com/notice (Notes for this process are in the Team OneNote under Python VS Code → Dev Process → Third-Party Notices / TPN file)
- [ ] Update [`ThirdPartyNotices-Repository.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Repository.txt) as appropriate. This file is manually edited so you can check with the teams if anything needs to be added here.
- [ ] Merge pull request into `main`
- [ ] Create the [`release` branch](https://github.com/microsoft/vscode-python/branches)
- [ ] If there are `release` branches that are two versions old you can delete them at this time
- [ ] Create a new `release/YYYY.minor` branch from `main`
- [ ] Create a draft [GitHub release](https://github.com/microsoft/vscode-python/releases) for the release notes (🤖)
- [ ] Update `main` post-release (🤖)
- [ ] Bump the minor version number to the next ("YYYY.[minor+1]") release in the `main` branch to an **odd** number (🤖)
- [ ] `package.json`
- [ ] `package-lock.json`
- [ ] Create a pull request against `main`
- [ ] Merge pull request into `main`
- [ ] Announce the code freeze is over on the same channels
- [ ] Update Component Governance (Notes are in the team OneNote under Python VS Code → Dev Process → Component Governance).
- [ ] Make sure there are no active alerts
- [ ] Manually add any repository/embedded/CG-incompatible dependencies
- [ ] Open appropriate [documentation issues](https://github.com/microsoft/vscode-docs/issues?q=is%3Aissue+is%3Aopen+label%3Apython)
- [ ] Begin drafting a [blog](http://aka.ms/pythonblog) post. Contact the PM team for this.
# Release (Wednesday, XXX XX)
## Preparation
- [ ] Make sure the [appropriate pull requests](https://github.com/microsoft/vscode-docs/pulls) for the [documentation](https://code.visualstudio.com/docs/python/python-tutorial) -- including the [WOW](https://code.visualstudio.com/docs/languages/python) page -- are ready
- [ ] Final updates to the `release-YYYY.minor` branch
- [ ] Create a branch against `release-YYYY.minor` for a pull request
- [ ] Update the version in [`package.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) to remove the `-rc` (🤖)
- [ ] Run `npm install` to make sure [`package-lock.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) is up-to-date (the only update should be the version number if `package-lock.json` has been kept up-to-date) (🤖)
- [ ] Update [`CHANGELOG.md`](https://github.com/Microsoft/vscode-python/blob/main/CHANGELOG.md) (🤖)
- [ ] Update version and date for the release section
- [ ] Run [`news`](https://github.com/Microsoft/vscode-python/tree/main/news) and copy-and-paste new entries (typically `python news --final | code-insiders -`; quite possibly nothing new to add)
- [ ] Update [`ThirdPartyNotices-Distribution.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Distribution.txt) by using https://tools.opensource.microsoft.com/notice (🤖; see team notes)
- [ ] Update [`ThirdPartyNotices-Repository.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Repository.txt) manually if necessary
- [ ] Create pull request against `release-YYYY.minor` (🤖)
- [ ] Merge pull request into `release-YYYY.minor`
- [ ] Make sure the [appropriate pull requests](https://github.com/microsoft/vscode-docs/pulls) for the [documentation](https://code.visualstudio.com/docs/python/python-tutorial) -- including the [WOW](https://code.visualstudio.com/docs/languages/python) page -- are ready
- [ ] Final updates to the `release-YYYY.minor` branch
- [ ] Create a branch against `release-YYYY.minor` for a pull request
- [ ] Update the version in [`package.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) to remove the `-rc` (🤖)
- [ ] Run `npm install` to make sure [`package-lock.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) is up-to-date (the only update should be the version number if `package-lock.json` has been kept up-to-date) (🤖)
- [ ] Update [`ThirdPartyNotices-Distribution.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Distribution.txt) by using https://tools.opensource.microsoft.com/notice (🤖; see team notes)
- [ ] Update [`ThirdPartyNotices-Repository.txt`](https://github.com/Microsoft/vscode-python/blob/main/ThirdPartyNotices-Repository.txt) manually if necessary
- [ ] Create pull request against `release/YYYY.minor` (🤖)
- [ ] Merge pull request into `release/YYYY.minor`
## Release
- [ ] Make sure [CI](https://github.com/microsoft/vscode-python/actions?query=workflow%3A%22Insiders+Build%22) is passing (🤖)
- [ ] Create a [GitHub release](https://github.com/microsoft/vscode-python/releases) (🤖)
- [ ] Start creating a new release
- [ ] Make the tag match the version of the released extension
- [ ] Copy the changelog entry into the release as the description
- [ ] Run the CD pipeline
- [ ] Publish [documentation changes](https://github.com/Microsoft/vscode-docs/pulls?q=is%3Apr+is%3Aopen+label%3Apython)
- [ ] Publish the [blog](http://aka.ms/pythonblog) post
- [ ] Determine if a hotfix is needed
- [ ] Merge the release branch back into `main`. Don't overwrite the main branch version. (🤖)
- [ ] Make sure [CI](https://github.com/microsoft/vscode-python/actions?query=workflow%3A%22Insiders+Build%22) is passing (🤖)
- [ ] Run the CD pipeline
- [ ] Create a [GitHub release](https://github.com/microsoft/vscode-python/releases) (🤖)
- [ ] Update the release notes
- [ ] Take the release out of draft
- [ ] Publish [documentation changes](https://github.com/Microsoft/vscode-docs/pulls?q=is%3Apr+is%3Aopen+label%3Apython)
- [ ] Publish the [blog](http://aka.ms/pythonblog) post
- [ ] Determine if a hotfix is needed
- [ ] Merge the release branch back into `main`. Don't overwrite the main branch version. (🤖)
## Prep for the _next_ release
- [ ] Create a new [release plan](https://raw.githubusercontent.com/microsoft/vscode-python/main/.github/release_plan.md) (🤖)
- [ ] [(Un-)pin](https://help.github.com/en/articles/pinning-an-issue-to-your-repository) [release plan issues](https://github.com/Microsoft/vscode-python/labels/release%20plan) (🤖)
- [ ] Create a new [release plan](https://raw.githubusercontent.com/microsoft/vscode-python/main/.github/release_plan.md) (🤖)
- [ ] [(Un-)pin](https://help.github.com/en/articles/pinning-an-issue-to-your-repository) [release plan issues](https://github.com/Microsoft/vscode-python/labels/release%20plan) (🤖)

Просмотреть файл

@ -46,7 +46,6 @@ images/**/*.gif
images/**/*.png
ipywidgets/**
i18n/**
news/**
node_modules/**
obj/**
out/**/*.stats.json

8
news/.vscode/settings.json поставляемый
Просмотреть файл

@ -1,8 +0,0 @@
{
"python.languageServer": "Pylance",
"python.formatting.provider": "black",
"editor.formatOnSave": true,
"python.testing.pytestArgs": ["."],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}

Просмотреть файл

@ -1,2 +0,0 @@
Changes that add new features.

Просмотреть файл

@ -1 +0,0 @@
Changes that fix broken behaviour.

Просмотреть файл

@ -1 +0,0 @@
Changes that should not be user-facing.

Просмотреть файл

@ -1,62 +0,0 @@
# News
Our changelog is automatically generated from individual news entry files.
This alleviates the burden of having to go back and try to figure out
what changed in a release. It also helps tie pull requests back to the
issue(s) it addresses. Finally, it avoids merge conflicts between pull requests
which would occur if multiple pull requests tried to edit the changelog.
If a change does not warrant a news entry, the `skip news` label can be added
to a pull request to signal this fact.
## Entries
Each news entry is represented by a Markdown file that contains the
relevant details of what changed. The file name of the news entry is
the issue that corresponds to the change along with an optional nonce in
case a single issue corresponds to multiple changes. The directory
the news entry is saved in specifies what section of the changelog the
change corresponds to. External contributors should also make sure to
thank themselves for taking the time and effort to contribute.
As an example, a change corresponding to a bug reported in issue #42
would be saved in the `2 Fixes` directory and named `42.md`
(or `42-nonce_value.md` if there was a need for multiple entries
regarding issue #42) and could contain the following:
```markdown
[Answer](<https://en.wikipedia.org/wiki/42_(number)>)
to the Ultimate Question of Life, the Universe, and Everything!
(thanks [Don Jaymanne](https://github.com/donjayamanne/))
```
This would then be made into an entry in the changelog that was in the
`Fixes` section, contained the details as found in the file, and tied
to issue #42.
## Generating the changelog
The `announce` script can do 3 possible things:
1. Validate that the changelog _could_ be successfully generated
2. Generate the changelog entries
3. Generate the changelog entries **and** `git-rm` the news entry files
The first option is used in CI to make sure any added news entries
will not cause trouble at release time. The second option is for
filling in the changelog for interim releases, e.g. a beta release.
The third option is for final releases that get published to the
[VS Code marketplace](https://marketplace.visualstudio.com/VSCode).
For options 2 & 3, the changelog is sent to stdout so it can be temporarily
saved to a file:
```sh
python3 news > entry.txt
```
It can also be redirected to an editor buffer, e.g.:
```sh
python3 news | code-insiders -
```

Просмотреть файл

@ -1,6 +0,0 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import runpy
runpy.run_module('announce', run_name='__main__', alter_sys=True)

Просмотреть файл

@ -1,194 +0,0 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
"""Generate the changelog.
Usage: announce [--dry_run | --interim | --final] [--update=<news_file>] [<directory>]
"""
import dataclasses
import datetime
import enum
import json
import operator
import os
import pathlib
import re
import subprocess
import sys
import docopt
FILENAME_RE = re.compile(r"(?P<issue>\d+)(?P<nonce>-\S+)?\.md")
@dataclasses.dataclass
class NewsEntry:
"""Representation of a news entry."""
issue_number: int
description: str
path: pathlib.Path
def news_entries(directory):
"""Yield news entries in the directory.
Entries are sorted by issue number.
"""
entries = []
for path in directory.iterdir():
if path.name == "README.md":
continue
match = FILENAME_RE.match(path.name)
if match is None:
raise ValueError(f"{path} has a bad file name")
issue = int(match.group("issue"))
try:
entry = path.read_text("utf-8")
except UnicodeDecodeError as exc:
raise ValueError(f"'{path}' is not encoded as UTF-8") from exc
if "\ufeff" in entry:
raise ValueError(f"'{path}' contains the BOM")
entries.append(NewsEntry(issue, entry, path))
entries.sort(key=operator.attrgetter("issue_number"))
yield from entries
@dataclasses.dataclass
class SectionTitle:
"""Create a data object for a section of the changelog."""
index: int
title: str
path: pathlib.Path
def sections(directory):
"""Yield the sections in their appropriate order."""
found = []
for path in directory.iterdir():
if not path.is_dir() or path.name.startswith((".", "_")):
continue
position, sep, title = path.name.partition(" ")
if not sep:
print(
f"directory {path.name!r} is missing a ranking; skipping",
file=sys.stderr,
)
continue
found.append(SectionTitle(int(position), title, path))
return sorted(found, key=operator.attrgetter("index"))
def gather(directory):
"""Gather all the entries together."""
data = []
for section in sections(directory):
data.append((section, list(news_entries(section.path))))
return data
def entry_markdown(entry):
"""Generate the Markdown for the specified entry."""
enumerated_item = "1. "
indent = " " * len(enumerated_item)
issue_url = (
f"https://github.com/Microsoft/vscode-python/issues/{entry.issue_number}"
)
issue_md = f"([#{entry.issue_number}]({issue_url}))"
entry_lines = entry.description.strip().splitlines()
formatted_lines = [f"{enumerated_item}{entry_lines[0]}"]
formatted_lines.extend(f"{indent}{line}" for line in entry_lines[1:])
formatted_lines.append(f"{indent}{issue_md}")
return "\n".join(formatted_lines)
def changelog_markdown(data):
"""Generate the Markdown for the release."""
changelog = []
for section, entries in data:
changelog.append(f"### {section.title}")
changelog.append("")
changelog.extend(map(entry_markdown, entries))
changelog.append("")
return "\n".join(changelog)
def git_rm(path):
"""Run git-rm on the path."""
status = subprocess.run(
["git", "rm", os.fspath(path.resolve())],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
try:
status.check_returncode()
except Exception:
print(status.stdout, file=sys.stderr)
raise
def cleanup(data):
"""Remove news entries from git and disk."""
for section, entries in data:
for entry in entries:
git_rm(entry.path)
class RunType(enum.Enum):
"""Possible run-time options."""
dry_run = 0
interim = 1
final = 2
def complete_news(version, entry, previous_news):
"""Prepend a news entry to the previous news file."""
title, _, previous_news = previous_news.partition("\n")
title = title.strip()
previous_news = previous_news.strip()
section_title = (
f"## {version} ({datetime.date.today().strftime('%d %B %Y')})"
).replace("(0", "(")
# TODO: Insert the "Thank you!" section (in monthly releases)?
return f"{title}\n\n{section_title}\n\n{entry.strip()}\n\n\n{previous_news}"
def main(run_type, directory, news_file=None):
directory = pathlib.Path(directory)
data = gather(directory)
markdown = changelog_markdown(data)
if news_file:
with open(news_file, "r", encoding="utf-8") as file:
previous_news = file.read()
package_config_path = pathlib.Path(news_file).parent / "package.json"
config = json.loads(package_config_path.read_text(encoding="utf-8"))
new_news = complete_news(config["version"], markdown, previous_news)
if run_type == RunType.dry_run:
print(f"would be written to {news_file}:")
print()
print(new_news)
else:
with open(news_file, "w", encoding="utf-8") as file:
file.write(new_news)
else:
print(markdown)
if run_type == RunType.final:
cleanup(data)
if __name__ == "__main__":
arguments = docopt.docopt(__doc__)
for possible_run_type in RunType:
if arguments[f"--{possible_run_type.name}"]:
run_type = possible_run_type
break
else:
run_type = RunType.interim
directory = arguments["<directory>"] or pathlib.Path(__file__).parent
main(run_type, directory, arguments["--update"])

Просмотреть файл

@ -1,7 +0,0 @@
# This file is used to generate requirements.txt.
# To update requirements.txt, run the following commands.
# 1) pip install pip-tools
# 2) pip-compile --generate-hashes --upgrade news\requirements.in
docopt
pytest

Просмотреть файл

@ -1,49 +0,0 @@
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile --generate-hashes 'news\requirements.in'
#
atomicwrites==1.4.0 \
--hash=sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197 \
--hash=sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a
# via pytest
attrs==21.2.0 \
--hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \
--hash=sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb
# via pytest
colorama==0.4.4 \
--hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \
--hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2
# via pytest
docopt==0.6.2 \
--hash=sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491
# via -r news\requirements.in
iniconfig==1.1.1 \
--hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \
--hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32
# via pytest
packaging==21.3 \
--hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \
--hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522
# via pytest
pluggy==1.0.0 \
--hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \
--hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3
# via pytest
py==1.11.0 \
--hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \
--hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378
# via pytest
pyparsing==3.0.6 \
--hash=sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4 \
--hash=sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81
# via packaging
pytest==6.2.5 \
--hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \
--hash=sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134
# via -r news\requirements.in
toml==0.10.2 \
--hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \
--hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f
# via pytest

Просмотреть файл

@ -1,208 +0,0 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import codecs
import datetime
import pathlib
import docopt
import pytest
import announce as ann
@pytest.fixture
def directory(tmpdir):
"""Fixture to create a temp directory wrapped in a pathlib.Path object."""
return pathlib.Path(tmpdir)
def test_news_entry_formatting(directory):
issue = 42
normal_entry = directory / f"{issue}.md"
nonce_entry = directory / f"{issue}-nonce.md"
body = "Hello, world!"
normal_entry.write_text(body, encoding="utf-8")
nonce_entry.write_text(body, encoding="utf-8")
results = list(ann.news_entries(directory))
assert len(results) == 2
for result in results:
assert result.issue_number == issue
assert result.description == body
def test_news_entry_sorting(directory):
oldest_entry = directory / "45.md"
newest_entry = directory / "123.md"
oldest_entry.write_text("45", encoding="utf-8")
newest_entry.write_text("123", encoding="utf-8")
results = list(ann.news_entries(directory))
assert len(results) == 2
assert results[0].issue_number == 45
assert results[1].issue_number == 123
def test_only_utf8(directory):
entry = directory / "42.md"
entry.write_text("Hello, world", encoding="utf-16")
with pytest.raises(ValueError):
list(ann.news_entries(directory))
def test_no_bom_allowed(directory):
entry = directory / "42.md"
entry.write_bytes(codecs.BOM_UTF8 + "Hello, world".encode("utf-8"))
with pytest.raises(ValueError):
list(ann.news_entries(directory))
def test_bad_news_entry_file_name(directory):
entry = directory / "bunk.md"
entry.write_text("Hello, world!")
with pytest.raises(ValueError):
list(ann.news_entries(directory))
def test_news_entry_README_skipping(directory):
entry = directory / "README.md"
entry.write_text("Hello, world!")
assert len(list(ann.news_entries(directory))) == 0
def test_sections_sorting(directory):
dir2 = directory / "2 Hello"
dir1 = directory / "1 World"
dir2.mkdir()
dir1.mkdir()
results = list(ann.sections(directory))
assert [found.title for found in results] == ["World", "Hello"]
def test_sections_naming(directory):
(directory / "Hello").mkdir()
assert not ann.sections(directory)
def test_gather(directory):
fixes = directory / "2 Fixes"
fixes.mkdir()
fix1 = fixes / "1.md"
fix1.write_text("Fix 1", encoding="utf-8")
fix2 = fixes / "3.md"
fix2.write_text("Fix 2", encoding="utf-8")
enhancements = directory / "1 Enhancements"
enhancements.mkdir()
enhancement1 = enhancements / "2.md"
enhancement1.write_text("Enhancement 1", encoding="utf-8")
enhancement2 = enhancements / "4.md"
enhancement2.write_text("Enhancement 2", encoding="utf-8")
results = ann.gather(directory)
assert len(results) == 2
section, entries = results[0]
assert section.title == "Enhancements"
assert len(entries) == 2
assert entries[0].description == "Enhancement 1"
assert entries[1].description == "Enhancement 2"
section, entries = results[1]
assert len(entries) == 2
assert section.title == "Fixes"
assert entries[0].description == "Fix 1"
assert entries[1].description == "Fix 2"
def test_entry_markdown():
markdown = ann.entry_markdown(ann.NewsEntry(42, "Hello, world!", None))
assert "42" in markdown
assert "Hello, world!" in markdown
assert "https://github.com/Microsoft/vscode-python/issues/42" in markdown
def test_changelog_markdown():
data = [
(
ann.SectionTitle(1, "Enhancements", None),
[
ann.NewsEntry(2, "Enhancement 1", None),
ann.NewsEntry(4, "Enhancement 2", None),
],
),
(
ann.SectionTitle(1, "Fixes", None),
[ann.NewsEntry(1, "Fix 1", None), ann.NewsEntry(3, "Fix 2", None)],
),
]
markdown = ann.changelog_markdown(data)
assert "### Enhancements" in markdown
assert "### Fixes" in markdown
assert "1" in markdown
assert "Fix 1" in markdown
assert "2" in markdown
assert "Enhancement 1" in markdown
assert "https://github.com/Microsoft/vscode-python/issues/2" in markdown
assert "3" in markdown
assert "Fix 2" in markdown
assert "https://github.com/Microsoft/vscode-python/issues/3" in markdown
assert "4" in markdown
assert "Enhancement 2" in markdown
def test_cleanup(directory, monkeypatch):
rm_path = None
def fake_git_rm(path):
nonlocal rm_path
rm_path = path
monkeypatch.setattr(ann, "git_rm", fake_git_rm)
fixes = directory / "2 Fixes"
fixes.mkdir()
fix1 = fixes / "1.md"
fix1.write_text("Fix 1", encoding="utf-8")
results = ann.gather(directory)
assert len(results) == 1
ann.cleanup(results)
section, entries = results.pop()
assert len(entries) == 1
assert rm_path == entries[0].path
TITLE = "# Our most excellent changelog"
OLD_NEWS = f"""\
## 2018.12.0 (31 Dec 2018)
We did things!
## 2017.11.16 (16 Nov 2017)
We started going stuff.
"""
NEW_NEWS = """\
We fixed all the things!
### Code Health
We deleted all the code to fix all the things. ;)
"""
def test_complete_news():
version = "2019.3.0"
# Remove leading `0`.
date = datetime.date.today().strftime("%d %B %Y").lstrip("0")
news = ann.complete_news(version, NEW_NEWS, f"{TITLE}\n\n\n{OLD_NEWS}")
expected = f"{TITLE}\n\n## {version} ({date})\n\n{NEW_NEWS.strip()}\n\n\n{OLD_NEWS.strip()}"
assert news == expected
def test_cli():
for option in ("--" + opt for opt in ["dry_run", "interim", "final"]):
args = docopt.docopt(ann.__doc__, [option])
assert args[option]
args = docopt.docopt(ann.__doc__, ["./news"])
assert args["<directory>"] == "./news"
args = docopt.docopt(ann.__doc__, ["--dry_run", "./news"])
assert args["--dry_run"]
assert args["<directory>"] == "./news"
args = docopt.docopt(ann.__doc__, ["--update", "CHANGELOG.md", "./news"])
assert args["--update"] == "CHANGELOG.md"
assert args["<directory>"] == "./news"