コード例 #1
0
def test_git(capfd: Capture, faker: Faker) -> None:

    create_project(
        capfd=capfd,
        name=random_project_name(faker),
    )
    init_project(capfd)

    assert git.get_repo("does/not/exist") is None
    do_repo = git.get_repo("submodules/do")
    assert do_repo is not None
    assert git.get_active_branch(None) is None
    assert git.get_active_branch(do_repo) == __version__
    assert not git.switch_branch(None, branch_name="0.7.3")
    # Same branch => no change => return True
    assert git.switch_branch(do_repo, branch_name=__version__)
    assert not git.switch_branch(do_repo, branch_name="XYZ")

    assert git.switch_branch(do_repo, branch_name="0.7.3")
    assert git.get_active_branch(do_repo) == "0.7.3"
    assert git.switch_branch(do_repo, branch_name=__version__)
    assert git.get_active_branch(do_repo) == __version__

    assert git.get_origin(None) is None

    r = git.get_repo(".")
    assert git.get_origin(r) == "https://your_remote_git/your_project.git"

    # Create an invalid repo (i.e. without any remote)
    r = git.init("../justatest")
    assert git.get_origin(r) is None
コード例 #2
0
    def git_submodules(from_path: Optional[Path] = None) -> None:
        """Check and/or clone git projects"""

        submodules = (Configuration.specs.get("variables",
                                              {}).get("submodules", {}).copy())

        main_repo = git.get_repo(".")
        # This is to reassure mypy, but this is check is already done
        # in preliminary checks, so it can never happen
        if not main_repo:  # pragma: no cover
            print_and_exit("Current folder is not a git main_repository")

        Application.gits["main"] = main_repo

        for name, submodule in submodules.items():
            repo = Application.working_clone(name,
                                             submodule,
                                             from_path=from_path)
            if repo:
                Application.gits[name] = repo
コード例 #3
0
    def inspect_main_folder(self, folder: Path) -> Optional[str]:
        """
        RAPyDo commands only works on rapydo projects, we want to ensure that
        the current folder have a rapydo-like structure. These checks are based
        on file existence. Further checks are performed in the following steps
        """

        r = git.get_repo(str(folder))
        if r is None or git.get_origin(r) is None:
            return f"""You are not in a git repository
\nPlease note that this command only works from inside a rapydo-like repository
Verify that you are in the right folder, now you are in: {Path.cwd()}
                """

        for fpath in self.expected_main_folders:
            if not folder.joinpath(fpath).is_dir():

                return f"""Folder not found: {fpath}
\nPlease note that this command only works from inside a rapydo-like repository
Verify that you are in the right folder, now you are in: {Path.cwd()}
                    """

        return None
コード例 #4
0
def create(
    project_name: str = typer.Argument(..., help="Name of your project"),
    auth: AuthTypes = typer.Option(...,
                                   "--auth",
                                   help="Auth service to enable"),
    frontend: FrontendTypes = typer.Option(
        ..., "--frontend", help="Frontend framework to enable"),
    extend: str = typer.Option(None,
                               "--extend",
                               help="Extend from another project"),
    services: List[ServiceTypes] = typer.Option(
        [],
        "--service",
        "-s",
        help="Service to be enabled (multiple is enabled)",
        shell_complete=Application.autocomplete_service,
    ),
    origin_url: Optional[str] = typer.Option(
        None, "--origin-url", help="Set the git origin url for the project"),
    envs: List[str] = typer.Option(
        None,
        "--env",
        "-e",
        help=
        "Command separated list of ENV=VALUE to be added in project_configuration",
    ),
    force_current: bool = typer.Option(
        False,
        "--current",
        help="Force creation in current folder",
        show_default=False,
    ),
    force: bool = typer.Option(
        False,
        "--force",
        help="Force files overwriting",
        show_default=False,
    ),
    auto: bool = typer.Option(
        True,
        "--no-auto",
        help="Disable automatic project creation",
        show_default=False,
    ),
    add_optionals: bool = typer.Option(
        False,
        "--add-optionals",
        help="Include all optionals files (html templates and customizers)",
        show_default=False,
    ),
) -> None:

    Application.print_command(
        Application.serialize_parameter("--auth", auth),
        Application.serialize_parameter("--frontend", frontend),
        Application.serialize_parameter("--extend", extend, IF=extend),
        Application.serialize_parameter("--service", services),
        Application.serialize_parameter("--origin-url",
                                        origin_url,
                                        IF=origin_url),
        Application.serialize_parameter("--env", envs),
        Application.serialize_parameter("--current",
                                        force_current,
                                        IF=force_current),
        Application.serialize_parameter("--force", force, IF=force),
        Application.serialize_parameter("--auto", auto, IF=auto),
        Application.serialize_parameter("--add-optionals", add_optionals),
        Application.serialize_parameter("", project_name),
    )

    Application.get_controller().controller_init()

    if extend is not None:
        if project_name == extend:
            print_and_exit("A project cannot extend itself")

        if not PROJECT_DIR.joinpath(extend).is_dir():
            print_and_exit("Invalid extend value: project {} not found",
                           extend)

    services_list: List[str] = [service.value for service in services]
    create_project(
        project_name=project_name,
        auth=auth.value,
        frontend=frontend.value,
        services=services_list,
        extend=extend,
        envs=envs,
        auto=auto,
        force=force,
        force_current=force_current,
        add_optionals=add_optionals,
    )

    log.info("Project {} successfully created", project_name)

    git_repo = git.get_repo(".")
    if git_repo is None:
        git_repo = git.init(".")

    print("\nYou can now init and start the project:\n")
    current_origin = git.get_origin(git_repo)

    if current_origin is None:
        if origin_url is None:  # pragma: no cover
            print(
                "git remote add origin https://your_remote_git/your_project.git"
            )
        else:
            git_repo.create_remote("origin", origin_url)

    print("rapydo init")
    print("rapydo pull")
    print("rapydo start")
コード例 #5
0
def test_base(capfd: Capture) -> None:

    execute_outside(capfd, "check")

    create_project(
        capfd=capfd,
        name="third",
        auth="postgres",
        frontend="angular",
    )
    init_project(capfd)

    repo = git.get_repo("submodules/http-api")
    git.switch_branch(repo, "0.7.6")
    exec_command(
        capfd,
        "check -i main",
        f"http-api: wrong branch 0.7.6, expected {__version__}",
        f"You can fix it with {colors.RED}rapydo init{colors.RESET}",
    )
    init_project(capfd)

    with TemporaryRemovePath(DATA_DIR):
        exec_command(
            capfd,
            "check -i main --no-git --no-builds",
            "Folder not found: data",
            "Please note that this command only works from inside a rapydo-like repo",
            "Verify that you are in the right folder, now you are in: ",
        )

    with TemporaryRemovePath(Path("projects/third/builds")):
        exec_command(
            capfd,
            "check -i main --no-git --no-builds",
            "Project third is invalid: required folder not found projects/third/builds",
        )

    with TemporaryRemovePath(Path(".gitignore")):
        exec_command(
            capfd,
            "check -i main --no-git --no-builds",
            "Project third is invalid: required file not found .gitignore",
        )

    # Add a custom image to extend base backend image:
    with open("projects/third/confs/commons.yml", "a") as f:
        f.write(
            """
services:
  backend:
    build: ${PROJECT_DIR}/builds/backend
    image: third/backend:${RAPYDO_VERSION}

    """
        )

    os.makedirs("projects/third/builds/backend")
    with open("projects/third/builds/backend/Dockerfile", "w+") as f:
        f.write(
            f"""
FROM rapydo/backend:{__version__}
RUN mkdir xyz
"""
        )

    # Skipping main because we are on a fake git repository
    exec_command(
        capfd,
        "check -i main",
        f" image, execute {colors.RED}rapydo pull",
        f" image, execute {colors.RED}rapydo build",
        f"Compose is installed with version {COMPOSE_VERSION}",
        f"Buildx is installed with version {BUILDX_VERSION}",
        "Checks completed",
    )

    exec_command(
        capfd,
        "--stack invalid check -i main",
        "Failed to read projects/third/confs/invalid.yml: File does not exist",
    )

    os.mkdir("submodules/rapydo-confs")
    exec_command(
        capfd,
        "check -i main --no-git --no-builds",
        "Project third contains an obsolete file or folder: submodules/rapydo-confs",
    )

    shutil.rmtree("submodules/rapydo-confs")

    # Test selection with two projects
    create_project(
        capfd=capfd,
        name="justanother",
        auth="postgres",
        frontend="no",
    )

    os.remove(".projectrc")

    exec_command(
        capfd,
        "check -i main --no-git --no-builds",
        "Multiple projects found, please use --project to specify one of the following",
    )

    # Test with zero projects
    with TemporaryRemovePath(Path("projects")):
        os.mkdir("projects")
        # in this case SystemExit is raised in the command init...
        with pytest.raises(SystemExit):
            exec_command(
                capfd,
                "check -i main --no-git --no-builds",
                "No project found (is projects folder empty?)",
            )
        shutil.rmtree("projects")

    exec_command(
        capfd,
        "-p third check -i main --no-git --no-builds",
        "Checks completed",
    )

    # Numbers are not allowed as first characters
    pname = "2invalidcharacter"
    os.makedirs(f"projects/{pname}")
    exec_command(
        capfd,
        f"-p {pname} check -i main --no-git --no-builds",
        "Wrong project name, found invalid characters: 2",
    )
    shutil.rmtree(f"projects/{pname}")

    invalid_characters = {
        "_": "_",
        "-": "-",
        "C": "C",
        # Invalid characters in output are ordered
        # Numbers are allowed if not leading
        "_C-2": "-C_",
    }
    # Check invalid and reserved project names
    for invalid_key, invalid_value in invalid_characters.items():
        pname = f"invalid{invalid_key}character"
        os.makedirs(f"projects/{pname}")
        exec_command(
            capfd,
            f"-p {pname} check -i main --no-git --no-builds",
            f"Wrong project name, found invalid characters: {invalid_value}",
        )
        shutil.rmtree(f"projects/{pname}")

    os.makedirs("projects/celery")
    exec_command(
        capfd,
        "-p celery check -i main --no-git --no-builds",
        "You selected a reserved name, invalid project name: celery",
    )
    shutil.rmtree("projects/celery")

    exec_command(
        capfd,
        "-p fourth check -i main --no-git --no-builds",
        "Wrong project fourth",
        "Select one of the following: ",
    )

    # Test init of data folders
    shutil.rmtree(LOGS_FOLDER)
    assert not LOGS_FOLDER.is_dir()
    # Let's restore .projectrc and data/logs
    init_project(capfd, "--project third")

    assert LOGS_FOLDER.is_dir()
    exec_command(
        capfd,
        "check -i main --no-git --no-builds",
        "Checks completed",
    )

    # Test dirty repo
    fin = open("submodules/do/new_file", "wt+")
    fin.write("xyz")
    fin.close()

    exec_command(
        capfd,
        "check -i main",
        "You have unstaged files on do",
        "Untracked files:",
        "submodules/do/new_file",
    )

    with open(".gitattributes", "a") as a_file:
        a_file.write("\n")
        a_file.write("# new line")

    exec_command(
        capfd,
        "check -i main",
        ".gitattributes changed, "
        f"please execute {colors.RED}rapydo upgrade --path .gitattributes",
    )

    exec_command(
        capfd,
        "--prod check -i main --no-git --no-builds",
        "The following variables are missing in your configuration",
        "You can fix this error by updating your .projectrc file",
    )

    # Default ALCHEMY_PASSWORD has as score of 2
    exec_command(
        capfd,
        "-e MIN_PASSWORD_SCORE=3 check -i main --no-git --no-builds",
        "The password used in ALCHEMY_PASSWORD is weak",
    )
    exec_command(
        capfd,
        "-e MIN_PASSWORD_SCORE=4 check -i main --no-git --no-builds",
        "The password used in ALCHEMY_PASSWORD is very weak",
    )
    exec_command(
        capfd,
        "-e MIN_PASSWORD_SCORE=4 -e AUTH_DEFAULT_PASSWORD=x check -i main --no-git --no-builds",
        "The password used in AUTH_DEFAULT_PASSWORD is extremely weak",
    )

    exec_command(
        capfd,
        "--prod init -f",
        "Created default .projectrc file",
        "Project initialized",
    )

    exec_command(
        capfd,
        "--prod check -i main --no-git --no-builds",
        "Checks completed",
    )

    if Configuration.swarm_mode:
        # Skipping main because we are on a fake git repository
        exec_command(
            capfd,
            "check -i main",
            "Swarm is correctly initialized",
            "Checks completed",
        )

        docker = Docker()
        docker.client.swarm.leave(force=True)

        exec_command(
            capfd,
            "check -i main",
            f"Swarm is not initialized, please execute {colors.RED}rapydo init",
        )
        exec_command(
            capfd,
            "init",
            "Swarm is now initialized",
            "Project initialized",
        )
        exec_command(
            capfd,
            "check -i main",
            "Swarm is correctly initialized",
            "Checks completed",
        )

        check = "check -i main --no-git --no-builds"

        exec_command(
            capfd,
            f"-e ASSIGNED_MEMORY_BACKEND=50G {check}",
            "Your deployment requires 50GB of RAM but your nodes only have",
            # The error does not halt the checks execution
            "Checks completed",
        )

        exec_command(
            capfd,
            f"-e ASSIGNED_CPU_BACKEND=50.0 {check}",
            "Your deployment requires ",
            " cpus but your nodes only have ",
            # The error does not halt the checks execution
            "Checks completed",
        )

        exec_command(
            capfd,
            f"-e DEFAULT_SCALE_BACKEND=55 -e ASSIGNED_MEMORY_BACKEND=1G {check}",
            "Your deployment requires 55GB of RAM but your nodes only have",
            # The error does not halt the checks execution
            "Checks completed",
        )

        exec_command(
            capfd,
            f"-e DEFAULT_SCALE_BACKEND=50 -e ASSIGNED_CPU_BACKEND=1.0 {check}",
            "Your deployment requires ",
            " cpus but your nodes only have ",
            # The error does not halt the checks execution
            "Checks completed",
        )
コード例 #6
0
def test_init(capfd: Capture, faker: Faker) -> None:

    execute_outside(capfd, "init")
    create_project(
        capfd=capfd,
        name=random_project_name(faker),
        auth="postgres",
        frontend="no",
    )

    exec_command(
        capfd,
        "check -i main",
        "Repo https://github.com/rapydo/http-api.git missing as submodules/http-api.",
        "You should init your project",
    )

    if Configuration.swarm_mode:
        exec_command(
            capfd,
            "-e HEALTHCHECK_INTERVAL=1s -e SWARM_MANAGER_ADDRESS=127.0.0.1 init",
            "docker compose is installed",
            "Initializing Swarm with manager IP 127.0.0.1",
            "Swarm is now initialized",
            "Project initialized",
        )

        docker = Docker()
        docker.client.swarm.leave(force=True)
        local_ip = system.get_local_ip(production=False)
        exec_command(
            capfd,
            "-e HEALTHCHECK_INTERVAL=1s -e SWARM_MANAGER_ADDRESS= init",
            "docker compose is installed",
            "Swarm is now initialized",
            f"Initializing Swarm with manager IP {local_ip}",
            "Project initialized",
        )

        exec_command(
            capfd,
            "init",
            "Swarm is already initialized",
            "Project initialized",
        )

    else:
        init_project(capfd)

    repo = git.get_repo("submodules/http-api")
    git.switch_branch(repo, "0.7.6")

    exec_command(
        capfd,
        "init",
        f"Switched http-api branch from 0.7.6 to {__version__}",
        f"build-templates already set on branch {__version__}",
        f"do already set on branch {__version__}",
    )

    os.rename("submodules", "submodules.bak")
    os.mkdir("submodules")

    # This is to re-fill the submodules folder,
    # these folder will be removed by the next init
    exec_command(capfd, "init", "Project initialized")

    modules_path = Path("submodules.bak").resolve()

    with TemporaryRemovePath(Path("submodules.bak/do")):
        exec_command(
            capfd,
            f"init --submodules-path {modules_path}",
            "Submodule do not found in ",
        )
    exec_command(
        capfd,
        f"init --submodules-path {modules_path}",
        "Path submodules/http-api already exists, removing",
        "Project initialized",
    )

    assert os.path.islink("submodules/do")
    assert not os.path.islink("submodules.bak/do")

    # Init again, this time in submodules there are links...
    # and will be removed as well as the folders
    exec_command(
        capfd,
        f"init --submodules-path {modules_path}",
        "Path submodules/http-api already exists, removing",
        "Project initialized",
    )

    exec_command(
        capfd,
        "init --submodules-path invalid/path",
        "Local path not found: invalid/path",
    )

    exec_command(
        capfd,
        "--prod init -f",
        "Created default .projectrc file",
        "Project initialized",
    )

    exec_command(
        capfd,
        "--prod -e MYVAR=MYVAL init -f",
        "Created default .projectrc file",
        "Project initialized",
    )

    with open(".projectrc") as projectrc:
        lines = [line.strip() for line in projectrc.readlines()]
        assert "MYVAR: MYVAL" in lines
コード例 #7
0
def test_install(capfd: Capture, faker: Faker) -> None:

    execute_outside(capfd, "install")

    project = random_project_name(faker)
    create_project(
        capfd=capfd,
        name=project,
        auth="postgres",
        frontend="no",
    )
    init_project(capfd)

    # Initially the controller is installed from pip
    exec_command(
        capfd,
        "update -i main",
        "Controller not updated because it is installed outside this project",
        "Installation path is ",
        ", the current folder is ",
        "All updated",
    )

    with TemporaryRemovePath(SUBMODULES_DIR.joinpath("do")):
        exec_command(
            capfd,
            "install",
            "missing as submodules/do. You should init your project",
        )

    exec_command(capfd, "install 100.0", "Invalid version")

    exec_command(capfd, "install docker", "Docker current version:",
                 "Docker installed version:")
    exec_command(capfd, "install compose", "Docker compose is installed")
    exec_command(
        capfd,
        "install buildx",
        "Docker buildx current version:",
        "Docker buildx installed version:",
    )

    exec_command(capfd, "install auto")

    r = git.get_repo("submodules/do")
    git.switch_branch(r, "0.7.6")

    exec_command(
        capfd,
        "install",
        f"Controller repository switched to {__version__}",
    )

    # Here the controller is installed in editable mode from the correct submodules
    # folder (this is exactly the default normal condition)
    exec_command(
        capfd,
        "update -i main",
        # Controller installed from {} and updated
        "Controller installed from ",
        " and updated",
        "All updated",
    )

    # Install the controller from a linked folder to verify that the post-update checks
    # are able to correctly resolve symlinks
    # ###########################################################
    # Copied from test_init_check_update.py from here...
    SUBMODULES_DIR.rename("submodules.bak")
    SUBMODULES_DIR.mkdir()

    # This is to re-fill the submodules folder,
    # these folder will be removed by the next init
    exec_command(capfd, "init", "Project initialized")

    modules_path = Path("submodules.bak").resolve()

    exec_command(
        capfd,
        f"init --submodules-path {modules_path}",
        "Path submodules/http-api already exists, removing",
        "Project initialized",
    )
    # ... to here
    # ###########################################################
    exec_command(
        capfd,
        "update -i main",
        # Controller installed from {} and updated
        "Controller installed from ",
        " and updated",
        "All updated",
    )

    # This test will change the required version
    pconf = f"projects/{project}/project_configuration.yaml"

    # Read and change the content
    fin = open(pconf)
    data = fin.read()
    data = data.replace(f'rapydo: "{__version__}"', 'rapydo: "0.7.6"')
    fin.close()
    # Write the new content
    fin = open(pconf, "wt")
    fin.write(data)
    fin.close()

    exec_command(
        capfd,
        "version",
        f"This project is not compatible with rapydo version {__version__}",
        "Please downgrade rapydo to version 0.7.6 or modify this project",
    )

    # Read and change the content
    fin = open(pconf)
    data = fin.read()
    data = data.replace('rapydo: "0.7.6"', 'rapydo: "99.99.99"')
    fin.close()
    # Write the new content
    fin = open(pconf, "wt")
    fin.write(data)
    fin.close()

    exec_command(
        capfd,
        "version",
        f"This project is not compatible with rapydo version {__version__}",
        "Please upgrade rapydo to version 99.99.99 or modify this project",
    )

    exec_command(capfd, "install --no-editable 0.8")

    exec_command(capfd, "install --no-editable")

    exec_command(capfd, "install")