chore: split build.sh and update-and-build-all.sh
- build.sh: deterministic local-only build, fail-fast, no git pull - update-and-build-all.sh: dev helper, pulls all repos, builds official plugins, then builds desktop - Docs: added Build Scripts section explaining the difference
This commit is contained in:
parent
86eeadd2a9
commit
a96ffb5801
|
|
@ -519,3 +519,27 @@ WorkspaceTree в sidebar:
|
||||||
- Создание case/folder
|
- Создание case/folder
|
||||||
- Выбор текущей ноды
|
- Выбор текущей ноды
|
||||||
- Индикатор статуса (active/archived/sleeping)
|
- Индикатор статуса (active/archived/sleeping)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build Scripts
|
||||||
|
|
||||||
|
В `verstak-desktop/scripts/` есть два скрипта:
|
||||||
|
|
||||||
|
### `build.sh` — локальная детерминированная сборка
|
||||||
|
|
||||||
|
- Собирает **только** `verstak-desktop` (core platform).
|
||||||
|
- Не трогает другие репозитории.
|
||||||
|
- **Fail-fast**: любая ошибка (go vet, go test, frontend build, wails build) прерывает сборку.
|
||||||
|
- Проверяет: deps → frontend build → go mod download → go vet → go build → go test → wails build + plugin copy.
|
||||||
|
- Используется в CI и для повседневной работы над core.
|
||||||
|
|
||||||
|
### `update-and-build-all.sh` — dev helper для полной пересборки связки
|
||||||
|
|
||||||
|
- **Не для CI.** Только для разработки, когда нужно быстро собрать всё вместе.
|
||||||
|
- Шаги:
|
||||||
|
1. `git pull --ff-only` во всех 6 репозиториях
|
||||||
|
2. Сборка official plugins (frontend npm build + backend go build для каждого плагина)
|
||||||
|
3. Копирование собранных плагинов в `verstak-desktop/plugins/`
|
||||||
|
4. Запуск `build.sh` для сборки desktop
|
||||||
|
- Ошибки pull и сборки плагинов не прерывают скрипт (best-effort), но ошибка build.sh прерывает (fail-fast).
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,92 @@
|
||||||
export namespace api {
|
export namespace api {
|
||||||
|
|
||||||
|
export class FlatSidebarItem {
|
||||||
|
pluginId: string;
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
view: string;
|
||||||
|
position?: number;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new FlatSidebarItem(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.pluginId = source["pluginId"];
|
||||||
|
this.id = source["id"];
|
||||||
|
this.title = source["title"];
|
||||||
|
this.icon = source["icon"];
|
||||||
|
this.view = source["view"];
|
||||||
|
this.position = source["position"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class FlatSettingsPanel {
|
||||||
|
pluginId: string;
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
component: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new FlatSettingsPanel(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.pluginId = source["pluginId"];
|
||||||
|
this.id = source["id"];
|
||||||
|
this.title = source["title"];
|
||||||
|
this.icon = source["icon"];
|
||||||
|
this.component = source["component"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class FlatCommand {
|
||||||
|
pluginId: string;
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
handler?: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new FlatCommand(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.pluginId = source["pluginId"];
|
||||||
|
this.id = source["id"];
|
||||||
|
this.title = source["title"];
|
||||||
|
this.icon = source["icon"];
|
||||||
|
this.handler = source["handler"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class FlatView {
|
||||||
|
pluginId: string;
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
component: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new FlatView(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.pluginId = source["pluginId"];
|
||||||
|
this.id = source["id"];
|
||||||
|
this.title = source["title"];
|
||||||
|
this.icon = source["icon"];
|
||||||
|
this.component = source["component"];
|
||||||
|
}
|
||||||
|
}
|
||||||
export class ContributionSummary {
|
export class ContributionSummary {
|
||||||
views: contribution.ContributionView[];
|
views: FlatView[];
|
||||||
commands: contribution.ContributionCommand[];
|
commands: FlatCommand[];
|
||||||
settingsPanels: contribution.ContributionSettingsPanel[];
|
settingsPanels: FlatSettingsPanel[];
|
||||||
sidebarItems: contribution.ContributionSidebarItem[];
|
sidebarItems: FlatSidebarItem[];
|
||||||
fileActions: contribution.ContributionAction[];
|
|
||||||
noteActions: contribution.ContributionAction[];
|
|
||||||
searchProviders: contribution.ContributionSearchProvider[];
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
static createFrom(source: any = {}) {
|
||||||
return new ContributionSummary(source);
|
return new ContributionSummary(source);
|
||||||
|
|
@ -15,13 +94,10 @@ export namespace api {
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
constructor(source: any = {}) {
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
this.views = this.convertValues(source["views"], contribution.ContributionView);
|
this.views = this.convertValues(source["views"], FlatView);
|
||||||
this.commands = this.convertValues(source["commands"], contribution.ContributionCommand);
|
this.commands = this.convertValues(source["commands"], FlatCommand);
|
||||||
this.settingsPanels = this.convertValues(source["settingsPanels"], contribution.ContributionSettingsPanel);
|
this.settingsPanels = this.convertValues(source["settingsPanels"], FlatSettingsPanel);
|
||||||
this.sidebarItems = this.convertValues(source["sidebarItems"], contribution.ContributionSidebarItem);
|
this.sidebarItems = this.convertValues(source["sidebarItems"], FlatSidebarItem);
|
||||||
this.fileActions = this.convertValues(source["fileActions"], contribution.ContributionAction);
|
|
||||||
this.noteActions = this.convertValues(source["noteActions"], contribution.ContributionAction);
|
|
||||||
this.searchProviders = this.convertValues(source["searchProviders"], contribution.ContributionSearchProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
|
|
@ -42,6 +118,9 @@ export namespace api {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,203 +147,6 @@ export namespace capability {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace contribution {
|
|
||||||
|
|
||||||
export class ContributionAction {
|
|
||||||
pluginId: string;
|
|
||||||
item: plugin.ContributionAction;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new ContributionAction(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
|
||||||
this.pluginId = source["pluginId"];
|
|
||||||
this.item = this.convertValues(source["item"], plugin.ContributionAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
|
||||||
if (!a) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
if (a.slice && a.map) {
|
|
||||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
|
||||||
} else if ("object" === typeof a) {
|
|
||||||
if (asMap) {
|
|
||||||
for (const key of Object.keys(a)) {
|
|
||||||
a[key] = new classs(a[key]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return new classs(a);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class ContributionCommand {
|
|
||||||
pluginId: string;
|
|
||||||
item: plugin.ContributionCommand;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new ContributionCommand(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
|
||||||
this.pluginId = source["pluginId"];
|
|
||||||
this.item = this.convertValues(source["item"], plugin.ContributionCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
|
||||||
if (!a) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
if (a.slice && a.map) {
|
|
||||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
|
||||||
} else if ("object" === typeof a) {
|
|
||||||
if (asMap) {
|
|
||||||
for (const key of Object.keys(a)) {
|
|
||||||
a[key] = new classs(a[key]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return new classs(a);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class ContributionSearchProvider {
|
|
||||||
pluginId: string;
|
|
||||||
item: plugin.ContributionSearchProvider;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new ContributionSearchProvider(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
|
||||||
this.pluginId = source["pluginId"];
|
|
||||||
this.item = this.convertValues(source["item"], plugin.ContributionSearchProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
|
||||||
if (!a) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
if (a.slice && a.map) {
|
|
||||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
|
||||||
} else if ("object" === typeof a) {
|
|
||||||
if (asMap) {
|
|
||||||
for (const key of Object.keys(a)) {
|
|
||||||
a[key] = new classs(a[key]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return new classs(a);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class ContributionSettingsPanel {
|
|
||||||
pluginId: string;
|
|
||||||
item: plugin.ContributionSettingsPanel;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new ContributionSettingsPanel(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
|
||||||
this.pluginId = source["pluginId"];
|
|
||||||
this.item = this.convertValues(source["item"], plugin.ContributionSettingsPanel);
|
|
||||||
}
|
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
|
||||||
if (!a) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
if (a.slice && a.map) {
|
|
||||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
|
||||||
} else if ("object" === typeof a) {
|
|
||||||
if (asMap) {
|
|
||||||
for (const key of Object.keys(a)) {
|
|
||||||
a[key] = new classs(a[key]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return new classs(a);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class ContributionSidebarItem {
|
|
||||||
pluginId: string;
|
|
||||||
item: plugin.ContributionSidebarItem;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new ContributionSidebarItem(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
|
||||||
this.pluginId = source["pluginId"];
|
|
||||||
this.item = this.convertValues(source["item"], plugin.ContributionSidebarItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
|
||||||
if (!a) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
if (a.slice && a.map) {
|
|
||||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
|
||||||
} else if ("object" === typeof a) {
|
|
||||||
if (asMap) {
|
|
||||||
for (const key of Object.keys(a)) {
|
|
||||||
a[key] = new classs(a[key]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return new classs(a);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class ContributionView {
|
|
||||||
pluginId: string;
|
|
||||||
item: plugin.ContributionView;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new ContributionView(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
|
||||||
this.pluginId = source["pluginId"];
|
|
||||||
this.item = this.convertValues(source["item"], plugin.ContributionView);
|
|
||||||
}
|
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
|
||||||
if (!a) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
if (a.slice && a.map) {
|
|
||||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
|
||||||
} else if ("object" === typeof a) {
|
|
||||||
if (asMap) {
|
|
||||||
for (const key of Object.keys(a)) {
|
|
||||||
a[key] = new classs(a[key]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return new classs(a);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace permissions {
|
export namespace permissions {
|
||||||
|
|
||||||
export class Entry {
|
export class Entry {
|
||||||
|
|
|
||||||
216
scripts/build.sh
216
scripts/build.sh
|
|
@ -2,166 +2,75 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
VERSTAK_ROOT="$(cd "$ROOT/.." && pwd)"
|
|
||||||
FAILED=0
|
|
||||||
GLOBAL_ERRORS=""
|
|
||||||
|
|
||||||
report() {
|
|
||||||
if [ "$2" -eq 0 ]; then
|
|
||||||
echo " ✅ $1"
|
|
||||||
else
|
|
||||||
echo " ❌ $1"
|
|
||||||
FAILED=1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Global update: pull all repos, build official plugins ─────────────────
|
|
||||||
global_update() {
|
|
||||||
local repos=("verstak-desktop" "verstak-sdk" "verstak-official-plugins" "verstak-sync-server" "verstak-browser-extension" "verstak-docs")
|
|
||||||
local errors=""
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "=== global update: pull all repos ==="
|
|
||||||
for repo in "${repos[@]}"; do
|
|
||||||
local repo_path="$VERSTAK_ROOT/$repo"
|
|
||||||
if [ ! -d "$repo_path" ]; then
|
|
||||||
errors="$errors ⚠️ $repo: directory not found at $repo_path\n"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
echo "[$repo]"
|
|
||||||
(cd "$repo_path" && git pull --ff-only 2>&1) && echo " ✅ pulled" || {
|
|
||||||
errors="$errors ❌ $repo: git pull failed\n"
|
|
||||||
echo " ❌ git pull failed"
|
|
||||||
}
|
|
||||||
done
|
|
||||||
|
|
||||||
# Build official plugins
|
|
||||||
local official_plugins="$VERSTAK_ROOT/verstak-official-plugins"
|
|
||||||
if [ -d "$official_plugins" ]; then
|
|
||||||
echo ""
|
|
||||||
echo "=== build official plugins ==="
|
|
||||||
(cd "$official_plugins" && if [ -f "package.json" ] && [ -d "plugins" ]; then
|
|
||||||
if [ ! -d "node_modules" ]; then
|
|
||||||
echo "[official-plugins] installing npm deps..."
|
|
||||||
npm install --no-audit --no-fund 2>&1 || true
|
|
||||||
fi
|
|
||||||
# Build each plugin that has a frontend
|
|
||||||
for plugin_dir in plugins/*/; do
|
|
||||||
local plugin_name=$(basename "$plugin_dir")
|
|
||||||
local fe_dir="$plugin_dir/frontend"
|
|
||||||
if [ -d "$fe_dir" ] && [ -f "$fe_dir/package.json" ]; then
|
|
||||||
echo "[$plugin_name] building frontend..."
|
|
||||||
(cd "$fe_dir" && [ ! -d "node_modules" ] && npm install --no-audit --no-fund 2>&1 || true; npm run build 2>&1 || true)
|
|
||||||
fi
|
|
||||||
# Build backend if main.go exists
|
|
||||||
local backend_dir="$plugin_dir/backend"
|
|
||||||
if [ -f "$backend_dir/main.go" ]; then
|
|
||||||
echo "[$plugin_name] building backend..."
|
|
||||||
(cd "$backend_dir" && go build -o "$(basename "$backend_dir")" . 2>&1 || true)
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo " ✅ official plugins built"
|
|
||||||
else
|
|
||||||
echo " ℹ️ no plugins to build"
|
|
||||||
fi)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy official plugins to desktop
|
|
||||||
local dest="$ROOT/plugins"
|
|
||||||
if [ -d "$official_plugins/plugins" ]; then
|
|
||||||
echo ""
|
|
||||||
echo "=== install official plugins to desktop ==="
|
|
||||||
rm -rf "$dest"
|
|
||||||
mkdir -p "$dest"
|
|
||||||
cp -r "$official_plugins/plugins/"* "$dest/" 2>/dev/null || true
|
|
||||||
echo " ✅ plugins installed to $dest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
GLOBAL_ERRORS="$errors"
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_npm_deps() {
|
|
||||||
local dir="$1"
|
|
||||||
if [ ! -f "$dir/package.json" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
if [ ! -d "$dir/node_modules" ]; then
|
|
||||||
echo " 📦 node_modules missing — installing..."
|
|
||||||
if [ -f "$dir/package-lock.json" ]; then
|
|
||||||
(cd "$dir" && npm ci --no-audit --no-fund)
|
|
||||||
else
|
|
||||||
(cd "$dir" && npm install --no-audit --no-fund)
|
|
||||||
fi
|
|
||||||
report "npm install in $(basename "$dir")" $?
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "=== verstak-desktop build ==="
|
echo "=== verstak-desktop build ==="
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── Global update (best-effort — errors collected, not fatal) ──
|
|
||||||
global_update
|
|
||||||
|
|
||||||
# ── Dependency checks ──
|
# ── Dependency checks ──
|
||||||
echo "[deps]"
|
echo "[deps]"
|
||||||
if ! command -v go &>/dev/null; then
|
if ! command -v go &>/dev/null; then
|
||||||
echo " ❌ go: not found. Install Go 1.24+ from https://go.dev/dl/"
|
echo " ❌ go: not found. Install Go 1.24+ from https://go.dev/dl/"
|
||||||
FAILED=1
|
|
||||||
else
|
|
||||||
echo " ✅ go $(go version | grep -oP 'go\S+')"
|
|
||||||
fi
|
|
||||||
if ! command -v node &>/dev/null; then
|
|
||||||
echo " ❌ node: not found. Install Node.js 20+"
|
|
||||||
FAILED=1
|
|
||||||
else
|
|
||||||
echo " ✅ node $(node --version)"
|
|
||||||
fi
|
|
||||||
if ! command -v npm &>/dev/null; then
|
|
||||||
echo " ❌ npm: not found"
|
|
||||||
FAILED=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$FAILED" -ne 0 ]; then
|
|
||||||
echo ""
|
|
||||||
echo "❌ build failed — missing core dependencies"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo " ✅ go $(go version | grep -oP 'go\S+')"
|
||||||
|
|
||||||
|
if ! command -v node &>/dev/null; then
|
||||||
|
echo " ❌ node: not found. Install Node.js 20+"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✅ node $(node --version)"
|
||||||
|
|
||||||
|
if ! command -v npm &>/dev/null; then
|
||||||
|
echo " ❌ npm: not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✅ npm $(npm --version)"
|
||||||
|
|
||||||
# ── Frontend (build first — Go //go:embed needs frontend/dist/) ──
|
# ── Frontend (build first — Go //go:embed needs frontend/dist/) ──
|
||||||
|
echo ""
|
||||||
echo "[frontend]"
|
echo "[frontend]"
|
||||||
if [ -f "$ROOT/frontend/package.json" ]; then
|
if [ -f "$ROOT/frontend/package.json" ]; then
|
||||||
ensure_npm_deps "$ROOT/frontend"
|
if [ ! -d "$ROOT/frontend/node_modules" ]; then
|
||||||
|
echo " 📦 node_modules missing — installing..."
|
||||||
|
if [ -f "$ROOT/frontend/package-lock.json" ]; then
|
||||||
|
(cd "$ROOT/frontend" && npm ci --no-audit --no-fund)
|
||||||
|
else
|
||||||
|
(cd "$ROOT/frontend" && npm install --no-audit --no-fund)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
(cd "$ROOT/frontend" && npm run build)
|
(cd "$ROOT/frontend" && npm run build)
|
||||||
report "frontend build" $?
|
echo " ✅ frontend build"
|
||||||
else
|
else
|
||||||
echo " ℹ️ frontend/package.json not found — skipping"
|
echo " ℹ️ frontend/package.json not found — skipping"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Go backend ──
|
# ── Go backend ──
|
||||||
|
echo ""
|
||||||
echo "[backend]"
|
echo "[backend]"
|
||||||
|
|
||||||
# Ensure Go module deps are downloaded
|
echo " 📦 go mod download..."
|
||||||
(cd "$ROOT" && go mod download)
|
(cd "$ROOT" && go mod download)
|
||||||
report "go mod download" $?
|
echo " ✅ go mod download"
|
||||||
|
|
||||||
|
echo " 🔍 go vet..."
|
||||||
(cd "$ROOT" && go vet ./...)
|
(cd "$ROOT" && go vet ./...)
|
||||||
report "go vet" $?
|
echo " ✅ go vet"
|
||||||
|
|
||||||
|
echo " 🔨 go build..."
|
||||||
(cd "$ROOT" && go build ./...)
|
(cd "$ROOT" && go build ./...)
|
||||||
report "go build" $?
|
echo " ✅ go build"
|
||||||
|
|
||||||
# Go test (best-effort — some packages may have no tests)
|
echo " 🧪 go test..."
|
||||||
(cd "$ROOT" && go test -count=1 ./... 2>&1 || true)
|
(cd "$ROOT" && go test -count=1 ./...)
|
||||||
report "go test" $?
|
echo " ✅ go test"
|
||||||
|
|
||||||
# ── Wails ──
|
# ── Wails ──
|
||||||
|
echo ""
|
||||||
echo "[wails]"
|
echo "[wails]"
|
||||||
WAILS=""
|
WAILS=""
|
||||||
if command -v wails &>/dev/null; then
|
if command -v wails &>/dev/null; then
|
||||||
WAILS="wails"
|
WAILS="wails"
|
||||||
else
|
else
|
||||||
# Check GO bin paths
|
|
||||||
GOBIN="$(go env GOBIN 2>/dev/null)"
|
GOBIN="$(go env GOBIN 2>/dev/null)"
|
||||||
GOPATH="$(go env GOPATH 2>/dev/null)"
|
GOPATH="$(go env GOPATH 2>/dev/null)"
|
||||||
if [ -n "$GOBIN" ] && [ -f "$GOBIN/wails" ]; then
|
if [ -n "$GOBIN" ] && [ -f "$GOBIN/wails" ]; then
|
||||||
|
|
@ -172,7 +81,6 @@ else
|
||||||
if [ -z "$WAILS" ]; then
|
if [ -z "$WAILS" ]; then
|
||||||
echo " 📦 wails not found — installing..."
|
echo " 📦 wails not found — installing..."
|
||||||
go install github.com/wailsapp/wails/v2/cmd/wails@latest
|
go install github.com/wailsapp/wails/v2/cmd/wails@latest
|
||||||
report "wails install" $?
|
|
||||||
if [ -n "$GOBIN" ] && [ -f "$GOBIN/wails" ]; then
|
if [ -n "$GOBIN" ] && [ -f "$GOBIN/wails" ]; then
|
||||||
WAILS="$GOBIN/wails"
|
WAILS="$GOBIN/wails"
|
||||||
elif [ -f "$GOPATH/bin/wails" ]; then
|
elif [ -f "$GOPATH/bin/wails" ]; then
|
||||||
|
|
@ -180,6 +88,7 @@ else
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
WAILS_BINARY="verstak-desktop"
|
WAILS_BINARY="verstak-desktop"
|
||||||
WAILS_TAGS=""
|
WAILS_TAGS=""
|
||||||
if command -v pkg-config &>/dev/null; then
|
if command -v pkg-config &>/dev/null; then
|
||||||
|
|
@ -194,37 +103,30 @@ if command -v pkg-config &>/dev/null; then
|
||||||
else
|
else
|
||||||
echo " ⚠️ pkg-config not found"
|
echo " ⚠️ pkg-config not found"
|
||||||
fi
|
fi
|
||||||
if [ -n "$WAILS" ]; then
|
|
||||||
echo " 🔨 wails build..."
|
if [ -z "$WAILS" ]; then
|
||||||
(cd "$ROOT" && "$WAILS" build -clean $WAILS_TAGS)
|
echo " ❌ wails: could not find or install"
|
||||||
report "wails build" $?
|
exit 1
|
||||||
# Copy plugins/ to build/bin/ so the binary can find them at runtime
|
fi
|
||||||
if [ -d "$ROOT/plugins" ]; then
|
|
||||||
mkdir -p "$ROOT/build/bin/plugins"
|
echo " 🔨 wails build..."
|
||||||
cp -r "$ROOT/plugins/"* "$ROOT/build/bin/plugins/" 2>/dev/null || true
|
(cd "$ROOT" && "$WAILS" build -clean $WAILS_TAGS)
|
||||||
echo " 📦 plugins copied to build/bin/plugins/"
|
echo " ✅ wails build"
|
||||||
fi
|
|
||||||
# Show where the binary ended up
|
# Copy plugins/ to build/bin/ so the binary can find them at runtime
|
||||||
if [ -f "$ROOT/build/bin/$WAILS_BINARY" ]; then
|
if [ -d "$ROOT/plugins" ]; then
|
||||||
echo " 📦 binary: $ROOT/build/bin/$WAILS_BINARY"
|
mkdir -p "$ROOT/build/bin/plugins"
|
||||||
fi
|
cp -r "$ROOT/plugins/"* "$ROOT/build/bin/plugins/" 2>/dev/null || true
|
||||||
if [ -f "$ROOT/$WAILS_BINARY" ]; then
|
echo " 📦 plugins copied to build/bin/plugins/"
|
||||||
echo " 📦 binary: $ROOT/$WAILS_BINARY"
|
fi
|
||||||
fi
|
|
||||||
else
|
# Show where the binary ended up
|
||||||
echo " ❌ wails: could not install"
|
if [ -f "$ROOT/build/bin/$WAILS_BINARY" ]; then
|
||||||
FAILED=1
|
echo " 📦 binary: $ROOT/build/bin/$WAILS_BINARY"
|
||||||
|
fi
|
||||||
|
if [ -f "$ROOT/$WAILS_BINARY" ]; then
|
||||||
|
echo " 📦 binary: $ROOT/$WAILS_BINARY"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
if [ "$FAILED" -eq 0 ] && [ -z "$GLOBAL_ERRORS" ]; then
|
echo "✅ build passed"
|
||||||
echo "✅ build passed"
|
|
||||||
else
|
|
||||||
echo "❌ build completed with issues"
|
|
||||||
if [ -n "$GLOBAL_ERRORS" ]; then
|
|
||||||
echo ""
|
|
||||||
echo "Global update errors:"
|
|
||||||
echo -e "$GLOBAL_ERRORS"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
exit "$FAILED"
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# update-and-build-all.sh — dev helper: pull all repos, build official plugins, build desktop
|
||||||
|
# This is NOT part of CI. For local deterministic build, use scripts/build.sh in each repo.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
VERSTAK_ROOT="$(cd "$ROOT/.." && pwd)"
|
||||||
|
|
||||||
|
echo "=== update-and-build-all ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 1. Pull all repos ──
|
||||||
|
echo "=== pull all repos ==="
|
||||||
|
repos=("verstak-desktop" "verstak-sdk" "verstak-official-plugins" "verstak-sync-server" "verstak-browser-extension" "verstak-docs")
|
||||||
|
for repo in "${repos[@]}"; do
|
||||||
|
repo_path="$VERSTAK_ROOT/$repo"
|
||||||
|
if [ ! -d "$repo_path" ]; then
|
||||||
|
echo " ⚠️ $repo: directory not found at $repo_path — skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo "[$repo]"
|
||||||
|
(cd "$repo_path" && git pull --ff-only 2>&1) && echo " ✅ pulled" || echo " ❌ git pull failed"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 2. Build official plugins ──
|
||||||
|
echo ""
|
||||||
|
echo "=== build official plugins ==="
|
||||||
|
OFFICIAL="$VERSTAK_ROOT/verstak-official-plugins"
|
||||||
|
if [ ! -d "$OFFICIAL" ]; then
|
||||||
|
echo " ⚠️ verstak-official-plugins not found — skipping"
|
||||||
|
else
|
||||||
|
# npm deps
|
||||||
|
if [ ! -d "$OFFICIAL/node_modules" ] && [ -f "$OFFICIAL/package.json" ]; then
|
||||||
|
echo " 📦 installing npm deps..."
|
||||||
|
(cd "$OFFICIAL" && npm install --no-audit --no-fund)
|
||||||
|
fi
|
||||||
|
# Build each plugin that has a frontend or backend
|
||||||
|
for plugin_dir in "$OFFICIAL"/plugins/*/; do
|
||||||
|
[ -d "$plugin_dir" ] || continue
|
||||||
|
plugin_name="$(basename "$plugin_dir")"
|
||||||
|
|
||||||
|
# Frontend build
|
||||||
|
fe_dir="$plugin_dir/frontend"
|
||||||
|
if [ -d "$fe_dir" ] && [ -f "$fe_dir/package.json" ]; then
|
||||||
|
echo "[$plugin_name] building frontend..."
|
||||||
|
if [ ! -d "$fe_dir/node_modules" ]; then
|
||||||
|
(cd "$fe_dir" && npm install --no-audit --no-fund)
|
||||||
|
fi
|
||||||
|
(cd "$fe_dir" && npm run build)
|
||||||
|
echo " ✅ $plugin_name frontend"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backend build
|
||||||
|
backend_dir="$plugin_dir/backend"
|
||||||
|
if [ -f "$backend_dir/main.go" ]; then
|
||||||
|
echo "[$plugin_name] building backend..."
|
||||||
|
(cd "$backend_dir" && go build -o "$(basename "$backend_dir")" .)
|
||||||
|
echo " ✅ $plugin_name backend"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo " ✅ official plugins built"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 3. Copy plugins to desktop ──
|
||||||
|
echo ""
|
||||||
|
echo "=== install plugins to desktop ==="
|
||||||
|
DEST="$ROOT/plugins"
|
||||||
|
rm -rf "$DEST"
|
||||||
|
mkdir -p "$DEST"
|
||||||
|
if [ -d "$OFFICIAL/plugins" ]; then
|
||||||
|
cp -r "$OFFICIAL/plugins/"* "$DEST/" 2>/dev/null
|
||||||
|
echo " ✅ plugins copied to $DEST"
|
||||||
|
else
|
||||||
|
echo " ℹ️ no plugins to copy"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4. Build desktop ──
|
||||||
|
echo ""
|
||||||
|
echo "=== build desktop ==="
|
||||||
|
exec "$ROOT/scripts/build.sh"
|
||||||
Loading…
Reference in New Issue