@multiplatform.one/cli 5.0.26 → 6.0.0

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.
package/package.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "name": "@multiplatform.one/cli",
3
- "version": "5.0.26",
3
+ "version": "6.0.0",
4
+ "description": "multiplatform.one cli",
5
+ "keywords": [],
6
+ "bugs": {
7
+ "url": "https://gitlab.com/bitspur/multiplatform.one/multiplatform.one/issues"
8
+ },
9
+ "license": "Apache-2.0",
4
10
  "author": "BitSpur <support@bitspur.com> (https://bitspur.com)",
5
11
  "contributors": [
6
12
  {
@@ -8,9 +14,23 @@
8
14
  "url": "https://clayrisser.com"
9
15
  }
10
16
  ],
11
- "description": "typegraphql for multiplatform.one",
12
- "module": "lib/index.mjs",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://gitlab.com/bitspur/multiplatform.one/multiplatform.one.git"
20
+ },
21
+ "bin": {
22
+ "mpo": "./bin/multiplatformOne.mjs"
23
+ },
13
24
  "source": "src/index.ts",
25
+ "files": [
26
+ "bin",
27
+ "lib",
28
+ "scripts",
29
+ "src",
30
+ "types"
31
+ ],
32
+ "type": "module",
33
+ "module": "lib/index.mjs",
14
34
  "types": "types/index.d.ts",
15
35
  "exports": {
16
36
  ".": {
@@ -23,53 +43,27 @@
23
43
  },
24
44
  "./package.json": "./package.json"
25
45
  },
26
- "bin": {
27
- "multiplatform.one": "./bin/multiplatformOne.mjs"
46
+ "publishConfig": {
47
+ "access": "public"
28
48
  },
29
- "files": [
30
- "bin",
31
- "lib",
32
- "scripts",
33
- "src",
34
- "types"
35
- ],
36
- "license": "Apache-2.0",
37
- "repository": {
38
- "type": "git",
39
- "url": "https://gitlab.com/bitspur/multiplatform.one/multiplatform.one.git"
49
+ "dependencies": {
50
+ "commander": "^14.0.3",
51
+ "inquirer": "^13.4.2",
52
+ "nano-spawn": "^2.1.0",
53
+ "yaml": "^2.8.3",
54
+ "yocto-spinner": "^1.1.0",
55
+ "@multiplatform.one/utils": "6.0.0"
40
56
  },
41
- "bugs": {
42
- "url": "https://gitlab.com/bitspur/multiplatform.one/multiplatform.one/issues"
57
+ "devDependencies": {
58
+ "@types/inquirer": "^9.0.9",
59
+ "@types/node": "^25.6.0",
60
+ "typescript": "~5.9.3"
43
61
  },
44
- "keywords": [
45
- "type-graphql",
46
- "graphql"
47
- ],
48
62
  "engines": {
49
63
  "node": ">= 16.0"
50
64
  },
51
- "devDependencies": {
52
- "@types/inquirer": "^9.0.7",
53
- "@types/node": "~20.12.14",
54
- "tsup": "^8.3.5",
55
- "typescript": "~5.3.3"
56
- },
57
- "dependencies": {
58
- "@multiplatform.one/utils": "5.0.26",
59
- "commander": "^9.5.0",
60
- "dotenv": "^16.4.7",
61
- "execa": "^9.5.2",
62
- "inquirer": "^9.3.7",
63
- "ora": "^8.1.1",
64
- "yaml": "^2.7.0"
65
- },
66
- "transpileModules": [
67
- "#ansi-styles",
68
- "#supports-color",
69
- "chalk"
70
- ],
71
65
  "scripts": {
72
- "build": "rm -rf lib types 2>/dev/null && tsc -b --emitDeclarationOnly && tsup",
66
+ "build": "rm -rf lib types 2>/dev/null && tsc -b --emitDeclarationOnly && tsdown",
73
67
  "typecheck": "tsc --noEmit"
74
68
  }
75
69
  }
@@ -0,0 +1,17 @@
1
+ #!/bin/sh
2
+ # Clone multiplatform.one repo into target directory.
3
+ # Usage: clone.sh <remote_url> <branch> <target_dir>
4
+ # Example: clone.sh https://gitlab.com/bitspur/multiplatform.one/multiplatform.one.git main ./my-app
5
+
6
+ set -e
7
+
8
+ if [ $# -lt 3 ]; then
9
+ echo "Usage: $0 <remote_url> <branch> <target_dir>" >&2
10
+ exit 1
11
+ fi
12
+
13
+ REMOTE_URL="$1"
14
+ BRANCH="$2"
15
+ TARGET_DIR="$3"
16
+
17
+ git clone --depth 1 --branch "$BRANCH" "$REMOTE_URL" "$TARGET_DIR"
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+ if [ -z "$ROOTDIR" ]; then
5
+ ROOTDIR="$(git rev-parse --show-toplevel 2>/dev/null)"
6
+ fi
7
+ BENCH_DIR="${BENCH_DIR:-$ROOTDIR/apps/frappe}"
8
+ cd "$BENCH_DIR"
9
+ exec uv tool run --from frappe-bench bench "$@"
@@ -0,0 +1,495 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ _mysql() {
5
+ if command -v mysql >/dev/null 2>&1; then
6
+ mysql "$@"
7
+ elif command -v mariadb >/dev/null 2>&1; then
8
+ mariadb "$@"
9
+ else
10
+ echo "mysql/mariadb client not found" >&2
11
+ return 1
12
+ fi
13
+ }
14
+
15
+ _sed() {
16
+ _expr="$1"
17
+ _file="$2"
18
+ if sed --version >/dev/null 2>&1; then
19
+ sed -i "$_expr" "$_file"
20
+ else
21
+ sed -i '' "$_expr" "$_file"
22
+ fi
23
+ }
24
+
25
+ . "$(cd "$(dirname "$0")" && pwd)/frappe-helpers.sh"
26
+
27
+ _clean_bench_artifacts() {
28
+ rm -rf "$BENCH_DIR/apps/frappe" 2>/dev/null || true
29
+ rm -rf "$BENCH_DIR/env" 2>/dev/null || true
30
+ rm -rf "$BENCH_DIR/sites" 2>/dev/null || true
31
+ rm -rf "$BENCH_DIR/config" 2>/dev/null || true
32
+ rm -rf "$BENCH_DIR/logs" 2>/dev/null || true
33
+ rm -f "$BENCH_DIR/Procfile" 2>/dev/null || true
34
+ rm -f "$BENCH_DIR/patches.txt" 2>/dev/null || true
35
+ frappe_keep_init
36
+ frappe_collect_keep_apps
37
+ frappe_remove_non_keep_apps
38
+ }
39
+
40
+ _install_deps() {
41
+ _APP_PATH="$1"
42
+ _REAL_NAME="$2"
43
+ if [ "$UPDATE" = "1" ] || ! uv pip show "$_REAL_NAME" >/dev/null 2>&1; then
44
+ uv pip install -e "$_APP_PATH" --link-mode=copy
45
+ if [ -f "$_APP_PATH/requirements.txt" ]; then
46
+ uv pip install -r "$_APP_PATH/requirements.txt" --link-mode=copy
47
+ fi
48
+ if [ -f "$_APP_PATH/dev-requirements.txt" ]; then
49
+ uv pip install -r "$_APP_PATH/dev-requirements.txt" --link-mode=copy
50
+ elif [ -f "$_APP_PATH/pyproject.toml" ]; then
51
+ _DEPS="$(python3 -c '
52
+ import tomllib, sys
53
+ d = tomllib.load(open(sys.argv[1], "rb")) \
54
+ .get("tool", {}).get("bench", {}).get("dev-dependencies", {})
55
+ print(" ".join(f"{k}=={v}" if "=" not in v else f"{k}{v}" for k, v in d.items()))
56
+ ' "$_APP_PATH/pyproject.toml")"
57
+ [ -n "$_DEPS" ] && uv pip install $_DEPS --link-mode=copy
58
+ fi
59
+ fi
60
+ if [ -f "$_APP_PATH/package.json" ]; then
61
+ (cd "$_APP_PATH" && yarn)
62
+ fi
63
+ }
64
+
65
+ _get_app_name() {
66
+ _APP_PATH="$1"
67
+ _FALLBACK="$2"
68
+ _NAME=""
69
+ if [ -f "$_APP_PATH/pyproject.toml" ]; then
70
+ _NAME="$(python3 -c '
71
+ import tomllib, sys
72
+ with open(sys.argv[1], "rb") as f:
73
+ print(tomllib.load(f).get("project", {}).get("name", ""))
74
+ ' "$_APP_PATH/pyproject.toml")"
75
+ fi
76
+ if [ -z "$_NAME" ] && [ -f "$_APP_PATH/setup.py" ]; then
77
+ _NAME="$(python3 -c '
78
+ import ast, sys
79
+ tree = ast.parse(open(sys.argv[1]).read())
80
+ for node in ast.walk(tree):
81
+ if isinstance(node, ast.Call) and any(
82
+ kw.arg == "name" and isinstance(kw.value, ast.Constant)
83
+ for kw in node.keywords
84
+ ):
85
+ print(next(kw.value.value for kw in node.keywords if kw.arg == "name"))
86
+ break
87
+ ' "$_APP_PATH/setup.py" 2>/dev/null || true)"
88
+ fi
89
+ _NAME="${_NAME:-$_FALLBACK}"
90
+ if [ -z "$_NAME" ]; then
91
+ echo "could not determine app name from $_APP_PATH" >&2
92
+ return 1
93
+ fi
94
+ echo "$_NAME"
95
+ }
96
+
97
+ _register_app() {
98
+ echo >> "$BENCH_DIR/sites/apps.txt"
99
+ echo "$1" >> "$BENCH_DIR/sites/apps.txt"
100
+ _sed '/^$/d' "$BENCH_DIR/sites/apps.txt"
101
+ }
102
+
103
+ _source_type() {
104
+ _SRC="$1"
105
+ case "$_SRC" in
106
+ file://* | ./* | ../* | /*)
107
+ case "$_SRC" in
108
+ *.tar.gz | *.tgz | *.tar | *.zip)
109
+ echo "archive-local" ;;
110
+ *)
111
+ echo "local" ;;
112
+ esac
113
+ ;;
114
+ git@* | ssh://git@*)
115
+ echo "git" ;;
116
+ https://* | http://*)
117
+ _BASE="$(echo "$_SRC" | sed 's/#.*//')"
118
+ case "$_BASE" in
119
+ *.tar.gz | *.tgz | *.tar | *.zip)
120
+ echo "archive-remote" ;;
121
+ *)
122
+ echo "git" ;;
123
+ esac
124
+ ;;
125
+ *)
126
+ echo "unknown" ;;
127
+ esac
128
+ }
129
+
130
+ _source_url() {
131
+ echo "$1" | sed 's/#.*//'
132
+ }
133
+
134
+ _source_ref() {
135
+ _FRAG="$(echo "$1" | sed -n 's/.*#\(.*\)/\1/p')"
136
+ echo "$_FRAG" | sed 's/:.*//'
137
+ }
138
+
139
+ _source_subpath() {
140
+ _FRAG="$(echo "$1" | sed -n 's/.*#\(.*\)/\1/p')"
141
+ case "$_FRAG" in
142
+ *:*) echo "$_FRAG" | sed 's/[^:]*://' ;;
143
+ *) echo "" ;;
144
+ esac
145
+ }
146
+
147
+ _install_local_source() {
148
+ _SOURCE="$1"
149
+ _PATH="$_SOURCE"
150
+ case "$_PATH" in
151
+ file://*) _PATH="$(echo "$_PATH" | sed 's|^file://||')" ;;
152
+ esac
153
+ case "$_PATH" in
154
+ ./* | ../*)
155
+ _PATH="$(realpath "$BENCH_DIR/$_PATH")"
156
+ ;;
157
+ esac
158
+ if [ ! -d "$_PATH" ]; then
159
+ echo "local source path does not exist $_PATH" >&2
160
+ return
161
+ fi
162
+ if [ ! -f "$_PATH/pyproject.toml" ] && [ ! -f "$_PATH/setup.py" ]; then
163
+ echo "not a valid frappe app (no pyproject.toml or setup.py) $_PATH" >&2
164
+ return
165
+ fi
166
+ REAL_NAME="$(_get_app_name "$_PATH" "$(basename "$_PATH")")" || return
167
+ [ "$REAL_NAME" = "frappe" ] && return
168
+ echo "installing local app $REAL_NAME from $_PATH"
169
+ case "$_PATH" in
170
+ "$BENCH_DIR/apps/"*)
171
+ if [ "$(basename "$_PATH")" != "$REAL_NAME" ]; then
172
+ mv "$_PATH" "$BENCH_DIR/apps/$REAL_NAME"
173
+ fi
174
+ ;;
175
+ *)
176
+ rm -rf "$BENCH_DIR/apps/$REAL_NAME" 2>/dev/null || true
177
+ ln -s "$(realpath "$_PATH")" "$BENCH_DIR/apps/$REAL_NAME"
178
+ ;;
179
+ esac
180
+ _install_deps "$BENCH_DIR/apps/$REAL_NAME" "$REAL_NAME"
181
+ _register_app "$REAL_NAME"
182
+ bench build --app "$REAL_NAME"
183
+ bench install-app "$REAL_NAME"
184
+ }
185
+
186
+ _install_git_source() {
187
+ _SOURCE="$1"
188
+ _URL="$(_source_url "$_SOURCE")"
189
+ _REF="$(_source_ref "$_SOURCE")"
190
+ _SUBPATH="$(_source_subpath "$_SOURCE")"
191
+ [ -z "$_REF" ] && _REF="main"
192
+ _ID="$(echo "$_URL" | sha256sum | cut -d' ' -f1)"
193
+ if [ -d "$FRAPPE_CACHE_DIR/apps/$_ID/.git" ]; then
194
+ (
195
+ cd "$FRAPPE_CACHE_DIR/apps/$_ID"
196
+ git add .
197
+ git reset --hard
198
+ if [ "$UPDATE" = "1" ]; then
199
+ git fetch --all
200
+ fi
201
+ git checkout "$_REF"
202
+ git merge "origin/$_REF"
203
+ )
204
+ else
205
+ git clone --branch "$_REF" "$_URL" "$FRAPPE_CACHE_DIR/apps/$_ID"
206
+ fi
207
+ _APP_SRC="$FRAPPE_CACHE_DIR/apps/$_ID"
208
+ if [ -n "$_SUBPATH" ]; then
209
+ _APP_SRC="$_APP_SRC/$_SUBPATH"
210
+ if [ ! -d "$_APP_SRC" ]; then
211
+ echo "subpath does not exist in repo $_SUBPATH" >&2
212
+ return
213
+ fi
214
+ fi
215
+ REAL_NAME="$(_get_app_name "$_APP_SRC" "$(basename "$_URL" .git)")" || return
216
+ [ "$REAL_NAME" = "frappe" ] && return
217
+ echo "installing git app $REAL_NAME from $_URL${_SUBPATH:+ (subpath $_SUBPATH)}"
218
+ rm -rf "$BENCH_DIR/apps/$REAL_NAME" 2>/dev/null || true
219
+ cp -r "$_APP_SRC" "$BENCH_DIR/apps/$REAL_NAME"
220
+ _install_deps "$BENCH_DIR/apps/$REAL_NAME" "$REAL_NAME"
221
+ _register_app "$REAL_NAME"
222
+ bench build --app "$REAL_NAME"
223
+ bench install-app "$REAL_NAME"
224
+ }
225
+
226
+ _install_archive_source() {
227
+ _SOURCE="$1"
228
+ _TYPE="$2"
229
+ _ID="$(echo "$_SOURCE" | sha256sum | cut -d' ' -f1)"
230
+ _EXTRACT_DIR="$FRAPPE_CACHE_DIR/apps/$_ID"
231
+ if [ "$UPDATE" = "1" ] || [ ! -d "$_EXTRACT_DIR" ]; then
232
+ rm -rf "$_EXTRACT_DIR" 2>/dev/null || true
233
+ mkdir -p "$_EXTRACT_DIR"
234
+ if [ "$_TYPE" = "archive-remote" ]; then
235
+ _URL="$(_source_url "$_SOURCE")"
236
+ _TMP="$(mktemp)"
237
+ curl -fsSL "$_URL" -o "$_TMP"
238
+ _ARCHIVE="$_TMP"
239
+ else
240
+ _ARCHIVE="$_SOURCE"
241
+ case "$_ARCHIVE" in
242
+ ./* | ../*)
243
+ _ARCHIVE="$(realpath "$BENCH_DIR/$_ARCHIVE")"
244
+ ;;
245
+ esac
246
+ fi
247
+ case "$_SOURCE" in
248
+ *.tar.gz | *.tgz) tar xzf "$_ARCHIVE" -C "$_EXTRACT_DIR" --strip-components=1 ;;
249
+ *.tar) tar xf "$_ARCHIVE" -C "$_EXTRACT_DIR" --strip-components=1 ;;
250
+ *.zip) unzip -qo "$_ARCHIVE" -d "$_EXTRACT_DIR" ;;
251
+ esac
252
+ [ -f "$_TMP" ] && rm -f "$_TMP"
253
+ fi
254
+ _FALLBACK="$(basename "$(_source_url "$_SOURCE")" | sed 's/\.tar\.gz$//;s/\.tgz$//;s/\.tar$//;s/\.zip$//')"
255
+ REAL_NAME="$(_get_app_name "$_EXTRACT_DIR" "$_FALLBACK")" || return
256
+ echo "installing archive app $REAL_NAME from $_SOURCE"
257
+ rm -rf "$BENCH_DIR/apps/$REAL_NAME" 2>/dev/null || true
258
+ cp -r "$_EXTRACT_DIR" "$BENCH_DIR/apps/$REAL_NAME"
259
+ _install_deps "$BENCH_DIR/apps/$REAL_NAME" "$REAL_NAME"
260
+ _register_app "$REAL_NAME"
261
+ bench build --app "$REAL_NAME"
262
+ bench install-app "$REAL_NAME"
263
+ }
264
+
265
+ if [ -z "$ROOTDIR" ]; then
266
+ ROOTDIR="$(git rev-parse --show-toplevel 2>/dev/null)"
267
+ fi
268
+ DOTENV_FILE="${DOTENV_FILE:-$ROOTDIR/.env}"
269
+ BENCH_DIR="${BENCH_DIR:-$ROOTDIR/apps/frappe}"
270
+ while [ $# -gt 0 ]; do
271
+ case "$1" in
272
+ --update)
273
+ UPDATE=1
274
+ ;;
275
+ *)
276
+ ;;
277
+ esac
278
+ shift
279
+ done
280
+ UPDATE="${UPDATE:-0}"
281
+ export COREPACK_ENABLE_STRICT=0
282
+ export COREPACK_ENABLE_PROJECT_SPEC=0
283
+ python3 -c "import distutils" 2>/dev/null || uv pip install setuptools --system --break-system-packages
284
+ if [ ! -f "$DOTENV_FILE" ] && [ -f "$ROOTDIR/.env.example" ]; then
285
+ cp "$ROOTDIR/.env.example" "$DOTENV_FILE"
286
+ fi
287
+ . "$DOTENV_FILE"
288
+ cd "$ROOTDIR"
289
+ FRAPPE_ADMIN_PASSWORD="${FRAPPE_ADMIN_PASSWORD:-P@ssw0rd}"
290
+ FRAPPE_BASE_URL="${FRAPPE_BASE_URL:-http://localhost:8000}"
291
+ FRAPPE_BRANCH="${FRAPPE_BRANCH:-version-15-hotfix}"
292
+ FRAPPE_DB_HOST="${FRAPPE_DB_HOST:-$([ "$FRAPPE_DB_TYPE" = "postgres" ] && echo "$POSTGRES_HOSTNAME" || echo "${MARIADB_HOSTNAME:-${MYSQL_HOSTNAME:-127.0.0.1}}")}"
293
+ if [ "$FRAPPE_DB_HOST" = "localhost" ]; then
294
+ FRAPPE_DB_HOST="127.0.0.1"
295
+ fi
296
+ FRAPPE_DB_NAME="${FRAPPE_DB_NAME:-frappe}"
297
+ FRAPPE_DB_PASSWORD="${FRAPPE_DB_PASSWORD:-$([ "$FRAPPE_DB_TYPE" = "postgres" ] && echo "$POSTGRES_PASSWORD" || echo "${MARIADB_PASSWORD:-${MYSQL_PASSWORD:-root}}")}"
298
+ FRAPPE_DB_PORT="${FRAPPE_DB_PORT:-$([ "$FRAPPE_DB_TYPE" = "postgres" ] && echo "$POSTGRES_PORT" || echo "${MARIADB_PORT:-${MYSQL_PORT:-3306}}")}"
299
+ FRAPPE_DB_TYPE="${FRAPPE_DB_TYPE:-mariadb}"
300
+ FRAPPE_DB_USER="${FRAPPE_DB_USER:-$([ "$FRAPPE_DB_TYPE" = "postgres" ] && echo "$POSTGRES_USERNAME" || echo "${MARIADB_USERNAME:-${MYSQL_USERNAME:-root}}")}"
301
+ FRAPPE_REPO="${FRAPPE_REPO:-https://github.com/frappe/frappe.git}"
302
+ FRAPPE_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/frappe"
303
+ REDIS_CACHE_URI="${REDIS_CACHE_URI:-redis://localhost:6379}"
304
+ REDIS_QUEUE_URI="${REDIS_QUEUE_URI:-redis://localhost:6380}"
305
+ SITE_NAME="${SITE_NAME:-$(echo "$FRAPPE_BASE_URL" | sed 's|^https*://||' | sed 's|:[0-9]*.*||' | sed 's|/.*||')}"
306
+ bench() {
307
+ uv tool run --from frappe-bench bench "$@"
308
+ }
309
+ _i=0
310
+ while true; do
311
+ if [ "$FRAPPE_DB_TYPE" = "postgres" ]; then
312
+ PGPASSWORD="$FRAPPE_DB_PASSWORD" psql -h "$FRAPPE_DB_HOST" -p "$FRAPPE_DB_PORT" -U "$FRAPPE_DB_USER" -d postgres -c "SELECT 1" >/dev/null 2>&1 && break
313
+ else
314
+ _mysql -h "$FRAPPE_DB_HOST" -P "$FRAPPE_DB_PORT" -u "$FRAPPE_DB_USER" -p"$FRAPPE_DB_PASSWORD" -e "SELECT 1" >/dev/null 2>&1 && break
315
+ fi
316
+ _i=$((_i + 1))
317
+ if [ "$_i" -gt 30 ]; then
318
+ printf "\nfailed to connect to database\n" >&2
319
+ exit 1
320
+ fi
321
+ printf .
322
+ sleep 1
323
+ done
324
+ [ "$_i" -gt 0 ] && printf "\n"
325
+ if [ ! -f "$BENCH_DIR/apps/frappe/package.json" ] || [ ! -f "$BENCH_DIR/Procfile" ] || \
326
+ ! "$BENCH_DIR/env/bin/python3" -c "from frappe.utils.bench_helper import main" 2>/dev/null || \
327
+ [ ! -d "$BENCH_DIR/apps/frappe/node_modules" ] || [ ! -d "$BENCH_DIR/sites" ]; then
328
+ _clean_bench_artifacts
329
+ if [ -d "$FRAPPE_CACHE_DIR/apps/frappe/.git" ]; then
330
+ (cd "$FRAPPE_CACHE_DIR/apps/frappe" && git add . && git reset --hard && git pull)
331
+ else
332
+ mkdir -p "$FRAPPE_CACHE_DIR/apps"
333
+ git clone --branch "$FRAPPE_BRANCH" "$FRAPPE_REPO" "$FRAPPE_CACHE_DIR/apps/frappe"
334
+ fi
335
+ echo n | bench init \
336
+ --ignore-exist \
337
+ --skip-assets \
338
+ --skip-redis-config-generation \
339
+ --frappe-path="$FRAPPE_CACHE_DIR/apps/frappe" \
340
+ --frappe-branch="$FRAPPE_BRANCH" \
341
+ "$BENCH_DIR"
342
+ _DID_INIT=1
343
+ fi
344
+ echo frappe > "$BENCH_DIR/sites/apps.txt"
345
+ cd "$BENCH_DIR"
346
+ . env/bin/activate
347
+ if [ "${_DID_INIT:-0}" = "1" ]; then
348
+ bench build --app frappe
349
+ fi
350
+ jq 'del(.redis_cache, .redis_queue, .redis_socketio)' \
351
+ "$BENCH_DIR/sites/common_site_config.json" > _common_site_config.json && \
352
+ mv _common_site_config.json "$BENCH_DIR/sites/common_site_config.json"
353
+ bench set-config -g db_type "$FRAPPE_DB_TYPE"
354
+ bench set-config -g developer_mode 1
355
+ bench set-config -g redis_cache "$REDIS_CACHE_URI"
356
+ bench set-config -g redis_queue "$REDIS_QUEUE_URI"
357
+ bench set-config -g redis_socketio "$REDIS_QUEUE_URI"
358
+ if [ "$FRAPPE_DB_TYPE" = "mariadb" ] || [ "$FRAPPE_DB_TYPE" = "mysql" ]; then
359
+ _mysql -h "$FRAPPE_DB_HOST" -P "$FRAPPE_DB_PORT" -u "$FRAPPE_DB_USER" -p"$FRAPPE_DB_PASSWORD" -e "
360
+ CREATE USER IF NOT EXISTS '$FRAPPE_DB_NAME'@'%' IDENTIFIED BY '$FRAPPE_DB_PASSWORD';
361
+ GRANT ALL PRIVILEGES ON *.* TO '$FRAPPE_DB_NAME'@'%' WITH GRANT OPTION;
362
+ FLUSH PRIVILEGES;
363
+ "
364
+ elif [ "$FRAPPE_DB_TYPE" = "postgres" ]; then
365
+ PGPASSWORD="$FRAPPE_DB_PASSWORD" psql -h "$FRAPPE_DB_HOST" -p "$FRAPPE_DB_PORT" -U "$FRAPPE_DB_USER" -d postgres -c "
366
+ DO \$\$
367
+ BEGIN
368
+ IF NOT EXISTS (SELECT FROM pg_catalog.pg_user WHERE usename = '$FRAPPE_DB_NAME') THEN
369
+ CREATE USER $FRAPPE_DB_NAME WITH PASSWORD '$FRAPPE_DB_PASSWORD';
370
+ END IF;
371
+ GRANT ALL PRIVILEGES ON DATABASE $FRAPPE_DB_NAME TO $FRAPPE_DB_NAME;
372
+ EXCEPTION WHEN OTHERS THEN
373
+ NULL;
374
+ END \$\$;
375
+ "
376
+ fi
377
+ _DB_INITIALIZED=1
378
+ if [ -f "$BENCH_DIR/sites/$SITE_NAME/site_config.json" ]; then
379
+ if [ "$FRAPPE_DB_TYPE" = "postgres" ]; then
380
+ PGPASSWORD="$FRAPPE_DB_PASSWORD" psql -h "$FRAPPE_DB_HOST" -p "$FRAPPE_DB_PORT" -U "$FRAPPE_DB_USER" -d "$FRAPPE_DB_NAME" -c "SELECT 1 FROM \"tabDefaultValue\" LIMIT 1" >/dev/null 2>&1 || _DB_INITIALIZED=0
381
+ else
382
+ _mysql -h "$FRAPPE_DB_HOST" -P "$FRAPPE_DB_PORT" -u "$FRAPPE_DB_USER" -p"$FRAPPE_DB_PASSWORD" -D "$FRAPPE_DB_NAME" -e "SELECT 1 FROM tabDefaultValue LIMIT 1" >/dev/null 2>&1 || _DB_INITIALIZED=0
383
+ fi
384
+ fi
385
+ if [ ! -f "$BENCH_DIR/sites/$SITE_NAME/site_config.json" ] || [ "$_DB_INITIALIZED" = "0" ]; then
386
+ if [ "$FRAPPE_DB_TYPE" = "postgres" ]; then
387
+ PGPASSWORD="$FRAPPE_DB_PASSWORD" psql -h "$FRAPPE_DB_HOST" -p "$FRAPPE_DB_PORT" -U "$FRAPPE_DB_USER" -c \
388
+ "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$FRAPPE_DB_NAME';"
389
+ else
390
+ _mysql -h "$FRAPPE_DB_HOST" -P "$FRAPPE_DB_PORT" -u "$FRAPPE_DB_USER" -p"$FRAPPE_DB_PASSWORD" -e \
391
+ "SELECT GROUP_CONCAT(CONCAT('KILL ',id,';') SEPARATOR ' ') FROM INFORMATION_SCHEMA.PROCESSLIST WHERE db='$FRAPPE_DB_NAME' INTO @kill_list; SET @kill_list = IFNULL(@kill_list,'SELECT 1'); PREPARE kill_stmt FROM @kill_list; EXECUTE kill_stmt; DEALLOCATE PREPARE kill_stmt;"
392
+ fi
393
+ if [ "$FRAPPE_DB_HOST" = "127.0.0.1" ]; then
394
+ if [ "$FRAPPE_DB_TYPE" = "postgres" ]; then
395
+ PGPASSWORD="$FRAPPE_DB_PASSWORD" psql -h "$FRAPPE_DB_HOST" -p "$FRAPPE_DB_PORT" -U "$FRAPPE_DB_USER" -c \
396
+ "DROP DATABASE IF EXISTS $FRAPPE_DB_NAME;"
397
+ else
398
+ _mysql -h "$FRAPPE_DB_HOST" -P "$FRAPPE_DB_PORT" -u "$FRAPPE_DB_USER" -p"$FRAPPE_DB_PASSWORD" -e \
399
+ "DROP DATABASE IF EXISTS $FRAPPE_DB_NAME;"
400
+ fi
401
+ fi
402
+ rm -rf "$BENCH_DIR/sites/$SITE_NAME" 2>/dev/null || true
403
+ bench new-site \
404
+ --admin-password="$FRAPPE_ADMIN_PASSWORD" \
405
+ --db-host="$FRAPPE_DB_HOST" \
406
+ --db-name="$FRAPPE_DB_NAME" \
407
+ --db-password="$FRAPPE_DB_PASSWORD" \
408
+ --db-port="$FRAPPE_DB_PORT" \
409
+ --db-root-password="$FRAPPE_DB_PASSWORD" \
410
+ --db-type="$FRAPPE_DB_TYPE" \
411
+ "$SITE_NAME"
412
+ fi
413
+ bench use "$SITE_NAME"
414
+ APPS_CONFIG_FILE="$BENCH_DIR/frappe.yaml"
415
+ if [ -f "$APPS_CONFIG_FILE" ]; then
416
+ python3 -c '
417
+ import yaml, sys
418
+ with open(sys.argv[1]) as f:
419
+ for app in (yaml.safe_load(f) or {}).get("apps", []):
420
+ print(app)
421
+ ' "$APPS_CONFIG_FILE" | while IFS= read -r SOURCE; do
422
+ if [ -z "$SOURCE" ]; then
423
+ continue
424
+ fi
425
+ _TYPE="$(_source_type "$SOURCE")"
426
+ case "$_TYPE" in
427
+ local)
428
+ _install_local_source "$SOURCE"
429
+ ;;
430
+ git)
431
+ _install_git_source "$SOURCE"
432
+ ;;
433
+ archive-local | archive-remote)
434
+ _install_archive_source "$SOURCE" "$_TYPE"
435
+ ;;
436
+ *)
437
+ echo "unknown source type for $SOURCE" >&2
438
+ ;;
439
+ esac
440
+ done
441
+ fi
442
+ SITE_CONFIG="$(cat "$BENCH_DIR/sites/$SITE_NAME/site_config.json")"
443
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.db_user // ""')" != "$FRAPPE_DB_USER" ]; then
444
+ bench --site "$SITE_NAME" set-config db_user "$FRAPPE_DB_USER"
445
+ fi
446
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.developer_mode // ""')" != "1" ]; then
447
+ bench --site "$SITE_NAME" set-config developer_mode 1
448
+ fi
449
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.allow_tests // ""')" != "1" ]; then
450
+ bench --site "$SITE_NAME" set-config allow_tests 1
451
+ fi
452
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.db_host // ""')" != "$FRAPPE_DB_HOST" ]; then
453
+ bench --site "$SITE_NAME" set-config db_host "$FRAPPE_DB_HOST"
454
+ fi
455
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.db_name // ""')" != "$FRAPPE_DB_NAME" ]; then
456
+ bench --site "$SITE_NAME" set-config db_name "$FRAPPE_DB_NAME"
457
+ fi
458
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.db_password // ""')" != "$FRAPPE_DB_PASSWORD" ]; then
459
+ bench --site "$SITE_NAME" set-config db_password "$FRAPPE_DB_PASSWORD"
460
+ fi
461
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.db_port // ""')" != "$FRAPPE_DB_PORT" ]; then
462
+ bench --site "$SITE_NAME" set-config db_port "$FRAPPE_DB_PORT"
463
+ fi
464
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.host_name // ""')" != "$FRAPPE_BASE_URL" ]; then
465
+ bench --site "$SITE_NAME" set-config host_name "$FRAPPE_BASE_URL"
466
+ fi
467
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.restart_supervisor_on_update // ""')" != "1" ]; then
468
+ bench --site "$SITE_NAME" set-config restart_supervisor_on_update 1
469
+ fi
470
+ # Dev proxy: route /auth and /api/auth through Frappe to One and Keycloak
471
+ if [ "$(echo "$SITE_CONFIG" | jq -r '.one_dev_proxy // ""')" != "true" ]; then
472
+ bench --site "$SITE_NAME" set-config one_dev_proxy 1
473
+ fi
474
+ if [ -n "${ONE_PORT:-}" ] && [ "$(echo "$SITE_CONFIG" | jq -r '.one_dev_proxy_target // ""')" != "http://localhost:${ONE_PORT}" ]; then
475
+ bench --site "$SITE_NAME" set-config one_dev_proxy_target "http://localhost:${ONE_PORT}"
476
+ fi
477
+ if [ -n "${KEYCLOAK_PORT:-}" ] && [ "$(echo "$SITE_CONFIG" | jq -r '.keycloak_dev_proxy_target // ""')" != "http://localhost:${KEYCLOAK_PORT}" ]; then
478
+ bench --site "$SITE_NAME" set-config keycloak_dev_proxy_target "http://localhost:${KEYCLOAK_PORT}"
479
+ fi
480
+ bench migrate
481
+ # Mark the Frappe setup wizard complete so freshly bootstrapped sites skip it.
482
+ # enable_setup_wizard_complete flips Installed Application.is_setup_complete=1
483
+ # (the exact lever the real wizard uses) -- on this Frappe the desk redirect is
484
+ # gated by that per-app flag, not System Settings.setup_complete. bench execute
485
+ # auto-commits; re-running over an already-complete row is a no-op (idempotent).
486
+ for _app in frappe erpnext; do
487
+ bench --site "$SITE_NAME" execute \
488
+ frappe.desk.page.setup_wizard.setup_wizard.enable_setup_wizard_complete \
489
+ --kwargs "{\"app_name\": \"$_app\"}" 2>/dev/null || true
490
+ done
491
+ # Land the desk on a workspace instead of the wizard route.
492
+ bench --site "$SITE_NAME" execute \
493
+ frappe.db.set_default --kwargs '{"key": "desktop:home_page", "val": "workspace"}' 2>/dev/null || true
494
+ bench clear-cache
495
+ bench enable-scheduler
@@ -0,0 +1,31 @@
1
+ #!/bin/sh
2
+ set -e
3
+ if [ -z "$ROOTDIR" ]; then
4
+ ROOTDIR="$(git rev-parse --show-toplevel 2>/dev/null)"
5
+ fi
6
+ BENCH_DIR="${BENCH_DIR:-$ROOTDIR/apps/frappe}"
7
+ FRAPPE_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/frappe"
8
+ . "$(cd "$(dirname "$0")" && pwd)/frappe-helpers.sh"
9
+ CLEAN_CACHE=0
10
+ while [ $# -gt 0 ]; do
11
+ case "$1" in
12
+ --cache) CLEAN_CACHE=1 ;;
13
+ *) ;;
14
+ esac
15
+ shift
16
+ done
17
+ frappe_keep_init
18
+ frappe_collect_keep_apps
19
+ frappe_remove_non_keep_apps
20
+ rm -rf "$BENCH_DIR/env" 2>/dev/null || true
21
+ rm -rf "$BENCH_DIR/sites" 2>/dev/null || true
22
+ rm -rf "$BENCH_DIR/config" 2>/dev/null || true
23
+ rm -rf "$BENCH_DIR/logs" 2>/dev/null || true
24
+ rm -f "$BENCH_DIR/Procfile" 2>/dev/null || true
25
+ rm -f "$BENCH_DIR/patches.txt" 2>/dev/null || true
26
+ if [ "$CLEAN_CACHE" = "1" ]; then
27
+ rm -rf "$FRAPPE_CACHE_DIR"
28
+ echo "cleaned bench and cache"
29
+ else
30
+ echo "cleaned bench"
31
+ fi