@wyxos/zephyr 0.2.21 → 0.2.22
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 +144 -144
- package/bin/zephyr.mjs +29 -29
- package/package.json +58 -58
- package/src/config/project.mjs +118 -118
- package/src/config/servers.mjs +57 -57
- package/src/dependency-scanner.mjs +412 -433
- package/src/deploy/local-repo.mjs +215 -215
- package/src/deploy/locks.mjs +171 -171
- package/src/deploy/preflight.mjs +117 -117
- package/src/deploy/remote-exec.mjs +99 -99
- package/src/deploy/snapshots.mjs +35 -35
- package/src/index.mjs +91 -91
- package/src/main.mjs +677 -652
- package/src/project/bootstrap.mjs +147 -147
- package/src/runtime/local-command.mjs +18 -18
- package/src/runtime/prompt.mjs +14 -14
- package/src/runtime/ssh-client.mjs +14 -14
- package/src/ssh/index.mjs +8 -8
- package/src/ssh/keys.mjs +146 -146
- package/src/ssh/ssh.mjs +134 -134
- package/src/utils/command.mjs +92 -92
- package/src/utils/config-flow.mjs +284 -284
- package/src/utils/git.mjs +91 -91
- package/src/utils/id.mjs +6 -6
- package/src/utils/log-file.mjs +76 -76
- package/src/utils/output.mjs +29 -29
- package/src/utils/paths.mjs +28 -28
- package/src/utils/php-version.mjs +137 -0
- package/src/utils/remote-path.mjs +23 -23
- package/src/utils/task-planner.mjs +99 -96
- package/src/version-checker.mjs +162 -162
package/src/config/project.mjs
CHANGED
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
|
|
3
|
-
import { ensureDirectory, getProjectConfigDir, getProjectConfigPath } from '../utils/paths.mjs'
|
|
4
|
-
import { generateId } from '../utils/id.mjs'
|
|
5
|
-
|
|
6
|
-
export function migrateApps(apps, servers) {
|
|
7
|
-
if (!Array.isArray(apps)) {
|
|
8
|
-
return { apps: [], needsMigration: false }
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const serverNameToId = new Map()
|
|
12
|
-
servers.forEach((server) => {
|
|
13
|
-
if (server.id && server.serverName) {
|
|
14
|
-
serverNameToId.set(server.serverName, server.id)
|
|
15
|
-
}
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
let needsMigration = false
|
|
19
|
-
const migrated = apps.map((app) => {
|
|
20
|
-
const updated = { ...app }
|
|
21
|
-
|
|
22
|
-
if (!app.id) {
|
|
23
|
-
needsMigration = true
|
|
24
|
-
updated.id = generateId()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (app.serverName && !app.serverId) {
|
|
28
|
-
const serverId = serverNameToId.get(app.serverName)
|
|
29
|
-
if (serverId) {
|
|
30
|
-
needsMigration = true
|
|
31
|
-
updated.serverId = serverId
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return updated
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
return { apps: migrated, needsMigration }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function migratePresets(presets, apps) {
|
|
42
|
-
if (!Array.isArray(presets)) {
|
|
43
|
-
return { presets: [], needsMigration: false }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const keyToAppId = new Map()
|
|
47
|
-
apps.forEach((app) => {
|
|
48
|
-
if (app.id && app.serverName && app.projectPath) {
|
|
49
|
-
const key = `${app.serverName}:${app.projectPath}`
|
|
50
|
-
keyToAppId.set(key, app.id)
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
let needsMigration = false
|
|
55
|
-
const migrated = presets.map((preset) => {
|
|
56
|
-
const updated = { ...preset }
|
|
57
|
-
|
|
58
|
-
if (preset.key && !preset.appId) {
|
|
59
|
-
const appId = keyToAppId.get(preset.key)
|
|
60
|
-
if (appId) {
|
|
61
|
-
needsMigration = true
|
|
62
|
-
updated.appId = appId
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return updated
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
return { presets: migrated, needsMigration }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export async function loadProjectConfig(rootDir, servers = [], { logSuccess, logWarning } = {}) {
|
|
73
|
-
const configPath = getProjectConfigPath(rootDir)
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
const raw = await fs.readFile(configPath, 'utf8')
|
|
77
|
-
const data = JSON.parse(raw)
|
|
78
|
-
const apps = Array.isArray(data?.apps) ? data.apps : []
|
|
79
|
-
const presets = Array.isArray(data?.presets) ? data.presets : []
|
|
80
|
-
|
|
81
|
-
const { apps: migratedApps, needsMigration: appsNeedMigration } = migrateApps(apps, servers)
|
|
82
|
-
const { presets: migratedPresets, needsMigration: presetsNeedMigration } = migratePresets(presets, migratedApps)
|
|
83
|
-
|
|
84
|
-
if (appsNeedMigration || presetsNeedMigration) {
|
|
85
|
-
await saveProjectConfig(rootDir, {
|
|
86
|
-
apps: migratedApps,
|
|
87
|
-
presets: migratedPresets
|
|
88
|
-
})
|
|
89
|
-
logSuccess?.('Migrated project configuration to use unique IDs.')
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return { apps: migratedApps, presets: migratedPresets }
|
|
93
|
-
} catch (error) {
|
|
94
|
-
if (error.code === 'ENOENT') {
|
|
95
|
-
return { apps: [], presets: [] }
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
logWarning?.('Failed to read .zephyr/config.json, starting with an empty list of apps.')
|
|
99
|
-
return { apps: [], presets: [] }
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export async function saveProjectConfig(rootDir, config) {
|
|
104
|
-
const configDir = getProjectConfigDir(rootDir)
|
|
105
|
-
await ensureDirectory(configDir)
|
|
106
|
-
|
|
107
|
-
const payload = JSON.stringify(
|
|
108
|
-
{
|
|
109
|
-
apps: config.apps ?? [],
|
|
110
|
-
presets: config.presets ?? []
|
|
111
|
-
},
|
|
112
|
-
null,
|
|
113
|
-
2
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
await fs.writeFile(getProjectConfigPath(rootDir), `${payload}\n`)
|
|
117
|
-
}
|
|
118
|
-
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
|
|
3
|
+
import { ensureDirectory, getProjectConfigDir, getProjectConfigPath } from '../utils/paths.mjs'
|
|
4
|
+
import { generateId } from '../utils/id.mjs'
|
|
5
|
+
|
|
6
|
+
export function migrateApps(apps, servers) {
|
|
7
|
+
if (!Array.isArray(apps)) {
|
|
8
|
+
return { apps: [], needsMigration: false }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const serverNameToId = new Map()
|
|
12
|
+
servers.forEach((server) => {
|
|
13
|
+
if (server.id && server.serverName) {
|
|
14
|
+
serverNameToId.set(server.serverName, server.id)
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
let needsMigration = false
|
|
19
|
+
const migrated = apps.map((app) => {
|
|
20
|
+
const updated = { ...app }
|
|
21
|
+
|
|
22
|
+
if (!app.id) {
|
|
23
|
+
needsMigration = true
|
|
24
|
+
updated.id = generateId()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (app.serverName && !app.serverId) {
|
|
28
|
+
const serverId = serverNameToId.get(app.serverName)
|
|
29
|
+
if (serverId) {
|
|
30
|
+
needsMigration = true
|
|
31
|
+
updated.serverId = serverId
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return updated
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return { apps: migrated, needsMigration }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function migratePresets(presets, apps) {
|
|
42
|
+
if (!Array.isArray(presets)) {
|
|
43
|
+
return { presets: [], needsMigration: false }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const keyToAppId = new Map()
|
|
47
|
+
apps.forEach((app) => {
|
|
48
|
+
if (app.id && app.serverName && app.projectPath) {
|
|
49
|
+
const key = `${app.serverName}:${app.projectPath}`
|
|
50
|
+
keyToAppId.set(key, app.id)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
let needsMigration = false
|
|
55
|
+
const migrated = presets.map((preset) => {
|
|
56
|
+
const updated = { ...preset }
|
|
57
|
+
|
|
58
|
+
if (preset.key && !preset.appId) {
|
|
59
|
+
const appId = keyToAppId.get(preset.key)
|
|
60
|
+
if (appId) {
|
|
61
|
+
needsMigration = true
|
|
62
|
+
updated.appId = appId
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return updated
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
return { presets: migrated, needsMigration }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function loadProjectConfig(rootDir, servers = [], { logSuccess, logWarning } = {}) {
|
|
73
|
+
const configPath = getProjectConfigPath(rootDir)
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const raw = await fs.readFile(configPath, 'utf8')
|
|
77
|
+
const data = JSON.parse(raw)
|
|
78
|
+
const apps = Array.isArray(data?.apps) ? data.apps : []
|
|
79
|
+
const presets = Array.isArray(data?.presets) ? data.presets : []
|
|
80
|
+
|
|
81
|
+
const { apps: migratedApps, needsMigration: appsNeedMigration } = migrateApps(apps, servers)
|
|
82
|
+
const { presets: migratedPresets, needsMigration: presetsNeedMigration } = migratePresets(presets, migratedApps)
|
|
83
|
+
|
|
84
|
+
if (appsNeedMigration || presetsNeedMigration) {
|
|
85
|
+
await saveProjectConfig(rootDir, {
|
|
86
|
+
apps: migratedApps,
|
|
87
|
+
presets: migratedPresets
|
|
88
|
+
})
|
|
89
|
+
logSuccess?.('Migrated project configuration to use unique IDs.')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { apps: migratedApps, presets: migratedPresets }
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error.code === 'ENOENT') {
|
|
95
|
+
return { apps: [], presets: [] }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
logWarning?.('Failed to read .zephyr/config.json, starting with an empty list of apps.')
|
|
99
|
+
return { apps: [], presets: [] }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function saveProjectConfig(rootDir, config) {
|
|
104
|
+
const configDir = getProjectConfigDir(rootDir)
|
|
105
|
+
await ensureDirectory(configDir)
|
|
106
|
+
|
|
107
|
+
const payload = JSON.stringify(
|
|
108
|
+
{
|
|
109
|
+
apps: config.apps ?? [],
|
|
110
|
+
presets: config.presets ?? []
|
|
111
|
+
},
|
|
112
|
+
null,
|
|
113
|
+
2
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
await fs.writeFile(getProjectConfigPath(rootDir), `${payload}\n`)
|
|
117
|
+
}
|
|
118
|
+
|
package/src/config/servers.mjs
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
|
|
5
|
-
import { ensureDirectory } from '../utils/paths.mjs'
|
|
6
|
-
import { generateId } from '../utils/id.mjs'
|
|
7
|
-
|
|
8
|
-
const GLOBAL_CONFIG_DIR = path.join(os.homedir(), '.config', 'zephyr')
|
|
9
|
-
const SERVERS_FILE = path.join(GLOBAL_CONFIG_DIR, 'servers.json')
|
|
10
|
-
|
|
11
|
-
export function migrateServers(servers) {
|
|
12
|
-
if (!Array.isArray(servers)) {
|
|
13
|
-
return { servers: [], needsMigration: false }
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let needsMigration = false
|
|
17
|
-
const migrated = servers.map((server) => {
|
|
18
|
-
if (!server.id) {
|
|
19
|
-
needsMigration = true
|
|
20
|
-
return { ...server, id: generateId() }
|
|
21
|
-
}
|
|
22
|
-
return server
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
return { servers: migrated, needsMigration }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function loadServers({ logSuccess, logWarning } = {}) {
|
|
29
|
-
try {
|
|
30
|
-
const raw = await fs.readFile(SERVERS_FILE, 'utf8')
|
|
31
|
-
const data = JSON.parse(raw)
|
|
32
|
-
const servers = Array.isArray(data) ? data : []
|
|
33
|
-
|
|
34
|
-
const { servers: migrated, needsMigration } = migrateServers(servers)
|
|
35
|
-
|
|
36
|
-
if (needsMigration) {
|
|
37
|
-
await saveServers(migrated)
|
|
38
|
-
logSuccess?.('Migrated servers configuration to use unique IDs.')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return migrated
|
|
42
|
-
} catch (error) {
|
|
43
|
-
if (error.code === 'ENOENT') {
|
|
44
|
-
return []
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
logWarning?.('Failed to read servers.json, starting with an empty list.')
|
|
48
|
-
return []
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function saveServers(servers) {
|
|
53
|
-
await ensureDirectory(GLOBAL_CONFIG_DIR)
|
|
54
|
-
const payload = JSON.stringify(servers, null, 2)
|
|
55
|
-
await fs.writeFile(SERVERS_FILE, `${payload}\n`)
|
|
56
|
-
}
|
|
57
|
-
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
import { ensureDirectory } from '../utils/paths.mjs'
|
|
6
|
+
import { generateId } from '../utils/id.mjs'
|
|
7
|
+
|
|
8
|
+
const GLOBAL_CONFIG_DIR = path.join(os.homedir(), '.config', 'zephyr')
|
|
9
|
+
const SERVERS_FILE = path.join(GLOBAL_CONFIG_DIR, 'servers.json')
|
|
10
|
+
|
|
11
|
+
export function migrateServers(servers) {
|
|
12
|
+
if (!Array.isArray(servers)) {
|
|
13
|
+
return { servers: [], needsMigration: false }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let needsMigration = false
|
|
17
|
+
const migrated = servers.map((server) => {
|
|
18
|
+
if (!server.id) {
|
|
19
|
+
needsMigration = true
|
|
20
|
+
return { ...server, id: generateId() }
|
|
21
|
+
}
|
|
22
|
+
return server
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return { servers: migrated, needsMigration }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function loadServers({ logSuccess, logWarning } = {}) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = await fs.readFile(SERVERS_FILE, 'utf8')
|
|
31
|
+
const data = JSON.parse(raw)
|
|
32
|
+
const servers = Array.isArray(data) ? data : []
|
|
33
|
+
|
|
34
|
+
const { servers: migrated, needsMigration } = migrateServers(servers)
|
|
35
|
+
|
|
36
|
+
if (needsMigration) {
|
|
37
|
+
await saveServers(migrated)
|
|
38
|
+
logSuccess?.('Migrated servers configuration to use unique IDs.')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return migrated
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error.code === 'ENOENT') {
|
|
44
|
+
return []
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
logWarning?.('Failed to read servers.json, starting with an empty list.')
|
|
48
|
+
return []
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function saveServers(servers) {
|
|
53
|
+
await ensureDirectory(GLOBAL_CONFIG_DIR)
|
|
54
|
+
const payload = JSON.stringify(servers, null, 2)
|
|
55
|
+
await fs.writeFile(SERVERS_FILE, `${payload}\n`)
|
|
56
|
+
}
|
|
57
|
+
|