amorphous-cli 0.3.9 → 0.3.10
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/lib/config.mjs +39 -9
- package/lib/config.test.mjs +79 -0
- package/package.json +1 -1
- package/skill/amorphous/SKILL.md +1 -1
package/lib/config.mjs
CHANGED
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs'
|
|
2
2
|
import { homedir } from 'node:os'
|
|
3
|
-
import { join } from 'node:path'
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const CLI_PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
7
|
+
|
|
8
|
+
function isWithin(root, path) {
|
|
9
|
+
const fromRoot = relative(resolve(root), resolve(path))
|
|
10
|
+
return fromRoot === '' || (fromRoot !== '..' && !fromRoot.startsWith(`..${sep}`))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function localRepositoryRoot(packageRoot) {
|
|
14
|
+
const root = resolve(packageRoot, '..')
|
|
15
|
+
try {
|
|
16
|
+
const appPackage = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
|
|
17
|
+
const cliPackage = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))
|
|
18
|
+
if (appPackage.name === 'amorphous' && cliPackage.name === 'amorphous-cli' && resolve(root, 'cli') === resolve(packageRoot)) {
|
|
19
|
+
return root
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
/* The published CLI has no enclosing Amorphous app repository. */
|
|
23
|
+
}
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function loadDotEnv(cwd = process.cwd(), options = {}) {
|
|
28
|
+
const packageRoot = options.packageRoot || CLI_PACKAGE_ROOT
|
|
29
|
+
const explicitRoot = options.amorphousRoot ?? process.env.AMORPHOUS_ROOT
|
|
6
30
|
const roots = []
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
31
|
+
if (explicitRoot) {
|
|
32
|
+
roots.push(explicitRoot)
|
|
33
|
+
} else {
|
|
34
|
+
const repoRoot = localRepositoryRoot(packageRoot)
|
|
35
|
+
if (!repoRoot || !isWithin(repoRoot, cwd)) return
|
|
36
|
+
let dir = resolve(cwd)
|
|
37
|
+
for (let i = 0; i < 8; i++) {
|
|
38
|
+
roots.push(dir)
|
|
39
|
+
if (dir === repoRoot) break
|
|
40
|
+
const parent = dirname(dir)
|
|
41
|
+
if (parent === dir || !isWithin(repoRoot, parent)) break
|
|
42
|
+
dir = parent
|
|
43
|
+
}
|
|
14
44
|
}
|
|
15
45
|
for (const base of roots) {
|
|
16
46
|
const path = join(base, '.env')
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import test from 'node:test'
|
|
6
|
+
import { loadDotEnv } from './config.mjs'
|
|
7
|
+
|
|
8
|
+
function withEnv(values, run) {
|
|
9
|
+
const previous = Object.fromEntries(Object.keys(values).map((name) => [name, process.env[name]]))
|
|
10
|
+
for (const [name, value] of Object.entries(values)) {
|
|
11
|
+
if (value === undefined) delete process.env[name]
|
|
12
|
+
else process.env[name] = value
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return run()
|
|
16
|
+
} finally {
|
|
17
|
+
for (const [name, value] of Object.entries(previous)) {
|
|
18
|
+
if (value === undefined) delete process.env[name]
|
|
19
|
+
else process.env[name] = value
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test('published CLI ignores an unrelated project .env by default', () => {
|
|
25
|
+
const root = mkdtempSync(join(tmpdir(), 'amorphous-config-'))
|
|
26
|
+
const project = join(root, 'project')
|
|
27
|
+
const packageRoot = join(root, 'npm-cache', 'amorphous-cli')
|
|
28
|
+
mkdirSync(project, { recursive: true })
|
|
29
|
+
mkdirSync(packageRoot, { recursive: true })
|
|
30
|
+
writeFileSync(join(project, '.env'), 'APP_ORIGIN=https://amorphous.test\n')
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
withEnv({ APP_ORIGIN: undefined, AMORPHOUS_ROOT: undefined }, () => {
|
|
34
|
+
loadDotEnv(project, { packageRoot })
|
|
35
|
+
assert.equal(process.env.APP_ORIGIN, undefined)
|
|
36
|
+
})
|
|
37
|
+
} finally {
|
|
38
|
+
rmSync(root, { recursive: true, force: true })
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('repository source CLI loads the repository .env for local development', () => {
|
|
43
|
+
const root = mkdtempSync(join(tmpdir(), 'amorphous-config-'))
|
|
44
|
+
const cliRoot = join(root, 'cli')
|
|
45
|
+
mkdirSync(cliRoot, { recursive: true })
|
|
46
|
+
writeFileSync(join(root, 'package.json'), JSON.stringify({ name: 'amorphous' }))
|
|
47
|
+
writeFileSync(join(cliRoot, 'package.json'), JSON.stringify({ name: 'amorphous-cli' }))
|
|
48
|
+
writeFileSync(join(root, '.env'), 'APP_ORIGIN=https://amorphous.test\n')
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
withEnv({ APP_ORIGIN: undefined, AMORPHOUS_ROOT: undefined }, () => {
|
|
52
|
+
loadDotEnv(root, { packageRoot: cliRoot })
|
|
53
|
+
assert.equal(process.env.APP_ORIGIN, 'https://amorphous.test')
|
|
54
|
+
})
|
|
55
|
+
} finally {
|
|
56
|
+
rmSync(root, { recursive: true, force: true })
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('AMORPHOUS_ROOT explicitly opts a published CLI into a local .env', () => {
|
|
61
|
+
const root = mkdtempSync(join(tmpdir(), 'amorphous-config-'))
|
|
62
|
+
const project = join(root, 'project')
|
|
63
|
+
const localRoot = join(root, 'local-app')
|
|
64
|
+
const packageRoot = join(root, 'npm-cache', 'amorphous-cli')
|
|
65
|
+
mkdirSync(project, { recursive: true })
|
|
66
|
+
mkdirSync(localRoot, { recursive: true })
|
|
67
|
+
mkdirSync(packageRoot, { recursive: true })
|
|
68
|
+
writeFileSync(join(localRoot, '.env'), 'APP_ORIGIN=https://amorphous.test\n')
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
withEnv({ APP_ORIGIN: undefined, AMORPHOUS_ROOT: undefined }, () => {
|
|
72
|
+
loadDotEnv(project, { packageRoot, amorphousRoot: localRoot })
|
|
73
|
+
assert.equal(process.env.APP_ORIGIN, 'https://amorphous.test')
|
|
74
|
+
})
|
|
75
|
+
} finally {
|
|
76
|
+
rmSync(root, { recursive: true, force: true })
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
package/package.json
CHANGED
package/skill/amorphous/SKILL.md
CHANGED
|
@@ -40,7 +40,7 @@ They tell you when to push, preview, publish, or invite. Do those when they ask.
|
|
|
40
40
|
|
|
41
41
|
## Start
|
|
42
42
|
|
|
43
|
-
Run CLI commands as `npx --yes amorphous-cli@latest <command>`; this uses the latest CLI without a global install. The command examples below abbreviate that prefix as `amorphous-cli`. If a command prints `Run: npx --yes amorphous-cli@latest setup`, run that line, then repeat the command. If you are not signed in, run `npx --yes amorphous-cli@latest login`. Show the person the link it prints and ask them to finish in the browser. Do not ask for an email or a code.
|
|
43
|
+
Run CLI commands as `npx --yes amorphous-cli@latest <command>`; this uses the latest CLI without a global install. The published CLI ignores `.env` files in the current project and uses the hosted app. To explicitly use local settings, set `AMORPHOUS_ROOT` to the local Amorphous repo root. The command examples below abbreviate that prefix as `amorphous-cli`. If a command prints `Run: npx --yes amorphous-cli@latest setup`, run that line, then repeat the command. If you are not signed in, run `npx --yes amorphous-cli@latest login`. Show the person the link it prints and ask them to finish in the browser. Do not ask for an email or a code.
|
|
44
44
|
|
|
45
45
|
Then choose the path that matches the request:
|
|
46
46
|
|