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
14 changes: 14 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ Generate a `.vsix` package for distribution:
npm run package
```

## Update the Topo CLI Version

To update the Topo CLI version bundled with the extension, pass the new version
as a positional argument:

```bash
npm run bump-topo -- 9.1.0
```

The `--` forwards the version argument from npm to the script. The command
checks that the version has artifacts for every supported platform, then updates
the Topo version and platform checksums in `package.json`. If any artifact is
missing, `package.json` is left unchanged.

## Access the `topo` Binary

When the extension is loaded, the `topo` binary path is added to `PATH` and can be accessed directly from the VS Code terminal:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@
"build": "vite build --mode extension",
"watch": "vite build --mode extension --watch --minify=false",
"download": "node --no-warnings --experimental-strip-types scripts/download.ts",
"bump-topo": "node --no-warnings --experimental-strip-types scripts/bump-topo.ts",
"calculate-new-version": "node --no-warnings --experimental-strip-types scripts/calculate-new-version.ts",
"prettier-check": "prettier -c .",
"eslint-check": "eslint .",
Expand Down
99 changes: 99 additions & 0 deletions scripts/bump-topo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import fs from 'node:fs';
import path from 'node:path';
import yargs from 'yargs';
import * as manifest from '../src/manifest.ts';
import {
DOWNLOAD_TARGETS,
type DownloadTarget,
getArtifactUrl,
} from './topoArtifacts.ts';

const VERSION_PATTERN = /^\d+\.\d+\.\d+$/;

const getChecksum = async (
version: string,
target: DownloadTarget,
): Promise<[DownloadTarget, string]> => {
const url = getArtifactUrl(version, target);
const response = await fetch(url, { method: 'HEAD' });
if (!response.ok) {
throw new Error(
`Topo ${version} does not exist for ${target}: ${response.status} ${response.statusText}`,
);
}

const checksum = response.headers.get('x-checksum-sha256');
if (checksum === null) {
throw new Error(`Artifactory returned no SHA-256 for ${target}`);
}

return [target, checksum.toLowerCase()];
};

const main = async (): Promise<void> => {
const parsedArgs = yargs(process.argv.slice(2))
.usage('$0 <version>')
.command(
'$0 <version>',
'Updates the bundled Topo CLI version and checksums',
(command) =>
command.positional('version', {
description: 'Topo CLI version in major.minor.patch format',
type: 'string',
demandOption: true,
}),
)
.check((args) => {
if (
typeof args.version !== 'string' ||
!VERSION_PATTERN.test(args.version)
) {
throw new Error('Version must use major.minor.patch format');
}
return true;
})
.help()
.alias('h', 'help')
.version(false)
.strict()
.parseSync();
const version = parsedArgs.version as string;

const pkgPath = path.resolve(process.cwd(), 'package.json');
if (!fs.existsSync(pkgPath)) {
throw new Error(`Couldn't find package.json at ${pkgPath}`);
}

const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as Record<
string,
unknown
>;
const section = pkg[manifest.TOPO_CLI];
if (
typeof section !== 'object' ||
section === null ||
Array.isArray(section)
) {
throw new Error(
`package.json must have a top-level "${manifest.TOPO_CLI}" object`,
);
}

console.log(`→ Checking Topo ${version}`);
const checksumEntries: [DownloadTarget, string][] = [];
for (const target of DOWNLOAD_TARGETS) {
checksumEntries.push(await getChecksum(version, target));
}

const topo = section as Record<string, unknown>;
topo.version = version;
topo.sha256 = Object.fromEntries(checksumEntries);
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 4)}\n`);
console.log(`✓ Updated Topo to ${version} in package.json`);
};

void main().catch((err: unknown) => {
const errorMsg = err instanceof Error ? err.message : err;
console.error('✖ Error bumping Topo:', errorMsg);
process.exit(1);
});
33 changes: 7 additions & 26 deletions scripts/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,12 @@ import yargs from 'yargs';
import extract from 'extract-zip';
import * as tar from 'tar';
import * as manifest from '../src/manifest.ts';

const DOWNLOAD_TARGETS = [
'win32-x64',
'win32-arm64',
'linux-x64',
'linux-arm64',
'darwin-x64',
'darwin-arm64',
] as const;
type DownloadTarget = (typeof DOWNLOAD_TARGETS)[number];

const SHA256_PATTERN = /^[a-f0-9]{64}$/i;

const isSha256 = (value: unknown): value is string =>
typeof value === 'string' && SHA256_PATTERN.test(value);
import {
DOWNLOAD_TARGETS,
type DownloadTarget,
getArtifactUrl,
isSha256,
} from './topoArtifacts.ts';

const readFromUrl = async (url: string): Promise<Response> => {
const response = await fetch(url);
Expand Down Expand Up @@ -178,22 +169,12 @@ if (!isSha256(expectedSha256)) {
}

// --- Determine asset name ---------------------------------------------------
const assetMapping: Record<DownloadTarget, string> = {
'linux-x64': 'linux/topo_linux_amd64.tar.gz',
'linux-arm64': 'linux/topo_linux_arm64.tar.gz',
'darwin-x64': 'macos/topo_darwin_amd64.tar.gz',
'darwin-arm64': 'macos/topo_darwin_arm64.tar.gz',
'win32-x64': 'windows/topo_windows_amd64.zip',
'win32-arm64': 'windows/topo_windows_arm64.zip',
};
const isWin = target.startsWith('win32');
const topoFilename = isWin ? `${manifest.TOPO_CLI_WINDOWS}` : manifest.TOPO_CLI;
const destFilename = `resources/${topoFilename}`;
const assetPath = assetMapping[target];

// --- Compose download URL ---------------------------------------------------
const tag = `v${version}`;
const downloadUrl = `https://artifacts.tools.arm.com/topo/${tag}/${assetPath}`;
const downloadUrl = getArtifactUrl(version, target);
console.log(`→ Downloading ${downloadUrl}`);

// --- Perform download --------------------------------------------------------
Expand Down
31 changes: 31 additions & 0 deletions scripts/topoArtifacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export const DOWNLOAD_TARGETS = [
'win32-x64',
'win32-arm64',
'linux-x64',
'linux-arm64',
'darwin-x64',
'darwin-arm64',
] as const;

export type DownloadTarget = (typeof DOWNLOAD_TARGETS)[number];

const ARTIFACT_BASE_URL = 'https://artifacts.tools.arm.com/topo';

const ASSET_MAPPING: Record<DownloadTarget, string> = {
'linux-x64': 'linux/topo_linux_amd64.tar.gz',
'linux-arm64': 'linux/topo_linux_arm64.tar.gz',
'darwin-x64': 'macos/topo_darwin_amd64.tar.gz',
'darwin-arm64': 'macos/topo_darwin_arm64.tar.gz',
'win32-x64': 'windows/topo_windows_amd64.zip',
'win32-arm64': 'windows/topo_windows_arm64.zip',
};

export const getArtifactUrl = (
version: string,
target: DownloadTarget,
): string => `${ARTIFACT_BASE_URL}/v${version}/${ASSET_MAPPING[target]}`;

const SHA256_PATTERN = /^[a-f0-9]{64}$/i;

export const isSha256 = (value: unknown): value is string =>
typeof value === 'string' && SHA256_PATTERN.test(value);