Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import { Manifest } from '../../../shared/lsp/types'
import { CfnLspServerEnvType } from './lspServerConfig'
import { CfnLspVersion, mapLegacyLinux, useOldLinuxVersion } from './utils'
import { getLogger } from '../../../shared/logger/logger'

interface CfnReleaseManifest {
manifestSchemaVersion: string
isManifestDeprecated: boolean
prod: CfnLspVersion[]
beta: CfnLspVersion[]
alpha: CfnLspVersion[]
}

/**
* Converts the raw CFN release manifest into the shared Manifest type,
* preferring the version flagged as `latest` if it exists.
* Remaps legacy Linux targets when running on older glibc systems.
*/
export function parseCfnManifest(content: string, environment: CfnLspServerEnvType): Manifest {
const raw: CfnReleaseManifest = JSON.parse(content)
let versions: CfnLspVersion[] = raw[environment] ?? []

if (useOldLinuxVersion()) {
getLogger('awsCfnLsp').info('In a legacy or sandbox Linux environment')
versions = mapLegacyLinux(versions)
}

const latestVersion = versions.find((v) => v.latest && !v.isDelisted)
const effectiveVersions = latestVersion ? [latestVersion] : versions

return {
manifestSchemaVersion: raw.manifestSchemaVersion,
artifactId: 'cloudformation-languageserver',
artifactDescription: 'AWS CloudFormation Language Server',
isManifestDeprecated: raw.isManifestDeprecated,
versions: effectiveVersions,
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
*/

import { BaseLspInstaller } from '../../../shared/lsp/baseLspInstaller'
import { GitHubManifestAdapter } from './githubManifestAdapter'
import { fs } from '../../../shared/fs/fs'
import { CfnLspName, CfnLspServerEnvType, CfnLspServerFile } from './lspServerConfig'
import { isAutomation, isBeta, isDebugInstance } from '../../../shared/vscode/env'
import { dirname, join } from 'path'
import { join } from 'path'
import { getLogger } from '../../../shared/logger/logger'
import { ResourcePaths } from '../../../shared/lsp/types'
import { Manifest, ResourcePaths } from '../../../shared/lsp/types'
import * as nodeFs from 'fs' // eslint-disable-line no-restricted-imports
import globals from '../../../shared/extensionGlobals'
import { ManifestResolver } from '../../../shared/lsp/manifestResolver'
import { parseCfnManifest } from './cfnManifest'
import { toString } from '../utils'

const cfnManifestUrl =
'https://raw.githubusercontent.com/aws-cloudformation/cloudformation-languageserver/refs/heads/main/assets/release-manifest.json'

function determineEnvironment(): CfnLspServerEnvType {
if (isDebugInstance()) {
return 'alpha'
Expand All @@ -24,64 +27,39 @@ function determineEnvironment(): CfnLspServerEnvType {
return 'prod'
}

export class CfnLspInstaller extends BaseLspInstaller {
private readonly githubManifest = new GitHubManifestAdapter(
'aws-cloudformation',
'cloudformation-languageserver',
determineEnvironment()
)
class CfnManifestResolver extends ManifestResolver {
constructor(private readonly environment: CfnLspServerEnvType) {
super(cfnManifestUrl, CfnLspName, 'cfnLsp')
}

protected override parseManifest(content: string): Manifest {
getLogger('awsCfnLsp').info(`Parsing CloudFormation LSP manifest for ${this.environment}`)
return parseCfnManifest(content, this.environment)
}
}

export class CfnLspInstaller extends BaseLspInstaller {
constructor() {
super(
{
manifestUrl: 'github',
manifestUrl: cfnManifestUrl,
supportedVersions: '<2.0.0',
id: CfnLspName,
suppressPromptPrefix: 'cfnLsp',
},
'awsCfnLsp',
{
resolve: async () => {
const log = getLogger('awsCfnLsp')
const cfnManifestStorageKey = 'aws.cloudformation.lsp.manifest'

try {
const manifest = await this.githubManifest.getManifest()
log.info(
`Creating CloudFormation LSP manifest for ${this.githubManifest.environment}`,
manifest.versions.map((v) => v.serverVersion)
)

// Cache in CloudFormation-specific global state storage
globals.globalState.tryUpdate(cfnManifestStorageKey, {
content: JSON.stringify(manifest),
})

return manifest
} catch (error) {
log.warn(`GitHub fetch failed, trying cached manifest: ${error}`)

// Try cached manifest from CloudFormation-specific storage
const manifestData = globals.globalState.tryGet(cfnManifestStorageKey, Object, {})

if (manifestData?.content) {
log.debug('Using cached manifest for offline mode')
return JSON.parse(manifestData.content)
}

log.error('No cached manifest found')
throw error
}
},
} as any,
new CfnManifestResolver(determineEnvironment()),
'sha256'
)
}

protected async postInstall(assetDirectory: string): Promise<void> {
const resourcePaths = this.resourcePaths(assetDirectory)
const rootDir = dirname(resourcePaths.lsp)
await fs.chmod(join(rootDir, 'bin', process.platform === 'win32' ? 'cfn-init.exe' : 'cfn-init'), 0o755)
const entries = nodeFs.readdirSync(assetDirectory, { withFileTypes: true })
const folder = entries.find((e) => e.isDirectory())
if (folder) {
const rootDir = join(assetDirectory, folder.name)
await fs.chmod(join(rootDir, 'bin', process.platform === 'win32' ? 'cfn-init.exe' : 'cfn-init'), 0o755)
}
}

protected resourcePaths(assetDirectory?: string): ResourcePaths {
Expand All @@ -92,7 +70,6 @@ export class CfnLspInstaller extends BaseLspInstaller {
}
}

// Find the single extracted directory
const entries = nodeFs.readdirSync(assetDirectory, { withFileTypes: true })
const folders = entries.filter((entry) => entry.isDirectory())

Expand Down
Loading
Loading