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";
5 import { getGit, sparseCheckout, sparseCloneRepository, execAsync } from "./gitUtil.mjs";
6 import Settings from "../settings.mjs";
7 import { checkForInstallationRequirements } from "./requirementsUtil.mjs";
8 import { cp } from "fs/promises";
9 import { get } from "https";
11 isInternetConnected, CURRENT_DATA_VERSION, getDataRoot
12 } from "./downloadHelpers.mjs";
14 const EXAMPLES_REPOSITORY_URL =
15 "https://github.com/raspberrypi/pico-examples.git";
16 const EXAMPLES_JSON_URL =
17 "https://raspberrypi.github.io/pico-vscode/" +
18 `${CURRENT_DATA_VERSION}/examples.json`;
19 const EXAMPLES_GITREF =
20 "7fe60d6b4027771e45d97f207532c41b1d8c5418";
24 export interface Example {
30 interface ExamplesFile {
37 function buildExamplesPath(): string {
38 return joinPosix(homedir().replaceAll("\\", "/"), ".pico-sdk", "examples");
41 function parseExamplesJson(data: string): Example[] {
43 const examples = JSON.parse(data.toString()) as ExamplesFile;
45 return Object.keys(examples).map(key => ({
46 path: examples[key].path,
47 name: examples[key].name,
51 Logger.log("Failed to parse examples.json");
57 export async function loadExamples(): Promise<Example[]> {
59 if (!(await isInternetConnected())) {
61 "Error while downloading examples list. " +
62 "No internet connection"
65 const result = await new Promise<Example[]>((resolve, reject) => {
66 // Download the INI file
67 get(EXAMPLES_JSON_URL, response => {
68 if (response.statusCode !== 200) {
71 "Error while downloading examples list. " +
72 `Status code: ${response.statusCode}`
78 // Append data as it arrives
79 response.on("data", chunk => {
83 // Parse the INI data when the download is complete
84 response.on("end", () => {
85 // Resolve with the array of SupportedToolchainVersion
86 resolve(parseExamplesJson(data));
90 response.on("error", error => {
97 Logger.log(`Successfully downloaded examples list from the internet.`);
101 Logger.log(error instanceof Error ? error.message : (error as string));
104 const examplesFile = readFileSync(
105 joinPosix(getDataRoot(), "examples.json")
108 return parseExamplesJson(examplesFile.toString("utf-8"));
110 Logger.log("Failed to load examples.json");
117 export async function setupExample(
120 ): Promise<boolean> {
121 const examplesRepoPath = buildExamplesPath();
122 const absoluteExamplePath = joinPosix(examplesRepoPath, example.path);
124 const settings = Settings.getInstance();
125 if (settings === undefined) {
126 Logger.log("Error: Settings not initialized.");
131 // TODO: this does take about 2s - may be reduced
132 const requirementsCheck = await checkForInstallationRequirements(
135 if (!requirementsCheck) {
139 const gitPath = await getGit(settings);
141 if (existsSync(examplesRepoPath)) {
142 let ref = await execAsync(
143 `cd "${examplesRepoPath}" && ${
144 process.env.ComSpec === "powershell.exe" ? "&" : ""
145 }"${gitPath}" rev-parse HEAD`
147 Logger.log(`Examples git ref is ${ref.stdout}\n`);
148 if (ref.stdout.trim() !== EXAMPLES_GITREF) {
149 Logger.log(`Removing old examples repo\n`);
150 rmSync(examplesRepoPath, { recursive: true, force: true });
154 if (!existsSync(examplesRepoPath)) {
155 const result = await sparseCloneRepository(
156 EXAMPLES_REPOSITORY_URL,
167 Logger.log(`Spare-checkout selected example: ${example.name}`);
168 const result = await sparseCheckout(
177 Logger.log(`Copying example from ${absoluteExamplePath} to ${targetPath}`);
178 // TODO: use example.name or example.search key for project folder name?
179 await cp(absoluteExamplePath, joinPosix(targetPath, example.searchKey), {
182 Logger.log("Done copying example.");