Skip to content

Commit

Permalink
feat: download Deno CLI from Deno repository (#17)
Browse files Browse the repository at this point in the history
* feat: download Deno CLI from Deno repository

* chore: add comment

* feat: get latest Deno version

* refactor: check that latest version satisfies range
  • Loading branch information
eduardoboucas committed Mar 29, 2022
1 parent 569399f commit 68c9d30
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 11 deletions.
11 changes: 7 additions & 4 deletions src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,17 @@ class DenoBridge {

await fs.mkdir(this.cacheDirectory, { recursive: true })

const binaryPath = await download(this.cacheDirectory)
const version = await DenoBridge.getBinaryVersion(binaryPath)
const binaryPath = await download(this.cacheDirectory, this.versionRange)
const downloadedVersion = await DenoBridge.getBinaryVersion(binaryPath)

if (version === undefined) {
// We should never get here, because it means that `DENO_VERSION_RANGE` is
// a malformed semver range. If this does happen, let's throw an error so
// that the tests catch it.
if (downloadedVersion === undefined) {
throw new Error('Could not read downloaded binary')
}

await this.writeVersionFile(version)
await this.writeVersionFile(downloadedVersion)

if (this.onAfterDownload) {
this.onAfterDownload()
Expand Down
52 changes: 46 additions & 6 deletions src/downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import path from 'path'

import fetch from 'node-fetch'
import StreamZip from 'node-stream-zip'
import semver from 'semver'

import { getBinaryExtension, getPlatformTarget } from './platform.js'

const download = async (targetDirectory: string) => {
const download = async (targetDirectory: string, versionRange: string) => {
const zipPath = path.join(targetDirectory, 'deno-cli-latest.zip')
const data = await downloadLatestRelease()
const data = await downloadVersion(versionRange)
const binaryName = `deno${getBinaryExtension()}`
const binaryPath = path.join(targetDirectory, binaryName)
const file = fs.createWriteStream(zipPath)
Expand All @@ -25,8 +26,9 @@ const download = async (targetDirectory: string) => {
return binaryPath
}

const downloadLatestRelease = async () => {
const url = getReleaseURL()
const downloadVersion = async (versionRange: string) => {
const version = await getLatestVersionForRange(versionRange)
const url = getReleaseURL(version)
const res = await fetch(url)

if (res.body === null) {
Expand All @@ -45,10 +47,48 @@ const extractBinaryFromZip = async (zipPath: string, binaryPath: string, binaryN
await fs.promises.chmod(binaryPath, '755')
}

const getReleaseURL = () => {
const getLatestVersion = async () => {
try {
const response = await fetch('https://dl.deno.land/release-latest.txt')
const data = await response.text()

// We want to extract <VERSION> from the format `v<VERSION>`.
const version = data.match(/^v?(\d+\.\d+\.\d+)/)

if (version === null) {
return
}

return version[1]
} catch {
// This is a no-op. If we failed to retrieve the latest version, let's
// return `undefined` and let the code upstream handle it.
}
}

const getLatestVersionForRange = async (range: string) => {
const minimumVersion = semver.minVersion(range)?.version

// We should never get here, because it means that `DENO_VERSION_RANGE` is
// a malformed semver range. If this does happen, let's throw an error so
// that the tests catch it.
if (minimumVersion === undefined) {
throw new Error('Incorrect version range specified by Edge Bundler')
}

const latestVersion = await getLatestVersion()

if (latestVersion === undefined || !semver.satisfies(latestVersion, range)) {
return minimumVersion
}

return latestVersion
}

const getReleaseURL = (version: string) => {
const target = getPlatformTarget()

return `https://github.com/denoland/deno/releases/latest/download/deno-${target}.zip`
return `https://dl.deno.land/release/v${version}/deno-${target}.zip`
}

export { download }
1 change: 0 additions & 1 deletion test/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { DenoBridge } from '../src/bridge.js'
test('Downloads the Deno CLI on demand and caches it for subsequent calls', async (t) => {
// TODO: Mock HTTP call to Deno source, so that we don't need to download
// the actual package as part of the test.
// eslint-disable-next-line no-magic-numbers
t.timeout(20_000)

const tmpDir = await tmp.dir()
Expand Down

0 comments on commit 68c9d30

Please sign in to comment.