Skip to content

Commit

Permalink
Merge pull request #235 from RasaHQ/1.10.x-merge
Browse files Browse the repository at this point in the history
1.10.x merge
  • Loading branch information
erohmensing authored Jun 30, 2020
2 parents e8a4e55 + 0eeda0a commit 6514a8a
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 177 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ This project adheres to `Semantic Versioning`_ starting with version 0.11.0.
.. towncrier release notes start
[1.10.2] - 2020-06-25
^^^^^^^^^^^^^^^^^^^^^

Bugfixes
--------

- Re-added an ``Action endpoint is up and running`` log that was removed in Rasa SDK 1.6.0.


[1.10.1] - 2020-05-11
^^^^^^^^^^^^^^^^^^^^^

Bugfixes
--------
- `#212 <https://github.com/rasahq/rasa/issues/212>`_: Fix ``ActionExecutor`` sometimes not loading user-defined actions that inherit from other user-defined ones.


[1.10.0] - 2020-04-28
^^^^^^^^^^^^^^^^^^^^^

Expand Down
288 changes: 154 additions & 134 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,5 @@ pytest-sanic = "^1.6.1"
questionary = "^1.5.2"
towncrier = "^19.2.0"
toml = "^0.10.0"
pep440-version-utils = "^0.3.0"
semantic_version = "^2.8.5"
2 changes: 2 additions & 0 deletions rasa_sdk/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ def run(
action_package_name, cors_origins=cors_origins, auto_reload=auto_reload
)
ssl_context = create_ssl_context(ssl_certificate, ssl_keyfile, ssl_password)
protocol = "https" if ssl_context else "http"

logger.info(f"Action endpoint is up and running on {protocol}://localhost:{port}")
app.run("0.0.0.0", port, ssl=ssl_context, workers=utils.number_of_sanic_workers())


Expand Down
14 changes: 6 additions & 8 deletions rasa_sdk/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import pkgutil
import warnings
from typing import Text, List, Dict, Any, Type, Union, Callable, Optional
from typing import Text, List, Dict, Any, Type, Union, Callable, Optional, Set
from collections import namedtuple
import types
import sys
Expand Down Expand Up @@ -145,25 +145,23 @@ def utter_image_url(self, image: Text, **kwargs: Any) -> None:


class ActionExecutor:
def __init__(self):
def __init__(self) -> None:
self.actions = {}
self._modules: Dict[Text, TimestampModule] = {}
self._loaded: Set[Type] = set()

def register_action(self, action: Union[Type["Action"], "Action"]) -> None:
if inspect.isclass(action):
if action.__module__.startswith("rasa."):
logger.warning(f"Skipping built in Action {action}.")
return
else:
if getattr(action, "_sdk_loaded", False):
# This Action subclass has already been loaded in the past;
# do not try to load it again as either 1) it is already
# loaded or 2) it has been replaced by a newer version
# already.
# Check if this class has already been loaded in the past.
if action in self._loaded:
return

# Mark the class as "loaded"
action._sdk_loaded = True
self._loaded.add(action)
action = action()

if isinstance(action, Action):
Expand Down
133 changes: 98 additions & 35 deletions scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
"""
import argparse
import os
import re
import sys
from pathlib import Path
from subprocess import CalledProcessError, check_call, check_output
from typing import Text, Set

import questionary
import semantic_version
from semantic_version import Version
from pep440_version_utils import Version, is_valid_version


VERSION_FILE_PATH = "rasa_sdk/version.py"

Expand All @@ -23,6 +24,10 @@

RELEASE_BRANCH_PREFIX = "prepare-release-"

PRERELEASE_FLAVORS = ("alpha", "rc")

RELEASE_BRANCH_PATTERN = re.compile(r"^\d+\.\d+\.x$")


def create_argument_parser() -> argparse.ArgumentParser:
"""Parse all the command line arguments for the release script."""
Expand All @@ -31,7 +36,7 @@ def create_argument_parser() -> argparse.ArgumentParser:
parser.add_argument(
"--next_version",
type=str,
help="Either next version number or 'major', 'minor', 'patch'",
help="Either next version number or 'major', 'minor', 'micro', 'alpha', 'rc'",
)

return parser
Expand All @@ -52,7 +57,7 @@ def pyproject_file_path() -> Path:
return project_root() / PYPROJECT_FILE_PATH


def write_version_file(version: Text) -> None:
def write_version_file(version: Version) -> None:
"""Dump a new version into the python version file."""

with version_file_path().open("w") as f:
Expand All @@ -64,7 +69,7 @@ def write_version_file(version: Text) -> None:
check_call(["git", "add", str(version_file_path().absolute())])


def write_version_to_pyproject(version: Text) -> None:
def write_version_to_pyproject(version: Version) -> None:
"""Dump a new version into the pyproject.toml."""

import toml
Expand All @@ -73,7 +78,7 @@ def write_version_to_pyproject(version: Text) -> None:

try:
data = toml.load(pyproject_file)
data["tool"]["poetry"]["version"] = version
data["tool"]["poetry"]["version"] = str(version)
with pyproject_file.open("w") as f:
toml.dump(data, f)
except (FileNotFoundError, TypeError):
Expand Down Expand Up @@ -104,10 +109,10 @@ def get_current_version() -> Text:
return _globals["__version__"]


def confirm_version(version: Text) -> bool:
def confirm_version(version: Version) -> bool:
"""Allow the user to confirm the version number."""

if version in git_existing_tags():
if str(version) in git_existing_tags():
confirmed = questionary.confirm(
f"Tag with version '{version}' already exists, overwrite?", default=False
).ask()
Expand All @@ -128,19 +133,37 @@ def ask_version() -> Text:
"""Allow the user to confirm the version number."""

def is_valid_version_number(v: Text) -> bool:
# noinspection PyBroadException
try:
return v in {"major", "minor", "patch"} or Version.coerce(v) is not None
except Exception:
# "coerce" did fail, this is probably not a valid version number
return False
return v in {"major", "minor", "micro", "alpha", "rc"} or is_valid_version(v)

current_version = Version(get_current_version())
next_micro_version = str(current_version.next_micro())
next_alpha_version = str(current_version.next_alpha())
version = questionary.text(
"What is the version number you want to release "
"('major', 'minor', 'patch' or valid version number]?",
f"What is the version number you want to release "
f"('major', 'minor', 'micro', 'alpha', 'rc' or valid version number "
f"e.g. '{next_micro_version}' or '{next_alpha_version}')?",
validate=is_valid_version_number,
).ask()

if version in PRERELEASE_FLAVORS and not current_version.pre:
# at this stage it's hard to guess the kind of version bump the
# releaser wants, so we ask them
if version == "alpha":
choices = [
str(current_version.next_alpha("minor")),
str(current_version.next_alpha("micro")),
str(current_version.next_alpha("major")),
]
else:
choices = [
str(current_version.next_release_candidate("minor")),
str(current_version.next_release_candidate("micro")),
str(current_version.next_release_candidate("major")),
]
version = questionary.select(
f"Which {version} do you want to release?", choices=choices,
).ask()

if version:
return version
else:
Expand All @@ -166,15 +189,27 @@ def git_current_branch() -> Text:
return "master"


def create_release_branch(version: Text) -> Text:
def git_current_branch_is_master_or_release() -> bool:
"""
Returns True if the current local git
branch is master or a release branch e.g. 1.10.x
"""
current_branch = git_current_branch()
return (
current_branch == "master"
or RELEASE_BRANCH_PATTERN.match(current_branch) is not None
)


def create_release_branch(version: Version) -> Text:
"""Create a new branch for this release. Returns the branch name."""

branch = f"{RELEASE_BRANCH_PREFIX}{version}"
check_call(["git", "checkout", "-b", branch])
return branch


def create_commit(version: Text) -> None:
def create_commit(version: Version) -> None:
"""Creates a git commit with all stashed changes."""
check_call(["git", "commit", "-m", f"prepared release of version {version}"])

Expand All @@ -194,31 +229,37 @@ def ensure_clean_git() -> None:
sys.exit(1)


def parse_next_version(version: Text) -> Text:
def parse_next_version(version: Text) -> Version:
"""Find the next version as a proper semantic version string."""
if version == "major":
return str(Version.coerce(get_current_version()).next_major())
return Version(get_current_version()).next_major()
elif version == "minor":
return str(Version.coerce(get_current_version()).next_minor())
elif version == "patch":
return str(Version.coerce(get_current_version()).next_patch())
elif semantic_version.validate(version):
return version
return Version(get_current_version()).next_minor()
elif version == "micro":
return Version(get_current_version()).next_micro()
elif version == "alpha":
return Version(get_current_version()).next_alpha()
elif version == "rc":
return Version(get_current_version()).next_release_candidate()
elif is_valid_version(version):
return Version(version)
else:
raise Exception(f"Invalid version number '{cmdline_args.next_version}'.")


def next_version(args: argparse.Namespace) -> Text:
def next_version(args: argparse.Namespace) -> Version:
"""Take cmdline args or ask the user for the next version and return semver."""
return parse_next_version(args.next_version or ask_version())


def generate_changelog(version: Text) -> None:
def generate_changelog(version: Version) -> None:
"""Call towncrier and create a changelog from all available changelog entries."""
check_call(["towncrier", "--yes", "--version", version], cwd=str(project_root()))
check_call(
["towncrier", "--yes", "--version", str(version)], cwd=str(project_root())
)


def print_done_message(branch: Text, base: Text, version: Text) -> None:
def print_done_message(branch: Text, base: Text, version: Version) -> None:
"""Print final information for the user on what to do next."""

pull_request_url = f"{REPO_BASE_URL}/compare/{base}...{branch}?expand=1"
Expand All @@ -229,6 +270,18 @@ def print_done_message(branch: Text, base: Text, version: Text) -> None:
print(f"Please open a PR on GitHub: {pull_request_url}")


def print_done_message_same_branch(version: Version) -> None:
"""
Print final information for the user in case changes
are directly committed on this branch.
"""

print()
print(
f"\033[94m All done - changes for version {version} where committed on this branch \033[0m"
)


def main(args: argparse.Namespace) -> None:
"""Start a release preparation."""

Expand All @@ -244,14 +297,24 @@ def main(args: argparse.Namespace) -> None:
write_version_file(version)
write_version_to_pyproject(version)

generate_changelog(version)
base = git_current_branch()
branch = create_release_branch(version)
if not version.pre:
# never update changelog on a prerelease version
generate_changelog(version)

# alpha workflow on feature branch when a version bump is required
if version.is_alpha and not git_current_branch_is_master_or_release():
create_commit(version)
push_changes()

print_done_message_same_branch(version)
else:
base = git_current_branch()
branch = create_release_branch(version)

create_commit(version)
push_changes()
create_commit(version)
push_changes()

print_done_message(branch, base, version)
print_done_message(branch, base, version)


if __name__ == "__main__":
Expand Down
23 changes: 23 additions & 0 deletions tests/test_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import random
import string
import time

from rasa_sdk import Action
from typing import Text, Optional, Generator

import pytest
Expand Down Expand Up @@ -197,3 +199,24 @@ async def test_reload_module(
"image": None,
"attachment": None,
}


class SubclassTestActionA(Action):
def name(self):
return "subclass_test_action_a"


class SubclassTestActionB(SubclassTestActionA):
def name(self):
return "subclass_test_action_b"


def test_load_subclasses(executor: ActionExecutor):
executor.register_action(SubclassTestActionB)
assert list(executor.actions) == ["subclass_test_action_b"]

executor.register_action(SubclassTestActionA)
assert sorted(list(executor.actions)) == [
"subclass_test_action_a",
"subclass_test_action_b",
]

0 comments on commit 6514a8a

Please sign in to comment.