]> Git Repo - pico-vscode.git/blob - src/utils/examplesUtil.mts
Fix linting errors
[pico-vscode.git] / src / utils / examplesUtil.mts
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 {
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";
12 import {
13   isInternetConnected, CURRENT_DATA_VERSION, getDataRoot
14 } from "./downloadHelpers.mjs";
15
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";
23 const EXAMPLES_TAG = 
24   "sdk-2.0.0";
25
26 export interface Example {
27   path: string;
28   name: string;
29   searchKey: string;
30 }
31
32 interface ExamplesFile {
33   [key: string]: {
34     path: string;
35     name: string;
36   };
37 }
38
39 function buildExamplesPath(): string {
40   return joinPosix(homedir().replaceAll("\\", "/"), ".pico-sdk", "examples");
41 }
42
43 function parseExamplesJson(data: string): Example[] {
44   try {
45     const examples = JSON.parse(data.toString()) as ExamplesFile;
46
47     return Object.keys(examples).map(key => ({
48       path: examples[key].path,
49       name: examples[key].name,
50       searchKey: key,
51     }));
52   } catch {
53     Logger.log("Failed to parse examples.json");
54
55     return [];
56   }
57 }
58
59 export async function loadExamples(): Promise<Example[]> {
60   try {
61     if (!(await isInternetConnected())) {
62       throw new Error(
63         "Error while downloading examples list. " +
64           "No internet connection"
65       );
66     }
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) {
71           reject(
72             new Error(
73               "Error while downloading examples list. " +
74                 `Status code: ${response.statusCode}`
75             )
76           );
77         }
78         let data = "";
79
80         // Append data as it arrives
81         response.on("data", chunk => {
82           data += chunk;
83         });
84
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));
89         });
90
91         // Handle errors
92         response.on("error", error => {
93           reject(error);
94         });
95       });
96     });
97
98     // TODO: Logger.debug
99     Logger.log(`Successfully downloaded examples list from the internet.`);
100
101     return result;
102   } catch (error) {
103     Logger.log(error instanceof Error ? error.message : (error as string));
104
105     try {
106       const examplesFile = readFileSync(
107         joinPosix(getDataRoot(), "examples.json")
108       );
109
110       return parseExamplesJson(examplesFile.toString("utf-8"));
111     } catch (e) {
112       Logger.log("Failed to load examples.json");
113
114       return [];
115     }
116   }
117 }
118
119 export async function setupExample(
120   example: Example,
121   targetPath: string
122 ): Promise<boolean> {
123   const examplesRepoPath = buildExamplesPath();
124   const absoluteExamplePath = joinPosix(examplesRepoPath, example.path);
125
126   const settings = Settings.getInstance();
127   if (settings === undefined) {
128     Logger.log("Error: Settings not initialized.");
129
130     return false;
131   }
132
133   // TODO: this does take about 2s - may be reduced
134   const requirementsCheck = await checkForInstallationRequirements(
135     settings
136   );
137   if (!requirementsCheck) {
138     return false;
139   }
140
141   const gitPath = await getGit(settings);
142
143   if (existsSync(examplesRepoPath)) {
144     const ref = await execAsync(
145       `cd "${examplesRepoPath}" && ${
146         process.env.ComSpec === "powershell.exe" ? "&" : ""
147       }"${gitPath}" rev-parse HEAD`
148     );
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 });
153     }
154   }
155
156   if (!existsSync(examplesRepoPath)) {
157     const result = await sparseCloneRepository(
158       EXAMPLES_REPOSITORY_URL,
159       EXAMPLES_TAG,
160       examplesRepoPath,
161       gitPath
162     );
163
164     if (!result) {
165       return result;
166     }
167   }
168
169   Logger.log(`Spare-checkout selected example: ${example.name}`);
170   const result = await sparseCheckout(
171     examplesRepoPath,
172     example.path,
173     gitPath
174   );
175   if (!result) {
176     return result;
177   }
178
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), {
182     recursive: true,
183   });
184   Logger.log("Done copying example.");
185
186   return result;
187 }
This page took 0.039055 seconds and 4 git commands to generate.