Delete the `news` directory (#19308)
Update the release plan as appropriate
This commit is contained in:
Родитель
bd54252831
Коммит
aadb2c4daa
|
@ -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
|
||||
|
|
|
@ -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)
|
194
news/announce.py
194
news/announce.py
|
@ -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"
|
Загрузка…
Ссылка в новой задаче