@@ -211,6 +211,150 @@ runs:
211211 dotnet-version : |
212212 ${{steps.resolve_dotnet_sdk_versions.outputs.sdk-versions}}
213213
214+ - name : " Prune old dotnet installs"
215+ uses : actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
216+ env :
217+ CURRENT_SDK_VERSIONS : ${{steps.resolve_dotnet_sdk_versions.outputs.sdk-versions}}
218+ with :
219+ script : |
220+ const fs = require('fs');
221+ const path = require('path');
222+
223+ const OLDER_VERSIONS_TO_KEEP = 1;
224+ const oldFormat = /^[0-9a-f]{40}-\d+-\d+-\d+$/;
225+
226+ const parse = (version) => {
227+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/.exec(version);
228+ if (!match) {
229+ return null;
230+ }
231+ return { version, major: Number(match[1]), parts: [Number(match[1]), Number(match[2]), Number(match[3])], pre: match[4] || '' };
232+ };
233+
234+ const comparePre = (a, b) => {
235+ const left = a.split('.');
236+ const right = b.split('.');
237+ for (let i = 0; i < Math.max(left.length, right.length); i++) {
238+ if (left[i] === undefined) {
239+ return -1;
240+ }
241+ if (right[i] === undefined) {
242+ return 1;
243+ }
244+ const bothNumeric = /^\d+$/.test(left[i]) && /^\d+$/.test(right[i]);
245+ if (bothNumeric && Number(left[i]) !== Number(right[i])) {
246+ return Number(left[i]) - Number(right[i]);
247+ }
248+ if (!bothNumeric && left[i] !== right[i]) {
249+ return left[i] < right[i] ? -1 : 1;
250+ }
251+ }
252+ return 0;
253+ };
254+
255+ const compare = (a, b) => {
256+ for (let i = 0; i < 3; i++) {
257+ if (a.parts[i] !== b.parts[i]) {
258+ return a.parts[i] - b.parts[i];
259+ }
260+ }
261+ if (a.pre === b.pre) {
262+ return 0;
263+ }
264+ if (a.pre === '') {
265+ return 1;
266+ }
267+ if (b.pre === '') {
268+ return -1;
269+ }
270+ return comparePre(a.pre, b.pre);
271+ };
272+
273+ try {
274+ const dotnetRoot = path.join(process.env.GITHUB_WORKSPACE, '.dotnet');
275+ const currentName = path.basename(process.env.DOTNET_ROOT);
276+ const currentVersions = process.env.CURRENT_SDK_VERSIONS.split('\n').map(v => v.trim()).filter(Boolean);
277+
278+ const directories = fs.readdirSync(dotnetRoot, { withFileTypes: true })
279+ .filter(entry => entry.isDirectory())
280+ .map(entry => {
281+ const sdkPath = path.join(dotnetRoot, entry.name, 'sdk');
282+ const versions = fs.existsSync(sdkPath) ? fs.readdirSync(sdkPath) : [];
283+ return { name: entry.name, versions };
284+ });
285+
286+ const deletions = directories.filter(d => oldFormat.test(d.name)).map(d => d.name);
287+ const candidates = directories.filter(d => !oldFormat.test(d.name));
288+
289+ const all = new Map();
290+ for (const directory of candidates) {
291+ for (const version of directory.versions) {
292+ const parsed = parse(version);
293+ if (parsed) {
294+ all.set(version, parsed);
295+ }
296+ }
297+ }
298+ const current = currentVersions.map(parse).filter(Boolean);
299+ if (current.length === 0) {
300+ core.warning('⚠️ No parsable current SDK versions - skipping prune');
301+ return;
302+ }
303+ for (const parsed of current) {
304+ all.set(parsed.version, parsed);
305+ }
306+
307+ const keep = new Set(currentVersions);
308+ const installedMajors = [...new Set(current.map(v => v.major))];
309+ const lowestInstalled = Math.min(...installedMajors);
310+
311+ const byMajor = new Map();
312+ for (const parsed of all.values()) {
313+ if (!byMajor.has(parsed.major)) {
314+ byMajor.set(parsed.major, []);
315+ }
316+ byMajor.get(parsed.major).push(parsed);
317+ }
318+ for (const list of byMajor.values()) {
319+ list.sort((a, b) => compare(b, a));
320+ }
321+
322+ const belowLowest = [...byMajor.keys()].filter(m => m < lowestInstalled).sort((a, b) => b - a)[0];
323+
324+ for (const [major, list] of byMajor) {
325+ if (installedMajors.includes(major)) {
326+ const anchor = current.filter(v => v.major === major).sort((a, b) => compare(b, a))[0];
327+ const anchorIndex = list.findIndex(v => v.version === anchor.version);
328+ list.slice(0, anchorIndex + 1 + OLDER_VERSIONS_TO_KEEP).forEach(v => keep.add(v.version));
329+ } else if (major > lowestInstalled) {
330+ list.forEach(v => keep.add(v.version));
331+ } else if (major === belowLowest) {
332+ keep.add(list[0].version);
333+ }
334+ }
335+
336+ for (const directory of candidates) {
337+ if (directory.name !== currentName && directory.versions.length > 0 && !directory.versions.some(v => keep.has(v))) {
338+ deletions.push(directory.name);
339+ }
340+ }
341+
342+ core.info(`ℹ️ Keeping SDK versions: ${[...keep].sort().join(', ')}`);
343+ for (const name of deletions) {
344+ const target = path.join(dotnetRoot, name);
345+ if (path.dirname(target) !== dotnetRoot) {
346+ continue;
347+ }
348+ fs.rmSync(target, { recursive: true, force: true });
349+ core.info(`✅ Removed old dotnet install ${name}`);
350+ }
351+ if (deletions.length === 0) {
352+ core.info('ℹ️ No old dotnet installs to remove');
353+ }
354+ } catch (error) {
355+ core.warning(`⚠️ Pruning old dotnet installs failed: ${error.message}`);
356+ }
357+
214358 - name : " Configure nuget feeds"
215359 uses : ./.github/actions/nuget
216360 with :
0 commit comments