Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/utils/create-app-task-run-init-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ensureTargetPath } from './ensure-target-path'
import { GetArgsResult } from './get-args-result'
import { deleteInitScript, getInitScript, InitScript } from './get-init-script'
import { getPackageJson } from './get-package-json'
import { initCheckVersion } from './init-check-version'
import { initScriptVersion } from './init-script-version'
import { searchAndReplace } from './search-and-replace'
import { Task, taskFail } from './vendor/clack-tasks'
import { namesValues } from './vendor/names'
Expand All @@ -23,7 +23,7 @@ export function createAppTaskRunInitScript(args: GetArgsResult): Task {
log.warn(`Running init script`)
}

await initCheckVersion(init)
await initScriptVersion(init.versions, args.verbose)
if (args.verbose) {
log.warn(`initCheckVersion done`)
}
Expand Down
15 changes: 8 additions & 7 deletions src/utils/get-init-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export function deleteInitScript(targetDirectory: string) {
writeFileSync(path, JSON.stringify(contents, undefined, 2) + '\n')
}

const InitScriptVersionsSchema = z.object({
adb: z.string().optional(),
anchor: z.string().optional(),
solana: z.string().optional(),
})

const InitScriptSchema = z
.object({
instructions: z.array(z.string()).optional(),
Expand All @@ -37,14 +43,9 @@ const InitScriptSchema = z
}),
)
.optional(),
versions: z
.object({
adb: z.string().optional(),
anchor: z.string().optional(),
solana: z.string().optional(),
})
.optional(),
versions: InitScriptVersionsSchema.optional(),
})
.optional()

export type InitScript = z.infer<typeof InitScriptSchema>
export type InitScriptVersions = z.infer<typeof InitScriptVersionsSchema>
16 changes: 0 additions & 16 deletions src/utils/init-check-version.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { bold, yellow } from 'picocolors'
import { getVersion } from './get-version'
import { validateVersion } from './validate-version'

export async function initCheckVersionAdb(required: string) {
export async function initScriptVersionAdb(required?: string, verbose = false) {
if (!required) {
return
}
try {
const { valid, version } = validateVersion({ required, version: getVersion('adb') })
if (verbose) {
log.warn(`initScriptVersionAdb: required: ${required}, version: ${version ?? '*none*'}, valid: ${valid}`)
}
if (!version) {
log.warn(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { bold, yellow } from 'picocolors'
import { getVersion } from './get-version'
import { validateVersion } from './validate-version'

export async function initCheckVersionAnchor(required: string) {
export async function initScriptVersionAnchor(required?: string, verbose = false) {
if (!required) {
return
}
try {
const { valid, version } = validateVersion({ required, version: getVersion('anchor') })
if (verbose) {
log.warn(`initScriptVersionAnchor: required: ${required}, version: ${version ?? '*none*'}, valid: ${valid}`)
}
if (!version) {
log.warn(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { bold, yellow } from 'picocolors'
import { getVersion } from './get-version'
import { validateVersion } from './validate-version'

export async function initCheckVersionSolana(required: string) {
export async function initScriptVersionSolana(required?: string, verbose = false) {
if (!required) {
return
}
try {
const { valid, version } = validateVersion({ required, version: getVersion('solana') })
if (verbose) {
log.warn(`initScriptVersionSolana: required: ${required}, version: ${version ?? '*none*'}, valid: ${valid}`)
}
if (!version) {
log.warn(
[
Expand Down
17 changes: 17 additions & 0 deletions src/utils/init-script-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { log } from '@clack/prompts'
import { InitScriptVersions } from './get-init-script'
import { initScriptVersionAdb } from './init-script-version-adb'
import { initScriptVersionAnchor } from './init-script-version-anchor'
import { initScriptVersionSolana } from './init-script-version-solana'

export async function initScriptVersion(versions?: InitScriptVersions, verbose = false) {
if (!versions) {
if (verbose) {
log.warn(`initScriptCheckVersion: no versions found`)
}
return
}
await initScriptVersionAdb(versions.adb, verbose)
await initScriptVersionAnchor(versions.anchor, verbose)
await initScriptVersionSolana(versions.solana, verbose)
}
44 changes: 44 additions & 0 deletions test/get-version.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as childProcess from 'node:child_process'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { getVersion, versionCommands } from '../src/utils/get-version'

vi.mock('node:child_process', () => ({
execSync: vi.fn(),
}))

describe('versionCommands', () => {
it('should have the expected commands', () => {
expect(Object.keys(versionCommands)).toEqual(['adb', 'anchor', 'avm', 'rust', 'solana'])
})

it('should have correct structure for each command', () => {
for (const cmd of Object.values(versionCommands)) {
expect(cmd).toHaveProperty('command', expect.any(String))
expect(cmd).toHaveProperty('name', expect.any(String))
expect(cmd).toHaveProperty('regex', expect.any(RegExp))
}
})
})

describe('getVersion', () => {
beforeEach(() => {
vi.resetAllMocks()
})

it('should return version for known command', () => {
;(childProcess.execSync as any).mockReturnValue('anchor-cli 0.24.2\n')
const version = getVersion('anchor')
expect(version).toBe('0.24.2')
expect(childProcess.execSync).toHaveBeenCalledWith('anchor --version', { stdio: ['ignore', 'pipe', 'ignore'] })
})

it('should throw error for unknown command', () => {
expect(() => getVersion('unknown' as any)).toThrow('Unknown command unknown')
})

it('should return undefined if parsing fails', () => {
;(childProcess.execSync as any).mockReturnValue('Invalid output\n')
const version = getVersion('anchor')
expect(version).toBeUndefined()
})
})
87 changes: 87 additions & 0 deletions test/init-script-version-adb.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { log } from '@clack/prompts'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { getVersion } from '../src/utils/get-version'
import { initScriptVersionAdb } from '../src/utils/init-script-version-adb'
import { validateVersion } from '../src/utils/validate-version'

vi.mock('../src/utils/get-version')
vi.mock('../src/utils/validate-version')
vi.mock('@clack/prompts', () => ({
log: {
warn: vi.fn(),
},
}))

describe('initScriptVersionAdb', () => {
beforeEach(() => {
vi.resetAllMocks()
})

afterEach(() => {
vi.clearAllMocks()
})

it('should return early if no required version is provided', async () => {
await initScriptVersionAdb()
expect(getVersion).not.toHaveBeenCalled()
expect(validateVersion).not.toHaveBeenCalled()
expect(log.warn).not.toHaveBeenCalled()
})

it('should log warning if adb version is not found', async () => {
const required = '1.0.0'
vi.mocked(getVersion).mockReturnValue(undefined)
vi.mocked(validateVersion).mockReturnValue({ valid: false, version: undefined })
await initScriptVersionAdb(required)
expect(getVersion).toHaveBeenCalledWith('adb')
expect(validateVersion).toHaveBeenCalledWith({ required, version: undefined })
expect(log.warn).toHaveBeenCalledWith(expect.stringContaining('Could not find adb version. Please install adb.'))
})

it('should log warning if adb version does not satisfy the requirement', async () => {
const required = '1.0.0'
const version = '0.9.0'
vi.mocked(getVersion).mockReturnValue(version)
vi.mocked(validateVersion).mockReturnValue({ valid: false, version })
await initScriptVersionAdb(required)
expect(getVersion).toHaveBeenCalledWith('adb')
expect(validateVersion).toHaveBeenCalledWith({ required, version })
expect(log.warn).toHaveBeenCalledWith(
expect.stringContaining(`Found adb version ${version}. Expected adb version ${required}.`),
)
})

it('should not log warning if adb version satisfies the requirement', async () => {
const required = '1.0.0'
const version = '1.0.0'
vi.mocked(getVersion).mockReturnValue(version)
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
await initScriptVersionAdb(required)
expect(getVersion).toHaveBeenCalledWith('adb')
expect(validateVersion).toHaveBeenCalledWith({ required, version })
expect(log.warn).not.toHaveBeenCalled()
})

it('should log verbose message if verbose is true', async () => {
const required = '1.0.0'
const version = '1.0.0'
vi.mocked(getVersion).mockReturnValue(version)
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
await initScriptVersionAdb(required, true)
expect(getVersion).toHaveBeenCalledWith('adb')
expect(validateVersion).toHaveBeenCalledWith({ required, version })
expect(log.warn).toHaveBeenCalledWith(
`initScriptVersionAdb: required: ${required}, version: ${version}, valid: true`,
)
})

it('should log error if an exception occurs', async () => {
const required = '1.0.0'
const error = new Error('Test error')
vi.mocked(getVersion).mockImplementation(() => {
throw error
})
await initScriptVersionAdb(required)
expect(log.warn).toHaveBeenCalledWith(`Error ${error}`)
})
})
89 changes: 89 additions & 0 deletions test/init-script-version-anchor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { log } from '@clack/prompts'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { getVersion } from '../src/utils/get-version'
import { initScriptVersionAnchor } from '../src/utils/init-script-version-anchor'
import { validateVersion } from '../src/utils/validate-version'

vi.mock('../src/utils/get-version')
vi.mock('../src/utils/validate-version')
vi.mock('@clack/prompts', () => ({
log: {
warn: vi.fn(),
},
}))

describe('initScriptVersionAnchor', () => {
beforeEach(() => {
vi.resetAllMocks()
})

afterEach(() => {
vi.clearAllMocks()
})

it('should return early if no required version is provided', async () => {
await initScriptVersionAnchor()
expect(getVersion).not.toHaveBeenCalled()
expect(validateVersion).not.toHaveBeenCalled()
expect(log.warn).not.toHaveBeenCalled()
})

it('should log warning if anchor version is not found', async () => {
const required = '1.0.0'
vi.mocked(getVersion).mockReturnValue(undefined)
vi.mocked(validateVersion).mockReturnValue({ valid: false, version: undefined })
await initScriptVersionAnchor(required)
expect(getVersion).toHaveBeenCalledWith('anchor')
expect(validateVersion).toHaveBeenCalledWith({ required, version: undefined })
expect(log.warn).toHaveBeenCalledWith(
expect.stringContaining('Could not find Anchor version. Please install Anchor.'),
)
})

it('should log warning if anchor version does not satisfy the requirement', async () => {
const required = '1.0.0'
const version = '0.9.0'
vi.mocked(getVersion).mockReturnValue(version)
vi.mocked(validateVersion).mockReturnValue({ valid: false, version })
await initScriptVersionAnchor(required)
expect(getVersion).toHaveBeenCalledWith('anchor')
expect(validateVersion).toHaveBeenCalledWith({ required, version })
expect(log.warn).toHaveBeenCalledWith(
expect.stringContaining(`Found Anchor version ${version}. Expected Anchor version ${required}.`),
)
})

it('should not log warning if anchor version satisfies the requirement', async () => {
const required = '1.0.0'
const version = '1.0.0'
vi.mocked(getVersion).mockReturnValue(version)
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
await initScriptVersionAnchor(required)
expect(getVersion).toHaveBeenCalledWith('anchor')
expect(validateVersion).toHaveBeenCalledWith({ required, version })
expect(log.warn).not.toHaveBeenCalled()
})

it('should log verbose message if verbose is true', async () => {
const required = '1.0.0'
const version = '1.0.0'
vi.mocked(getVersion).mockReturnValue(version)
vi.mocked(validateVersion).mockReturnValue({ valid: true, version })
await initScriptVersionAnchor(required, true)
expect(getVersion).toHaveBeenCalledWith('anchor')
expect(validateVersion).toHaveBeenCalledWith({ required, version })
expect(log.warn).toHaveBeenCalledWith(
`initScriptVersionAnchor: required: ${required}, version: ${version}, valid: true`,
)
})

it('should log error if an exception occurs', async () => {
const required = '1.0.0'
const error = new Error('Test error')
vi.mocked(getVersion).mockImplementation(() => {
throw error
})
await initScriptVersionAnchor(required)
expect(log.warn).toHaveBeenCalledWith(`Error ${error}`)
})
})
Loading