import { IGithubRelease, IReleaseLookup } from './asset-lookup' import { DartSass } from './constants' import { components } from '@octokit/openapi-types' import * as tc from '@actions/tool-cache' import * as core from '@actions/core' import { Platform } from './os' import * as os from 'node:os' import path from 'path' import { mv, rmRF } from '@actions/io' import { randomUUID } from 'crypto' import { errorMsg } from './utils/error' export interface IDartSassInstallCommand { version: string } export class DartSassInstaller { private readonly releaseLookup: IReleaseLookup private readonly platform: Platform constructor(releaseLookup: IReleaseLookup, platform?: Platform) { this.platform = platform ?? new Platform() this.releaseLookup = releaseLookup } async install(cmd: IDartSassInstallCommand): Promise<void> { const release = await this.releaseLookup.getRelease( DartSass.Org, DartSass.Repo, cmd.version, DartSassReleaseTransformer ) core.debug(`Operating System: ${this.platform.os}`) core.debug(`Processor Architecture: ${this.platform.arch}`) const tmpDir = os.tmpdir() try { const cachedDartSass = tc.find( DartSass.Name, release.tag_name, this.platform.arch ) if (cachedDartSass) { core.addPath(cachedDartSass) return } } catch (e) { core.warning(`Failed to lookup cached version: ${errorMsg(e)}`) } const toolUrl = release.assetUrl(this.platform) if (!toolUrl) { throw new Error('No matching URL detected for given platform') } const destPath = path.join( tmpDir, `dart-sass-${randomUUID()}${this.platform.archiveExtension()}` ) await tc.downloadTool(toolUrl, destPath) core.debug(`Extract archive: ${destPath}`) if (this.platform.isWindows()) { await tc.extractZip(destPath, tmpDir) } else { await tc.extractTar(destPath, tmpDir) } await rmRF(destPath) core.debug(`Add 'dart-sass' directory to cache`) try { const cachedDartSass = await tc.cacheDir( path.join(tmpDir, 'dart-sass'), DartSass.Name, release.tag_name, this.platform.arch ) core.addPath(cachedDartSass) } catch (e) { core.warning(`Failed to cache dart-sass directory: ${errorMsg(e)}`) const binDir = await this.platform.ensureBinDir() core.debug(`Move binaries to binDir: ${binDir}`) await mv(path.join(tmpDir, 'dart-sass'), binDir) core.addPath(path.join(binDir, 'dart-sass')) } } } export const DartSassReleaseTransformer = { map(release: components['schemas']['release']): DartSassRelease { return new DartSassRelease( release.tag_name.replace('v', ''), release.assets ) } } export class DartSassRelease implements IGithubRelease { private static readonly keyReplacementRegex = new RegExp( 'dart-sass-*(\\d+.\\d+.\\d+)-' ) private static readonly platformMapping: { [index: string]: string } = { linux: 'linux', win32: 'windows', darwin: 'macos' } private readonly assets: Map<string, string> readonly tag_name: string constructor( tag_name: string, assets: components['schemas']['release']['assets'] ) { this.tag_name = tag_name this.assets = new Map<string, string>() for (const asset of assets) { this.assets.set( asset.name.replace(DartSassRelease.keyReplacementRegex, ''), asset.browser_download_url ) } } assetUrl(platform: Platform): string | undefined { const mappedOS = DartSassRelease.platformMapping[platform.os] return this.assets.get( `${mappedOS}-${platform.arch}${platform.archiveExtension()}` ) } }