Skip to content

Commit 31e2d64

Browse files
authored
Resolve JSON schema from flowkit instead of Flow CLI (#831)
1 parent ace3a92 commit 31e2d64

File tree

5 files changed

+42
-90
lines changed

5 files changed

+42
-90
lines changed

extension/src/flow-cli/cli-versions-provider.ts

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,25 @@ import { execDefault } from '../utils/shell/exec'
44
import { Observable, distinctUntilChanged } from 'rxjs'
55
import { isEqual } from 'lodash'
66

7-
const CHECK_FLOW_CLI_CMD = (flowCommand: string): string => `${flowCommand} version --output=json`
8-
const CHECK_FLOW_CLI_CMD_NO_JSON = (flowCommand: string): string => `${flowCommand} version`
7+
const CHECK_FLOW_CLI_CMD = (flowCommand: string): string => `${flowCommand} version -v --output=json`
8+
const FLOWKIT_PACKAGE_NAME = 'github.com/onflow/flowkit/v2'
99

1010
export enum KNOWN_FLOW_COMMANDS {
1111
DEFAULT = 'flow',
1212
}
1313

14-
// Matches the version number from the output of the Flow CLI
15-
const LEGACY_VERSION_REGEXP = /Version:\s*v(.*)(?:\s|$)/m
16-
1714
export interface CliBinary {
1815
command: string
1916
version: semver.SemVer
17+
flowkitVersion: semver.SemVer
2018
}
2119

2220
interface FlowVersionOutput {
2321
version: string
22+
dependencies?: Array<{
23+
package: string
24+
version: string
25+
}>
2426
}
2527

2628
export class CliVersionsProvider {
@@ -84,31 +86,13 @@ export class CliVersionsProvider {
8486
// Format version string from output
8587
const versionInfo: FlowVersionOutput = JSON.parse(buffer)
8688

87-
return cliBinaryFromVersion(bin, versionInfo.version)
88-
} catch {
89-
// Fallback to old method if JSON is not supported/fails
90-
return await this.#fetchBinaryInformationOld(bin)
91-
}
92-
}
93-
94-
// Old version of fetchBinaryInformation (before JSON was supported)
95-
// Used as fallback for old CLI versions
96-
async #fetchBinaryInformationOld (bin: string): Promise<CliBinary | null> {
97-
try {
98-
// Get user's version information
99-
const output = (await execDefault(CHECK_FLOW_CLI_CMD_NO_JSON(
100-
bin
101-
)))
102-
103-
let versionStr: string | null = parseFlowCliVersion(output.stdout)
104-
if (versionStr === null) {
105-
// Try to fallback to stderr as patch for bugged version
106-
versionStr = parseFlowCliVersion(output.stderr)
107-
}
108-
109-
if (versionStr == null) return null
89+
// Extract flowkit version from dependencies
90+
const flowkitDep = versionInfo.dependencies?.find(dep =>
91+
dep.package === FLOWKIT_PACKAGE_NAME
92+
)
93+
const flowkitVersionStr = flowkitDep?.version
11094

111-
return cliBinaryFromVersion(bin, versionStr)
95+
return cliBinaryFromVersion(bin, versionInfo.version, flowkitVersionStr)
11296
} catch {
11397
return null
11498
}
@@ -130,16 +114,15 @@ export class CliVersionsProvider {
130114
}
131115
}
132116

133-
export function parseFlowCliVersion (buffer: Buffer | string): string | null {
134-
const rawMatch = buffer.toString().match(LEGACY_VERSION_REGEXP)?.[1] ?? null
135-
if (rawMatch == null) return null
136-
return semver.clean(rawMatch)
137-
}
138-
139-
function cliBinaryFromVersion (bin: string, versionStr: string): CliBinary | null {
117+
function cliBinaryFromVersion (bin: string, versionStr: string, flowkitVersionStr?: string): CliBinary | null {
140118
// Ensure user has a compatible version number installed
141119
const version: semver.SemVer | null = semver.parse(versionStr)
142120
if (version === null) return null
143121

144-
return { command: bin, version }
122+
// Parse flowkit version - both CLI and flowkit versions are required
123+
if (flowkitVersionStr == null) return null
124+
const flowkitVersion: semver.SemVer | null = semver.parse(flowkitVersionStr)
125+
if (flowkitVersion === null) return null
126+
127+
return { command: bin, version, flowkitVersion }
145128
}

extension/src/json-schema-provider.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import fetch from 'node-fetch'
66
import { CliProvider } from './flow-cli/cli-provider'
77

88
const CADENCE_SCHEMA_URI = 'cadence-schema'
9-
const GET_FLOW_SCHEMA_URL = (version: string): string => `https://raw.githubusercontent.com/onflow/flow-cli/v${version}/flowkit/schema.json`
9+
const GET_FLOW_SCHEMA_URL = (version: string): string => `https://raw.githubusercontent.com/onflow/flowkit/v${version}/schema.json`
1010

1111
// This class provides the JSON schema for the flow.json file
1212
// It is accessible via the URI scheme "cadence-schema:///flow.json"
@@ -37,21 +37,21 @@ export class JSONSchemaProvider implements vscode.FileSystemProvider, vscode.Dis
3737
return await this.getLocalSchema()
3838
}
3939

40-
const version = cliBinary.version.format()
41-
if (this.#schemaCache[version] == null) {
42-
// Try to get schema from flow-cli repo based on the flow-cli version
43-
this.#schemaCache[version] = fetch(GET_FLOW_SCHEMA_URL(version)).then(async (response: Response) => {
40+
const flowkitVersion = cliBinary.flowkitVersion.format()
41+
if (this.#schemaCache[flowkitVersion] == null) {
42+
// Try to get schema from flowkit repo based on the flowkit version
43+
this.#schemaCache[flowkitVersion] = fetch(GET_FLOW_SCHEMA_URL(flowkitVersion)).then(async (response: Response) => {
4444
if (!response.ok) {
45-
throw new Error(`Failed to fetch schema for flow-cli version ${version}`)
45+
throw new Error(`Failed to fetch schema for flowkit version ${flowkitVersion}`)
4646
}
4747
return await response.text()
4848
}).catch(async () => {
49-
void vscode.window.showWarningMessage('Failed to fetch flow.json schema from flow-cli repo, using local schema instead. Please update flow-cli to the latest version to get the latest schema.')
49+
void vscode.window.showWarningMessage('Failed to fetch flow.json schema from flowkit repo, using local schema instead. Please update flow-cli to the latest version to get the latest schema.')
5050
return await this.getLocalSchema()
5151
})
5252
}
5353

54-
return await this.#schemaCache[version]
54+
return await this.#schemaCache[flowkitVersion]
5555
}
5656

5757
async getLocalSchema (): Promise<string> {

extension/test/integration/1 - language-server.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ suite('Language Server & Emulator Integration', () => {
2323
// create a mock cli provider without invokign the constructor
2424
cliBinary$ = new BehaviorSubject<CliBinary>({
2525
command: 'flow',
26-
version: new SemVer('1.0.0')
26+
version: new SemVer('1.0.0'),
27+
flowkitVersion: new SemVer('1.0.0')
2728
})
2829
const mockCliProvider = {
2930
currentBinary$: cliBinary$,
@@ -61,7 +62,8 @@ suite('Language Server & Emulator Integration', () => {
6162
// Check that client remains stopped even if CLI binary changes
6263
cliBinary$.next({
6364
command: 'flow',
64-
version: new SemVer('1.0.1')
65+
version: new SemVer('1.0.1'),
66+
flowkitVersion: new SemVer('1.0.1')
6567
})
6668

6769
assert.equal(client?.state, State.Stopped)

extension/test/integration/3 - schema.test.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Subject } from 'rxjs'
1313

1414
suite('JSON schema tests', () => {
1515
let mockFlowVersionValue: SemVer | null = null
16+
let mockFlowkitVersionValue: SemVer | null = null
1617
let mockCliProvider: CliProvider
1718
let extensionPath: string
1819
let schemaProvider: JSONSchemaProvider
@@ -28,10 +29,11 @@ suite('JSON schema tests', () => {
2829
// Mock cli provider
2930
mockCliProvider = {
3031
currentBinary$: new Subject(),
31-
getCurrentBinary: sinon.stub().callsFake(async () => ((mockFlowVersionValue != null)
32+
getCurrentBinary: sinon.stub().callsFake(async () => ((mockFlowVersionValue != null && mockFlowkitVersionValue != null)
3233
? {
3334
name: 'flow',
34-
version: mockFlowVersionValue
35+
version: mockFlowVersionValue,
36+
flowkitVersion: mockFlowkitVersionValue
3537
}
3638
: null))
3739
} as any
@@ -41,7 +43,7 @@ suite('JSON schema tests', () => {
4143
;(fetch as unknown as any).default = async (url: string) => {
4244
// only mock valid response for version 1.0.0 for testing
4345
// other versions will return 404 and emulate a missing schema
44-
if (url === 'https://raw.githubusercontent.com/onflow/flow-cli/v1.0.0/flowkit/schema.json') {
46+
if (url === 'https://raw.githubusercontent.com/onflow/flowkit/v1.0.0/schema.json') {
4547
return {
4648
ok: true,
4749
text: async () => JSON.stringify({
@@ -72,6 +74,7 @@ suite('JSON schema tests', () => {
7274

7375
test('Defaults to local schema when version not found', async () => {
7476
mockFlowVersionValue = new SemVer('0.0.0')
77+
mockFlowkitVersionValue = new SemVer('0.0.0')
7578

7679
// Assert that the schema is the same as the local schema
7780
await vscode.workspace.fs.readFile(vscode.Uri.parse('cadence-schema:///flow.json')).then((data) => {
@@ -81,15 +84,17 @@ suite('JSON schema tests', () => {
8184

8285
test('Defaults to local schema when version is invalid', async () => {
8386
mockFlowVersionValue = null
87+
mockFlowkitVersionValue = null
8488

8589
// Assert that the schema is the same as the local schema
8690
await vscode.workspace.fs.readFile(vscode.Uri.parse('cadence-schema:///flow.json')).then((data) => {
8791
assert.strictEqual(data.toString(), readFileSync(path.resolve(extensionPath, './flow-schema.json'), 'utf-8'))
8892
})
8993
}).timeout(MaxTimeout)
9094

91-
test('Fetches remote schema for current CLI version', async () => {
92-
mockFlowVersionValue = new SemVer('1.0.0')
95+
test('Fetches remote schema for current flowkit version', async () => {
96+
mockFlowVersionValue = new SemVer('2.0.0')
97+
mockFlowkitVersionValue = new SemVer('1.0.0')
9398

9499
// Assert that the schema is the same as the remote schema
95100
await vscode.workspace.fs.readFile(vscode.Uri.parse('cadence-schema:///flow.json')).then((data) => {

extension/test/unit/parser.test.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)