Skip to content

Commit c67b929

Browse files
committed
refactor: clean up init script version check
1 parent d50c791 commit c67b929

13 files changed

+413
-28
lines changed

src/utils/create-app-task-run-init-script.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ensureTargetPath } from './ensure-target-path'
44
import { GetArgsResult } from './get-args-result'
55
import { deleteInitScript, getInitScript, InitScript } from './get-init-script'
66
import { getPackageJson } from './get-package-json'
7-
import { initCheckVersion } from './init-check-version'
7+
import { initScriptVersion } from './init-script-version'
88
import { searchAndReplace } from './search-and-replace'
99
import { Task, taskFail } from './vendor/clack-tasks'
1010
import { namesValues } from './vendor/names'
@@ -23,7 +23,7 @@ export function createAppTaskRunInitScript(args: GetArgsResult): Task {
2323
log.warn(`Running init script`)
2424
}
2525

26-
await initCheckVersion(init)
26+
await initScriptVersion(init.versions, args.verbose)
2727
if (args.verbose) {
2828
log.warn(`initCheckVersion done`)
2929
}

src/utils/get-init-script.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ export function deleteInitScript(targetDirectory: string) {
2626
writeFileSync(path, JSON.stringify(contents, undefined, 2) + '\n')
2727
}
2828

29+
const InitScriptVersionsSchema = z.object({
30+
adb: z.string().optional(),
31+
anchor: z.string().optional(),
32+
solana: z.string().optional(),
33+
})
34+
2935
const InitScriptSchema = z
3036
.object({
3137
instructions: z.array(z.string()).optional(),
@@ -37,14 +43,9 @@ const InitScriptSchema = z
3743
}),
3844
)
3945
.optional(),
40-
versions: z
41-
.object({
42-
adb: z.string().optional(),
43-
anchor: z.string().optional(),
44-
solana: z.string().optional(),
45-
})
46-
.optional(),
46+
versions: InitScriptVersionsSchema.optional(),
4747
})
4848
.optional()
4949

5050
export type InitScript = z.infer<typeof InitScriptSchema>
51+
export type InitScriptVersions = z.infer<typeof InitScriptVersionsSchema>

src/utils/init-check-version.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ import { bold, yellow } from 'picocolors'
33
import { getVersion } from './get-version'
44
import { validateVersion } from './validate-version'
55

6-
export async function initCheckVersionAdb(required: string) {
6+
export async function initScriptVersionAdb(required?: string, verbose = false) {
7+
if (!required) {
8+
return
9+
}
710
try {
811
const { valid, version } = validateVersion({ required, version: getVersion('adb') })
12+
if (verbose) {
13+
log.warn(`initScriptVersionAdb: required: ${required}, version: ${version ?? '*none*'}, valid: ${valid}`)
14+
}
915
if (!version) {
1016
log.warn(
1117
[
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ import { bold, yellow } from 'picocolors'
33
import { getVersion } from './get-version'
44
import { validateVersion } from './validate-version'
55

6-
export async function initCheckVersionAnchor(required: string) {
6+
export async function initScriptVersionAnchor(required?: string, verbose = false) {
7+
if (!required) {
8+
return
9+
}
710
try {
811
const { valid, version } = validateVersion({ required, version: getVersion('anchor') })
12+
if (verbose) {
13+
log.warn(`initScriptVersionAnchor: required: ${required}, version: ${version ?? '*none*'}, valid: ${valid}`)
14+
}
915
if (!version) {
1016
log.warn(
1117
[
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ import { bold, yellow } from 'picocolors'
33
import { getVersion } from './get-version'
44
import { validateVersion } from './validate-version'
55

6-
export async function initCheckVersionSolana(required: string) {
6+
export async function initScriptVersionSolana(required?: string, verbose = false) {
7+
if (!required) {
8+
return
9+
}
710
try {
811
const { valid, version } = validateVersion({ required, version: getVersion('solana') })
12+
if (verbose) {
13+
log.warn(`initScriptVersionSolana: required: ${required}, version: ${version ?? '*none*'}, valid: ${valid}`)
14+
}
915
if (!version) {
1016
log.warn(
1117
[

src/utils/init-script-version.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { log } from '@clack/prompts'
2+
import { InitScriptVersions } from './get-init-script'
3+
import { initScriptVersionAdb } from './init-script-version-adb'
4+
import { initScriptVersionAnchor } from './init-script-version-anchor'
5+
import { initScriptVersionSolana } from './init-script-version-solana'
6+
7+
export async function initScriptVersion(versions?: InitScriptVersions, verbose = false) {
8+
if (!versions) {
9+
if (verbose) {
10+
log.warn(`initScriptCheckVersion: no versions found`)
11+
}
12+
return
13+
}
14+
await initScriptVersionAdb(versions.adb, verbose)
15+
await initScriptVersionAnchor(versions.anchor, verbose)
16+
await initScriptVersionSolana(versions.solana, verbose)
17+
}

test/get-version.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as childProcess from 'node:child_process'
2+
import { beforeEach, describe, expect, it, vi } from 'vitest'
3+
import { getVersion, versionCommands } from '../src/utils/get-version'
4+
5+
vi.mock('node:child_process', () => ({
6+
execSync: vi.fn(),
7+
}))
8+
9+
describe('versionCommands', () => {
10+
it('should have the expected commands', () => {
11+
expect(Object.keys(versionCommands)).toEqual(['adb', 'anchor', 'avm', 'rust', 'solana'])
12+
})
13+
14+
it('should have correct structure for each command', () => {
15+
for (const cmd of Object.values(versionCommands)) {
16+
expect(cmd).toHaveProperty('command', expect.any(String))
17+
expect(cmd).toHaveProperty('name', expect.any(String))
18+
expect(cmd).toHaveProperty('regex', expect.any(RegExp))
19+
}
20+
})
21+
})
22+
23+
describe('getVersion', () => {
24+
beforeEach(() => {
25+
vi.resetAllMocks()
26+
})
27+
28+
it('should return version for known command', () => {
29+
;(childProcess.execSync as any).mockReturnValue('anchor-cli 0.24.2\n')
30+
const version = getVersion('anchor')
31+
expect(version).toBe('0.24.2')
32+
expect(childProcess.execSync).toHaveBeenCalledWith('anchor --version', { stdio: ['ignore', 'pipe', 'ignore'] })
33+
})
34+
35+
it('should throw error for unknown command', () => {
36+
expect(() => getVersion('unknown' as any)).toThrow('Unknown command unknown')
37+
})
38+
39+
it('should return undefined if parsing fails', () => {
40+
;(childProcess.execSync as any).mockReturnValue('Invalid output\n')
41+
const version = getVersion('anchor')
42+
expect(version).toBeUndefined()
43+
})
44+
})
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { log } from '@clack/prompts'
2+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3+
import { getVersion } from '../src/utils/get-version'
4+
import { initScriptVersionAdb } from '../src/utils/init-script-version-adb'
5+
import { validateVersion } from '../src/utils/validate-version'
6+
7+
vi.mock('../src/utils/get-version')
8+
vi.mock('../src/utils/validate-version')
9+
vi.mock('@clack/prompts', () => ({
10+
log: {
11+
warn: vi.fn(),
12+
},
13+
}))
14+
15+
describe('initScriptVersionAdb', () => {
16+
beforeEach(() => {
17+
vi.resetAllMocks()
18+
})
19+
20+
afterEach(() => {
21+
vi.clearAllMocks()
22+
})
23+
24+
it('should return early if no required version is provided', async () => {
25+
await initScriptVersionAdb()
26+
expect(getVersion).not.toHaveBeenCalled()
27+
expect(validateVersion).not.toHaveBeenCalled()
28+
expect(log.warn).not.toHaveBeenCalled()
29+
})
30+
31+
it('should log warning if adb version is not found', async () => {
32+
const required = '1.0.0'
33+
vi.mocked(getVersion).mockReturnValue(undefined)
34+
vi.mocked(validateVersion).mockReturnValue({ valid: false, version: undefined })
35+
await initScriptVersionAdb(required)
36+
expect(getVersion).toHaveBeenCalledWith('adb')
37+
expect(validateVersion).toHaveBeenCalledWith({ required, version: undefined })
38+
expect(log.warn).toHaveBeenCalledWith(expect.stringContaining('Could not find adb version. Please install adb.'))
39+
})
40+
41+
it('should log warning if adb version does not satisfy the requirement', async () => {
42+
const required = '1.0.0'
43+
const version = '0.9.0'
44+
vi.mocked(getVersion).mockReturnValue(version)
45+
vi.mocked(validateVersion).mockReturnValue({ valid: false, version })
46+
await initScriptVersionAdb(required)
47+
expect(getVersion).toHaveBeenCalledWith('adb')
48+
expect(validateVersion).toHaveBeenCalledWith({ required, version })
49+
expect(log.warn).toHaveBeenCalledWith(
50+
expect.stringContaining(`Found adb version ${version}. Expected adb version ${required}.`),
51+
)
52+
})
53+
54+
it('should not log warning if adb version satisfies the requirement', async () => {
55+
const required = '1.0.0'
56+
const version = '1.0.0'
57+
vi.mocked(getVersion).mockReturnValue(version)
58+
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
59+
await initScriptVersionAdb(required)
60+
expect(getVersion).toHaveBeenCalledWith('adb')
61+
expect(validateVersion).toHaveBeenCalledWith({ required, version })
62+
expect(log.warn).not.toHaveBeenCalled()
63+
})
64+
65+
it('should log verbose message if verbose is true', async () => {
66+
const required = '1.0.0'
67+
const version = '1.0.0'
68+
vi.mocked(getVersion).mockReturnValue(version)
69+
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
70+
await initScriptVersionAdb(required, true)
71+
expect(getVersion).toHaveBeenCalledWith('adb')
72+
expect(validateVersion).toHaveBeenCalledWith({ required, version })
73+
expect(log.warn).toHaveBeenCalledWith(
74+
`initScriptVersionAdb: required: ${required}, version: ${version}, valid: true`,
75+
)
76+
})
77+
78+
it('should log error if an exception occurs', async () => {
79+
const required = '1.0.0'
80+
const error = new Error('Test error')
81+
vi.mocked(getVersion).mockImplementation(() => {
82+
throw error
83+
})
84+
await initScriptVersionAdb(required)
85+
expect(log.warn).toHaveBeenCalledWith(`Error ${error}`)
86+
})
87+
})
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { log } from '@clack/prompts'
2+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3+
import { getVersion } from '../src/utils/get-version'
4+
import { initScriptVersionAnchor } from '../src/utils/init-script-version-anchor'
5+
import { validateVersion } from '../src/utils/validate-version'
6+
7+
vi.mock('../src/utils/get-version')
8+
vi.mock('../src/utils/validate-version')
9+
vi.mock('@clack/prompts', () => ({
10+
log: {
11+
warn: vi.fn(),
12+
},
13+
}))
14+
15+
describe('initScriptVersionAnchor', () => {
16+
beforeEach(() => {
17+
vi.resetAllMocks()
18+
})
19+
20+
afterEach(() => {
21+
vi.clearAllMocks()
22+
})
23+
24+
it('should return early if no required version is provided', async () => {
25+
await initScriptVersionAnchor()
26+
expect(getVersion).not.toHaveBeenCalled()
27+
expect(validateVersion).not.toHaveBeenCalled()
28+
expect(log.warn).not.toHaveBeenCalled()
29+
})
30+
31+
it('should log warning if anchor version is not found', async () => {
32+
const required = '1.0.0'
33+
vi.mocked(getVersion).mockReturnValue(undefined)
34+
vi.mocked(validateVersion).mockReturnValue({ valid: false, version: undefined })
35+
await initScriptVersionAnchor(required)
36+
expect(getVersion).toHaveBeenCalledWith('anchor')
37+
expect(validateVersion).toHaveBeenCalledWith({ required, version: undefined })
38+
expect(log.warn).toHaveBeenCalledWith(
39+
expect.stringContaining('Could not find Anchor version. Please install Anchor.'),
40+
)
41+
})
42+
43+
it('should log warning if anchor version does not satisfy the requirement', async () => {
44+
const required = '1.0.0'
45+
const version = '0.9.0'
46+
vi.mocked(getVersion).mockReturnValue(version)
47+
vi.mocked(validateVersion).mockReturnValue({ valid: false, version })
48+
await initScriptVersionAnchor(required)
49+
expect(getVersion).toHaveBeenCalledWith('anchor')
50+
expect(validateVersion).toHaveBeenCalledWith({ required, version })
51+
expect(log.warn).toHaveBeenCalledWith(
52+
expect.stringContaining(`Found Anchor version ${version}. Expected Anchor version ${required}.`),
53+
)
54+
})
55+
56+
it('should not log warning if anchor version satisfies the requirement', async () => {
57+
const required = '1.0.0'
58+
const version = '1.0.0'
59+
vi.mocked(getVersion).mockReturnValue(version)
60+
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
61+
await initScriptVersionAnchor(required)
62+
expect(getVersion).toHaveBeenCalledWith('anchor')
63+
expect(validateVersion).toHaveBeenCalledWith({ required, version })
64+
expect(log.warn).not.toHaveBeenCalled()
65+
})
66+
67+
it('should log verbose message if verbose is true', async () => {
68+
const required = '1.0.0'
69+
const version = '1.0.0'
70+
vi.mocked(getVersion).mockReturnValue(version)
71+
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
72+
await initScriptVersionAnchor(required, true)
73+
expect(getVersion).toHaveBeenCalledWith('anchor')
74+
expect(validateVersion).toHaveBeenCalledWith({ required, version })
75+
expect(log.warn).toHaveBeenCalledWith(
76+
`initScriptVersionAnchor: required: ${required}, version: ${version}, valid: true`,
77+
)
78+
})
79+
80+
it('should log error if an exception occurs', async () => {
81+
const required = '1.0.0'
82+
const error = new Error('Test error')
83+
vi.mocked(getVersion).mockImplementation(() => {
84+
throw error
85+
})
86+
await initScriptVersionAnchor(required)
87+
expect(log.warn).toHaveBeenCalledWith(`Error ${error}`)
88+
})
89+
})

0 commit comments

Comments
 (0)