pybiolib 0.2.951__py3-none-any.whl → 1.2.1890__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- biolib/__init__.py +357 -11
- biolib/_data_record/data_record.py +380 -0
- biolib/_index/__init__.py +0 -0
- biolib/_index/index.py +55 -0
- biolib/_index/query_result.py +103 -0
- biolib/_internal/__init__.py +0 -0
- biolib/_internal/add_copilot_prompts.py +58 -0
- biolib/_internal/add_gui_files.py +81 -0
- biolib/_internal/data_record/__init__.py +1 -0
- biolib/_internal/data_record/data_record.py +85 -0
- biolib/_internal/data_record/push_data.py +116 -0
- biolib/_internal/data_record/remote_storage_endpoint.py +43 -0
- biolib/_internal/errors.py +5 -0
- biolib/_internal/file_utils.py +125 -0
- biolib/_internal/fuse_mount/__init__.py +1 -0
- biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
- biolib/_internal/http_client.py +159 -0
- biolib/_internal/lfs/__init__.py +1 -0
- biolib/_internal/lfs/cache.py +51 -0
- biolib/_internal/libs/__init__.py +1 -0
- biolib/_internal/libs/fusepy/__init__.py +1257 -0
- biolib/_internal/push_application.py +488 -0
- biolib/_internal/runtime.py +22 -0
- biolib/_internal/string_utils.py +13 -0
- biolib/_internal/templates/__init__.py +1 -0
- biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +10 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +20 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +16 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +11 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +12 -0
- biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
- biolib/_internal/templates/github_workflow_template/.github/workflows/biolib.yml +21 -0
- biolib/_internal/templates/gitignore_template/.gitignore +10 -0
- biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
- biolib/_internal/templates/gui_template/App.tsx +53 -0
- biolib/_internal/templates/gui_template/Dockerfile +27 -0
- biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
- biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
- biolib/_internal/templates/gui_template/index.css +5 -0
- biolib/_internal/templates/gui_template/index.html +13 -0
- biolib/_internal/templates/gui_template/index.tsx +10 -0
- biolib/_internal/templates/gui_template/package.json +27 -0
- biolib/_internal/templates/gui_template/tsconfig.json +24 -0
- biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
- biolib/_internal/templates/gui_template/vite.config.mts +10 -0
- biolib/_internal/templates/init_template/.biolib/config.yml +19 -0
- biolib/_internal/templates/init_template/Dockerfile +14 -0
- biolib/_internal/templates/init_template/requirements.txt +1 -0
- biolib/_internal/templates/init_template/run.py +12 -0
- biolib/_internal/templates/init_template/run.sh +4 -0
- biolib/_internal/templates/templates.py +25 -0
- biolib/_internal/tree_utils.py +106 -0
- biolib/_internal/utils/__init__.py +65 -0
- biolib/_internal/utils/auth.py +46 -0
- biolib/_internal/utils/job_url.py +33 -0
- biolib/_internal/utils/multinode.py +263 -0
- biolib/_runtime/runtime.py +157 -0
- biolib/_session/session.py +44 -0
- biolib/_shared/__init__.py +0 -0
- biolib/_shared/types/__init__.py +74 -0
- biolib/_shared/types/account.py +12 -0
- biolib/_shared/types/account_member.py +8 -0
- biolib/_shared/types/app.py +9 -0
- biolib/_shared/types/data_record.py +40 -0
- biolib/_shared/types/experiment.py +32 -0
- biolib/_shared/types/file_node.py +17 -0
- biolib/_shared/types/push.py +6 -0
- biolib/_shared/types/resource.py +37 -0
- biolib/_shared/types/resource_deploy_key.py +11 -0
- biolib/_shared/types/resource_permission.py +14 -0
- biolib/_shared/types/resource_version.py +19 -0
- biolib/_shared/types/result.py +14 -0
- biolib/_shared/types/typing.py +10 -0
- biolib/_shared/types/user.py +19 -0
- biolib/_shared/utils/__init__.py +7 -0
- biolib/_shared/utils/resource_uri.py +75 -0
- biolib/api/__init__.py +6 -0
- biolib/api/client.py +168 -0
- biolib/app/app.py +252 -49
- biolib/app/search_apps.py +45 -0
- biolib/biolib_api_client/api_client.py +126 -31
- biolib/biolib_api_client/app_types.py +24 -4
- biolib/biolib_api_client/auth.py +31 -8
- biolib/biolib_api_client/biolib_app_api.py +147 -52
- biolib/biolib_api_client/biolib_job_api.py +161 -141
- biolib/biolib_api_client/job_types.py +21 -5
- biolib/biolib_api_client/lfs_types.py +7 -23
- biolib/biolib_api_client/user_state.py +56 -0
- biolib/biolib_binary_format/__init__.py +1 -4
- biolib/biolib_binary_format/file_in_container.py +105 -0
- biolib/biolib_binary_format/module_input.py +24 -7
- biolib/biolib_binary_format/module_output_v2.py +149 -0
- biolib/biolib_binary_format/remote_endpoints.py +34 -0
- biolib/biolib_binary_format/remote_stream_seeker.py +59 -0
- biolib/biolib_binary_format/saved_job.py +3 -2
- biolib/biolib_binary_format/{attestation_document.py → stdout_and_stderr.py} +8 -8
- biolib/biolib_binary_format/system_status_update.py +3 -2
- biolib/biolib_binary_format/utils.py +175 -0
- biolib/biolib_docker_client/__init__.py +11 -2
- biolib/biolib_errors.py +36 -0
- biolib/biolib_logging.py +27 -10
- biolib/cli/__init__.py +38 -0
- biolib/cli/auth.py +46 -0
- biolib/cli/data_record.py +164 -0
- biolib/cli/index.py +32 -0
- biolib/cli/init.py +421 -0
- biolib/cli/lfs.py +101 -0
- biolib/cli/push.py +50 -0
- biolib/cli/run.py +63 -0
- biolib/cli/runtime.py +14 -0
- biolib/cli/sdk.py +16 -0
- biolib/cli/start.py +56 -0
- biolib/compute_node/cloud_utils/cloud_utils.py +110 -161
- biolib/compute_node/job_worker/cache_state.py +66 -88
- biolib/compute_node/job_worker/cache_types.py +1 -6
- biolib/compute_node/job_worker/docker_image_cache.py +112 -37
- biolib/compute_node/job_worker/executors/__init__.py +0 -3
- biolib/compute_node/job_worker/executors/docker_executor.py +532 -199
- biolib/compute_node/job_worker/executors/docker_types.py +9 -1
- biolib/compute_node/job_worker/executors/types.py +19 -9
- biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +30 -0
- biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +3 -5
- biolib/compute_node/job_worker/job_storage.py +108 -0
- biolib/compute_node/job_worker/job_worker.py +397 -212
- biolib/compute_node/job_worker/large_file_system.py +87 -38
- biolib/compute_node/job_worker/network_alloc.py +99 -0
- biolib/compute_node/job_worker/network_buffer.py +240 -0
- biolib/compute_node/job_worker/utilization_reporter_thread.py +197 -0
- biolib/compute_node/job_worker/utils.py +9 -24
- biolib/compute_node/remote_host_proxy.py +400 -98
- biolib/compute_node/utils.py +31 -9
- biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
- biolib/compute_node/webserver/proxy_utils.py +28 -0
- biolib/compute_node/webserver/webserver.py +130 -44
- biolib/compute_node/webserver/webserver_types.py +2 -6
- biolib/compute_node/webserver/webserver_utils.py +77 -12
- biolib/compute_node/webserver/worker_thread.py +183 -42
- biolib/experiments/__init__.py +0 -0
- biolib/experiments/experiment.py +356 -0
- biolib/jobs/__init__.py +1 -0
- biolib/jobs/job.py +741 -0
- biolib/jobs/job_result.py +185 -0
- biolib/jobs/types.py +50 -0
- biolib/py.typed +0 -0
- biolib/runtime/__init__.py +14 -0
- biolib/sdk/__init__.py +91 -0
- biolib/tables.py +34 -0
- biolib/typing_utils.py +2 -7
- biolib/user/__init__.py +1 -0
- biolib/user/sign_in.py +54 -0
- biolib/utils/__init__.py +162 -0
- biolib/utils/cache_state.py +94 -0
- biolib/utils/multipart_uploader.py +194 -0
- biolib/utils/seq_util.py +150 -0
- biolib/utils/zip/remote_zip.py +640 -0
- pybiolib-1.2.1890.dist-info/METADATA +41 -0
- pybiolib-1.2.1890.dist-info/RECORD +177 -0
- {pybiolib-0.2.951.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
- pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
- README.md +0 -17
- biolib/app/app_result.py +0 -68
- biolib/app/utils.py +0 -62
- biolib/biolib-js/0-biolib.worker.js +0 -1
- biolib/biolib-js/1-biolib.worker.js +0 -1
- biolib/biolib-js/2-biolib.worker.js +0 -1
- biolib/biolib-js/3-biolib.worker.js +0 -1
- biolib/biolib-js/4-biolib.worker.js +0 -1
- biolib/biolib-js/5-biolib.worker.js +0 -1
- biolib/biolib-js/6-biolib.worker.js +0 -1
- biolib/biolib-js/index.html +0 -10
- biolib/biolib-js/main-biolib.js +0 -1
- biolib/biolib_api_client/biolib_account_api.py +0 -21
- biolib/biolib_api_client/biolib_large_file_system_api.py +0 -108
- biolib/biolib_binary_format/aes_encrypted_package.py +0 -42
- biolib/biolib_binary_format/module_output.py +0 -58
- biolib/biolib_binary_format/rsa_encrypted_aes_package.py +0 -57
- biolib/biolib_push.py +0 -114
- biolib/cli.py +0 -203
- biolib/cli_utils.py +0 -273
- biolib/compute_node/cloud_utils/enclave_parent_types.py +0 -7
- biolib/compute_node/enclave/__init__.py +0 -2
- biolib/compute_node/enclave/enclave_remote_hosts.py +0 -53
- biolib/compute_node/enclave/nitro_secure_module_utils.py +0 -64
- biolib/compute_node/job_worker/executors/base_executor.py +0 -18
- biolib/compute_node/job_worker/executors/pyppeteer_executor.py +0 -173
- biolib/compute_node/job_worker/executors/remote/__init__.py +0 -1
- biolib/compute_node/job_worker/executors/remote/nitro_enclave_utils.py +0 -81
- biolib/compute_node/job_worker/executors/remote/remote_executor.py +0 -51
- biolib/lfs.py +0 -196
- biolib/pyppeteer/.circleci/config.yml +0 -100
- biolib/pyppeteer/.coveragerc +0 -3
- biolib/pyppeteer/.gitignore +0 -89
- biolib/pyppeteer/.pre-commit-config.yaml +0 -28
- biolib/pyppeteer/CHANGES.md +0 -253
- biolib/pyppeteer/CONTRIBUTING.md +0 -26
- biolib/pyppeteer/LICENSE +0 -12
- biolib/pyppeteer/README.md +0 -137
- biolib/pyppeteer/docs/Makefile +0 -177
- biolib/pyppeteer/docs/_static/custom.css +0 -28
- biolib/pyppeteer/docs/_templates/layout.html +0 -10
- biolib/pyppeteer/docs/changes.md +0 -1
- biolib/pyppeteer/docs/conf.py +0 -299
- biolib/pyppeteer/docs/index.md +0 -21
- biolib/pyppeteer/docs/make.bat +0 -242
- biolib/pyppeteer/docs/reference.md +0 -211
- biolib/pyppeteer/docs/server.py +0 -60
- biolib/pyppeteer/poetry.lock +0 -1699
- biolib/pyppeteer/pyppeteer/__init__.py +0 -135
- biolib/pyppeteer/pyppeteer/accessibility.py +0 -286
- biolib/pyppeteer/pyppeteer/browser.py +0 -401
- biolib/pyppeteer/pyppeteer/browser_fetcher.py +0 -194
- biolib/pyppeteer/pyppeteer/command.py +0 -22
- biolib/pyppeteer/pyppeteer/connection/__init__.py +0 -242
- biolib/pyppeteer/pyppeteer/connection/cdpsession.py +0 -101
- biolib/pyppeteer/pyppeteer/coverage.py +0 -346
- biolib/pyppeteer/pyppeteer/device_descriptors.py +0 -787
- biolib/pyppeteer/pyppeteer/dialog.py +0 -79
- biolib/pyppeteer/pyppeteer/domworld.py +0 -597
- biolib/pyppeteer/pyppeteer/emulation_manager.py +0 -53
- biolib/pyppeteer/pyppeteer/errors.py +0 -48
- biolib/pyppeteer/pyppeteer/events.py +0 -63
- biolib/pyppeteer/pyppeteer/execution_context.py +0 -156
- biolib/pyppeteer/pyppeteer/frame/__init__.py +0 -299
- biolib/pyppeteer/pyppeteer/frame/frame_manager.py +0 -306
- biolib/pyppeteer/pyppeteer/helpers.py +0 -245
- biolib/pyppeteer/pyppeteer/input.py +0 -371
- biolib/pyppeteer/pyppeteer/jshandle.py +0 -598
- biolib/pyppeteer/pyppeteer/launcher.py +0 -683
- biolib/pyppeteer/pyppeteer/lifecycle_watcher.py +0 -169
- biolib/pyppeteer/pyppeteer/models/__init__.py +0 -103
- biolib/pyppeteer/pyppeteer/models/_protocol.py +0 -12460
- biolib/pyppeteer/pyppeteer/multimap.py +0 -82
- biolib/pyppeteer/pyppeteer/network_manager.py +0 -678
- biolib/pyppeteer/pyppeteer/options.py +0 -8
- biolib/pyppeteer/pyppeteer/page.py +0 -1728
- biolib/pyppeteer/pyppeteer/pipe_transport.py +0 -59
- biolib/pyppeteer/pyppeteer/target.py +0 -147
- biolib/pyppeteer/pyppeteer/task_queue.py +0 -24
- biolib/pyppeteer/pyppeteer/timeout_settings.py +0 -36
- biolib/pyppeteer/pyppeteer/tracing.py +0 -93
- biolib/pyppeteer/pyppeteer/us_keyboard_layout.py +0 -305
- biolib/pyppeteer/pyppeteer/util.py +0 -18
- biolib/pyppeteer/pyppeteer/websocket_transport.py +0 -47
- biolib/pyppeteer/pyppeteer/worker.py +0 -101
- biolib/pyppeteer/pyproject.toml +0 -97
- biolib/pyppeteer/spell.txt +0 -137
- biolib/pyppeteer/tox.ini +0 -72
- biolib/pyppeteer/utils/generate_protocol_types.py +0 -603
- biolib/start_cli.py +0 -7
- biolib/utils.py +0 -47
- biolib/validators/validate_app_version.py +0 -183
- biolib/validators/validate_argument.py +0 -134
- biolib/validators/validate_module.py +0 -323
- biolib/validators/validate_zip_file.py +0 -40
- biolib/validators/validator_utils.py +0 -103
- pybiolib-0.2.951.dist-info/LICENSE +0 -21
- pybiolib-0.2.951.dist-info/METADATA +0 -61
- pybiolib-0.2.951.dist-info/RECORD +0 -153
- pybiolib-0.2.951.dist-info/entry_points.txt +0 -3
- /LICENSE → /pybiolib-1.2.1890.dist-info/licenses/LICENSE +0 -0
biolib/cli/init.py
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from biolib import (
|
|
9
|
+
biolib_errors,
|
|
10
|
+
utils, # Import like this to let BASE_URL_IS_PUBLIC_BIOLIB be set correctly
|
|
11
|
+
)
|
|
12
|
+
from biolib._internal.add_copilot_prompts import add_copilot_prompts
|
|
13
|
+
from biolib._internal.add_gui_files import add_gui_files
|
|
14
|
+
from biolib._internal.http_client import HttpClient, HttpError
|
|
15
|
+
from biolib._internal.string_utils import normalize_for_docker_tag
|
|
16
|
+
from biolib._internal.templates import templates
|
|
17
|
+
from biolib._internal.utils import get_pip_command
|
|
18
|
+
from biolib.api import client as api_client
|
|
19
|
+
from biolib.biolib_api_client.api_client import BiolibApiClient
|
|
20
|
+
from biolib.biolib_api_client.biolib_app_api import BiolibAppApi
|
|
21
|
+
from biolib.biolib_logging import logger_no_user_data
|
|
22
|
+
from biolib.typing_utils import Dict, List, Optional, Set
|
|
23
|
+
from biolib.user.sign_in import sign_in
|
|
24
|
+
from biolib.utils import BIOLIB_PACKAGE_VERSION
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_latest_pypi_version() -> Optional[str]:
|
|
28
|
+
try:
|
|
29
|
+
response = HttpClient.request(
|
|
30
|
+
url='https://pypi.org/pypi/pybiolib/json',
|
|
31
|
+
timeout_in_seconds=5,
|
|
32
|
+
retries=1,
|
|
33
|
+
)
|
|
34
|
+
data = response.json()
|
|
35
|
+
version = data.get('info', {}).get('version')
|
|
36
|
+
if isinstance(version, str):
|
|
37
|
+
return version
|
|
38
|
+
return None
|
|
39
|
+
except Exception as error:
|
|
40
|
+
logger_no_user_data.debug(f'Failed to fetch latest version from PyPI: {error}')
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _is_current_version_outdated(current: str, latest: str) -> bool:
|
|
45
|
+
try:
|
|
46
|
+
current_parts = [int(x) for x in current.split('.')]
|
|
47
|
+
latest_parts = [int(x) for x in latest.split('.')]
|
|
48
|
+
return current_parts < latest_parts
|
|
49
|
+
except (ValueError, AttributeError):
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _check_version_and_prompt_upgrade() -> bool:
|
|
54
|
+
latest_version = _get_latest_pypi_version()
|
|
55
|
+
if latest_version and _is_current_version_outdated(BIOLIB_PACKAGE_VERSION, latest_version):
|
|
56
|
+
print(f'A newer version of pybiolib is available: {latest_version} (current: {BIOLIB_PACKAGE_VERSION})')
|
|
57
|
+
pip_command = get_pip_command()
|
|
58
|
+
print(f'To upgrade, run: {pip_command} install --upgrade pybiolib')
|
|
59
|
+
print()
|
|
60
|
+
continue_input = input('Do you want to continue with the current version? [y/N]: ')
|
|
61
|
+
if continue_input.lower() not in ['y', 'yes']:
|
|
62
|
+
print('Please upgrade pybiolib and run `biolib init` again.')
|
|
63
|
+
return False
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _prompt_for_app_uri(prompt_text: str) -> Optional[str]:
|
|
68
|
+
app_uri = input(prompt_text)
|
|
69
|
+
|
|
70
|
+
if app_uri and not app_uri.startswith('@'):
|
|
71
|
+
try:
|
|
72
|
+
response = api_client.get('system/enterprise/config/', authenticate=False)
|
|
73
|
+
config = response.json()
|
|
74
|
+
prefix = config.get('resource_hostname_prefix')
|
|
75
|
+
if prefix:
|
|
76
|
+
app_uri = f'@{prefix}/{app_uri}'
|
|
77
|
+
print(f'Detected enterprise deployment, using URI: {app_uri}')
|
|
78
|
+
except HttpError as error:
|
|
79
|
+
if error.code not in [404, 501]:
|
|
80
|
+
print(f'Warning: Could not detect enterprise configuration: {error}')
|
|
81
|
+
except Exception as error:
|
|
82
|
+
print(f'Warning: Could not detect enterprise configuration: {error}')
|
|
83
|
+
|
|
84
|
+
return app_uri if app_uri else None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _validate_or_create_app(app_uri: str) -> bool:
|
|
88
|
+
try:
|
|
89
|
+
if BiolibApiClient.is_reauthentication_needed():
|
|
90
|
+
sign_in_input = input('You need to sign in to validate/create apps. Would you like to sign in? [y/N]: ')
|
|
91
|
+
if sign_in_input.lower() in ['y', 'yes']:
|
|
92
|
+
sign_in()
|
|
93
|
+
else:
|
|
94
|
+
print('Skipping app validation and creation. You can set the URI in .biolib/config.yml later.')
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
BiolibAppApi.get_by_uri(app_uri)
|
|
98
|
+
print(f'App {app_uri} already exists.')
|
|
99
|
+
except biolib_errors.NotFound:
|
|
100
|
+
create_app_input = input(f'App {app_uri} does not exist. Would you like to create it? [y/N]: ')
|
|
101
|
+
if create_app_input.lower() in ['y', 'yes']:
|
|
102
|
+
try:
|
|
103
|
+
BiolibAppApi.create_app(app_uri)
|
|
104
|
+
print(f'Successfully created app {app_uri}')
|
|
105
|
+
except Exception as error:
|
|
106
|
+
print(f'Failed to create app {app_uri}: {str(error)}')
|
|
107
|
+
print('You can create the app manually later or set the URI in .biolib/config.yml')
|
|
108
|
+
else:
|
|
109
|
+
print('App creation skipped. You can create the app manually later or set the URI in .biolib/config.yml')
|
|
110
|
+
except Exception as error:
|
|
111
|
+
print(f'Failed to validate app {app_uri}: {str(error)}')
|
|
112
|
+
print('Continuing with initialization...')
|
|
113
|
+
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _find_conflicting_files(template_dir: str, cwd: str) -> List[str]:
|
|
118
|
+
conflicting_files: List[str] = []
|
|
119
|
+
for root, dirs, filenames in os.walk(template_dir):
|
|
120
|
+
dirs[:] = [d for d in dirs if '__pycache__' not in d]
|
|
121
|
+
relative_dir = os.path.relpath(root, template_dir)
|
|
122
|
+
destination_dir = cwd if relative_dir == '.' else os.path.join(cwd, relative_dir)
|
|
123
|
+
|
|
124
|
+
for filename in filenames:
|
|
125
|
+
source_file = os.path.join(root, filename)
|
|
126
|
+
destination_file = os.path.join(destination_dir, filename)
|
|
127
|
+
if os.path.exists(destination_file):
|
|
128
|
+
with open(source_file, 'rb') as fsrc, open(destination_file, 'rb') as fdest:
|
|
129
|
+
if fsrc.read() != fdest.read():
|
|
130
|
+
conflicting_files.append(os.path.relpath(destination_file, cwd))
|
|
131
|
+
return conflicting_files
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _prompt_for_overwrites(conflicting_files: List[str]) -> Set[str]:
|
|
135
|
+
files_to_overwrite: Set[str] = set()
|
|
136
|
+
if conflicting_files:
|
|
137
|
+
print('The following files already exist and would be overwritten:')
|
|
138
|
+
for conflicting_file in conflicting_files:
|
|
139
|
+
print(f' {conflicting_file}')
|
|
140
|
+
print()
|
|
141
|
+
|
|
142
|
+
for conflicting_file in conflicting_files:
|
|
143
|
+
choice = input(f'Overwrite {conflicting_file}? [y/N]: ').lower().strip()
|
|
144
|
+
if choice in ['y', 'yes']:
|
|
145
|
+
files_to_overwrite.add(conflicting_file)
|
|
146
|
+
return files_to_overwrite
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _copy_template_files(
|
|
150
|
+
template_dir: str,
|
|
151
|
+
cwd: str,
|
|
152
|
+
files_to_overwrite: Set[str],
|
|
153
|
+
replacements: Dict[str, str],
|
|
154
|
+
) -> None:
|
|
155
|
+
for root, dirs, filenames in os.walk(template_dir):
|
|
156
|
+
dirs[:] = [d for d in dirs if '__pycache__' not in d]
|
|
157
|
+
relative_dir = os.path.relpath(root, template_dir)
|
|
158
|
+
destination_dir = os.path.join(cwd, relative_dir)
|
|
159
|
+
|
|
160
|
+
os.makedirs(destination_dir, exist_ok=True)
|
|
161
|
+
|
|
162
|
+
for filename in filenames:
|
|
163
|
+
if utils.BASE_URL_IS_PUBLIC_BIOLIB and filename == 'biolib.yml':
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
source_file = os.path.join(root, filename)
|
|
167
|
+
destination_file = os.path.join(destination_dir, filename)
|
|
168
|
+
relative_file_path = os.path.relpath(destination_file, cwd)
|
|
169
|
+
|
|
170
|
+
if not os.path.exists(destination_file) or relative_file_path in files_to_overwrite:
|
|
171
|
+
try:
|
|
172
|
+
with open(source_file) as f:
|
|
173
|
+
content = f.read()
|
|
174
|
+
|
|
175
|
+
new_content = content
|
|
176
|
+
for old_value, new_value in replacements.items():
|
|
177
|
+
new_content = new_content.replace(old_value, new_value)
|
|
178
|
+
|
|
179
|
+
with open(destination_file, 'w') as f:
|
|
180
|
+
f.write(new_content)
|
|
181
|
+
except UnicodeDecodeError:
|
|
182
|
+
shutil.copy2(source_file, destination_file)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _create_readme_if_needed(cwd: str, app_name: Optional[str]) -> None:
|
|
186
|
+
readme_path = os.path.join(cwd, 'README.md')
|
|
187
|
+
if not os.path.exists(readme_path) and app_name:
|
|
188
|
+
with open(readme_path, 'w') as readme_file:
|
|
189
|
+
readme_file.write(f'# {app_name}\n')
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _copy_gitignore(cwd: str) -> None:
|
|
193
|
+
gitignore_template_dir = templates.gitignore_template()
|
|
194
|
+
source_file = os.path.join(gitignore_template_dir, '.gitignore')
|
|
195
|
+
destination_file = os.path.join(cwd, '.gitignore')
|
|
196
|
+
if not os.path.exists(destination_file):
|
|
197
|
+
shutil.copy2(source_file, destination_file)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _copy_github_workflow(cwd: str, replacements: Dict[str, str]) -> None:
|
|
201
|
+
workflow_template_dir = templates.github_workflow_template()
|
|
202
|
+
source_file = os.path.join(workflow_template_dir, '.github', 'workflows', 'biolib.yml')
|
|
203
|
+
destination_dir = os.path.join(cwd, '.github', 'workflows')
|
|
204
|
+
destination_file = os.path.join(destination_dir, 'biolib.yml')
|
|
205
|
+
|
|
206
|
+
if not os.path.exists(destination_file):
|
|
207
|
+
os.makedirs(destination_dir, exist_ok=True)
|
|
208
|
+
with open(source_file) as f:
|
|
209
|
+
content = f.read()
|
|
210
|
+
for old_value, new_value in replacements.items():
|
|
211
|
+
content = content.replace(old_value, new_value)
|
|
212
|
+
with open(destination_file, 'w') as f:
|
|
213
|
+
f.write(content)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _prompt_and_run_yarn_install() -> None:
|
|
217
|
+
yarn_install_input = input('Do you want to run yarn install? [Y/n]: ')
|
|
218
|
+
if yarn_install_input.lower() not in ['n', 'no']:
|
|
219
|
+
print('Running yarn install...')
|
|
220
|
+
try:
|
|
221
|
+
subprocess.run(['yarn', 'install'], check=True)
|
|
222
|
+
print('yarn install completed successfully.')
|
|
223
|
+
except FileNotFoundError:
|
|
224
|
+
print(
|
|
225
|
+
'Error: yarn is not installed or not found in PATH. Please install yarn and run yarn install manually.'
|
|
226
|
+
)
|
|
227
|
+
except subprocess.CalledProcessError as error:
|
|
228
|
+
print(f'yarn install failed with exit code {error.returncode}. Please run yarn install manually.')
|
|
229
|
+
except Exception as error:
|
|
230
|
+
print(f'yarn install failed: {error}. Please run yarn install manually.')
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _create_dashboard_dockerfile(cwd: str) -> None:
|
|
234
|
+
gui_template_dir = templates.gui_template()
|
|
235
|
+
gui_dockerfile = os.path.join(gui_template_dir, 'Dockerfile')
|
|
236
|
+
destination_file = os.path.join(cwd, 'Dockerfile')
|
|
237
|
+
|
|
238
|
+
if not os.path.exists(destination_file):
|
|
239
|
+
with open(gui_dockerfile) as f:
|
|
240
|
+
lines = f.readlines()
|
|
241
|
+
|
|
242
|
+
gui_builder_lines = []
|
|
243
|
+
for line in lines:
|
|
244
|
+
gui_builder_lines.append(line)
|
|
245
|
+
if line.strip() == 'RUN yarn build':
|
|
246
|
+
break
|
|
247
|
+
|
|
248
|
+
dist_export_stage = '\nFROM scratch AS dist_export\nCOPY --from=gui_builder /home/biolib/gui/dist /dist\n'
|
|
249
|
+
with open(destination_file, 'w') as f:
|
|
250
|
+
f.writelines(gui_builder_lines)
|
|
251
|
+
f.write(dist_export_stage)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _copy_gui_files_for_dashboard(cwd: str, app_name: str) -> None:
|
|
255
|
+
gui_template_dir = templates.gui_template()
|
|
256
|
+
gui_root_files = ['package.json', 'vite.config.mts', '.yarnrc.yml']
|
|
257
|
+
|
|
258
|
+
for root, _, filenames in os.walk(gui_template_dir):
|
|
259
|
+
relative_dir = os.path.relpath(root, gui_template_dir)
|
|
260
|
+
|
|
261
|
+
for filename in filenames:
|
|
262
|
+
if filename == 'Dockerfile':
|
|
263
|
+
continue
|
|
264
|
+
|
|
265
|
+
if filename in gui_root_files:
|
|
266
|
+
destination_dir = cwd
|
|
267
|
+
else:
|
|
268
|
+
if relative_dir == '.':
|
|
269
|
+
destination_dir = os.path.join(cwd, 'gui')
|
|
270
|
+
else:
|
|
271
|
+
destination_dir = os.path.join(cwd, 'gui', relative_dir)
|
|
272
|
+
|
|
273
|
+
source_file = os.path.join(root, filename)
|
|
274
|
+
destination_file = os.path.join(destination_dir, filename)
|
|
275
|
+
|
|
276
|
+
if not os.path.exists(destination_file):
|
|
277
|
+
os.makedirs(destination_dir, exist_ok=True)
|
|
278
|
+
try:
|
|
279
|
+
with open(source_file) as f:
|
|
280
|
+
content = f.read()
|
|
281
|
+
new_content = content.replace('BIOLIB_REPLACE_APP_NAME', app_name)
|
|
282
|
+
with open(destination_file, 'w') as f:
|
|
283
|
+
f.write(new_content)
|
|
284
|
+
except UnicodeDecodeError:
|
|
285
|
+
shutil.copy2(source_file, destination_file)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _init_dashboard() -> None:
|
|
289
|
+
if not _check_version_and_prompt_upgrade():
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
cwd = os.getcwd()
|
|
293
|
+
app_uri = _prompt_for_app_uri('What URI do you want to create the dashboard under? (leave blank to skip): ')
|
|
294
|
+
app_name = app_uri.split('/')[-1] if app_uri else None
|
|
295
|
+
|
|
296
|
+
if app_uri:
|
|
297
|
+
if not _validate_or_create_app(app_uri):
|
|
298
|
+
return
|
|
299
|
+
else:
|
|
300
|
+
print(
|
|
301
|
+
'Remember to set the app URI in the .biolib/config.yml file later, '
|
|
302
|
+
'and update the .github/workflows/biolib.yml file.'
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
dashboard_template_dir = templates.dashboard_template()
|
|
306
|
+
|
|
307
|
+
try:
|
|
308
|
+
conflicting_files = _find_conflicting_files(dashboard_template_dir, cwd)
|
|
309
|
+
files_to_overwrite = _prompt_for_overwrites(conflicting_files)
|
|
310
|
+
|
|
311
|
+
replace_app_uri = app_uri if app_uri else 'PUT_APP_URI_HERE'
|
|
312
|
+
replace_app_name = app_name if app_name else 'biolib-dashboard'
|
|
313
|
+
|
|
314
|
+
replacements = {
|
|
315
|
+
'BIOLIB_REPLACE_APP_URI': replace_app_uri,
|
|
316
|
+
'BIOLIB_REPLACE_APP_NAME': replace_app_name,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
_copy_template_files(dashboard_template_dir, cwd, files_to_overwrite, replacements)
|
|
320
|
+
_create_readme_if_needed(cwd, app_name)
|
|
321
|
+
_copy_gitignore(cwd)
|
|
322
|
+
_copy_github_workflow(
|
|
323
|
+
cwd,
|
|
324
|
+
{
|
|
325
|
+
'BIOLIB_REPLACE_APP_URI': replace_app_uri,
|
|
326
|
+
'BIOLIB_REPLACE_BUILD_COMMAND': 'docker build --target dist_export -o type=local,dest=. .',
|
|
327
|
+
},
|
|
328
|
+
)
|
|
329
|
+
_create_dashboard_dockerfile(cwd)
|
|
330
|
+
_copy_gui_files_for_dashboard(cwd, replace_app_name)
|
|
331
|
+
_prompt_and_run_yarn_install()
|
|
332
|
+
|
|
333
|
+
print('Dashboard template initialized successfully.')
|
|
334
|
+
|
|
335
|
+
except KeyboardInterrupt:
|
|
336
|
+
print('\nInit command cancelled.', file=sys.stderr)
|
|
337
|
+
exit(1)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
@click.command(help='Initialize a BioLib project', hidden=True)
|
|
341
|
+
@click.argument('template_type', required=False, default=None)
|
|
342
|
+
def init(template_type: Optional[str]) -> None:
|
|
343
|
+
if template_type == 'dashboard':
|
|
344
|
+
_init_dashboard()
|
|
345
|
+
return
|
|
346
|
+
|
|
347
|
+
if template_type is not None:
|
|
348
|
+
print(f"Unknown template type: '{template_type}'. Available templates: dashboard")
|
|
349
|
+
print('Run `biolib init` without arguments for the default Python application template.')
|
|
350
|
+
return
|
|
351
|
+
|
|
352
|
+
if not _check_version_and_prompt_upgrade():
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
cwd = os.getcwd()
|
|
356
|
+
app_uri = _prompt_for_app_uri('What URI do you want to create the application under? (leave blank to skip): ')
|
|
357
|
+
app_name = app_uri.split('/')[-1] if app_uri else None
|
|
358
|
+
docker_tag = normalize_for_docker_tag(app_name) if app_name else None
|
|
359
|
+
|
|
360
|
+
if app_uri:
|
|
361
|
+
if not _validate_or_create_app(app_uri):
|
|
362
|
+
return
|
|
363
|
+
else:
|
|
364
|
+
print(
|
|
365
|
+
'Remember to set the app URI in the .biolib/config.yml file later, '
|
|
366
|
+
'and docker image name in the .biolib/config.yml and .github/workflows/biolib.yml files.'
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
advanced_setup_input = input('Do you want to set up advanced features like Copilot and GUI? [y/N]: ')
|
|
370
|
+
advanced_setup = advanced_setup_input.lower() == 'y'
|
|
371
|
+
include_copilot = False
|
|
372
|
+
include_gui = False
|
|
373
|
+
if advanced_setup:
|
|
374
|
+
copilot_enabled_input = input('Do you want to include Copilot instructions and prompts? [y/N]: ')
|
|
375
|
+
include_copilot = copilot_enabled_input.lower() == 'y'
|
|
376
|
+
include_gui_input = input('Do you want to include GUI setup? [y/N]: ')
|
|
377
|
+
include_gui = include_gui_input.lower() == 'y'
|
|
378
|
+
|
|
379
|
+
init_template_dir = templates.init_template()
|
|
380
|
+
|
|
381
|
+
try:
|
|
382
|
+
conflicting_files = _find_conflicting_files(init_template_dir, cwd)
|
|
383
|
+
files_to_overwrite = _prompt_for_overwrites(conflicting_files)
|
|
384
|
+
|
|
385
|
+
replace_app_uri = app_uri if app_uri else 'PUT_APP_URI_HERE'
|
|
386
|
+
replace_app_name = app_name if app_name else 'biolib-app'
|
|
387
|
+
|
|
388
|
+
gui_config = "main_output_file: '/result.html'\n" if include_gui else ''
|
|
389
|
+
gui_mv_command = 'mv result.html output/result.html\n' if include_gui else ''
|
|
390
|
+
|
|
391
|
+
replacements = {
|
|
392
|
+
'BIOLIB_REPLACE_PYBIOLIB_VERSION': BIOLIB_PACKAGE_VERSION,
|
|
393
|
+
'BIOLIB_REPLACE_APP_URI': replace_app_uri,
|
|
394
|
+
'BIOLIB_REPLACE_DOCKER_TAG': docker_tag if docker_tag else 'PUT_DOCKER_TAG_HERE',
|
|
395
|
+
'BIOLIB_REPLACE_APP_NAME': replace_app_name,
|
|
396
|
+
'BIOLIB_REPLACE_GUI_CONFIG\n': gui_config,
|
|
397
|
+
'BIOLIB_REPLACE_GUI_MV_COMMAND\n': gui_mv_command,
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
_copy_template_files(init_template_dir, cwd, files_to_overwrite, replacements)
|
|
401
|
+
_create_readme_if_needed(cwd, app_name)
|
|
402
|
+
_copy_gitignore(cwd)
|
|
403
|
+
build_tag = docker_tag if docker_tag else 'PUT_DOCKER_TAG_HERE'
|
|
404
|
+
_copy_github_workflow(
|
|
405
|
+
cwd,
|
|
406
|
+
{
|
|
407
|
+
'BIOLIB_REPLACE_APP_URI': replace_app_uri,
|
|
408
|
+
'BIOLIB_REPLACE_BUILD_COMMAND': f'docker build -t {build_tag}:latest .',
|
|
409
|
+
},
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
if include_copilot:
|
|
413
|
+
add_copilot_prompts(force=False, silent=True)
|
|
414
|
+
|
|
415
|
+
if include_gui:
|
|
416
|
+
add_gui_files(force=False, silent=True)
|
|
417
|
+
_prompt_and_run_yarn_install()
|
|
418
|
+
|
|
419
|
+
except KeyboardInterrupt:
|
|
420
|
+
print('\nInit command cancelled.', file=sys.stderr)
|
|
421
|
+
exit(1)
|
biolib/cli/lfs.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from biolib import biolib_errors
|
|
10
|
+
from biolib._data_record.data_record import DataRecord
|
|
11
|
+
from biolib._internal.lfs import prune_lfs_cache
|
|
12
|
+
from biolib.biolib_logging import logger, logger_no_user_data
|
|
13
|
+
from biolib.typing_utils import Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group(help='Manage Large File Systems', hidden=True)
|
|
17
|
+
def lfs() -> None:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@lfs.command(help='Create a Large File System')
|
|
22
|
+
@click.argument('uri', required=True)
|
|
23
|
+
def create(uri: str) -> None:
|
|
24
|
+
logger.warning('This is command deprecated, please use "biolib data-record create" instead.')
|
|
25
|
+
logger.configure(default_log_level=logging.INFO)
|
|
26
|
+
logger_no_user_data.configure(default_log_level=logging.INFO)
|
|
27
|
+
DataRecord.create(destination=uri)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@lfs.command(help='Push a new version of a Large File System')
|
|
31
|
+
@click.argument('uri', required=True)
|
|
32
|
+
@click.option('--path', required=True, type=click.Path(exists=True))
|
|
33
|
+
@click.option('--chunk-size', default=None, required=False, type=click.INT, help='The size of each chunk (In MB)')
|
|
34
|
+
def push(uri: str, path: str, chunk_size: Optional[int]) -> None:
|
|
35
|
+
logger.warning('This is command deprecated, please use "biolib data-record update" instead.')
|
|
36
|
+
logger.configure(default_log_level=logging.INFO)
|
|
37
|
+
logger_no_user_data.configure(default_log_level=logging.INFO)
|
|
38
|
+
try:
|
|
39
|
+
DataRecord.get_by_uri(uri=uri).update(data_path=path, chunk_size_in_mb=chunk_size)
|
|
40
|
+
except biolib_errors.BioLibError as error:
|
|
41
|
+
print(f'An error occurred:\n{error.message}', file=sys.stderr)
|
|
42
|
+
exit(1)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@lfs.command(help='Download a file from a Large File System')
|
|
46
|
+
@click.argument('uri', required=True)
|
|
47
|
+
@click.option('--file-path', required=True, type=str)
|
|
48
|
+
def download_file(uri: str, file_path: str) -> None:
|
|
49
|
+
logger.warning('This is command deprecated, please use "biolib data-record download" instead.')
|
|
50
|
+
logger.configure(default_log_level=logging.INFO)
|
|
51
|
+
logger_no_user_data.configure(default_log_level=logging.INFO)
|
|
52
|
+
try:
|
|
53
|
+
record = DataRecord.get_by_uri(uri=uri)
|
|
54
|
+
try:
|
|
55
|
+
file_obj = [file_obj for file_obj in record.list_files() if file_obj.path == file_path][0]
|
|
56
|
+
except IndexError:
|
|
57
|
+
raise Exception('File not found in data record') from None
|
|
58
|
+
|
|
59
|
+
assert not os.path.exists(file_obj.name), 'File already exists in current directory'
|
|
60
|
+
with open(file_obj.name, 'wb') as file_handle:
|
|
61
|
+
file_handle.write(file_obj.get_data())
|
|
62
|
+
|
|
63
|
+
except biolib_errors.BioLibError as error:
|
|
64
|
+
print(f'An error occurred:\n{error.message}', file=sys.stderr)
|
|
65
|
+
exit(1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@lfs.command(help='Describe a Large File System')
|
|
69
|
+
@click.argument('uri', required=True)
|
|
70
|
+
@click.option('--json', 'output_as_json', is_flag=True, default=False, required=False, help='Format output as JSON')
|
|
71
|
+
def describe(uri: str, output_as_json: bool) -> None:
|
|
72
|
+
logger.warning('This is command deprecated, please use "biolib data-record describe" instead.')
|
|
73
|
+
data_record = DataRecord.get_by_uri(uri)
|
|
74
|
+
files_info: List[Dict] = []
|
|
75
|
+
total_size_in_bytes = 0
|
|
76
|
+
for file in data_record.list_files():
|
|
77
|
+
files_info.append({'path': file.path, 'size_bytes': file.length})
|
|
78
|
+
total_size_in_bytes += file.length
|
|
79
|
+
|
|
80
|
+
if output_as_json:
|
|
81
|
+
print(
|
|
82
|
+
json.dumps(
|
|
83
|
+
obj={'uri': data_record.uri, 'size_bytes': total_size_in_bytes, 'files': files_info},
|
|
84
|
+
indent=4,
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
print(f'Large File System {data_record.uri}\ntotal {total_size_in_bytes} bytes\n')
|
|
89
|
+
print('size bytes path')
|
|
90
|
+
for file_info in files_info:
|
|
91
|
+
size_string = str(file_info['size_bytes'])
|
|
92
|
+
leading_space_string = ' ' * (10 - len(size_string))
|
|
93
|
+
print(f"{leading_space_string}{size_string} {file_info['path']}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@lfs.command(help='Prune LFS cache', hidden=True)
|
|
97
|
+
@click.option('--dry-run', type=click.BOOL, default=True, required=False)
|
|
98
|
+
def prune_cache(dry_run: bool) -> None:
|
|
99
|
+
logger.configure(default_log_level=logging.INFO)
|
|
100
|
+
logger_no_user_data.configure(default_log_level=logging.INFO)
|
|
101
|
+
prune_lfs_cache(dry_run)
|
biolib/cli/push.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from biolib._internal.errors import AuthenticationError
|
|
8
|
+
from biolib._internal.push_application import push_application
|
|
9
|
+
from biolib.biolib_logging import logger, logger_no_user_data
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command(help='Push an application to BioLib')
|
|
13
|
+
@click.argument('uri')
|
|
14
|
+
@click.option('--path', default='.', required=False)
|
|
15
|
+
@click.option('--copy-images-from-version', required=False)
|
|
16
|
+
@click.option('--dev', is_flag=True, default=False, required=False)
|
|
17
|
+
@click.option('--pre-release', is_flag=True, default=False, required=False)
|
|
18
|
+
@click.option(
|
|
19
|
+
'--dry-run',
|
|
20
|
+
is_flag=True,
|
|
21
|
+
default=False,
|
|
22
|
+
required=False,
|
|
23
|
+
help='Perform validation without pushing',
|
|
24
|
+
)
|
|
25
|
+
def push(uri, path: str, copy_images_from_version: Optional[str], dev: bool, pre_release: bool, dry_run: bool) -> None:
|
|
26
|
+
logger.configure(default_log_level=logging.INFO)
|
|
27
|
+
logger_no_user_data.configure(default_log_level=logging.INFO)
|
|
28
|
+
set_as_active = True
|
|
29
|
+
set_as_published = True
|
|
30
|
+
if dev and pre_release:
|
|
31
|
+
print('Error: you cannot set both --dev and --pre-release, please select one.')
|
|
32
|
+
exit(1)
|
|
33
|
+
elif dev:
|
|
34
|
+
set_as_active = False
|
|
35
|
+
set_as_published = False
|
|
36
|
+
elif pre_release:
|
|
37
|
+
set_as_active = False
|
|
38
|
+
set_as_published = True
|
|
39
|
+
try:
|
|
40
|
+
push_application(
|
|
41
|
+
app_path=path,
|
|
42
|
+
app_uri=uri,
|
|
43
|
+
app_version_to_copy_images_from=copy_images_from_version,
|
|
44
|
+
set_as_active=set_as_active,
|
|
45
|
+
set_as_published=set_as_published,
|
|
46
|
+
dry_run=dry_run,
|
|
47
|
+
)
|
|
48
|
+
except AuthenticationError as error:
|
|
49
|
+
print(error.message, file=sys.stderr)
|
|
50
|
+
exit(1)
|
biolib/cli/run.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from biolib import biolib_errors, utils
|
|
6
|
+
from biolib.app import BioLibApp
|
|
7
|
+
from biolib.experiments.experiment import Experiment
|
|
8
|
+
from biolib.typing_utils import Optional, Tuple
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command(
|
|
12
|
+
context_settings=dict(ignore_unknown_options=True, allow_interspersed_args=False),
|
|
13
|
+
help='Run an application on BioLib.',
|
|
14
|
+
)
|
|
15
|
+
@click.option('--experiment', type=str, required=False, help='Experiment name or URI to add the run to.')
|
|
16
|
+
@click.option('--local', is_flag=True, required=False, hidden=True)
|
|
17
|
+
@click.option('--non-blocking', is_flag=True, required=False, help='Run the application non blocking.')
|
|
18
|
+
@click.argument('uri', required=True)
|
|
19
|
+
@click.argument('args', nargs=-1, type=click.UNPROCESSED)
|
|
20
|
+
def run(experiment: Optional[str], local: bool, non_blocking: bool, uri: str, args: Tuple[str]) -> None:
|
|
21
|
+
if local:
|
|
22
|
+
print('Error: Running applications locally with --local is no longer supported.', file=sys.stderr)
|
|
23
|
+
sys.exit(1)
|
|
24
|
+
|
|
25
|
+
if experiment:
|
|
26
|
+
with Experiment(uri=experiment):
|
|
27
|
+
_run(non_blocking=non_blocking, uri=uri, args=args)
|
|
28
|
+
else:
|
|
29
|
+
_run(non_blocking=non_blocking, uri=uri, args=args)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _run(non_blocking: bool, uri: str, args: Tuple[str]) -> None:
|
|
33
|
+
try:
|
|
34
|
+
app = BioLibApp(uri=uri)
|
|
35
|
+
except biolib_errors.BioLibError as error:
|
|
36
|
+
print(f'An error occurred:\n {error.message}', file=sys.stderr)
|
|
37
|
+
exit(1)
|
|
38
|
+
|
|
39
|
+
def _get_stdin():
|
|
40
|
+
stdin = None
|
|
41
|
+
if not sys.stdin.isatty() and not utils.IS_DEV:
|
|
42
|
+
stdin = sys.stdin.read()
|
|
43
|
+
return stdin
|
|
44
|
+
|
|
45
|
+
blocking = not non_blocking
|
|
46
|
+
job = app.cli(
|
|
47
|
+
args=list(args),
|
|
48
|
+
stdin=_get_stdin(),
|
|
49
|
+
files=None,
|
|
50
|
+
blocking=blocking,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if blocking:
|
|
54
|
+
job.save_files('biolib_results', overwrite=True)
|
|
55
|
+
|
|
56
|
+
# Write stdout and stderr if it has not been streamed (Markdown is not streamed)
|
|
57
|
+
if app.version.get('stdout_render_type') == 'markdown' or not sys.stdout.isatty():
|
|
58
|
+
sys.stdout.buffer.write(job.get_stdout())
|
|
59
|
+
sys.stderr.buffer.write(job.get_stderr())
|
|
60
|
+
|
|
61
|
+
exit(job.get_exit_code())
|
|
62
|
+
else:
|
|
63
|
+
print('{"job_id": "' + str(job.id) + '"}')
|
biolib/cli/runtime.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
import biolib.runtime
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.group(help='Commands available within a BioLib runtime')
|
|
7
|
+
def runtime() -> None:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@runtime.command(help='Set the name prefix of the main result')
|
|
12
|
+
@click.argument('result-prefix', required=True)
|
|
13
|
+
def set_main_result_prefix(result_prefix: str) -> None:
|
|
14
|
+
biolib.runtime.set_main_result_prefix(result_prefix)
|
biolib/cli/sdk.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from biolib._internal.add_copilot_prompts import add_copilot_prompts
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.group(name='sdk', help='Advanced commands for developers')
|
|
7
|
+
def sdk():
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@sdk.command(
|
|
12
|
+
name='add-copilot-prompts', help='Add BioLib-specific GitHub Copilot prompts and instructions to your repository'
|
|
13
|
+
)
|
|
14
|
+
@click.option('--force', is_flag=True, help='Force overwrite existing files.')
|
|
15
|
+
def add_copilot_prompts_command(force: bool) -> None:
|
|
16
|
+
add_copilot_prompts(force)
|