Skip to content

Commit 80c1e54

Browse files
committed
fix(site): While new-site creation with multi apps, install those if not on bench
1 parent 039c355 commit 80c1e54

10 files changed

Lines changed: 264 additions & 55 deletions

File tree

admin/backend/tasks/jobs/get_and_install_app_task.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from pilot.commands.get_app import GetAppCommand
22
from pilot.core.site import Site, SiteConfig
3-
from pilot.exceptions import BenchError
43

54
from .base_task import BaseTask
5+
from .marketplace_fetcher import MarketplaceFetcher
66

77

88
class GetAndInstallAppTask(BaseTask):
@@ -27,7 +27,10 @@ def __init__(self, bench, bench_root, args):
2727
self.sites = args.sites or []
2828

2929
def run(self) -> None:
30-
cmds = self._fetch_from_marketplace() if self.marketplace_app else [self._fetch_custom()]
30+
if self.marketplace_app:
31+
cmds = MarketplaceFetcher(self.bench, self._step).fetch(self.marketplace_app)
32+
else:
33+
cmds = [self._fetch_custom()]
3134
self._install_on_sites(cmds)
3235
self._step("done")
3336

@@ -37,21 +40,6 @@ def _fetch_custom(self) -> GetAppCommand:
3740
cmd.run()
3841
return cmd
3942

40-
def _fetch_from_marketplace(self) -> list[GetAppCommand]:
41-
from pilot.core.marketplace import Marketplace
42-
43-
apps = Marketplace(self.bench).read_all_apps()
44-
resolver = next((a for a in apps if a.app == self.marketplace_app), None)
45-
if not resolver:
46-
raise BenchError(f"'{self.marketplace_app}' not found in marketplace.")
47-
cmds = []
48-
for dep in resolver.resolve():
49-
self._step("fetch", f"Fetch {dep.app}")
50-
cmd = GetAppCommand(self.bench, dep.repo, dep.target)
51-
cmd.run()
52-
cmds.append(cmd)
53-
return cmds
54-
5543
def _install_on_sites(self, cmds: list[GetAppCommand]) -> None:
5644
from pilot.managers.python_env_manager import PythonEnvManager
5745

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from pilot.commands.get_app import GetAppCommand
2-
from pilot.exceptions import BenchError
32
from .base_task import BaseTask
3+
from .marketplace_fetcher import MarketplaceFetcher
44

55

66
class GetAppTask(BaseTask):
@@ -20,23 +20,12 @@ def __init__(self, bench, bench_root, args):
2020

2121
def run(self) -> None:
2222
if self.marketplace_app:
23-
self._install_from_marketplace()
23+
MarketplaceFetcher(self.bench, self._step).fetch(self.marketplace_app)
2424
else:
2525
self._step("fetch", f"Fetch {self.repo}")
2626
GetAppCommand(self.bench, self.repo, self.branch).run()
2727
self._step("done")
2828

29-
def _install_from_marketplace(self) -> None:
30-
from pilot.core.marketplace import Marketplace
31-
32-
apps = Marketplace(self.bench).read_all_apps()
33-
resolver = next((a for a in apps if a.app == self.marketplace_app), None)
34-
if not resolver:
35-
raise BenchError(f"'{self.marketplace_app}' not found in marketplace.")
36-
for dep in resolver.resolve():
37-
self._step("fetch", f"Fetch {dep.app}")
38-
GetAppCommand(self.bench, dep.repo, dep.target).run()
39-
4029

4130
if __name__ == "__main__":
4231
GetAppTask.main()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from __future__ import annotations
2+
3+
from typing import Callable
4+
5+
from pilot.commands.get_app import GetAppCommand
6+
from pilot.core.marketplace import Marketplace
7+
from pilot.exceptions import BenchError
8+
9+
10+
class MarketplaceFetcher:
11+
"""Fetches a marketplace app and its dependencies via `bench get-app`,
12+
reporting each fetch through a task's `_step` callback."""
13+
14+
def __init__(self, bench, step: Callable[[str, str], None]) -> None:
15+
self._bench = bench
16+
self._step = step
17+
18+
def fetch(self, app_name: str) -> list[GetAppCommand]:
19+
apps = Marketplace(self._bench).read_all_apps()
20+
resolver = next((a for a in apps if a.app == app_name), None)
21+
if not resolver:
22+
raise BenchError(f"'{app_name}' not found in marketplace.")
23+
cmds = []
24+
for dep in resolver.resolve():
25+
self._step("fetch", f"Fetch {dep.app}")
26+
cmd = GetAppCommand(self._bench, dep.repo, dep.target)
27+
cmd.run()
28+
cmds.append(cmd)
29+
return cmds

admin/backend/tasks/jobs/new_site_task.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from pilot.commands.new_site import NewSiteCommand
22
from .base_task import BaseTask
3+
from .marketplace_fetcher import MarketplaceFetcher
34

45

56
class NewSiteTask(BaseTask):
@@ -20,10 +21,21 @@ def __init__(self, bench, bench_root, args):
2021
self.apps = args.apps
2122

2223
def run(self) -> None:
24+
self._fetch_missing_apps()
2325
self._step("create", f"Create site {self.name}")
2426
NewSiteCommand(self.bench, self.name, self.apps, self.admin_password, db_type=self.db_type).run()
2527
self._step("done")
2628

29+
def _fetch_missing_apps(self) -> None:
30+
"""The new-site wizard offers marketplace apps that may not be cloned onto
31+
this bench yet; fetch those before NewSiteCommand validates the app list."""
32+
apps_txt = self.bench.sites_path / "apps.txt"
33+
installed = set(apps_txt.read_text().splitlines()) if apps_txt.exists() else set()
34+
fetcher = MarketplaceFetcher(self.bench, self._step)
35+
for app_name in self.apps:
36+
if app_name not in installed:
37+
fetcher.fetch(app_name)
38+
2739

2840
if __name__ == "__main__":
2941
NewSiteTask.main()

admin/frontend/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

admin/frontend/src/components/NewSiteDialog.vue

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
</div>
6262

6363
<!-- Choose apps -->
64-
<div v-if="!loading && registry.length">
64+
<div v-if="!loading && combinedApps.length">
6565
<div class="flex justify-between items-center mb-2">
6666
<span class="text-ink-gray-7 text-p-sm-medium">Choose apps</span>
6767
<span class="text-ink-gray-5 text-xs">
@@ -109,6 +109,7 @@
109109
import { ref, computed, watch } from 'vue'
110110
import { Button, Checkbox, Dialog, ErrorMessage, FormControl } from 'frappe-ui'
111111
import AppIcon from '@/components/AppIcon.vue'
112+
import { appsApi } from '@/api/apps'
112113
import { authApi } from '@/api/auth'
113114
import { sitesApi } from '@/api/sites'
114115
import { useAppRegistry } from '@/composables/useAppRegistry'
@@ -122,6 +123,7 @@ const emit = defineEmits(['started'])
122123
const open = defineModel()
123124
124125
const { registry, load: loadRegistry } = useAppRegistry()
126+
const benchApps = ref([])
125127
126128
const newSiteName = ref('')
127129
const sitePrefix = ref('')
@@ -137,10 +139,23 @@ const dbType = ref('mariadb')
137139
const selectedApps = ref([])
138140
const appSearch = ref('')
139141
142+
// Apps already cloned onto this bench but not published to the marketplace
143+
// (e.g. a private/custom app) — offer those too, alongside the catalog.
144+
const customApps = computed(() => {
145+
const registryNames = new Set(registry.value.map((app) => app.name))
146+
return benchApps.value
147+
.filter((app) => app.name !== 'frappe' && !registryNames.has(app.name))
148+
.map((app) => ({ name: app.name, title: app.title || app.name, stars: null }))
149+
})
150+
151+
const combinedApps = computed(() => [
152+
...registry.value.filter((app) => app.name !== 'frappe'),
153+
...customApps.value,
154+
])
155+
140156
const filteredRegistry = computed(() => {
141157
const query = appSearch.value.toLowerCase().trim()
142-
return registry.value
143-
.filter((app) => app.name !== 'frappe')
158+
return combinedApps.value
144159
.filter(
145160
(app) =>
146161
!query ||
@@ -177,10 +192,18 @@ async function reset() {
177192
selectedApps.value = []
178193
appSearch.value = ''
179194
loading.value = true
180-
await Promise.all([loadWildcardDomains(), loadBenchDbType(), loadRegistry()])
195+
await Promise.all([loadWildcardDomains(), loadBenchDbType(), loadRegistry(), loadBenchApps()])
181196
loading.value = false
182197
}
183198
199+
async function loadBenchApps() {
200+
try {
201+
benchApps.value = await appsApi.installed()
202+
} catch {
203+
benchApps.value = []
204+
}
205+
}
206+
184207
function toggleApp(name) {
185208
const index = selectedApps.value.indexOf(name)
186209
if (index === -1) selectedApps.value.push(name)

admin/frontend/src/components/settings/S3Bucket.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
<Alert v-if="!connected" theme="blue" title="Why connect S3?" :dismissible="false">
77
<template #description>
88
<p class="text-ink-gray-6 text-p-sm">
9-
Connect an S3-compatible bucket to send offsite backups and snapshots. Enter your credentials below;
10-
the bucket is created automatically if it doesn't exist.
9+
Connect an S3-compatible bucket to send offsite backups and snapshots.
1110
</p>
1211
</template>
1312
</Alert>

admin/frontend/yarn.lock

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@codemirror/view" "^6.17.0"
5151
"@lezer/common" "^1.0.0"
5252

53-
"@codemirror/commands@6.x", "@codemirror/commands@^6.0.0":
53+
"@codemirror/commands@^6.0.0", "@codemirror/commands@6.x":
5454
version "6.10.4"
5555
resolved "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.4.tgz"
5656
integrity sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==
@@ -179,7 +179,7 @@
179179
"@lezer/lr" "^1.0.0"
180180
"@lezer/yaml" "^1.0.0"
181181

182-
"@codemirror/language@6.x", "@codemirror/language@^6.0.0", "@codemirror/language@^6.3.0", "@codemirror/language@^6.4.0", "@codemirror/language@^6.6.0", "@codemirror/language@^6.8.0":
182+
"@codemirror/language@^6.0.0", "@codemirror/language@^6.3.0", "@codemirror/language@^6.4.0", "@codemirror/language@^6.6.0", "@codemirror/language@^6.8.0", "@codemirror/language@6.x":
183183
version "6.12.4"
184184
resolved "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz"
185185
integrity sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==
@@ -209,14 +209,14 @@
209209
"@codemirror/view" "^6.37.0"
210210
crelt "^1.0.5"
211211

212-
"@codemirror/state@6.x", "@codemirror/state@^6.0.0", "@codemirror/state@^6.7.0":
212+
"@codemirror/state@^6.0.0", "@codemirror/state@^6.7.0", "@codemirror/state@6.x":
213213
version "6.7.0"
214214
resolved "https://registry.npmjs.org/@codemirror/state/-/state-6.7.0.tgz"
215215
integrity sha512-Zbl9NyscLMZkfXPQnNAIIAFftidrA1UbcJEIMp24C0Bukc2I5T8wJS0wsXYsnDOqCFJUeJ1BITGNs5CqPDSmSg==
216216
dependencies:
217217
"@marijn/find-cluster-break" "^1.0.0"
218218

219-
"@codemirror/view@6.x", "@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.37.0", "@codemirror/view@^6.42.0", "@codemirror/view@^6.43.4":
219+
"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.37.0", "@codemirror/view@^6.42.0", "@codemirror/view@^6.43.4", "@codemirror/view@6.x":
220220
version "6.43.4"
221221
resolved "https://registry.npmjs.org/@codemirror/view/-/view-6.43.4.tgz"
222222
integrity sha512-YImu23iyKfncJzT7sRy+rEqEhSc8RhOHqDxwy4WzXRKJwYm6iwf/9OJk5ctCAdZ6yi2ZqaGEvmf55fSVqMDrgg==
@@ -950,11 +950,6 @@
950950
dependencies:
951951
vue-demi ">=0.14.8"
952952

953-
"@vueuse/shared@14.3.0", "@vueuse/shared@^14.1.0":
954-
version "14.3.0"
955-
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-14.3.0.tgz"
956-
integrity sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==
957-
958953
acorn@^8.14.1, acorn@^8.15.0, acorn@^8.16.0:
959954
version "8.17.0"
960955
resolved "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz"
@@ -1120,7 +1115,7 @@ clone@^1.0.2:
11201115
resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz"
11211116
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
11221117

1123-
codemirror@^6.0.1:
1118+
codemirror@^6.0.1, codemirror@6.x:
11241119
version "6.0.2"
11251120
resolved "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz"
11261121
integrity sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==
@@ -1858,10 +1853,10 @@ postcss-nested@^6.2.0:
18581853
dependencies:
18591854
postcss-selector-parser "^6.1.1"
18601855

1861-
postcss-selector-parser@6.0.10:
1862-
version "6.0.10"
1863-
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz"
1864-
integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
1856+
postcss-selector-parser@^6.1.1:
1857+
version "6.1.4"
1858+
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz"
1859+
integrity sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==
18651860
dependencies:
18661861
cssesc "^3.0.0"
18671862
util-deprecate "^1.0.2"
@@ -2305,7 +2300,7 @@ ts-interface-checker@^0.1.9:
23052300
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
23062301
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
23072302

2308-
tslib@2.3.0, tslib@^2.0.0:
2303+
tslib@^2.0.0, tslib@2.3.0:
23092304
version "2.3.0"
23102305
resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz"
23112306
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
@@ -2452,7 +2447,12 @@ vue-codemirror@^6.1.1:
24522447
"@codemirror/state" "6.x"
24532448
"@codemirror/view" "6.x"
24542449

2455-
vue-demi@>=0.13.0, vue-demi@>=0.14.8:
2450+
vue-demi@>=0.13.0:
2451+
version "0.14.10"
2452+
resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz"
2453+
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
2454+
2455+
vue-demi@>=0.14.8:
24562456
version "0.14.10"
24572457
resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz"
24582458
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
@@ -2469,7 +2469,7 @@ vue-sonner@^2.0.9:
24692469
resolved "https://registry.npmjs.org/vue-sonner/-/vue-sonner-2.0.9.tgz"
24702470
integrity sha512-i6BokNlNDL93fpzNxN/LZSn6D6MzlO+i3qXt6iVZne3x1k7R46d5HlFB4P8tYydhgqOrRbIZEsnRd3kG7qGXyw==
24712471

2472-
"vue@^2.7.0 || ^3.0.0", vue@^3.0.0, "vue@^3.0.0-0 || ^2.6.0", vue@^3.2.0, vue@^3.2.25, vue@^3.5.0, "vue@>= 3.4.0", vue@>=3.5.0, vue@3.5.39:
2472+
"vue@^2.7.0 || ^3.0.0", vue@^3.0.0, "vue@^3.0.0-0 || ^2.6.0", vue@^3.2.0, vue@^3.2.25, vue@^3.5.0, "vue@>= 3.4.0", vue@>=3.5.0, vue@3.5.39, vue@3.x:
24732473
version "3.5.39"
24742474
resolved "https://registry.npmjs.org/vue/-/vue-3.5.39.tgz"
24752475
integrity sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==

0 commit comments

Comments
 (0)