1 import { join as joinPosix } from "path/posix";
2 import Logger from "../logger.mjs";
3 import { existsSync, readFileSync, rmSync } from "fs";
4 import { homedir } from "os";
6 getGit, sparseCheckout, sparseCloneRepository, execAsync
7 } from "./gitUtil.mjs";
8 import Settings from "../settings.mjs";
9 import { checkForInstallationRequirements } from "./requirementsUtil.mjs";
10 import { cp } from "fs/promises";
11 import { get } from "https";
13 isInternetConnected, CURRENT_DATA_VERSION, getDataRoot
14 } from "./downloadHelpers.mjs";
16 const EXAMPLES_REPOSITORY_URL =
17 "https://github.com/raspberrypi/pico-examples.git";
18 const EXAMPLES_JSON_URL =
19 "https://raspberrypi.github.io/pico-vscode/" +
20 `${CURRENT_DATA_VERSION}/examples.json`;
21 const EXAMPLES_GITREF =
22 "7fe60d6b4027771e45d97f207532c41b1d8c5418";
26 export interface Example {
32 interface ExamplesFile {
39 function buildExamplesPath(): string {
40 return joinPosix(homedir().replaceAll("\\", "/"), ".pico-sdk", "examples");
43 function parseExamplesJson(data: string): Example[] {
45 const examples = JSON.parse(data.toString()) as ExamplesFile;
47 return Object.keys(examples).map(key => ({
48 path: examples[key].path,
49 name: examples[key].name,
53 Logger.log("Failed to parse examples.json");
59 export async function loadExamples(): Promise<Example[]> {
61 if (!(await isInternetConnected())) {
63 "Error while downloading examples list. " +
64 "No internet connection"
67 const result = await new Promise<Example[]>((resolve, reject) => {
68 // Download the INI file
69 get(EXAMPLES_JSON_URL, response => {
70 if (response.statusCode !== 200) {
73 "Error while downloading examples list. " +
74 `Status code: ${response.statusCode}`
80 // Append data as it arrives
81 response.on("data", chunk => {
85 // Parse the INI data when the download is complete
86 response.on("end", () => {
87 // Resolve with the array of SupportedToolchainVersion
88 resolve(parseExamplesJson(data));
92 response.on("error", error => {
99 Logger.log(`Successfully downloaded examples list from the internet.`);
103 Logger.log(error instanceof Error ? error.message : (error as string));
106 const examplesFile = readFileSync(
107 joinPosix(getDataRoot(), "examples.json")
110 return parseExamplesJson(examplesFile.toString("utf-8"));
112 Logger.log("Failed to load examples.json");
119 export async function setupExample(
122 ): Promise<boolean> {
123 const examplesRepoPath = buildExamplesPath();
124 const absoluteExamplePath = joinPosix(examplesRepoPath, example.path);
126 const settings = Settings.getInstance();
127 if (settings === undefined) {
128 Logger.log("Error: Settings not initialized.");
133 // TODO: this does take about 2s - may be reduced
134 const requirementsCheck = await checkForInstallationRequirements(
137 if (!requirementsCheck) {
141 const gitPath = await getGit(settings);
143 if (existsSync(examplesRepoPath)) {
144 const ref = await execAsync(
145 `cd "${examplesRepoPath}" && ${
146 process.env.ComSpec === "powershell.exe" ? "&" : ""
147 }"${gitPath}" rev-parse HEAD`
149 Logger.log(`Examples git ref is ${ref.stdout}\n`);
150 if (ref.stdout.trim() !== EXAMPLES_GITREF) {
151 Logger.log(`Removing old examples repo\n`);
152 rmSync(examplesRepoPath, { recursive: true, force: true });
156 if (!existsSync(examplesRepoPath)) {
157 const result = await sparseCloneRepository(
158 EXAMPLES_REPOSITORY_URL,
169 Logger.log(`Spare-checkout selected example: ${example.name}`);
170 const result = await sparseCheckout(
179 Logger.log(`Copying example from ${absoluteExamplePath} to ${targetPath}`);
180 // TODO: use example.name or example.search key for project folder name?
181 await cp(absoluteExamplePath, joinPosix(targetPath, example.searchKey), {
184 Logger.log("Done copying example.");