|
49 | 49 | make_api_bundle,
|
50 | 50 | make_notebook_html_bundle,
|
51 | 51 | make_notebook_source_bundle,
|
| 52 | + make_voila_bundle, |
52 | 53 | read_manifest_app_mode,
|
53 | 54 | write_notebook_manifest_json,
|
54 | 55 | write_api_manifest_json,
|
55 | 56 | write_environment_file,
|
56 | 57 | write_quarto_manifest_json,
|
| 58 | + write_voila_manifest_json, |
57 | 59 | validate_entry_point,
|
58 | 60 | validate_extra_files,
|
59 | 61 | validate_file_is_notebook,
|
@@ -842,6 +844,108 @@ def deploy_notebook(
|
842 | 844 | ce.deploy_bundle().save_deployed_info().emit_task_log()
|
843 | 845 |
|
844 | 846 |
|
| 847 | +# noinspection SpellCheckingInspection,DuplicatedCode |
| 848 | +@deploy.command( |
| 849 | + name="voila", |
| 850 | + short_help="Deploy Jupyter notebook in Voila mode to RStudio Connect [v2023.03.0+].", |
| 851 | + help=("Deploy a Jupyter notebook in Voila mode to RStudio Connect."), |
| 852 | + no_args_is_help=True, |
| 853 | +) |
| 854 | +@server_args |
| 855 | +@content_args |
| 856 | +@click.option( |
| 857 | + "--entrypoint", |
| 858 | + "-e", |
| 859 | + help=("The module and executable object which serves as the entry point."), |
| 860 | +) |
| 861 | +@click.option( |
| 862 | + "--multi-notebook", |
| 863 | + "-m", |
| 864 | + is_flag=True, |
| 865 | + help=("Deploy in multi-notebook mode."), |
| 866 | +) |
| 867 | +@click.option( |
| 868 | + "--exclude", |
| 869 | + "-x", |
| 870 | + multiple=True, |
| 871 | + help=( |
| 872 | + "Specify a glob pattern for ignoring files when building the bundle. Note that your shell may try " |
| 873 | + "to expand this which will not do what you expect. Generally, it's safest to quote the pattern. " |
| 874 | + "This option may be repeated." |
| 875 | + ), |
| 876 | +) |
| 877 | +@click.option( |
| 878 | + "--python", |
| 879 | + "-p", |
| 880 | + type=click.Path(exists=True), |
| 881 | + help=( |
| 882 | + "Path to Python interpreter whose environment should be used. " |
| 883 | + "The Python environment must have the rsconnect package installed." |
| 884 | + ), |
| 885 | +) |
| 886 | +@click.option( |
| 887 | + "--force-generate", |
| 888 | + "-g", |
| 889 | + is_flag=True, |
| 890 | + help='Force generating "requirements.txt", even if it already exists.', |
| 891 | +) |
| 892 | +@click.option( |
| 893 | + "--image", |
| 894 | + "-I", |
| 895 | + help="Target image to be used during content execution (only applicable if the RStudio Connect " |
| 896 | + "server is configured to use off-host execution)", |
| 897 | +) |
| 898 | +@click.argument("path", type=click.Path(exists=True, dir_okay=True, file_okay=True)) |
| 899 | +@click.argument( |
| 900 | + "extra_files", |
| 901 | + nargs=-1, |
| 902 | + type=click.Path(exists=True, dir_okay=False, file_okay=True), |
| 903 | +) |
| 904 | +@cli_exception_handler |
| 905 | +def deploy_voila( |
| 906 | + path: str = None, |
| 907 | + entrypoint: str = None, |
| 908 | + python=None, |
| 909 | + force_generate=False, |
| 910 | + extra_files=None, |
| 911 | + exclude=None, |
| 912 | + image: str = "", |
| 913 | + title: str = None, |
| 914 | + env_vars: typing.Dict[str, str] = None, |
| 915 | + verbose: bool = False, |
| 916 | + new: bool = False, |
| 917 | + app_id: str = None, |
| 918 | + name: str = None, |
| 919 | + server: str = None, |
| 920 | + api_key: str = None, |
| 921 | + insecure: bool = False, |
| 922 | + cacert: typing.IO = None, |
| 923 | + connect_server: api.RSConnectServer = None, |
| 924 | + multi_notebook: bool = False, |
| 925 | +): |
| 926 | + kwargs = locals() |
| 927 | + set_verbosity(verbose) |
| 928 | + app_mode = AppModes.JUPYTER_VOILA |
| 929 | + kwargs["extra_files"] = extra_files = validate_extra_files(dirname(path), extra_files) |
| 930 | + environment = create_python_environment( |
| 931 | + path if isdir(path) else dirname(path), |
| 932 | + force_generate, |
| 933 | + python, |
| 934 | + ) |
| 935 | + ce = RSConnectExecutor(**kwargs).validate_server().validate_app_mode(app_mode=app_mode) |
| 936 | + ce.make_bundle( |
| 937 | + make_voila_bundle, |
| 938 | + path, |
| 939 | + entrypoint, |
| 940 | + extra_files, |
| 941 | + exclude, |
| 942 | + force_generate, |
| 943 | + environment, |
| 944 | + image=image, |
| 945 | + multi_notebook=multi_notebook, |
| 946 | + ).deploy_bundle().save_deployed_info().emit_task_log() |
| 947 | + |
| 948 | + |
845 | 949 | # noinspection SpellCheckingInspection,DuplicatedCode
|
846 | 950 | @deploy.command(
|
847 | 951 | name="manifest",
|
@@ -1358,6 +1462,110 @@ def write_manifest_notebook(
|
1358 | 1462 | write_environment_file(environment, base_dir)
|
1359 | 1463 |
|
1360 | 1464 |
|
| 1465 | +@write_manifest.command( |
| 1466 | + name="voila", |
| 1467 | + short_help="Create a manifest.json file for a Voila notebook.", |
| 1468 | + help=( |
| 1469 | + "Create a manifest.json file for a Voila notebook for later deployment. " |
| 1470 | + 'This will create an environment file ("requirements.txt") if one does ' |
| 1471 | + "not exist. All files are created in the same directory as the notebook file." |
| 1472 | + ), |
| 1473 | +) |
| 1474 | +@click.option("--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists.") |
| 1475 | +@click.option( |
| 1476 | + "--python", |
| 1477 | + "-p", |
| 1478 | + type=click.Path(exists=True), |
| 1479 | + help="Path to Python interpreter whose environment should be used. " |
| 1480 | + + "The Python environment must have the rsconnect package installed.", |
| 1481 | +) |
| 1482 | +@click.option( |
| 1483 | + "--force-generate", |
| 1484 | + "-g", |
| 1485 | + is_flag=True, |
| 1486 | + help='Force generating "requirements.txt", even if it already exists.', |
| 1487 | +) |
| 1488 | +@click.option("--verbose", "-v", "verbose", is_flag=True, help="Print detailed messages") |
| 1489 | +@click.option( |
| 1490 | + "--image", |
| 1491 | + "-I", |
| 1492 | + help="Target image to be used during content execution (only applicable if the RStudio Connect " |
| 1493 | + "server is configured to use off-host execution)", |
| 1494 | +) |
| 1495 | +@click.argument("path", type=click.Path(exists=True, dir_okay=True, file_okay=True)) |
| 1496 | +@click.argument( |
| 1497 | + "extra_files", |
| 1498 | + nargs=-1, |
| 1499 | + type=click.Path(exists=True, dir_okay=False, file_okay=True), |
| 1500 | +) |
| 1501 | +@click.option("--entrypoint", "-e", help=("The module and executable object which serves as the entry point.")) |
| 1502 | +@click.option( |
| 1503 | + "--exclude", |
| 1504 | + "-x", |
| 1505 | + multiple=True, |
| 1506 | + help=( |
| 1507 | + "Specify a glob pattern for ignoring files when building the bundle. Note that your shell may try " |
| 1508 | + "to expand this which will not do what you expect. Generally, it's safest to quote the pattern. " |
| 1509 | + "This option may be repeated." |
| 1510 | + ), |
| 1511 | +) |
| 1512 | +@click.option( |
| 1513 | + "--multi-notebook", |
| 1514 | + "-m", |
| 1515 | + is_flag=True, |
| 1516 | + help=("Set the manifest for multi-notebook mode."), |
| 1517 | +) |
| 1518 | +def write_manifest_voila( |
| 1519 | + path: str, |
| 1520 | + entrypoint: str, |
| 1521 | + overwrite, |
| 1522 | + python, |
| 1523 | + force_generate, |
| 1524 | + verbose, |
| 1525 | + extra_files, |
| 1526 | + exclude, |
| 1527 | + image, |
| 1528 | + multi_notebook, |
| 1529 | +): |
| 1530 | + set_verbosity(verbose) |
| 1531 | + with cli_feedback("Checking arguments"): |
| 1532 | + base_dir = dirname(path) |
| 1533 | + extra_files = validate_extra_files(base_dir, extra_files) |
| 1534 | + manifest_path = join(base_dir, "manifest.json") |
| 1535 | + |
| 1536 | + if exists(manifest_path) and not overwrite: |
| 1537 | + raise RSConnectException("manifest.json already exists. Use --overwrite to overwrite.") |
| 1538 | + |
| 1539 | + with cli_feedback("Inspecting Python environment"): |
| 1540 | + python, environment = get_python_env_info(path, python, False, force_generate) |
| 1541 | + |
| 1542 | + _warn_on_ignored_conda_env(environment) |
| 1543 | + |
| 1544 | + environment_file_exists = exists(join(base_dir, environment.filename)) |
| 1545 | + |
| 1546 | + if environment_file_exists and not force_generate: |
| 1547 | + click.secho( |
| 1548 | + " Warning: %s already exists and will not be overwritten." % environment.filename, |
| 1549 | + fg="yellow", |
| 1550 | + ) |
| 1551 | + else: |
| 1552 | + with cli_feedback("Creating %s" % environment.filename): |
| 1553 | + write_environment_file(environment, base_dir) |
| 1554 | + |
| 1555 | + with cli_feedback("Creating manifest.json"): |
| 1556 | + write_voila_manifest_json( |
| 1557 | + path, |
| 1558 | + entrypoint, |
| 1559 | + environment, |
| 1560 | + AppModes.JUPYTER_VOILA, |
| 1561 | + extra_files, |
| 1562 | + exclude, |
| 1563 | + force_generate, |
| 1564 | + image, |
| 1565 | + multi_notebook, |
| 1566 | + ) |
| 1567 | + |
| 1568 | + |
1361 | 1569 | @write_manifest.command(
|
1362 | 1570 | name="quarto",
|
1363 | 1571 | short_help="Create a manifest.json file for Quarto content.",
|
|
0 commit comments