def build_entry(branch, repo, auth, changelog_path, resolve_backports):
    """Build a python version entry"""
    repo = repo or util.get_repo()
    branch = branch or util.get_branch()

    # Get the new version
    version = util.get_version()

    # Get the existing changelog and run some validation
    changelog = Path(changelog_path).read_text(encoding="utf-8")

    if START_MARKER not in changelog or END_MARKER not in changelog:
        raise ValueError("Missing insert marker for changelog")

    if changelog.find(START_MARKER) != changelog.rfind(START_MARKER):
        raise ValueError("Insert marker appears more than once in changelog")

    # Get changelog entry

    entry = get_version_entry(
        f"origin/{branch}",
        repo,
        version,
        auth=auth,
        resolve_backports=resolve_backports,
    )

    changelog = insert_entry(changelog, entry, version=version)
    Path(changelog_path).write_text(changelog, encoding="utf-8")

    # Stage changelog
    util.run(f"git add {util.normalize_path(changelog_path)}")
def test_create_release_commit(py_package, build_mock):
    util.bump_version("0.0.2a0")
    version = util.get_version()
    util.run("python -m build .")
    shas = util.create_release_commit(version)
    assert util.normalize_path("dist/foo-0.0.2a0.tar.gz") in shas
    assert util.normalize_path("dist/foo-0.0.2a0-py3-none-any.whl") in shas
def test_get_changelog_version_entry(py_package, mocker):
    version = util.get_version()

    mocked_gen = mocker.patch(
        "jupyter_releaser.changelog.generate_activity_md")
    mocked_gen.return_value = testutil.CHANGELOG_ENTRY
    branch = "foo"
    resp = changelog.get_version_entry(branch, "bar/baz", version)
    mocked_gen.assert_called_with("bar/baz",
                                  since=None,
                                  kind="pr",
                                  branch=branch,
                                  heading_level=2,
                                  auth=None)

    assert f"## {version}" in resp
    assert testutil.PR_ENTRY in resp

    mocked_gen.return_value = testutil.CHANGELOG_ENTRY
    resp = changelog.get_version_entry(branch,
                                       "bar/baz",
                                       version,
                                       resolve_backports=True,
                                       auth="bizz")
    mocked_gen.assert_called_with(
        "bar/baz",
        since=None,
        kind="pr",
        branch=branch,
        heading_level=2,
        auth="bizz",
    )

    assert f"## {version}" in resp
    assert testutil.PR_ENTRY in resp
示例#4
0
def bump(force, spec):
    status = run("git status --porcelain").strip()
    if len(status) > 0:
        raise Exception("Must be in a clean git state with no untracked files")

    curr = parse_version(get_version())
    if spec == 'next':
        spec = f"{curr.major}.{curr.minor}."
        if curr.pre:
            p, x = curr.pre
            spec += f"{curr.micro}{p}{x + 1}"
        else:
            spec += f"{curr.micro + 1}"

    version = parse_version(spec)

    # bump the Python package
    python_cmd = f"{TBUMP_CMD} {version}"
    run(python_cmd)

    # convert the Python version
    js_version = f"{version.major}.{version.minor}.{version.micro}"
    if version.pre:
        p, x = version.pre
        p = p.replace("a", "alpha").replace("b", "beta")
        js_version += f"-{p}.{x}"

    # bump the JS packages
    lerna_cmd = f"{LERNA_CMD} {js_version}"
    if force:
        lerna_cmd += " --yes"
    run(lerna_cmd)
示例#5
0
def draft_changelog(version_spec, branch, repo, auth, dry_run):
    """Create a changelog entry PR"""
    repo = repo or util.get_repo()
    branch = branch or util.get_branch()
    version = util.get_version()

    tags = util.run("git --no-pager tag")
    if f"v{version}" in tags.splitlines():
        raise ValueError(f"Tag v{version} already exists")

    # Check out any unstaged files from version bump
    util.run("git checkout -- .")

    title = f"{changelog.PR_PREFIX} for {version} on {branch}"
    commit_message = f'git commit -a -m "{title}"'
    body = title

    # Check for multiple versions
    if util.PACKAGE_JSON.exists():
        body += npm.get_package_versions(version)

    body += '\n\nAfter merging this PR run the "Draft Release" Workflow with the following inputs'
    body += f"""
| Input  | Value |
| ------------- | ------------- |
| Target | {repo}  |
| Branch  | {branch}  |
| Version Spec | {version_spec} |
"""
    util.log(body)

    make_changelog_pr(auth, branch, repo, title, commit_message, body, dry_run=dry_run)
示例#6
0
def check_entry(branch, repo, auth, changelog_path, since, resolve_backports,
                output):
    """Check changelog entry"""
    branch = branch or util.get_branch()

    # Get the new version
    version = util.get_version()

    # Finalize changelog
    changelog = Path(changelog_path).read_text(encoding="utf-8")

    start = changelog.find(START_MARKER)
    end = changelog.find(END_MARKER)

    if start == -1 or end == -1:  # pragma: no cover
        raise ValueError("Missing new changelog entry delimiter(s)")

    if start != changelog.rfind(START_MARKER):  # pragma: no cover
        raise ValueError("Insert marker appears more than once in changelog")

    final_entry = changelog[start + len(START_MARKER):end]

    repo = repo or util.get_repo()

    raw_entry = get_version_entry(
        f"origin/{branch}",
        repo,
        version,
        since=since,
        auth=auth,
        resolve_backports=resolve_backports,
    )

    if f"# {version}" not in final_entry:  # pragma: no cover
        util.log(final_entry)
        raise ValueError(f"Did not find entry for {version}")

    final_prs = re.findall(r"\[#(\d+)\]", final_entry)
    raw_prs = re.findall(r"\[#(\d+)\]", raw_entry)

    for pr in raw_prs:
        # Allow for changelog PR to not be in changelog itself
        skip = False
        for line in raw_entry.splitlines():
            if f"[#{pr}]" in line and "changelog" in line.lower():
                skip = True
                break
        if skip:
            continue
        if not f"[#{pr}]" in final_entry:  # pragma: no cover
            raise ValueError(f"Missing PR #{pr} in changelog")
    for pr in final_prs:
        if not f"[#{pr}]" in raw_entry:  # pragma: no cover
            raise ValueError(
                f"PR #{pr} does not belong in changelog for {version}")

    if output:
        Path(output).write_text(final_entry, encoding="utf-8")
示例#7
0
def tag_release(dist_dir, no_git_tag_workspace):
    """Create release commit and tag"""
    # Get the new version
    version = util.get_version()

    # Create the release commit
    util.create_release_commit(version, dist_dir)

    # Create the annotated release tag
    tag_name = f"v{version}"
    util.run(f'git tag {tag_name} -a -m "Release {tag_name}"')

    # Create annotated release tags for workspace packages if given
    if not no_git_tag_workspace:
        npm.tag_workspace_packages()
示例#8
0
def update(spec, force=False):
    prev = get_version()

    is_final = not is_prerelease(prev)

    if is_final and spec == "release":
        raise Exception(
            'Use "major" or "minor" to switch back to alpha release')

    if is_final and spec == "build":
        raise Exception("Cannot increment a build on a final release")

    # If this is a major release during the alpha cycle, bump
    # just the Python version.
    if "a" in prev and spec == "major":
        run(f"bumpversion {spec}")
        return

    # Determine the version spec to use for lerna.
    lerna_version = "preminor"
    if spec == "build":
        lerna_version = "prerelease"
    # a -> b
    elif spec == "release" and "a" in prev:
        lerna_version = "prerelease --preid=beta"
    # b -> rc
    elif spec == "release" and "b" in prev:
        lerna_version = "prerelease --preid=rc"
    # rc -> final
    elif spec == "release" and "c" in prev:
        lerna_version = "patch"
    if lerna_version == "preminor":
        lerna_version += " --preid=alpha"

    cmd = f"jlpm run lerna version --force-publish --no-push --no-git-tag-version {lerna_version}"
    if force:
        cmd += " --yes"

    # For a preminor release, we bump 10 minor versions so that we do
    # not conflict with versions during minor releases of the top level package.
    if lerna_version == "preminor":
        for i in range(10):
            run(cmd)
    else:
        run(cmd)

    # Bump the version.
    run(f"bumpversion {spec} --allow-dirty")
def test_create_release_commit_hybrid(py_package, build_mock):
    # Add an npm package and test with that
    util.bump_version("0.0.2a0")
    version = util.get_version()
    testutil.create_npm_package(py_package)
    pkg_json = py_package / "package.json"
    data = json.loads(pkg_json.read_text(encoding="utf-8"))
    data["version"] = version
    pkg_json.write_text(json.dumps(data, indent=4), encoding="utf-8")
    txt = (py_package / "tbump.toml").read_text(encoding="utf-8")
    txt += testutil.TBUMP_NPM_TEMPLATE
    (py_package / "tbump.toml").write_text(txt, encoding="utf-8")

    util.run("python -m build .")
    shas = util.create_release_commit(version)
    assert len(shas) == 2
    assert util.normalize_path("dist/foo-0.0.2a0.tar.gz") in shas
示例#10
0
def bump(force, spec):
    status = run("git status --porcelain").strip()
    if len(status) > 0:
        raise Exception("Must be in a clean git state with no untracked files")

    # Make sure we have a valid version spec.
    if spec not in OPTIONS:
        raise ValueError(f"Version spec must be one of: {OPTIONS}")

    prev = get_version()
    is_final = not is_prerelease(prev)
    if spec == "next":
        spec = "patch" if is_final else "build"

    if spec == "patch":
        patch(force)
        return

    update(spec, force)
示例#11
0
def patch(force=False):
    version = get_version()
    if is_prerelease(version):
        raise Exception("Can only make a patch release from a final version")

    run("bumpversion patch", quiet=True)
    # switches to alpha
    run("bumpversion release --allow-dirty", quiet=True)
    # switches to beta
    run("bumpversion release --allow-dirty", quiet=True)
    # switches to rc.
    run("bumpversion release --allow-dirty", quiet=True)
    # switches to final.

    # Version the changed
    cmd = "jlpm run lerna version patch --no-push --force-publish --no-git-tag-version"
    if force:
        cmd += " --yes"
    run(cmd)
def test_get_changelog_version_entry(py_package, mocker):
    version = util.get_version()

    mocked_gen = mocker.patch(
        "jupyter_releaser.changelog.generate_activity_md")
    mocked_gen.return_value = testutil.CHANGELOG_ENTRY
    branch = "foo"
    resp = changelog.get_version_entry(branch, "bar/baz", version)
    until = util.run(
        f'git --no-pager log -n 1 origin/{branch} --pretty=format:"%H"')
    until = until.replace("%", "")
    mocked_gen.assert_called_with(
        "bar/baz",
        since="v0.0.1",
        kind="pr",
        branch=branch,
        heading_level=2,
        auth=None,
        until=until,
    )

    assert f"## {version}" in resp
    assert testutil.PR_ENTRY in resp

    mocked_gen.return_value = testutil.CHANGELOG_ENTRY
    resp = changelog.get_version_entry(branch,
                                       "bar/baz",
                                       version,
                                       resolve_backports=True,
                                       auth="bizz")
    mocked_gen.assert_called_with(
        "bar/baz",
        since="v0.0.1",
        until=until,
        kind="pr",
        branch=branch,
        heading_level=2,
        auth="bizz",
    )

    assert f"## {version}" in resp
    assert testutil.PR_ENTRY in resp
示例#13
0
def bump_version(version_spec, version_cmd):
    """Bump the version and verify new version"""
    util.bump_version(version_spec, version_cmd=version_cmd)

    version = util.get_version()

    # A properly parsed version will have a "major" attribute
    parsed = parse_version(version)

    if util.SETUP_PY.exists() and not hasattr(parsed, "major"):
        raise ValueError(f"Invalid version {version}")

    # Bail if tag already exists
    tag_name = f"v{version}"
    if tag_name in util.run("git --no-pager tag").splitlines():
        msg = f"Tag {tag_name} already exists!"
        msg += " To delete run: `git push --delete origin {tag_name}`"
        raise ValueError(msg)

    return version
def test_get_version_python(py_package):
    assert util.get_version() == "0.0.1"
    util.bump_version("0.0.2a0")
    assert util.get_version() == "0.0.2a0"
示例#15
0
def draft_release(
    ref,
    branch,
    repo,
    auth,
    changelog_path,
    version_cmd,
    dist_dir,
    dry_run,
    post_version_spec,
    assets,
):
    """Publish Draft GitHub release and handle post version bump"""
    branch = branch or util.get_branch()
    repo = repo or util.get_repo()
    assets = assets or glob(f"{dist_dir}/*")
    version = util.get_version()
    body = changelog.extract_current(changelog_path)
    prerelease = util.is_prerelease(version)

    # Bump to post version if given
    if post_version_spec:
        post_version = bump_version(post_version_spec, version_cmd)

        util.log(f"Bumped version to {post_version}")
        util.run(f'git commit -a -m "Bump to {post_version}"')

    if dry_run:
        return

    owner, repo_name = repo.split("/")
    gh = GhApi(owner=owner, repo=repo_name, token=auth)

    # Remove draft releases over a day old
    if bool(os.environ.get("GITHUB_ACTIONS")):
        for release in gh.repos.list_releases():
            if str(release.draft).lower() == "false":
                continue
            created = release.created_at
            d_created = datetime.strptime(created, r"%Y-%m-%dT%H:%M:%SZ")
            delta = datetime.utcnow() - d_created
            if delta.days > 0:
                gh.repos.delete_release(release.id)

    remote_url = util.run("git config --get remote.origin.url")
    if not os.path.exists(remote_url):
        util.run(f"git push origin HEAD:{branch} --follow-tags --tags")

    util.log(f"Creating release for {version}")
    util.log(f"With assets: {assets}")
    release = gh.create_release(
        f"v{version}",
        branch,
        f"Release v{version}",
        body,
        True,
        prerelease,
        files=assets,
    )

    # Set the GitHub action output
    util.actions_output("release_url", release.html_url)
def test_bump_version(npm_package, runner):
    runner(["prep-git", "--git-url", npm_package])
    runner(["bump-version", "--version-spec", "1.0.1-rc0"])
    os.chdir(util.CHECKOUT_NAME)
    version = util.get_version()
    assert version == "1.0.1-rc0"
def test_bump_version(py_package):
    for spec in ["1.0.1", "1.0.1.dev1", "1.0.3a4"]:
        util.bump_version(spec)
        assert util.get_version() == spec
def test_get_version_npm(npm_package):
    assert util.get_version() == "1.0.0"
    npm = util.normalize_path(shutil.which("npm"))
    run(f"{npm} version patch")
    assert util.get_version() == "1.0.1"