| // Yarn plugin to download binary artifact releases from GitHub and make | 
 | // them available on the Yarn CLI. | 
 | // | 
 | // The plugin implements a Resolver and a Locator that take in dependency | 
 | // with reference specification like: | 
 | // | 
 | // github-release:org/repo:version/binary | 
 | // | 
 | // e.g. in package.json: | 
 | // | 
 | // { | 
 | //   dependencies: { | 
 | //     'example-binary': 'github-release:example/binary:1.0/exe' | 
 | //   } | 
 | // } | 
 | // | 
 | // With that example the `exe` binary can be invoked via `yarn run exe` | 
 | // (or shorter `yarn exe`. | 
 | // | 
 | // The plugin does this by creating a faux package containing the wrapper | 
 | // JavaScript file and the binary. So the resulting package can be cached | 
 | // in Yarn cache. | 
 | // | 
 | // There is limited support for template literals, so when needed the | 
 | // reference specification can contain an expression within `${...}`. | 
 | // | 
 | // For example it is possible to specify this dependency: | 
 | // | 
 | // { | 
 | //   dependencies: { | 
 | //     'example-binary': 'github-release:example/binary:1.0/exe-${process.arch}' | 
 | //   } | 
 | // } | 
 | // | 
 | // Even though that is possible, it might not be desired in some cases. | 
 | // Notably when the expression is platform dependent (such as in the example | 
 | // above) the package will be different (e.g. containing binaries for | 
 | // different platforms) and the hash of the package will be different from | 
 | // the value persisted in Yarn lockfile, and would cause an error. | 
 | // | 
 | const YARN_FS_VERSION = '2.3.0'; | 
 |  | 
 | const reference_pattern = /^github-release:(?<organization>[^\/]+)\/(?<repository>[^:]+)(?::(?<version>[^\/]+))?\/(?<binary>[^:]+)$/g; | 
 |  | 
 | const supports = (ref) => { | 
 |   reference_pattern.lastIndex = 0; | 
 |   return reference_pattern.test(ref); | 
 | } | 
 |  | 
 | const parse = (ref) => { | 
 |   const reference = new Function(`return \`${ref}\`;`).call(); | 
 |  | 
 |   reference_pattern.lastIndex = 0; | 
 |   const parts = [...reference.matchAll(reference_pattern)][0]; | 
 |  | 
 |   return { | 
 |     organization: parts[1], | 
 |     repository: parts[2], | 
 |     version: parts[3] || 'latest', | 
 |     binary: parts[4] | 
 |   } | 
 | } | 
 |  | 
 | module.exports = { | 
 |   name: `github-release-binary`, | 
 |   factory: require => { | 
 |     const util = require('util'); | 
 |     const { httpUtils, structUtils, LinkType } = require('@yarnpkg/core'); | 
 |     const { ppath, xfs, ZipFS } = require('@yarnpkg/fslib'); | 
 |     const { getLibzipPromise } = require('@yarnpkg/libzip'); | 
 |  | 
 |     class GitHubReleaseFetcher { | 
 |  | 
 |       supports(locator, opts) { | 
 |         return supports(locator.reference); | 
 |       } | 
 |  | 
 |       getLocalPath(locator, opts) { | 
 |         return null; | 
 |       } | 
 |  | 
 |       async fetch(locator, opts) { | 
 |         const expectedChecksum = opts.checksums.get(locator.locatorHash) || null; | 
 |  | 
 |         const [packageFs, releaseFs, checksum] = await opts.cache.fetchPackageFromCache(locator, expectedChecksum, { | 
 |           onHit: () => opts.report.reportCacheHit(locator), | 
 |           onMiss: () => opts.report.reportCacheMiss(locator, `${structUtils.prettyLocator(opts.project.configuration, locator)} can't be found in the cache and will be fetched from the remote server`), | 
 |           loader: () => this.fetchFromNetwork(locator, opts), | 
 |           skipIntegrityCheck: opts.skipIntegrityCheck, | 
 |         }); | 
 |  | 
 |         return { | 
 |           packageFs, | 
 |           releaseFs, | 
 |           prefixPath: structUtils.getIdentVendorPath(locator), | 
 |           checksum, | 
 |         }; | 
 |       } | 
 |  | 
 |       async fetchFromNetwork(locator, opts) { | 
 |         // 1980-01-01, like Fedora | 
 |         const defaultTime = 315532800; | 
 |  | 
 |         const parts = parse(locator.reference); | 
 |  | 
 |         const releaseBuffer = await httpUtils.get(`https://github.com/${parts.organization}/${parts.repository}/releases/download/${parts.version}/${parts.binary}`, { | 
 |           configuration: opts.project.configuration, | 
 |         }); | 
 |  | 
 |         const packageName = ppath.join(locator.scope !== null ? '@' + locator.scope : '', locator.name); | 
 |  | 
 |         const tmpDir = xfs.mktempSync(); | 
 |  | 
 |         const zipFS = new ZipFS(ppath.join(tmpDir, 'release.zip'), { create: true, libzip: await getLibzipPromise() }); | 
 |  | 
 |         zipFS.writeFileSync('package.json', `{ "name": "${packageName}", "dependencies": { "@yarnpkg/fslib": "${YARN_FS_VERSION}" } }`); | 
 |         zipFS.utimesSync('package.json', defaultTime, defaultTime); | 
 |  | 
 |         const dir = ppath.join('node_modules', packageName); | 
 |         zipFS.mkdirSync(dir, { recursive: true }); | 
 |  | 
 |         const stubFile = ppath.join(dir, 'exec.js'); | 
 |         zipFS.writeFileSync(stubFile, `const { xfs } = require('@yarnpkg/fslib'); | 
 | const fs = require('fs'); | 
 | const path = require('path'); | 
 | const os = require('os'); | 
 | const { spawn } = require('child_process'); | 
 |  | 
 | const execute = (path, args) => { | 
 |   const child = spawn(path, args); | 
 |  | 
 |   process.stdin.pipe(child.stdin); | 
 |   child.stdout.pipe(process.stdout); | 
 |   child.stderr.pipe(process.stderr); | 
 |  | 
 |   child.on('error', err => { process.stderr.write(err + '\\n'); process.exit(1) } ); | 
 |   child.on('exit', status => process.exit(status)); | 
 | } | 
 |  | 
 | (async () => { | 
 |   const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'github-release-binary')); | 
 |   process.on('exit', () => { | 
 |     fs.rmdirSync(tmpDir, { recursive: true }); | 
 |   }) | 
 |  | 
 |   const binary = process.argv[1].replace('exec.js', '${parts.binary}'); | 
 |   const binaryPath = path.join(tmpDir, '${parts.binary}'); | 
 |   await xfs.copyFilePromise(binary, binaryPath); | 
 |   await xfs.chmodPromise(binaryPath, 0o755); | 
 |   execute(binaryPath, process.argv.slice(2)); | 
 | })();`); | 
 |         zipFS.chmodSync(stubFile, 0o755); | 
 |         zipFS.utimesSync(stubFile, defaultTime, defaultTime); | 
 |  | 
 |         const binaryPath = ppath.join(dir, parts.binary); | 
 |         zipFS.writeFileSync(binaryPath, releaseBuffer); | 
 |         zipFS.chmodSync(binaryPath, 0o755); | 
 |         zipFS.utimesSync(binaryPath, defaultTime, defaultTime); | 
 |  | 
 |         xfs.detachTemp(tmpDir); | 
 |  | 
 |         return zipFS; | 
 |       } | 
 |  | 
 |     } | 
 |  | 
 |     class GitHubReleaseResolver { | 
 |  | 
 |       supportsDescriptor(descriptor, opts) { | 
 |         return supports(descriptor.range); | 
 |       } | 
 |  | 
 |       supportsLocator(locator, opts) { | 
 |         return supports(locator.reference); | 
 |       } | 
 |  | 
 |       shouldPersistResolution(locator, opts) { | 
 |         return true; | 
 |       } | 
 |  | 
 |       bindDescriptor(descriptor, fromLocator, opts) { | 
 |         return descriptor; | 
 |       } | 
 |  | 
 |       getResolutionDependencies(descriptor, opts) { | 
 |         return []; | 
 |       } | 
 |  | 
 |       async getCandidates(descriptor, dependencies, opts) { | 
 |         return [structUtils.convertDescriptorToLocator(descriptor)]; | 
 |       } | 
 |  | 
 |       async getSatisfying(descriptor, references, opts) { | 
 |         return null; | 
 |       } | 
 |  | 
 |       async resolve(locator, opts) { | 
 |         const parts = parse(locator.reference); | 
 |  | 
 |         const fsLibDep = structUtils.makeDescriptor(structUtils.makeIdent('yarnpkg', 'fslib'), YARN_FS_VERSION) | 
 |  | 
 |         const dependencies = new Map(); | 
 |         dependencies.set(fsLibDep.identHash, fsLibDep); | 
 |  | 
 |         return { | 
 |           ...locator, | 
 |           version: parts.version, | 
 |           languageName: opts.project.configuration.get(`defaultLanguageName`), | 
 |           linkType: LinkType.HARD, | 
 |           dependencies: dependencies, | 
 |           bin: [ | 
 |             [ parts.binary, 'exec.js' ] | 
 |           ] | 
 |         }; | 
 |       } | 
 |  | 
 |     } | 
 |  | 
 |     return { | 
 |       fetchers: [ GitHubReleaseFetcher ], | 
 |       resolvers: [ GitHubReleaseResolver ], | 
 |     } | 
 |   } | 
 | }; |