@humanu/orchestra 0.5.49 → 0.5.52
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/README.md +1 -0
- package/bin/orchestra.js +4 -1
- package/install.js +25 -2
- package/package.json +1 -1
- package/resources/prebuilt/linux-x64/gw-env-copy +0 -0
- package/resources/prebuilt/linux-x64/orchestra +0 -0
- package/resources/prebuilt/macos-arm64/gw-env-copy +0 -0
- package/resources/prebuilt/macos-arm64/orchestra +0 -0
- package/resources/prebuilt/macos-intel/gw-env-copy +0 -0
- package/resources/prebuilt/macos-intel/orchestra +0 -0
- package/resources/scripts/gw-bridge.sh +43 -3
- package/resources/scripts/gw.sh +4 -7
- package/resources/scripts/shell/AGENTS.md +13 -29
- package/resources/scripts/shell/bridge/ai.sh +3 -3
- package/resources/scripts/shell/env_copy_command.sh +29 -0
- package/resources/scripts/shell/git/bridge_create_worktree.sh +8 -3
- package/resources/scripts/shell/git/checkout_worktree.sh +3 -3
- package/resources/scripts/shell/git/create_worktree.sh +2 -2
- package/resources/scripts/shell/gw_env_copy.sh +65 -0
- package/resources/scripts/shell/gw_load.sh +1 -0
- package/resources/scripts/shell/gwr/check-updates.sh +24 -7
- package/resources/scripts/shell/tmux/new_session_command.sh +2 -2
- package/resources/scripts/copy_env.sh +0 -251
- package/resources/scripts/shell/bridge/copy_env.sh +0 -84
- package/resources/scripts/shell/env/copy_env_command.sh +0 -27
- package/resources/scripts/shell/env/copy_env_constants.sh +0 -3
- package/resources/scripts/shell/env/copy_env_core.sh +0 -178
- package/resources/scripts/shell/env/copy_env_debug_parse.sh +0 -14
- package/resources/scripts/shell/env/copy_env_load.sh +0 -9
- package/resources/scripts/shell/env/copy_env_locations.sh +0 -34
- package/resources/scripts/shell/env/copy_env_logging.sh +0 -17
- package/resources/scripts/shell/env/copy_env_state.sh +0 -5
package/README.md
CHANGED
package/bin/orchestra.js
CHANGED
|
@@ -4,7 +4,10 @@ const path = require('path');
|
|
|
4
4
|
const { spawnSync } = require('child_process');
|
|
5
5
|
|
|
6
6
|
const installScript = path.join(__dirname, '..', 'install.js');
|
|
7
|
-
const
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const upgradeCommands = new Set(['upgrade', 'update', '--update', '--check-updates']);
|
|
9
|
+
const invoke = upgradeCommands.has(args[0]) ? 'gw' : 'orchestra';
|
|
10
|
+
const result = spawnSync(process.execPath, [installScript, `--invoke=${invoke}`, ...args], {
|
|
8
11
|
stdio: 'inherit',
|
|
9
12
|
});
|
|
10
13
|
process.exit(result.status ?? 0);
|
package/install.js
CHANGED
|
@@ -5,7 +5,8 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const BINARY_NAME = 'orchestra';
|
|
8
|
-
const
|
|
8
|
+
const ENV_COPY_NAME = 'gw-env-copy';
|
|
9
|
+
const SHELL_SCRIPTS = ['gwr.sh', 'gw.sh', 'gw-bridge.sh', 'orchestra-local.sh'];
|
|
9
10
|
const SHELL_DIRS = ['shell'];
|
|
10
11
|
const SUPPORTED_PLATFORMS = {
|
|
11
12
|
'darwin-x64': 'macos-intel',
|
|
@@ -135,6 +136,7 @@ function linkCompatibilityBinary(binaryPath) {
|
|
|
135
136
|
function installPrebuiltBinary() {
|
|
136
137
|
const platform = getPlatformKey();
|
|
137
138
|
const source = path.join(prebuiltDir, platform, BINARY_NAME);
|
|
139
|
+
const envCopySource = path.join(prebuiltDir, platform, ENV_COPY_NAME);
|
|
138
140
|
|
|
139
141
|
if (!fs.existsSync(source)) {
|
|
140
142
|
console.log(`⚠️ No prebuilt binary found for ${platform}`);
|
|
@@ -159,6 +161,13 @@ function installPrebuiltBinary() {
|
|
|
159
161
|
fs.copyFileSync(source, destination);
|
|
160
162
|
fs.chmodSync(destination, 0o755);
|
|
161
163
|
linkCompatibilityBinary(destination);
|
|
164
|
+
if (fs.existsSync(envCopySource)) {
|
|
165
|
+
const envCopyDestination = path.join(distDir, ENV_COPY_NAME);
|
|
166
|
+
fs.copyFileSync(envCopySource, envCopyDestination);
|
|
167
|
+
fs.chmodSync(envCopyDestination, 0o755);
|
|
168
|
+
} else {
|
|
169
|
+
console.warn(`⚠️ Missing ${ENV_COPY_NAME} for ${platform}`);
|
|
170
|
+
}
|
|
162
171
|
return true;
|
|
163
172
|
}
|
|
164
173
|
|
|
@@ -226,12 +235,26 @@ function buildFromSource() {
|
|
|
226
235
|
fs.copyFileSync(builtBinary, destination);
|
|
227
236
|
fs.chmodSync(destination, 0o755);
|
|
228
237
|
linkCompatibilityBinary(destination);
|
|
238
|
+
|
|
239
|
+
const envCopyBinary = path.join(projectRoot, 'gw-tui', 'target', 'release', ENV_COPY_NAME);
|
|
240
|
+
if (!fs.existsSync(envCopyBinary)) {
|
|
241
|
+
console.error(`❌ ${ENV_COPY_NAME} not found after build.`);
|
|
242
|
+
console.error(' Expected location:', envCopyBinary);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
const envCopyDestination = path.join(distDir, ENV_COPY_NAME);
|
|
246
|
+
fs.copyFileSync(envCopyBinary, envCopyDestination);
|
|
247
|
+
fs.chmodSync(envCopyDestination, 0o755);
|
|
229
248
|
|
|
230
249
|
console.log('✅ Successfully built from source!');
|
|
231
250
|
}
|
|
232
251
|
|
|
233
252
|
function assetsReady() {
|
|
234
|
-
return
|
|
253
|
+
return (
|
|
254
|
+
fs.existsSync(path.join(distDir, BINARY_NAME)) &&
|
|
255
|
+
fs.existsSync(path.join(distDir, ENV_COPY_NAME)) &&
|
|
256
|
+
fs.existsSync(path.join(distDir, 'gwr.sh'))
|
|
257
|
+
);
|
|
235
258
|
}
|
|
236
259
|
|
|
237
260
|
function printShellWrapperInstructions(binaryPath) {
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -16,14 +16,12 @@ set -euo pipefail
|
|
|
16
16
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
17
|
source "$SCRIPT_DIR/api/git.sh"
|
|
18
18
|
source "$SCRIPT_DIR/api/tmux.sh"
|
|
19
|
-
source "$SCRIPT_DIR/shell/
|
|
20
|
-
copy_env_init
|
|
19
|
+
source "$SCRIPT_DIR/shell/gw_env_copy.sh"
|
|
21
20
|
|
|
22
21
|
# Source bridge modules
|
|
23
22
|
source "$SCRIPT_DIR/shell/bridge/utils.sh"
|
|
24
23
|
source "$SCRIPT_DIR/shell/bridge/tmux.sh"
|
|
25
24
|
source "$SCRIPT_DIR/shell/bridge/ai.sh"
|
|
26
|
-
source "$SCRIPT_DIR/shell/bridge/copy_env.sh"
|
|
27
25
|
|
|
28
26
|
# Source git bridge modules (now modular)
|
|
29
27
|
source "$SCRIPT_DIR/shell/git/bridge_worktree.sh"
|
|
@@ -37,6 +35,48 @@ err() { printf '❌ %s\n' "$*" >&2; }
|
|
|
37
35
|
info() { printf '%s\n' "$*"; }
|
|
38
36
|
have_cmd() { command -v "$1" >/dev/null 2>&1; }
|
|
39
37
|
|
|
38
|
+
bridge_copy_env_files() {
|
|
39
|
+
if [[ -z "${1:-}" ]] || [[ -z "${2:-}" ]]; then
|
|
40
|
+
json_error "Source and target paths required"
|
|
41
|
+
return 1
|
|
42
|
+
fi
|
|
43
|
+
local source_path="$1"
|
|
44
|
+
local target_path="$2"
|
|
45
|
+
local raw_locations="${3-}"
|
|
46
|
+
|
|
47
|
+
local repo_root
|
|
48
|
+
repo_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
|
|
49
|
+
local shared_root
|
|
50
|
+
shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null || echo "")"
|
|
51
|
+
if [[ -n "$shared_root" ]]; then
|
|
52
|
+
if [[ "$shared_root" != /* ]]; then
|
|
53
|
+
shared_root="$(cd "$source_path" 2>/dev/null && pwd -P)/$shared_root"
|
|
54
|
+
fi
|
|
55
|
+
shared_root="$(dirname "$shared_root")"
|
|
56
|
+
else
|
|
57
|
+
shared_root="$repo_root"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
local bin
|
|
61
|
+
if ! bin="$(gw_env_copy_bin)"; then
|
|
62
|
+
json_error "gw-env-copy binary not found"
|
|
63
|
+
return 1
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
local args=(
|
|
67
|
+
--source "$source_path"
|
|
68
|
+
--target "$target_path"
|
|
69
|
+
--repo "$shared_root"
|
|
70
|
+
--format boolean
|
|
71
|
+
--quiet
|
|
72
|
+
)
|
|
73
|
+
if [[ -n "$raw_locations" ]]; then
|
|
74
|
+
args+=(--locations-json "$raw_locations")
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
"$bin" "${args[@]}"
|
|
78
|
+
}
|
|
79
|
+
|
|
40
80
|
# Ensure JSON backend is available (jq preferred, python/node fallback)
|
|
41
81
|
bridge_init_json_backend
|
|
42
82
|
|
package/resources/scripts/gw.sh
CHANGED
|
@@ -47,16 +47,13 @@ fi
|
|
|
47
47
|
|
|
48
48
|
# Source command-line operations
|
|
49
49
|
COMMANDS_DIR="$SCRIPT_DIR/shell"
|
|
50
|
-
source "$SCRIPT_DIR/shell/env/copy_env_load.sh"
|
|
51
|
-
copy_env_init
|
|
52
|
-
|
|
53
50
|
for command_file in \
|
|
54
51
|
"git/create_worktree.sh" \
|
|
55
52
|
"git/delete_worktree.sh" \
|
|
56
53
|
"git/list_worktrees.sh" \
|
|
57
54
|
"git/checkout_worktree.sh" \
|
|
58
55
|
"git/status.sh" \
|
|
59
|
-
"
|
|
56
|
+
"env_copy_command.sh" \
|
|
60
57
|
"tmux/new_session_command.sh"
|
|
61
58
|
do
|
|
62
59
|
if [[ -f "$COMMANDS_DIR/$command_file" ]]; then
|
|
@@ -137,7 +134,7 @@ usage() {
|
|
|
137
134
|
echo " ch, checkout <branch> Switch to the worktree of <branch>"
|
|
138
135
|
echo " copy-env <worktreename> Copy env files from current worktree to target"
|
|
139
136
|
echo " status [git status args] Show current worktree + git status"
|
|
140
|
-
echo " update, --update
|
|
137
|
+
echo " upgrade, update, --update Check for available updates"
|
|
141
138
|
echo ""
|
|
142
139
|
echo "Tmux flags:"
|
|
143
140
|
echo " --branch <branch> Branch name for tmux session worktree"
|
|
@@ -149,7 +146,7 @@ usage() {
|
|
|
149
146
|
echo " $(basename "$0") ls # list worktrees only"
|
|
150
147
|
echo " $(basename "$0") ch -b feat/x # create and switch to worktree"
|
|
151
148
|
echo " $(basename "$0") --branch main --new-tmux --cmd \"git status\""
|
|
152
|
-
echo " $(basename "$0")
|
|
149
|
+
echo " $(basename "$0") upgrade # check for available updates"
|
|
153
150
|
echo ""
|
|
154
151
|
echo "Note: tmux session management is available via flags or the interactive menu."
|
|
155
152
|
}
|
|
@@ -242,7 +239,7 @@ case "$COMMAND" in
|
|
|
242
239
|
ch|checkout) cmd_checkout_worktree "$@" ;;
|
|
243
240
|
status) cmd_status "$@" ;;
|
|
244
241
|
copy-env) cmd_copy_env "$@" ;;
|
|
245
|
-
update|--update) "$SCRIPT_DIR/shell/gwr/check-updates.sh" ;;
|
|
242
|
+
upgrade|update|--update) "$SCRIPT_DIR/shell/gwr/check-updates.sh" ;;
|
|
246
243
|
help|-h|--help) usage ;;
|
|
247
244
|
*) err "Unknown command '$COMMAND'"; usage ;;
|
|
248
245
|
esac
|
|
@@ -20,7 +20,7 @@ shell/
|
|
|
20
20
|
├── gw_git.sh # Git operations for gw CLI
|
|
21
21
|
├── gw_worktree.sh # Worktree management for gw CLI
|
|
22
22
|
├── gw_usage.sh # Usage text for gw command
|
|
23
|
-
├──
|
|
23
|
+
├── gw_env_copy.sh # Rust env copy helper/wrapper
|
|
24
24
|
├── commands.sh # Shared command utilities
|
|
25
25
|
├── build/ # Build system components
|
|
26
26
|
│ ├── build_bridge.sh # Bridge building logic
|
|
@@ -30,15 +30,6 @@ shell/
|
|
|
30
30
|
│ ├── build_logging.sh # Build-specific logging
|
|
31
31
|
│ ├── build_rust.sh # Rust compilation logic
|
|
32
32
|
│ └── build_usage.sh # Build command usage
|
|
33
|
-
├── env/ # Environment management
|
|
34
|
-
│ ├── copy_env_command.sh # Command processing for env copying
|
|
35
|
-
│ ├── copy_env_constants.sh # Constants and paths
|
|
36
|
-
│ ├── copy_env_core.sh # Core environment copying logic
|
|
37
|
-
│ ├── copy_env_debug_parse.sh # Debug parsing for env copying
|
|
38
|
-
│ ├── copy_env_load.sh # Environment loader
|
|
39
|
-
│ ├── copy_env_locations.sh # Path and location management
|
|
40
|
-
│ ├── copy_env_logging.sh # Environment copying logging
|
|
41
|
-
│ └── copy_env_state.sh # State management for env copying
|
|
42
33
|
├── git/ # Git-specific utilities
|
|
43
34
|
│ └── gwr_worktree_title.sh # Worktree title generation
|
|
44
35
|
└── gwr/ # gwr-specific components
|
|
@@ -122,13 +113,12 @@ shell/
|
|
|
122
113
|
|
|
123
114
|
## Shared System Files
|
|
124
115
|
|
|
125
|
-
###
|
|
126
|
-
**Purpose**:
|
|
127
|
-
**Usage**:
|
|
116
|
+
### gw_env_copy.sh
|
|
117
|
+
**Purpose**: Rust env copy helper for CLI and bridge flows
|
|
118
|
+
**Usage**: Locates and invokes the `gw-env-copy` binary
|
|
128
119
|
**Key Functions**:
|
|
129
|
-
- `
|
|
130
|
-
-
|
|
131
|
-
- Used by both gwr and gw systems
|
|
120
|
+
- `gw_env_copy_bin()` - Resolve the env copy binary path
|
|
121
|
+
- `gw_env_copy_files()` - Execute env copy with repo config
|
|
132
122
|
|
|
133
123
|
### commands.sh
|
|
134
124
|
**Purpose**: Shared command utilities
|
|
@@ -157,17 +147,11 @@ shell/
|
|
|
157
147
|
**Usage**: Handle specific aspects of the build process
|
|
158
148
|
**Key Functions**: Each focuses on a specific build operation
|
|
159
149
|
|
|
160
|
-
##
|
|
150
|
+
## Rust Env Copy
|
|
161
151
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
**Key Functions**: `copy_env()` - Core function for environment management
|
|
166
|
-
|
|
167
|
-
### copy_env_command.sh, copy_env_constants.sh, copy_env_debug_parse.sh, copy_env_load.sh, copy_env_locations.sh, copy_env_logging.sh, copy_env_state.sh
|
|
168
|
-
**Purpose**: Specialized environment copying components
|
|
169
|
-
**Usage**: Handle specific aspects of environment management
|
|
170
|
-
**Key Functions**: Each module handles a specific aspect of the complex environment copying system
|
|
152
|
+
Environment file copying is implemented in Rust via the `gw-env-copy` binary.
|
|
153
|
+
Shell wrappers invoke the binary and read `.orchestra/options.json` for
|
|
154
|
+
`copy_locations` when needed.
|
|
171
155
|
|
|
172
156
|
## Git Utilities (git/)
|
|
173
157
|
|
|
@@ -196,7 +180,7 @@ shell/
|
|
|
196
180
|
|
|
197
181
|
### gw.sh Integration
|
|
198
182
|
- Sources `gw_load.sh` to load all gw components
|
|
199
|
-
- Uses
|
|
183
|
+
- Uses `gw_env_copy.sh` to invoke the Rust env copy binary
|
|
200
184
|
- Provides CLI interface to same underlying functionality
|
|
201
185
|
|
|
202
186
|
### Bridge System Integration
|
|
@@ -209,7 +193,7 @@ shell/
|
|
|
209
193
|
1. **Modular Loading**: Each system (gwr/gw) has its own loader that sources components in dependency order
|
|
210
194
|
2. **Color Consistency**: Separate color files ensure consistent theming across components
|
|
211
195
|
3. **Logging Standardization**: Standardized logging functions provide consistent output
|
|
212
|
-
4. **Environment Isolation**:
|
|
196
|
+
4. **Environment Isolation**: Rust env copy binary handles .env file replication across worktrees
|
|
213
197
|
5. **Binary Discovery**: Sophisticated binary finding supports development, build, and installed scenarios
|
|
214
198
|
6. **Update Integration**: Update checking integrated into main launcher with installation method detection
|
|
215
199
|
|
|
@@ -233,4 +217,4 @@ shell/
|
|
|
233
217
|
3. Follow existing build system patterns
|
|
234
218
|
4. Update `build_usage.sh` for new build options
|
|
235
219
|
|
|
236
|
-
This documentation provides the foundation for AI agents to understand and work effectively with the Orchestrator shell system.
|
|
220
|
+
This documentation provides the foundation for AI agents to understand and work effectively with the Orchestrator shell system.
|
|
@@ -160,8 +160,8 @@ bridge_manual_rename_session() {
|
|
|
160
160
|
return 0
|
|
161
161
|
fi
|
|
162
162
|
|
|
163
|
-
# Validate new display name (alphanumeric
|
|
164
|
-
if [[ ! "$new_display_name" =~ ^[a-zA-Z0-9_]+$ ]]; then
|
|
163
|
+
# Validate new display name (alphanumeric, underscore, hyphen only)
|
|
164
|
+
if [[ ! "$new_display_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
165
165
|
echo "\"invalid_name\""
|
|
166
166
|
return 0
|
|
167
167
|
fi
|
|
@@ -172,4 +172,4 @@ bridge_manual_rename_session() {
|
|
|
172
172
|
else
|
|
173
173
|
echo "\"rename_failed\""
|
|
174
174
|
fi
|
|
175
|
-
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
|
|
3
|
+
cmd_copy_env() {
|
|
4
|
+
local target_branch="${1-}"
|
|
5
|
+
if [[ -z "$target_branch" ]]; then
|
|
6
|
+
err "Target worktree name required"
|
|
7
|
+
echo "Usage: gw copy-env <worktreename>"
|
|
8
|
+
return 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
git_require_repo || return 1
|
|
12
|
+
|
|
13
|
+
local target_path
|
|
14
|
+
target_path="$(git_branch_to_worktree_path "$target_branch")"
|
|
15
|
+
if [[ -z "$target_path" ]]; then
|
|
16
|
+
err "Worktree for branch '$target_branch' not found"
|
|
17
|
+
return 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
local root
|
|
21
|
+
root="$(git_repo_root)"
|
|
22
|
+
local shared_root
|
|
23
|
+
shared_root="$(git_shared_root 2>/dev/null)"
|
|
24
|
+
[[ -z "$shared_root" ]] && shared_root="$root"
|
|
25
|
+
|
|
26
|
+
info "Copying env files from '$root' -> '$target_path'"
|
|
27
|
+
gw_env_copy_files "$root" "$target_path" "$shared_root"
|
|
28
|
+
info "Env files synced to '$target_branch'"
|
|
29
|
+
}
|
|
@@ -25,11 +25,16 @@ bridge_create_worktree() {
|
|
|
25
25
|
shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
|
|
26
26
|
fi
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
local env_copy_bin
|
|
29
|
+
if ! env_copy_bin="$(gw_env_copy_bin)"; then
|
|
30
|
+
json_error "gw-env-copy binary not found"
|
|
31
|
+
return 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
"$env_copy_bin" --source "$source_path" --target "$target_path" --repo "$shared_root" --format boolean --quiet >/dev/null
|
|
30
35
|
|
|
31
36
|
echo "\"$worktree_path\""
|
|
32
37
|
else
|
|
33
38
|
json_error "Not a git repository"
|
|
34
39
|
fi
|
|
35
|
-
}
|
|
40
|
+
}
|
|
@@ -6,7 +6,7 @@ cmd_checkout_worktree() {
|
|
|
6
6
|
local root; root="$(git_require_repo_root)" || return 1
|
|
7
7
|
local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
|
|
8
8
|
[[ -z "$shared_root" ]] && shared_root="$root"
|
|
9
|
-
local source_root="$
|
|
9
|
+
local source_root="$root"
|
|
10
10
|
|
|
11
11
|
if [[ "${1-}" == "-b" ]]; then
|
|
12
12
|
[[ -z "${2-}" ]] && { err "Branch name required for -b"; echo "Usage: gw checkout -b <branch>"; return 1; }
|
|
@@ -30,7 +30,7 @@ cmd_checkout_worktree() {
|
|
|
30
30
|
# Create worktree for existing branch
|
|
31
31
|
info "🌳 Creating worktree for existing branch '$branch_name'..."
|
|
32
32
|
wt="$(git_ensure_worktree_for_branch "$branch_name")"
|
|
33
|
-
|
|
33
|
+
gw_env_copy_files "$source_root" "$wt" "$shared_root"
|
|
34
34
|
echo "cd \"$wt\""
|
|
35
35
|
fi
|
|
36
36
|
return 0
|
|
@@ -57,7 +57,7 @@ cmd_checkout_worktree() {
|
|
|
57
57
|
if git_branch_exists "$branch_name"; then
|
|
58
58
|
info "🌳 Creating worktree for existing branch '$branch_name'..."
|
|
59
59
|
wt="$(git_ensure_worktree_for_branch "$branch_name")"
|
|
60
|
-
|
|
60
|
+
gw_env_copy_files "$source_root" "$wt" "$shared_root"
|
|
61
61
|
echo "cd \"$wt\""
|
|
62
62
|
return 0
|
|
63
63
|
else
|
|
@@ -10,13 +10,13 @@ cmd_create_worktree() {
|
|
|
10
10
|
local root; root="$(git_require_repo_root)" || return 1
|
|
11
11
|
local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
|
|
12
12
|
[[ -z "$shared_root" ]] && shared_root="$root"
|
|
13
|
-
local source_root="$
|
|
13
|
+
local source_root="$root"
|
|
14
14
|
|
|
15
15
|
local dir; dir="$(git_build_worktree_path "$branch_name")"
|
|
16
16
|
|
|
17
17
|
info "🌳 Creating branch '$branch_name' and worktree at '$dir'..."
|
|
18
18
|
dir="$(git_create_branch_and_worktree "$branch_name")"
|
|
19
|
-
|
|
19
|
+
gw_env_copy_files "$source_root" "$dir" "$shared_root"
|
|
20
20
|
info "✅ Success! Worktree created at '$dir'."
|
|
21
21
|
echo "cd \"$dir\""
|
|
22
22
|
(cd "$dir" && git_status_short)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
|
|
3
|
+
gw_env_copy_bin() {
|
|
4
|
+
local candidate=""
|
|
5
|
+
|
|
6
|
+
if [[ -n "${GW_ENV_COPY_BIN:-}" && -x "$GW_ENV_COPY_BIN" ]]; then
|
|
7
|
+
printf '%s\n' "$GW_ENV_COPY_BIN"
|
|
8
|
+
return 0
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
if [[ -n "${SCRIPT_DIR:-}" ]]; then
|
|
12
|
+
candidate="$SCRIPT_DIR/gw-env-copy"
|
|
13
|
+
if [[ -x "$candidate" ]]; then
|
|
14
|
+
printf '%s\n' "$candidate"
|
|
15
|
+
return 0
|
|
16
|
+
fi
|
|
17
|
+
candidate="$SCRIPT_DIR/gw-tui/target/release/gw-env-copy"
|
|
18
|
+
if [[ -x "$candidate" ]]; then
|
|
19
|
+
printf '%s\n' "$candidate"
|
|
20
|
+
return 0
|
|
21
|
+
fi
|
|
22
|
+
candidate="$SCRIPT_DIR/gw-tui/target/debug/gw-env-copy"
|
|
23
|
+
if [[ -x "$candidate" ]]; then
|
|
24
|
+
printf '%s\n' "$candidate"
|
|
25
|
+
return 0
|
|
26
|
+
fi
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if [[ -n "${GW_ORCHESTRATOR_ROOT:-}" ]]; then
|
|
30
|
+
candidate="$GW_ORCHESTRATOR_ROOT/gw-env-copy"
|
|
31
|
+
if [[ -x "$candidate" ]]; then
|
|
32
|
+
printf '%s\n' "$candidate"
|
|
33
|
+
return 0
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if command -v gw-env-copy >/dev/null 2>&1; then
|
|
38
|
+
command -v gw-env-copy
|
|
39
|
+
return 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
return 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
gw_env_copy_files() {
|
|
46
|
+
local source_root="$1"
|
|
47
|
+
local target_root="$2"
|
|
48
|
+
local repo_root="${3:-$source_root}"
|
|
49
|
+
|
|
50
|
+
local bin
|
|
51
|
+
if ! bin="$(gw_env_copy_bin)"; then
|
|
52
|
+
if declare -f err >/dev/null 2>&1; then
|
|
53
|
+
err "gw-env-copy binary not found"
|
|
54
|
+
else
|
|
55
|
+
printf 'Error: %s\n' "gw-env-copy binary not found" >&2
|
|
56
|
+
fi
|
|
57
|
+
return 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
"$bin" \
|
|
61
|
+
--source "$source_root" \
|
|
62
|
+
--target "$target_root" \
|
|
63
|
+
--repo "$repo_root" \
|
|
64
|
+
--format text
|
|
65
|
+
}
|
|
@@ -37,9 +37,17 @@ get_npm_latest_version() {
|
|
|
37
37
|
# Function to get latest homebrew version
|
|
38
38
|
get_brew_latest_version() {
|
|
39
39
|
if command -v brew >/dev/null 2>&1; then
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
if command -v jq >/dev/null 2>&1; then
|
|
41
|
+
brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
|
|
42
|
+
jq -r '.[0].versions.stable // "unknown"' 2>/dev/null || \
|
|
43
|
+
echo "unknown"
|
|
44
|
+
elif command -v node >/dev/null 2>&1; then
|
|
45
|
+
brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
|
|
46
|
+
node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d[0]?.versions?.stable||'unknown');" 2>/dev/null || \
|
|
47
|
+
echo "unknown"
|
|
48
|
+
else
|
|
49
|
+
echo "unknown"
|
|
50
|
+
fi
|
|
43
51
|
else
|
|
44
52
|
echo "brew_not_found"
|
|
45
53
|
fi
|
|
@@ -106,9 +114,18 @@ check_updates() {
|
|
|
106
114
|
latest_version=$(get_npm_latest_version)
|
|
107
115
|
;;
|
|
108
116
|
"brew")
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
if command -v jq >/dev/null 2>&1; then
|
|
118
|
+
current_version=$(brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
|
|
119
|
+
jq -r '.[0].installed[0].version // ""' 2>/dev/null)
|
|
120
|
+
elif command -v node >/dev/null 2>&1; then
|
|
121
|
+
current_version=$(brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
|
|
122
|
+
node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d[0]?.installed?.[0]?.version||'');" 2>/dev/null)
|
|
123
|
+
else
|
|
124
|
+
current_version=""
|
|
125
|
+
fi
|
|
126
|
+
if [[ -z "$current_version" ]]; then
|
|
127
|
+
current_version="$CURRENT_VERSION"
|
|
128
|
+
fi
|
|
112
129
|
latest_version=$(get_brew_latest_version)
|
|
113
130
|
;;
|
|
114
131
|
"local")
|
|
@@ -171,4 +188,4 @@ check_updates() {
|
|
|
171
188
|
}
|
|
172
189
|
|
|
173
190
|
# Run the update check
|
|
174
|
-
check_updates "$@"
|
|
191
|
+
check_updates "$@"
|
|
@@ -7,7 +7,7 @@ cmd_new_tmux_session_with_command() {
|
|
|
7
7
|
local root; root="$(git_require_repo_root)" || return 1
|
|
8
8
|
local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
|
|
9
9
|
[[ -z "$shared_root" ]] && shared_root="$root"
|
|
10
|
-
local source_root
|
|
10
|
+
local source_root="$root"
|
|
11
11
|
|
|
12
12
|
if ! tmux_available; then
|
|
13
13
|
err "tmux not installed"
|
|
@@ -30,7 +30,7 @@ cmd_new_tmux_session_with_command() {
|
|
|
30
30
|
if [[ -z "$wt" ]]; then
|
|
31
31
|
info "🌳 Creating worktree for existing branch '$branch_name'..."
|
|
32
32
|
wt="$(git_ensure_worktree_for_branch "$branch_name")"
|
|
33
|
-
|
|
33
|
+
gw_env_copy_files "$source_root" "$wt" "$shared_root"
|
|
34
34
|
fi
|
|
35
35
|
else
|
|
36
36
|
err "Branch '$branch_name' does not exist"
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
###############################################################################
|
|
4
|
-
# copy_env.sh – Shared helpers for copying .env/config files into worktrees
|
|
5
|
-
# ---------------------------------------------------------------------------
|
|
6
|
-
# Provides reusable functions consumed by commands.sh, gw-bridge.sh, and any
|
|
7
|
-
# other shell entry point. Adds structured logging so we can troubleshoot which
|
|
8
|
-
# files were selected and whether individual copy operations succeeded.
|
|
9
|
-
###############################################################################
|
|
10
|
-
|
|
11
|
-
# Global variables populated by copy_env_files
|
|
12
|
-
COPY_ENV_LAST_COPIED=0
|
|
13
|
-
COPY_ENV_LAST_TARGET=""
|
|
14
|
-
COPY_ENV_LOG_FILE=""
|
|
15
|
-
|
|
16
|
-
# Internal: write a log line (timestamped). Respects COPY_ENV_VERBOSE: if set to
|
|
17
|
-
# 1, the message is also echoed to stderr for real-time debugging.
|
|
18
|
-
copy_env_log() {
|
|
19
|
-
local level="$1"
|
|
20
|
-
shift
|
|
21
|
-
local message="$*"
|
|
22
|
-
local timestamp
|
|
23
|
-
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
24
|
-
if [[ -n "$COPY_ENV_LOG_FILE" ]]; then
|
|
25
|
-
if ! printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >>"$COPY_ENV_LOG_FILE" 2>/dev/null; then
|
|
26
|
-
# If we cannot write to the configured log file, drop logging to disk
|
|
27
|
-
COPY_ENV_LOG_FILE=""
|
|
28
|
-
fi
|
|
29
|
-
fi
|
|
30
|
-
if [[ ${COPY_ENV_VERBOSE:-0} -eq 1 ]]; then
|
|
31
|
-
printf '[copy-env][%s] %s\n' "$level" "$message" >&2
|
|
32
|
-
fi
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# Determine which locations to copy by reading <repo>/.orchestra/options.json.
|
|
36
|
-
copy_env_load_locations() {
|
|
37
|
-
local repo_root="$1"
|
|
38
|
-
local config_file="$repo_root/.orchestra/options.json"
|
|
39
|
-
|
|
40
|
-
if [[ -f "$config_file" ]]; then
|
|
41
|
-
if command -v jq >/dev/null 2>&1; then
|
|
42
|
-
jq -r '(.copy_locations // ["/"])[]' "$config_file" 2>/dev/null || printf '/\n'
|
|
43
|
-
return
|
|
44
|
-
elif command -v python3 >/dev/null 2>&1; then
|
|
45
|
-
python3 - "$config_file" <<'PY'
|
|
46
|
-
import json
|
|
47
|
-
import sys
|
|
48
|
-
from pathlib import Path
|
|
49
|
-
|
|
50
|
-
config_path = Path(sys.argv[1])
|
|
51
|
-
try:
|
|
52
|
-
data = json.loads(config_path.read_text())
|
|
53
|
-
except Exception:
|
|
54
|
-
data = {}
|
|
55
|
-
locations = data.get("copy_locations")
|
|
56
|
-
if isinstance(locations, list) and locations:
|
|
57
|
-
for entry in locations:
|
|
58
|
-
if isinstance(entry, str):
|
|
59
|
-
print(entry)
|
|
60
|
-
else:
|
|
61
|
-
print("/")
|
|
62
|
-
PY
|
|
63
|
-
return
|
|
64
|
-
fi
|
|
65
|
-
fi
|
|
66
|
-
# Fallback: default to repo root
|
|
67
|
-
printf '/\n'
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
# Copy .env files from the source worktree into the target worktree.
|
|
71
|
-
# Arguments:
|
|
72
|
-
# $1 - source root (worktree we are copying from)
|
|
73
|
-
# $2 - target root (new or existing worktree)
|
|
74
|
-
# $3 - repository shared root (defaults to source root)
|
|
75
|
-
# $4 - quiet flag (1 suppresses user-facing echo output)
|
|
76
|
-
copy_env_files() {
|
|
77
|
-
local source_root="$1"
|
|
78
|
-
local target_root="$2"
|
|
79
|
-
local repo_root="${3:-$source_root}"
|
|
80
|
-
local quiet="${4:-0}"
|
|
81
|
-
|
|
82
|
-
COPY_ENV_LAST_COPIED=0
|
|
83
|
-
COPY_ENV_LAST_TARGET="$target_root"
|
|
84
|
-
|
|
85
|
-
local enable_logs=${COPY_ENV_ENABLE_LOG:-0}
|
|
86
|
-
local log_file="${COPY_ENV_LOG_FILE:-}"
|
|
87
|
-
local log_dir="${COPY_ENV_LOG_DIR:-}"
|
|
88
|
-
|
|
89
|
-
if [[ -n "$log_file" ]]; then
|
|
90
|
-
mkdir -p "$(dirname "$log_file")" 2>/dev/null || true
|
|
91
|
-
COPY_ENV_LOG_FILE="$log_file"
|
|
92
|
-
elif [[ $enable_logs -eq 1 || -n "$log_dir" ]]; then
|
|
93
|
-
log_dir="${log_dir:-$repo_root/.orchestra/logs}"
|
|
94
|
-
mkdir -p "$log_dir" 2>/dev/null || true
|
|
95
|
-
COPY_ENV_LOG_FILE="$log_dir/copy-env.log"
|
|
96
|
-
else
|
|
97
|
-
COPY_ENV_LOG_FILE=""
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
copy_env_log "INFO" "Starting copy (source=$source_root, target=$target_root, repo=$repo_root)"
|
|
101
|
-
|
|
102
|
-
local override_locations="${COPY_ENV_OVERRIDE_LOCATIONS:-}"
|
|
103
|
-
unset COPY_ENV_OVERRIDE_LOCATIONS
|
|
104
|
-
local locations=()
|
|
105
|
-
if [[ -n "$override_locations" ]]; then
|
|
106
|
-
while IFS= read -r line; do
|
|
107
|
-
[[ -z "$line" ]] && continue
|
|
108
|
-
locations+=("$line")
|
|
109
|
-
done <<<"$override_locations"
|
|
110
|
-
else
|
|
111
|
-
while IFS= read -r line; do
|
|
112
|
-
[[ -z "$line" ]] && continue
|
|
113
|
-
locations+=("$line")
|
|
114
|
-
done < <(copy_env_load_locations "$repo_root")
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
if [[ ${#locations[@]} -eq 0 ]]; then
|
|
118
|
-
locations=("/")
|
|
119
|
-
fi
|
|
120
|
-
|
|
121
|
-
local bases=("$source_root")
|
|
122
|
-
if [[ -n "$repo_root" && "$repo_root" != "$source_root" ]]; then
|
|
123
|
-
bases+=("$repo_root")
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
|
-
local overall_copied=0
|
|
127
|
-
local overall_failed=0
|
|
128
|
-
|
|
129
|
-
(( quiet != 1 )) && echo "🔧 Copying .env files to worktree..."
|
|
130
|
-
|
|
131
|
-
for raw_location in "${locations[@]}"; do
|
|
132
|
-
local trimmed
|
|
133
|
-
trimmed="$(printf '%s' "$raw_location" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
|
134
|
-
[[ -z "$trimmed" ]] && continue
|
|
135
|
-
|
|
136
|
-
local rel="${trimmed#/}"
|
|
137
|
-
rel="${rel#./}"
|
|
138
|
-
|
|
139
|
-
local location_copied=0
|
|
140
|
-
local location_failed=0
|
|
141
|
-
|
|
142
|
-
for base in "${bases[@]}"; do
|
|
143
|
-
[[ -z "$base" ]] && continue
|
|
144
|
-
|
|
145
|
-
copy_env_log "DEBUG" "Checking location '$trimmed' in base '$base'"
|
|
146
|
-
|
|
147
|
-
if [[ "$trimmed" == "/" ]]; then
|
|
148
|
-
local found_root=0
|
|
149
|
-
while IFS= read -r -d '' env_file; do
|
|
150
|
-
[[ -f "$env_file" ]] || continue
|
|
151
|
-
local rel_path="${env_file#$base/}"
|
|
152
|
-
if [[ "$rel_path" == "$env_file" ]]; then
|
|
153
|
-
rel_path="$(basename "$env_file")"
|
|
154
|
-
fi
|
|
155
|
-
local dest_path="$target_root/$rel_path"
|
|
156
|
-
mkdir -p "$(dirname "$dest_path")"
|
|
157
|
-
local err_msg
|
|
158
|
-
err_msg="$(cp "$env_file" "$dest_path" 2>&1)"
|
|
159
|
-
if [[ $? -eq 0 ]]; then
|
|
160
|
-
(( overall_copied++ ))
|
|
161
|
-
location_copied=1
|
|
162
|
-
found_root=1
|
|
163
|
-
(( quiet != 1 )) && echo " ✅ Copied $rel_path from $base/"
|
|
164
|
-
copy_env_log "INFO" "Copied $rel_path from $base/"
|
|
165
|
-
else
|
|
166
|
-
(( overall_failed++ ))
|
|
167
|
-
(( location_failed++ ))
|
|
168
|
-
(( quiet != 1 )) && echo " ❌ Failed to copy $rel_path from $base/"
|
|
169
|
-
copy_env_log "ERROR" "Failed to copy $rel_path from $base/: ${err_msg:-unknown error}"
|
|
170
|
-
fi
|
|
171
|
-
done < <(find "$base" \
|
|
172
|
-
\( -path "$target_root" -o -path "$target_root/*" -o -path "$base/.git" -o -path "$base/.git/*" -o -path "$base/worktrees" -o -path "$base/worktrees/*" \) -prune -o \
|
|
173
|
-
-type f -name '.env*' -print0 2>/dev/null)
|
|
174
|
-
if [[ $found_root -eq 1 ]]; then
|
|
175
|
-
break
|
|
176
|
-
fi
|
|
177
|
-
else
|
|
178
|
-
local source_path="$base/$rel"
|
|
179
|
-
if [[ -d "$source_path" ]]; then
|
|
180
|
-
local dest_dir="$target_root/$rel"
|
|
181
|
-
mkdir -p "$dest_dir"
|
|
182
|
-
local found_any=0
|
|
183
|
-
for env_file in "$source_path"/.env*; do
|
|
184
|
-
[[ -f "$env_file" ]] || continue
|
|
185
|
-
local err_msg
|
|
186
|
-
err_msg="$(cp "$env_file" "$dest_dir/$(basename "$env_file")" 2>&1)"
|
|
187
|
-
if [[ $? -eq 0 ]]; then
|
|
188
|
-
(( overall_copied++ ))
|
|
189
|
-
location_copied=1
|
|
190
|
-
found_any=1
|
|
191
|
-
(( quiet != 1 )) && echo " ✅ Copied $(basename "$env_file") from $rel/"
|
|
192
|
-
copy_env_log "INFO" "Copied $(basename "$env_file") from $rel/"
|
|
193
|
-
else
|
|
194
|
-
(( overall_failed++ ))
|
|
195
|
-
(( location_failed++ ))
|
|
196
|
-
(( quiet != 1 )) && echo " ❌ Failed to copy $(basename "$env_file") from $rel/"
|
|
197
|
-
copy_env_log "ERROR" "Failed to copy $(basename "$env_file") from $rel/: ${err_msg:-unknown error}"
|
|
198
|
-
fi
|
|
199
|
-
done
|
|
200
|
-
if [[ $found_any -eq 1 ]]; then
|
|
201
|
-
break
|
|
202
|
-
fi
|
|
203
|
-
elif [[ -f "$source_path" ]]; then
|
|
204
|
-
local dest_dir
|
|
205
|
-
dest_dir="$(dirname "$rel")"
|
|
206
|
-
if [[ "$dest_dir" != "." ]]; then
|
|
207
|
-
mkdir -p "$target_root/$dest_dir"
|
|
208
|
-
else
|
|
209
|
-
mkdir -p "$target_root"
|
|
210
|
-
fi
|
|
211
|
-
local err_msg
|
|
212
|
-
err_msg="$(cp "$source_path" "$target_root/$rel" 2>&1)"
|
|
213
|
-
if [[ $? -eq 0 ]]; then
|
|
214
|
-
(( overall_copied++ ))
|
|
215
|
-
location_copied=1
|
|
216
|
-
(( quiet != 1 )) && echo " ✅ Copied $rel"
|
|
217
|
-
copy_env_log "INFO" "Copied file $rel"
|
|
218
|
-
else
|
|
219
|
-
(( overall_failed++ ))
|
|
220
|
-
(( location_failed++ ))
|
|
221
|
-
(( quiet != 1 )) && echo " ❌ Failed to copy $rel"
|
|
222
|
-
copy_env_log "ERROR" "Failed to copy file $rel: ${err_msg:-unknown error}"
|
|
223
|
-
fi
|
|
224
|
-
break
|
|
225
|
-
fi
|
|
226
|
-
fi
|
|
227
|
-
done
|
|
228
|
-
|
|
229
|
-
if [[ $location_copied -eq 0 ]]; then
|
|
230
|
-
if [[ "$trimmed" == "/" ]]; then
|
|
231
|
-
(( quiet != 1 )) && echo " ⚠️ No .env files found in any root"
|
|
232
|
-
copy_env_log "WARN" "No .env files discovered for '/'"
|
|
233
|
-
else
|
|
234
|
-
(( quiet != 1 )) && echo " ⚠️ No .env files found for $trimmed"
|
|
235
|
-
copy_env_log "WARN" "No .env files found for '$trimmed'"
|
|
236
|
-
fi
|
|
237
|
-
fi
|
|
238
|
-
done
|
|
239
|
-
|
|
240
|
-
COPY_ENV_LAST_COPIED=$overall_copied
|
|
241
|
-
|
|
242
|
-
if [[ $overall_copied -eq 0 ]]; then
|
|
243
|
-
copy_env_log "WARN" "Completed with no files copied"
|
|
244
|
-
(( quiet != 1 )) && echo " ⚠️ No environment files copied."
|
|
245
|
-
else
|
|
246
|
-
copy_env_log "INFO" "Completed: $overall_copied file(s) copied, failures=$overall_failed"
|
|
247
|
-
(( quiet != 1 )) && echo " ✅ Environment files copied ($overall_copied)."
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
return 0
|
|
251
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# shellcheck shell=bash
|
|
4
|
-
|
|
5
|
-
# Copy environment files
|
|
6
|
-
bridge_copy_env_files() {
|
|
7
|
-
if [[ -z "${1:-}" ]] || [[ -z "${2:-}" ]]; then
|
|
8
|
-
json_error "Source and target paths required"
|
|
9
|
-
return 1
|
|
10
|
-
fi
|
|
11
|
-
source_path="$1"
|
|
12
|
-
target_path="$2"
|
|
13
|
-
raw_locations="${3-}"
|
|
14
|
-
|
|
15
|
-
repo_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
|
|
16
|
-
shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null || echo "")"
|
|
17
|
-
if [[ -n "$shared_root" ]]; then
|
|
18
|
-
if [[ "$shared_root" != /* ]]; then
|
|
19
|
-
shared_root="$(cd "$source_path" 2>/dev/null && pwd -P)/$shared_root"
|
|
20
|
-
fi
|
|
21
|
-
shared_root="$(dirname "$shared_root")"
|
|
22
|
-
else
|
|
23
|
-
shared_root="$repo_root"
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
if [[ -n "$raw_locations" ]]; then
|
|
27
|
-
if have_cmd jq; then
|
|
28
|
-
override_list="$(printf '%s' "$raw_locations" | jq -r '.[]' 2>/dev/null || printf '/\n')"
|
|
29
|
-
else
|
|
30
|
-
bridge_init_json_backend
|
|
31
|
-
case "$BRIDGE_JSON_BACKEND" in
|
|
32
|
-
python)
|
|
33
|
-
override_list="$(python3 - <<'PY' "$raw_locations"
|
|
34
|
-
import json
|
|
35
|
-
import sys
|
|
36
|
-
try:
|
|
37
|
-
data = json.loads(sys.argv[1])
|
|
38
|
-
if isinstance(data, list) and data:
|
|
39
|
-
for item in data:
|
|
40
|
-
if isinstance(item, str):
|
|
41
|
-
print(item)
|
|
42
|
-
else:
|
|
43
|
-
print('/')
|
|
44
|
-
except Exception:
|
|
45
|
-
print('/')
|
|
46
|
-
PY
|
|
47
|
-
)"
|
|
48
|
-
;;
|
|
49
|
-
node)
|
|
50
|
-
override_list="$(node - <<'NODE' "$raw_locations"
|
|
51
|
-
let data;
|
|
52
|
-
try {
|
|
53
|
-
data = JSON.parse(process.argv[2]);
|
|
54
|
-
if (Array.isArray(data) && data.length) {
|
|
55
|
-
for (const item of data) {
|
|
56
|
-
if (typeof item === 'string') {
|
|
57
|
-
process.stdout.write(item + '\n');
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
process.stdout.write('/\n');
|
|
62
|
-
}
|
|
63
|
-
} catch (err) {
|
|
64
|
-
process.stdout.write('/\n');
|
|
65
|
-
}
|
|
66
|
-
NODE
|
|
67
|
-
)"
|
|
68
|
-
;;
|
|
69
|
-
*)
|
|
70
|
-
override_list="/\n"
|
|
71
|
-
;;
|
|
72
|
-
esac
|
|
73
|
-
fi
|
|
74
|
-
else
|
|
75
|
-
override_list=""
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
COPY_ENV_OVERRIDE_LOCATIONS="$override_list" copy_env_files "$source_path" "$target_path" "$shared_root" 1 >/dev/null
|
|
79
|
-
if [[ ${COPY_ENV_LAST_COPIED:-0} -gt 0 ]]; then
|
|
80
|
-
echo "true"
|
|
81
|
-
else
|
|
82
|
-
echo "false"
|
|
83
|
-
fi
|
|
84
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# shellcheck shell=bash
|
|
2
|
-
|
|
3
|
-
cmd_copy_env() {
|
|
4
|
-
local target_branch="${1-}"
|
|
5
|
-
if [[ -z "$target_branch" ]]; then
|
|
6
|
-
err "Target worktree name required"
|
|
7
|
-
echo "Usage: gw copy-env <worktreename>"
|
|
8
|
-
return 1
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
git_require_repo || return 1
|
|
12
|
-
|
|
13
|
-
# Find absolute path of target worktree by branch name
|
|
14
|
-
local target_path; target_path="$(git_branch_to_worktree_path "$target_branch")"
|
|
15
|
-
if [[ -z "$target_path" ]]; then
|
|
16
|
-
err "Worktree for branch '$target_branch' not found"
|
|
17
|
-
return 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
local root; root="$(git_repo_root)"
|
|
21
|
-
local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
|
|
22
|
-
[[ -z "$shared_root" ]] && shared_root="$root"
|
|
23
|
-
local source_root="$root"
|
|
24
|
-
info "🔧 Copying env files from '$source_root' -> '$target_path'"
|
|
25
|
-
copy_env_files "$source_root" "$target_path" "$shared_root"
|
|
26
|
-
info "✅ Env files synced to '$target_branch'"
|
|
27
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
# shellcheck shell=bash
|
|
2
|
-
|
|
3
|
-
copy_env_files() {
|
|
4
|
-
local source_root="$1"
|
|
5
|
-
local target_root="$2"
|
|
6
|
-
local repo_root="${3:-$source_root}"
|
|
7
|
-
local quiet="${4:-0}"
|
|
8
|
-
|
|
9
|
-
COPY_ENV_LAST_COPIED=0
|
|
10
|
-
COPY_ENV_LAST_TARGET="$target_root"
|
|
11
|
-
|
|
12
|
-
local enable_logs=${COPY_ENV_ENABLE_LOG:-0}
|
|
13
|
-
local log_file="${COPY_ENV_LOG_FILE:-}"
|
|
14
|
-
local log_dir="${COPY_ENV_LOG_DIR:-}"
|
|
15
|
-
|
|
16
|
-
if [[ -n "$log_file" ]]; then
|
|
17
|
-
mkdir -p "$(dirname "$log_file")" 2>/dev/null || true
|
|
18
|
-
COPY_ENV_LOG_FILE="$log_file"
|
|
19
|
-
elif [[ $enable_logs -eq 1 || -n "$log_dir" ]]; then
|
|
20
|
-
log_dir="${log_dir:-$repo_root/.orchestra/logs}"
|
|
21
|
-
mkdir -p "$log_dir" 2>/dev/null || true
|
|
22
|
-
COPY_ENV_LOG_FILE="$log_dir/copy-env.log"
|
|
23
|
-
else
|
|
24
|
-
COPY_ENV_LOG_FILE=""
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
copy_env_log "INFO" "Starting copy (source=$source_root, target=$target_root, repo=$repo_root)"
|
|
28
|
-
|
|
29
|
-
local override_locations="${COPY_ENV_OVERRIDE_LOCATIONS:-}"
|
|
30
|
-
unset COPY_ENV_OVERRIDE_LOCATIONS
|
|
31
|
-
local locations=()
|
|
32
|
-
if [[ -n "$override_locations" ]]; then
|
|
33
|
-
while IFS= read -r line; do
|
|
34
|
-
[[ -z "$line" ]] && continue
|
|
35
|
-
locations+=("$line")
|
|
36
|
-
done <<<"$override_locations"
|
|
37
|
-
else
|
|
38
|
-
while IFS= read -r line; do
|
|
39
|
-
[[ -z "$line" ]] && continue
|
|
40
|
-
locations+=("$line")
|
|
41
|
-
done < <(copy_env_load_locations "$repo_root")
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
if [[ ${#locations[@]} -eq 0 ]]; then
|
|
45
|
-
locations=("$COPY_ENV_DEFAULT_LOCATION")
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
local bases=("$source_root")
|
|
49
|
-
if [[ -n "$repo_root" && "$repo_root" != "$source_root" ]]; then
|
|
50
|
-
bases+=("$repo_root")
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
local overall_copied=0
|
|
54
|
-
local overall_failed=0
|
|
55
|
-
|
|
56
|
-
(( quiet != 1 )) && echo "🔧 Copying .env files to worktree..."
|
|
57
|
-
|
|
58
|
-
for raw_location in "${locations[@]}"; do
|
|
59
|
-
local trimmed
|
|
60
|
-
trimmed="$(printf '%s' "$raw_location" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
|
61
|
-
[[ -z "$trimmed" ]] && continue
|
|
62
|
-
|
|
63
|
-
local rel="${trimmed#/}"
|
|
64
|
-
rel="${rel#./}"
|
|
65
|
-
|
|
66
|
-
local location_copied=0
|
|
67
|
-
local location_failed=0
|
|
68
|
-
|
|
69
|
-
for base in "${bases[@]}"; do
|
|
70
|
-
[[ -z "$base" ]] && continue
|
|
71
|
-
|
|
72
|
-
copy_env_log "DEBUG" "Checking location '$trimmed' in base '$base'"
|
|
73
|
-
|
|
74
|
-
if [[ "$trimmed" == "/" ]]; then
|
|
75
|
-
local found_root=0
|
|
76
|
-
while IFS= read -r -d '' env_file; do
|
|
77
|
-
[[ -f "$env_file" ]] || continue
|
|
78
|
-
local rel_path="${env_file#$base/}"
|
|
79
|
-
if [[ "$rel_path" == "$env_file" ]]; then
|
|
80
|
-
rel_path="$(basename "$env_file")"
|
|
81
|
-
fi
|
|
82
|
-
local dest_path="$target_root/$rel_path"
|
|
83
|
-
mkdir -p "$(dirname "$dest_path")"
|
|
84
|
-
local err_msg
|
|
85
|
-
err_msg="$(cp "$env_file" "$dest_path" 2>&1)"
|
|
86
|
-
if [[ $? -eq 0 ]]; then
|
|
87
|
-
(( overall_copied++ ))
|
|
88
|
-
location_copied=1
|
|
89
|
-
found_root=1
|
|
90
|
-
(( quiet != 1 )) && echo " ✅ Copied $rel_path from $base/"
|
|
91
|
-
copy_env_log "INFO" "Copied $rel_path from $base/"
|
|
92
|
-
else
|
|
93
|
-
(( overall_failed++ ))
|
|
94
|
-
(( location_failed++ ))
|
|
95
|
-
(( quiet != 1 )) && echo " ❌ Failed to copy $rel_path from $base/"
|
|
96
|
-
copy_env_log "ERROR" "Failed to copy $rel_path from $base/: ${err_msg:-unknown error}"
|
|
97
|
-
fi
|
|
98
|
-
done < <(find "$base" \
|
|
99
|
-
\( -path "$target_root" -o -path "$target_root/*" -o -path "$base/.git" -o -path "$base/.git/*" -o -path "$base/worktrees" -o -path "$base/worktrees/*" \) -prune -o \
|
|
100
|
-
-type f -name '.env*' -print0 2>/dev/null)
|
|
101
|
-
if [[ $found_root -eq 1 ]]; then
|
|
102
|
-
break
|
|
103
|
-
fi
|
|
104
|
-
else
|
|
105
|
-
local source_path="$base/$rel"
|
|
106
|
-
if [[ -d "$source_path" ]]; then
|
|
107
|
-
local dest_dir="$target_root/$rel"
|
|
108
|
-
mkdir -p "$dest_dir"
|
|
109
|
-
local found_any=0
|
|
110
|
-
for env_file in "$source_path"/.env*; do
|
|
111
|
-
[[ -f "$env_file" ]] || continue
|
|
112
|
-
local err_msg
|
|
113
|
-
err_msg="$(cp "$env_file" "$dest_dir/$(basename "$env_file")" 2>&1)"
|
|
114
|
-
if [[ $? -eq 0 ]]; then
|
|
115
|
-
(( overall_copied++ ))
|
|
116
|
-
location_copied=1
|
|
117
|
-
found_any=1
|
|
118
|
-
(( quiet != 1 )) && echo " ✅ Copied $(basename "$env_file") from $rel/"
|
|
119
|
-
copy_env_log "INFO" "Copied $(basename "$env_file") from $rel/"
|
|
120
|
-
else
|
|
121
|
-
(( overall_failed++ ))
|
|
122
|
-
(( location_failed++ ))
|
|
123
|
-
(( quiet != 1 )) && echo " ❌ Failed to copy $(basename "$env_file") from $rel/"
|
|
124
|
-
copy_env_log "ERROR" "Failed to copy $(basename "$env_file") from $rel/: ${err_msg:-unknown error}"
|
|
125
|
-
fi
|
|
126
|
-
done
|
|
127
|
-
if [[ $found_any -eq 1 ]]; then
|
|
128
|
-
break
|
|
129
|
-
fi
|
|
130
|
-
elif [[ -f "$source_path" ]]; then
|
|
131
|
-
local dest_dir
|
|
132
|
-
dest_dir="$(dirname "$rel")"
|
|
133
|
-
if [[ "$dest_dir" != "." ]]; then
|
|
134
|
-
mkdir -p "$target_root/$dest_dir"
|
|
135
|
-
else
|
|
136
|
-
mkdir -p "$target_root"
|
|
137
|
-
fi
|
|
138
|
-
local err_msg
|
|
139
|
-
err_msg="$(cp "$source_path" "$target_root/$rel" 2>&1)"
|
|
140
|
-
if [[ $? -eq 0 ]]; then
|
|
141
|
-
(( overall_copied++ ))
|
|
142
|
-
location_copied=1
|
|
143
|
-
(( quiet != 1 )) && echo " ✅ Copied $rel"
|
|
144
|
-
copy_env_log "INFO" "Copied file $rel"
|
|
145
|
-
else
|
|
146
|
-
(( overall_failed++ ))
|
|
147
|
-
(( location_failed++ ))
|
|
148
|
-
(( quiet != 1 )) && echo " ❌ Failed to copy $rel"
|
|
149
|
-
copy_env_log "ERROR" "Failed to copy file $rel: ${err_msg:-unknown error}"
|
|
150
|
-
fi
|
|
151
|
-
break
|
|
152
|
-
fi
|
|
153
|
-
fi
|
|
154
|
-
done
|
|
155
|
-
|
|
156
|
-
if [[ $location_copied -eq 0 ]]; then
|
|
157
|
-
if [[ "$trimmed" == "/" ]]; then
|
|
158
|
-
(( quiet != 1 )) && echo " ⚠️ No .env files found in any root"
|
|
159
|
-
copy_env_log "WARN" "No .env files discovered for '/'"
|
|
160
|
-
else
|
|
161
|
-
(( quiet != 1 )) && echo " ⚠️ No .env files found for $trimmed"
|
|
162
|
-
copy_env_log "WARN" "No .env files found for '$trimmed'"
|
|
163
|
-
fi
|
|
164
|
-
fi
|
|
165
|
-
done
|
|
166
|
-
|
|
167
|
-
COPY_ENV_LAST_COPIED=$overall_copied
|
|
168
|
-
|
|
169
|
-
if [[ $overall_copied -eq 0 ]]; then
|
|
170
|
-
copy_env_log "WARN" "Completed with no files copied"
|
|
171
|
-
(( quiet != 1 )) && echo " ⚠️ No environment files copied."
|
|
172
|
-
else
|
|
173
|
-
copy_env_log "INFO" "Completed: $overall_copied file(s) copied, failures=$overall_failed"
|
|
174
|
-
(( quiet != 1 )) && echo " ✅ Environment files copied ($overall_copied)."
|
|
175
|
-
fi
|
|
176
|
-
|
|
177
|
-
return 0
|
|
178
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# shellcheck shell=bash
|
|
2
|
-
|
|
3
|
-
copy_env_init
|
|
4
|
-
source "$SCRIPT_DIR/api/git.sh"
|
|
5
|
-
source "$SCRIPT_DIR/api/tmux.sh"
|
|
6
|
-
|
|
7
|
-
SESSION="orchestra__performance-analysis__cdedeb95__20250827__155000__auto_test_session"
|
|
8
|
-
|
|
9
|
-
export GW_DEBUG_RENAME=1
|
|
10
|
-
|
|
11
|
-
echo "Testing parsing of: $SESSION"
|
|
12
|
-
echo ""
|
|
13
|
-
|
|
14
|
-
tmux_rename_session "$SESSION" "test_name" 2>&1
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# shellcheck shell=bash
|
|
2
|
-
|
|
3
|
-
copy_env_init() {
|
|
4
|
-
source "$SCRIPT_DIR/shell/env/copy_env_state.sh"
|
|
5
|
-
source "$SCRIPT_DIR/shell/env/copy_env_constants.sh"
|
|
6
|
-
source "$SCRIPT_DIR/shell/env/copy_env_logging.sh"
|
|
7
|
-
source "$SCRIPT_DIR/shell/env/copy_env_locations.sh"
|
|
8
|
-
source "$SCRIPT_DIR/shell/env/copy_env_core.sh"
|
|
9
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# shellcheck shell=bash
|
|
2
|
-
|
|
3
|
-
copy_env_load_locations() {
|
|
4
|
-
local repo_root="$1"
|
|
5
|
-
local config_file="$repo_root/.orchestra/options.json"
|
|
6
|
-
|
|
7
|
-
if [[ -f "$config_file" ]]; then
|
|
8
|
-
if command -v jq >/dev/null 2>&1; then
|
|
9
|
-
jq -r '(.copy_locations // ["/"])[]' "$config_file" 2>/dev/null || printf '/\n'
|
|
10
|
-
return
|
|
11
|
-
elif command -v python3 >/dev/null 2>&1; then
|
|
12
|
-
python3 - "$config_file" <<'PY'
|
|
13
|
-
import json
|
|
14
|
-
import sys
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
|
|
17
|
-
config_path = Path(sys.argv[1])
|
|
18
|
-
try:
|
|
19
|
-
data = json.loads(config_path.read_text())
|
|
20
|
-
except Exception:
|
|
21
|
-
data = {}
|
|
22
|
-
locations = data.get("copy_locations")
|
|
23
|
-
if isinstance(locations, list) and locations:
|
|
24
|
-
for entry in locations:
|
|
25
|
-
if isinstance(entry, str):
|
|
26
|
-
print(entry)
|
|
27
|
-
else:
|
|
28
|
-
print("/")
|
|
29
|
-
PY
|
|
30
|
-
return
|
|
31
|
-
fi
|
|
32
|
-
fi
|
|
33
|
-
printf '/\n'
|
|
34
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# shellcheck shell=bash
|
|
2
|
-
|
|
3
|
-
copy_env_log() {
|
|
4
|
-
local level="$1"
|
|
5
|
-
shift
|
|
6
|
-
local message="$*"
|
|
7
|
-
local timestamp
|
|
8
|
-
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
9
|
-
if [[ -n "$COPY_ENV_LOG_FILE" ]]; then
|
|
10
|
-
if ! printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >>"$COPY_ENV_LOG_FILE" 2>/dev/null; then
|
|
11
|
-
COPY_ENV_LOG_FILE=""
|
|
12
|
-
fi
|
|
13
|
-
fi
|
|
14
|
-
if [[ ${COPY_ENV_VERBOSE:-0} -eq 1 ]]; then
|
|
15
|
-
printf '[copy-env][%s] %s\n' "$level" "$message" >&2
|
|
16
|
-
fi
|
|
17
|
-
}
|