Skip to content

Commit

Permalink
Merge pull request #16 from jtpio/finalize
Browse files Browse the repository at this point in the history
Prep for PyPI Release
  • Loading branch information
blink1073 authored May 4, 2021
2 parents 5ed3cfa + 55dcf82 commit f7f8968
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 34 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ on:
release_url:
description: "The URL of the draft GitHub release"
required: true
pypi_registry:
description: "The PYPI registry"
default: https://pypi.org/simple/
npm_registry:
description: "The npm registry"
default: https://registry.npmjs.org/
jobs:
publish_release:
runs-on: ubuntu-latest
Expand All @@ -30,8 +36,11 @@ jobs:
- name: Publish Release
id: publish-release
env:
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }} # use final when ready to publish
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
TWINE_USERNAME: __token__
TWINE_REGISTRY: $${{ github.event.inputs.pypi_registry }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_REGISTRY: $${{ github.event.inputs.npm_registry }}
uses: jupyter-server/jupyter_releaser/.github/actions/publish-release@v1
with:
token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
Expand Down
34 changes: 24 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ See [checklist](#Checklist-for-Adoption) below for details:
- Markdown changelog
- Bump version configuration (if using Python), for example [tbump](https://github.com/dmerejkowsky/tbump)
- [Access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with access to target GitHub repo to run GitHub Actions.
- Access token for the test [PyPI registry](https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#saving-credentials-on-github)
- Access token for the [PyPI registry](https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#saving-credentials-on-github)
- If needed, access token for [npm](https://docs.npmjs.com/creating-and-viewing-access-tokens).

## Typical Workflow
Expand Down Expand Up @@ -191,7 +191,9 @@ A. Prep the `jupyter_releaser` fork:
- [ ] Clone this repository onto your GitHub user account.
- [ ] Add a [GitHub Access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with access to target GitHub repo to run GitHub Actions, saved as
`ADMIN_GITHUB_TOKEN` in the [repository secrets](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository).
- [ ] Add access tokens for the test [PyPI registry](https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#saving-credentials-on-github) stored as `TEST_PYPI_TOKEN`
- [ ] Add access token for the [PyPI registry](https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#saving-credentials-on-github) stored as `PYPI_TOKEN`.
_Note_ For security reasons, it is recommended that you scope the access
to a single repository, and update the value of `PYPI_TOKEN` for each repository that you are releasing.
- [ ] If needed, add access token for [npm](https://docs.npmjs.com/creating-and-viewing-access-tokens), saved as `NPM_TOKEN`.

B. Prep target repository:
Expand All @@ -204,14 +206,21 @@ B. Prep target repository:
- [ ] Add [tbump](https://github.com/tankerhq/tbump) support if using Python - see example metadata in [pyproject.toml](./pyproject.toml)
- We recommend putting `setuptools` metadata in `setup.cfg` and using `version attr: <package_name>.__version__`, see example [`setup.cfg`](./setup.cfg)
- See documentation on `setup.cfg` [metadata](https://setuptools.readthedocs.io/en/latest/userguide/declarative_config.html)
- If previously providing `version_info`, use `get_version_info` from `jupyter_packaging`, since `tbump` requires the intact version string, e.g.
- If previously providing `version_info`, use a snippet like the one below, since `tbump` requires the intact version string, e.g.

```python
from jupyter_packaging import get_version_info
import re

# Version string must appear intact for tbump versioning
__version__ = '1.4.0.dev0'
version_info = get_version_info(__version__)

# Build up version_info tuple for backwards compatibility
pattern = r'(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)'
match = re.match(pattern, __version__)
parts = [int(match[part]) for part in ['major', 'minor', 'patch']]
if match['rest']:
parts.append(match['rest'])
version_info = tuple(parts)
```

- [ ] Add a GitHub Actions CI step to run the `check_release` action. For example:
Expand All @@ -238,6 +247,7 @@ _Note_ The check release action needs `contents: write` [permission](https://doc
- [ ] Optionally add [configuration](#Configuration) to the target repository if non-standard options or hooks are needed.
- [ ] If desired, add `check_release` job, changelog, and `tbump` support to other active release branches
- [ ] Try out the `Draft Changelog` and `Draft Release` process against a fork of the target repo first so you don't accidentally push tags and GitHub releases to the source repository.
- [ ] Try the `Publish Release` process using a prerelease version before publishing a final version.

## Backport Branches

Expand Down Expand Up @@ -303,18 +313,22 @@ Detailed workflows are available to draft a changelog, draft a release, publish
### Check Release Workflow

- Runs on CI in the target repository to verify compatibility and release-ability.
- Runs the `Draft Changelog`, `Draft Release`, and `Publish Release` actions in dry run mode
- Publishes to the Test PyPI server
- Deletes the Release
- Runs the `Draft Changelog` and `Draft Release` actions in dry run mode
- Publishes to the local PyPI server and/or dry-run `npm publish`.
- Does not make PRs or push git changes

## Troubleshooting
## FAQs

### Changelog gets out of sync
### My changelog is out of sync

Create a new manual PR to fix the PR and re-orient the changelog entry markers.

### PR is merged to the target branch in the middle of a "Draft Release"

The release will fail to push commits because it will not be up to date. Delete the pushed tags and re-start with "Draft Changelog" to
pick up the new PR.

## How to keep fork of Jupyter Releaser up to date

The manual workflow files target the `@v1` actions in the source repository, which means that as long as
the workflow files themselves are up to date, you will always be running the most up to date actions.
26 changes: 7 additions & 19 deletions jupyter_releaser/lib.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import atexit
import os
import os.path as osp
import re
Expand All @@ -11,8 +10,6 @@
from datetime import datetime
from glob import glob
from pathlib import Path
from subprocess import PIPE
from subprocess import Popen
from tempfile import TemporaryDirectory

import requests
Expand Down Expand Up @@ -350,26 +347,17 @@ def publish_assets(dist_dir, npm_token, npm_cmd, twine_cmd, dry_run, use_checkou
if dry_run:
# Start local pypi server with no auth, allowing overwrites,
# in a temporary directory
temp_dir = TemporaryDirectory()
cmd = f"pypi-server -p 8081 -P . -a . -o -v {temp_dir.name}"
proc = Popen(shlex.split(cmd), stderr=PIPE)
# Wait for the server to start
while True:
line = proc.stderr.readline().decode("utf-8").strip()
util.log(line)
if "Listening on" in line:
break
atexit.register(proc.kill)
atexit.register(temp_dir.cleanup)
twine_cmd = "twine upload --repository-url=http://localhost:8081"
os.environ["TWINE_USERNAME"] = "foo"
os.environ["TWINE_PASSWORD"] = "bar"
if len(glob(f"{dist_dir}/*.whl")):
python.start_local_pypi()
twine_cmd = "twine upload --repository-url=http://localhost:8081"
os.environ["TWINE_USERNAME"] = "foo"
os.environ["TWINE_PASSWORD"] = "bar"
npm_cmd = "npm publish --dry-run"
else:
os.environ.setdefault("TWINE_USERNAME", "__token__")

if npm_token:
npm.handle_auth_token(npm_token)
if len(glob(f"{dist_dir}/*.tgz")):
npm.handle_npm_config(npm_token)

found = False
for path in glob(f"{dist_dir}/*.*"):
Expand Down
9 changes: 6 additions & 3 deletions jupyter_releaser/npm.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,13 @@ def extract_package(path):
return data


def handle_auth_token(npm_token):
"""Handle token auth for npm registry"""
def handle_npm_config(npm_token):
"""Handle npm_config"""
npmrc = Path(".npmrc")
text = "//registry.npmjs.org/:_authToken={npm_token}"
registry = os.environ.get("NPM_REGISTRY", "registry.npmjs.org")
text = f"registry={registry}"
if npm_token:
text += f"\n///{registry}:_authToken={npm_token}"
if npmrc.exists():
text = npmrc.read_text(encoding="utf-8") + text
npmrc.write_text(text, encoding="utf-8")
Expand Down
19 changes: 19 additions & 0 deletions jupyter_releaser/python.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import atexit
import os
import os.path as osp
import re
import shlex
from glob import glob
from pathlib import Path
from subprocess import PIPE
from subprocess import Popen
from tempfile import TemporaryDirectory

from jupyter_releaser import util
Expand Down Expand Up @@ -54,3 +58,18 @@ def check_dist(dist_file, test_cmd=""):
util.run(f"{bin_path}/python -m pip install -U pip")
util.run(f"{bin_path}/pip install -q {dist_file}")
util.run(f"{bin_path}/{test_cmd}")


def start_local_pypi():
"""Start a local PyPI server"""
temp_dir = TemporaryDirectory()
cmd = f"pypi-server -p 8081 -P . -a . -o -v {temp_dir.name}"
proc = Popen(shlex.split(cmd), stderr=PIPE)
# Wait for the server to start
while True:
line = proc.stderr.readline().decode("utf-8").strip()
util.log(line)
if "Listening on" in line:
break
atexit.register(proc.kill)
atexit.register(temp_dir.cleanup)

0 comments on commit f7f8968

Please sign in to comment.