﻿# release


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Overview

`nbdev.release` provides 3 commands that you can run from your shell to
manage your changelog file and git releases:

- `nbdev-changelog`: creates a CHANGELOG.md file from closed and labeled
  GitHub issues
- `nbdev-release-git`: tags and creates a release in GitHub for the
  current version
- `nbdev-release-gh`: calls `nbdev-changelog`, lets you edit the result,
  then pushes to git and calls `nbdev-release-git`

It provides 3 futher commands for releasing packages on pypi or conda:

- `nbdev-pypi`: Create and upload a pypi installer
- `nbdev-conda`: Create and upload a conda installer
- `nbdev-release-both`: Create and upload both pypi and conda installers

Here’s a brief demonstration of how to use the changelog and git release
tools in `nbdev.release`. This demo first creates an issue using the
[`gh`](https://cli.github.com/) command line tool, and then closes it
using `git`; you can also use GitHub’s web interface for both of these
tasks. (Note that this functionality used to be in a project called
`fastrelease`, so in the video the command line tools have different
names, starting with `fastrelease_` instead of `nbdev-`).

[<img src="images/release.svg" width="900" />](images/release.svg)

### Setup

You’ll need to get a GitHub [personal access
token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token)
if you haven’t already. To do so, [click
here](https://github.com/settings/tokens/new) and enter “nbdev” in the
“Note” section, and click the `repo` checkbox.

Then click “Generate Token” at the bottom of the screen, and copy the
token (the long string of letters and numbers shown). You can easily do
that by clicking the little clipboard icon next to the token.

<img alt="Copying your token" width="743" caption="Copying your token" src="images/token.png">

It’s easiest to save the token as an environment variable `GITHUB_TOKEN`
that can be automatically accessed. We recommend you do this is by
adding the following to the end of your `.bashrc` or `.zshrc` file:

``` bash
export GITHUB_TOKEN=xxx
```

…and then replace the `xxx` with the token you just copied. It will
automatically be available whenever you start a new shell (but don’t
forget to `source` that file or open a new shell after you change it.).

### Creating release notes

Now you’re ready to create your release notes. These are created in a
file called `CHANGELOG.md`. Here’s an example of what it creates: [nbdev
CHANGELOG](https://github.com/fastai/nbdev/blob/master/CHANGELOG.md).

All issues with the label **bug**, **enhancement**, or **breaking** that
have been closed in your repo since your last release will be added to
the top of this file. If you haven’t made any releases before, then all
issues with those labels will be included.

Therefore, before you create or update `CHANGELOG.md`, go to your GitHub
issues page, remove `is:open` from the filter, and label any issues you
want included with one of the labels above. When you’ve done that, you
can create or update your release notes by running in your terminal:

    nbdev-changelog

Each issue title will be added. Open `CHANGELOG.md` in your editor and
make any edits that you want, and then commit the file to your repo
(remember to `git add` it!)

### Tagging a release

You should now tag a release. This will create a tag in GitHub with your
current version number in `__init__.py`, and will then make it into a
release, using your latest release notes as the description of the
release:

    nbdev-release-git

After you run this, be sure to increment your version number. You can
either edit it manually, or if you use nbdev it can be done for you by
running:

    nbdev-bump-version

### Doing both (creating release notes, and tagging a release)

To complete both of the steps above, run:

    nbdev-release-gh

If the changelog was generated and reviewed separately, complete the
release without regenerating it, opening an editor, or prompting:

    nbdev-release-gh --no_changelog --no_editor --yes

Automatic changelog generation stops when the current branch does not
contain the latest GitHub release, as happens for an update to an older
version. Write `CHANGELOG.md` manually and use the command above. The
next section shows the complete process.

See the screencast above for a demonstration of the interactive
workflow.

### Releasing an update to an older version

Sometimes the latest release is 2.x, but you need to fix version 1.x.
The example below starts from release `1.1.0` and publishes `1.1.1`.

A Git worktree is a second project folder connected to the same
repository. It lets you open the older version without changing your
usual project folder:

``` sh
git fetch origin --tags
git worktree add ../myproject-1.x -b 1.x 1.1.0
cd ../myproject-1.x
```

Bump the version, make the fix, and test it:

``` sh
nbdev-bump-version
```

Write the new `CHANGELOG.md` entry yourself. Automatic changelog
generation is intended for the latest release, so skip it here. Review
the changes before releasing:

``` sh
git diff
git status --short
nbdev-release-gh --no_changelog --no_editor --yes
nbdev-pypi
nbdev-bump-version
git commit -am bump
git push
```

The release command pushes the `1.x` branch to GitHub, then tags the
code it released. Because `1.x` is not the repository’s default branch,
the release is not marked as “Latest” on GitHub, so the repo front page
keeps showing the newest version. After checking the GitHub release and
PyPI package, remove the extra folder:

``` sh
cd ../myproject
git worktree remove ../myproject-1.x
```

The extra folder uses a little more disk space, and you need to run each
command in the right folder. In return, the latest and older versions
remain available at the same time. This is useful when other terminals
or Python sessions are using the main project folder.

For a small update with a clean project folder, you can instead switch
that folder to the older release:

``` sh
git fetch origin --tags
git switch -c 1.x 1.1.0
```

Then follow the same bump, fix, changelog, test, and release steps
above. When finished, return to the latest code:

``` sh
git switch main
git pull --ff-only
```

This alternative uses one folder and fewer commands. While the release
is in progress, however, your usual project folder and any editable
Python installation based on it will contain the older version.

## Python API

Releases target the exact pushed commit. Automatic changelogs are
limited to the latest release line; maintenance releases use a manually
written changelog.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L63"
target="_blank" style="float:right; font-size:smaller">source</a>

### update_changelog

``` python
def update_changelog(
    txt, ver, notes, marker:str='<!-- do not remove -->\n'
):
```

*Insert `notes` into changelog `txt` after `marker`, replacing any
existing section for `ver`*

[`update_changelog`](https://nbdev.fast.ai/api/release.html#update_changelog)
makes changelog generation idempotent: if the changelog already has a
section for `ver` (e.g. from an earlier run whose release never
happened), it’s replaced rather than duplicated. That also means
regenerating discards any manual edits to that pending section, so
curate the changelog after its final generation. It also owns the
spacing: two blank lines between version sections and a single newline
at EOF, whether or not `notes` has trailing newlines.

``` python
log = "# Release notes\n\n<!-- do not remove -->\n\n## 0.0.1\n\n- First release\n"
test_eq(update_changelog("# Release notes\n\n<!-- do not remove -->\n", '0.0.1', '\n## 0.0.1\n\n- First release'), log)
upd = update_changelog(log, '0.0.2', '\n## 0.0.2\n\n- New feature')
assert upd.index('## 0.0.2') < upd.index('## 0.0.1')
assert '- New feature\n\n\n## 0.0.1' in upd
print(upd)
```

    # Release notes

    <!-- do not remove -->

    ## 0.0.2

    - New feature


    ## 0.0.1

    - First release

Regenerating notes for a version that already has a pending section
gives the same result as generating them fresh:

``` python
upd2 = update_changelog(upd, '0.0.2', '\n## 0.0.2\n\n- Improved feature\n')
test_eq(upd2, update_changelog(log, '0.0.2', '\n## 0.0.2\n\n- Improved feature\n'))
test_eq(upd2.count('## 0.0.2'), 1)
assert 'New feature' not in upd2 and '## 0.0.1' in upd2
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L69"
target="_blank" style="float:right; font-size:smaller">source</a>

### Release

``` python
def Release(
    owner:NoneType=None, repo:NoneType=None, token:NoneType=None, **groups
):
```

*Create CHANGELOG.md from GitHub issues*

To create a markdown changelog, first create a
[`Release`](https://nbdev.fast.ai/api/release.html#release) object,
optionally passing a mapping from GitHub labels to markdown titles. Put
your github token in a file named `token` at the root of your repo.
[`Release`](https://nbdev.fast.ai/api/release.html#release) attempts to
fetch values for arguments from the following locations if not supplied:

- **owner:** fetched from the GitHub URL in `[project.urls]` in
  `pyproject.toml`. This is the owner name of the repository on GitHub.
  For example for the repo `fastai/fastcore` the owner would be
  `fastai`.
- **repo:** fetched from the field `name` in `[project]` in
  `pyproject.toml`. This is the name of the repository on GitHub. For
  example for the repo `fastai/fastcore` the repo would be `fastcore`.
  You can instead pass `"owner/repo"` (e.g. `"fastai/fastcore"`) to set
  both at once.
- **token:** fetched from a file named `token` at the root of your repo.
  Creating a token is discussed in [the
  setup](https://fastrelease.fast.ai/#Set-up) section.
- **groups:** (optional) fetched from the field `label_groups` in
  `[tool.nbdev]` in `pyproject.toml`. This is a mapping from label names
  to titles in your release notes. If not specified, this defaults to:

``` python
{"breaking": "Breaking Changes", "enhancement":"New Features", "bug":"Bugs Squashed"}
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L94"
target="_blank" style="float:right; font-size:smaller">source</a>

### Release.changelog

``` python
async def changelog(
    debug:bool=False, # Just print the latest changes, instead of updating file
):
```

*Create the CHANGELOG.md file, or return the proposed text if `debug` is
`True`*

``` python
rel = Release()
# print(await rel.changelog(debug=True))
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L121"
target="_blank" style="float:right; font-size:smaller">source</a>

### Release.release

``` python
async def release():
```

*Tag and create a release in GitHub for the current version*

This uses the version information from your `__init__.py`. When
releasing from a branch other than the repo’s default branch (e.g. a fix
to an older version), the release is not marked as the repo’s latest on
GitHub.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L132"
target="_blank" style="float:right; font-size:smaller">source</a>

### Release.latest_notes

``` python
def latest_notes():
```

*Latest CHANGELOG entry*

All relevant pull requests and issues are fetched from the GitHub API,
and are categorized according to a user-supplied mapping from labels to
markdown headings.

## CLI functions

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L141"
target="_blank" style="float:right; font-size:smaller">source</a>

### changelog

``` python
async def changelog(
    debug:store_true=False, # Print info to be added to CHANGELOG, instead of updating file
    repo:str=None, # "repo" or "owner/repo" to use instead of pyproject.toml values
    token:str=None, # Optional GitHub token (otherwise `token` file is used)
):
```

*Create a CHANGELOG.md file from closed and labeled GitHub issues*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L151"
target="_blank" style="float:right; font-size:smaller">source</a>

### push_release

``` python
async def push_release(
    token:str=None, repo:str=None
):
```

*Create a GitHub release (changelog should already be committed/pushed).
Returns the release.*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L157"
target="_blank" style="float:right; font-size:smaller">source</a>

### release_git

``` python
async def release_git(
    token:str=None
):
```

*Tag and create a release in GitHub for the current version*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L163"
target="_blank" style="float:right; font-size:smaller">source</a>

### release_gh

``` python
async def release_gh(
    token:str=None, # Optional GitHub token (otherwise `token` file is used)
    repo:str=None, # "repo" or "owner/repo" to use instead of pyproject.toml values
    no_changelog:store_true=False, # Skip changelog creation (assumes CHANGELOG.md is up to date)
    no_editor:store_true=False, # Skip opening CHANGELOG.md in an editor
    yes:store_true=False, # Release without asking for confirmation
):
```

*Create the changelog, optionally edit it, then push and create the
GitHub release*

## Publish Packages

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L193"
target="_blank" style="float:right; font-size:smaller">source</a>

### pypi_json

``` python
def pypi_json(
    s
):
```

*Dictionary decoded JSON for PYPI path `s`*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L198"
target="_blank" style="float:right; font-size:smaller">source</a>

### latest_pypi

``` python
def latest_pypi(
    name
):
```

*Latest version of `name` on pypi*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L204"
target="_blank" style="float:right; font-size:smaller">source</a>

### pypi_details

``` python
def pypi_details(
    name
):
```

*Version, URL, and SHA256 for `name` from pypi*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L226"
target="_blank" style="float:right; font-size:smaller">source</a>

### conda_output_path

``` python
def conda_output_path(
    name, build:str='build'
):
```

*Output path for conda build*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L283"
target="_blank" style="float:right; font-size:smaller">source</a>

### write_conda_meta

``` python
def write_conda_meta(
    path:str='conda'
):
```

*Writes a `meta.yaml` file to the `conda` directory of the current
directory*

This function is used in the `conda_package` CLI command.

**NB**: you need to first of all upload your package to PyPi, before
creating the conda package.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L289"
target="_blank" style="float:right; font-size:smaller">source</a>

### write_requirements

``` python
def write_requirements(
    path:str=''
):
```

*Writes a `requirements.txt` file to `directory` based on
pyproject.toml.*

This function can be used in situations where you need to generate a
`requirements.txt` file for a project.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L299"
target="_blank" style="float:right; font-size:smaller">source</a>

### anaconda_upload

``` python
def anaconda_upload(
    name, loc:NoneType=None, user:NoneType=None, token:NoneType=None, env_token:NoneType=None
):
```

*Upload `name` to anaconda*

``` python
from fastcore.xtras import globtastic
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L311"
target="_blank" style="float:right; font-size:smaller">source</a>

### release_conda

``` python
def release_conda(
    path:str='conda', # Path where package will be created
    do_build:bool_arg=True, # Run `conda build` step
    build_args:str='', # Additional args (as str) to send to `conda build`
    skip_upload:store_true=False, # Skip `anaconda upload` step
    mambabuild:store_true=False, # Use `mambabuild` (requires `boa`)
    upload_user:str=None, # Optional user to upload package to
):
```

*Create a `meta.yaml` file ready to be built into a package, and
optionally build and upload it*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L342"
target="_blank" style="float:right; font-size:smaller">source</a>

### chk_conda_rel

``` python
def chk_conda_rel(
    nm:str, # Package name on pypi
    apkg:str=None, # Anaconda Package (defaults to {nm})
    channel:str='fastai', # Anaconda Channel
    force:store_true=False, # Always return github tag
):
```

*Prints GitHub tag only if a newer release exists on Pypi compared to an
Anaconda Repo.*

To build and upload a conda package, cd to the root of your repo, and
then:

    nbdev-conda-package

Or to do things more manually:

    nbdev-conda-package --do_build false
    cd conda
    conda build --no-anaconda-upload --output-folder build {name}
    anaconda upload build/noarch/{name}-{ver}-*.tar.bz2

Add `--debug` to the `conda build command` to debug any problems that
occur. Note that the build step takes a few minutes. Add `-u {org_name}`
to the `anaconda upload` command if you wish to upload to an
organization, or pass `upload_user` to `nbdev-conda-package`.

**NB**: you need to first of all upload your package to PyPi, before
creating the conda package.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L357"
target="_blank" style="float:right; font-size:smaller">source</a>

### release_pypi

``` python
def release_pypi(
    repository:str='pypi', # Respository to upload to (defined in ~/.pypirc)
    quiet:bool=False, # Reduce output verbosity
    verbose:bool=False, # Pass --verbose to twine upload
):
```

*Create and upload Python package to PyPI*

Use `--repository` flag to upload to [TestPypi](https://test.pypi.org/)
(e.g. `nbdev-pypi --repository testpypi`) and custom/private
repositories.

The
[~/.pypirc](https://packaging.python.org/en/latest/specifications/pypirc/)
file can be updated to configure the additional repositories, see
example below:

``` toml
[distutils]
index-servers =
    pypi
    testpypi
    private-repository

[pypi]
username = __token__
password = <PyPI token>

[testpypi]
username = __token__
password = <TestPyPI token>

[private-repository]
repository = <private-repository URL>
username = <private-repository username>
password = <private-repository password>
```

Use `nbdev-pypi --repository private-repository` to upload to the
private repository.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L372"
target="_blank" style="float:right; font-size:smaller">source</a>

### release_both

``` python
def release_both(
    path:str='conda', # Path where package will be created
    do_build:bool_arg=True, # Run `conda build` step
    build_args:str='', # Additional args (as str) to send to `conda build`
    skip_upload:store_true=False, # Skip `anaconda upload` step
    mambabuild:store_true=False, # Use `mambabuild` (requires `boa`)
    upload_user:str=None, # Optional user to upload package to
    repository:str='pypi', # Pypi respository to upload to (defined in ~/.pypirc)
):
```

*Release both conda and PyPI packages*

## Bump Version

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/nbdev/blob/main/nbdev/release.py#L388"
target="_blank" style="float:right; font-size:smaller">source</a>

### nbdev_bump_version

``` python
def nbdev_bump_version(
    part:int=2, # Part of version to bump
    unbump:bool=False, # Reduce version instead of increasing it
):
```

*Increment version in **init**.py by one*
