From 1b13f2984ade330dd09f9a27bd0c5f06341c6db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 13 Nov 2018 17:01:52 +0000 Subject: [PATCH 1/4] Implements "yarn self set" --- src/cli/commands/index.js | 2 + src/cli/commands/self.js | 135 ++++++++++++++++++++++++++++++++ src/registries/yarn-registry.js | 8 +- 3 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 src/cli/commands/self.js diff --git a/src/cli/commands/index.js b/src/cli/commands/index.js index 182f9958aa..1380b9c5cb 100644 --- a/src/cli/commands/index.js +++ b/src/cli/commands/index.js @@ -35,6 +35,7 @@ import * as pack from './pack.js'; import * as publish from './publish.js'; import * as remove from './remove.js'; import * as run from './run.js'; +import * as self from './self.js'; import * as tag from './tag.js'; import * as team from './team.js'; import * as unplug from './unplug.js'; @@ -82,6 +83,7 @@ const commands = { publish, remove, run, + self, tag, team, unplug, diff --git a/src/cli/commands/self.js b/src/cli/commands/self.js new file mode 100644 index 0000000000..1cd5bf9a35 --- /dev/null +++ b/src/cli/commands/self.js @@ -0,0 +1,135 @@ +/* @flow */ + +import type {Reporter} from '../../reporters/index.js'; +import type Config from '../../config.js'; +import buildSubCommands from './_build-sub-commands.js'; +import {getRcConfigForCwd} from '../../rc.js'; +import * as fs from '../../util/fs.js'; +import {stringify} from '../../lockfile'; + +const chalk = require('chalk'); +const invariant = require('invariant'); +const path = require('path'); +const semver = require('semver'); + +type ReleaseAsset = {| + id: any, + + name: string, + browser_download_url: string, +|}; + +type Release = {| + id: any, + + draft: boolean, + prerelease: boolean, + + tag_name: string, + version: {| + version: string, + |}, + + assets: Array, +|}; + +function getBundleAsset(release: Release): ?ReleaseAsset { + return release.assets.find(asset => { + return asset.name.match(/^yarn-[0-9]+\.[0-9]+\.[0-9]+\.js$/); + }); +} + +type FetchReleasesOptions = {| + includePrereleases: boolean, +|}; + +async function fetchReleases( + config: Config, + {includePrereleases = false}: FetchReleasesOptions = {}, +): Promise> { + const request: Array = await config.requestManager.request({ + url: `https://api.github.com/repos/yarnpkg/yarn/releases`, + json: true, + }); + + const releases = request.filter(release => { + if (release.draft) { + return false; + } + + if (release.prerelease && !includePrereleases) { + return false; + } + + // $FlowFixMe + release.version = semver.coerce(release.tag_name); + + if (!release.version) { + return false; + } + + if (!getBundleAsset(release)) { + return false; + } + + return true; + }); + + releases.sort((a, b) => { + // $FlowFixMe + return -semver.compare(a.version, b.version); + }); + + return releases; +} + +async function fetchBundle(config: Config, asset: ReleaseAsset): Promise { + const data: Buffer = await config.requestManager.request({ + url: asset.browser_download_url, + buffer: true, + }); + + return data.toString(); +} + +export function hasWrapper(flags: Object, args: Array): boolean { + return false; +} + +const {run, setFlags, examples} = buildSubCommands('self', { + async set(config: Config, reporter: Reporter, flags: Object, args: Array): Promise { + const releases = await fetchReleases(config); + + const release = releases.find(release => { + // $FlowFixMe + return semver.satisfies(release.version, args[0]); + }); + + if (!release) { + throw new Error(`Release not found: ${args[0]}`); + } + + const asset = getBundleAsset(release); + invariant(asset, 'The bundle asset should exist'); + + reporter.log(`Downloading ${chalk.green(asset.name)}...`); + + const bundle = await fetchBundle(config, asset); + const rc = getRcConfigForCwd(config.lockfileFolder, []); + + const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/${release.version.version}.js`); + reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`); + await fs.mkdirp(path.dirname(yarnPath)); + await fs.writeFile(yarnPath, bundle); + await fs.chmod(yarnPath, 0o755); + + const rcPath = `${config.lockfileFolder}/.yarnrc`; + reporter.log(`Updating ${chalk.magenta(rcPath)}...`); + rc['yarn-path'] = path.relative(config.lockfileFolder, yarnPath); + await fs.writeFilePreservingEol(rcPath, `${stringify(rc)}\n`); + + reporter.log(`Done!`); + }, +}); + +export {run, setFlags, examples}; diff --git a/src/registries/yarn-registry.js b/src/registries/yarn-registry.js index 020638afa5..5e63209129 100644 --- a/src/registries/yarn-registry.js +++ b/src/registries/yarn-registry.js @@ -31,7 +31,8 @@ export const DEFAULTS = { 'user-agent': [`yarn/${version}`, 'npm/?', `node/${process.version}`, process.platform, process.arch].join(' '), }; -const RELATIVE_KEYS = ['yarn-offline-mirror', 'cache-folder', 'offline-cache-folder']; +const RELATIVE_KEYS = ['yarn-offline-mirror', 'cache-folder', 'offline-cache-folder', 'yarn-path']; +const FOLDER_KEY = ['yarn-offline-mirror', 'cache-folder', 'offline-cache-folder']; const npmMap = { 'version-git-sign': 'sign-git-tag', @@ -96,7 +97,10 @@ export default class YarnRegistry extends NpmRegistry { if (!this.config[key] && valueLoc) { const resolvedLoc = (config[key] = path.resolve(path.dirname(loc), valueLoc)); - await fs.mkdirp(resolvedLoc); + + if (FOLDER_KEY.includes(key)) { + await fs.mkdirp(resolvedLoc); + } } } From 065abe16c2cf2e5af167f60e40f9247cfefda704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 13 Nov 2018 17:29:38 +0000 Subject: [PATCH 2/4] Adds support for nightlies --- src/cli/commands/self.js | 57 ++++++++++++++++++++++++++++------------ src/util/fs.js | 4 ++- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/cli/commands/self.js b/src/cli/commands/self.js index 1cd5bf9a35..3062abbd2b 100644 --- a/src/cli/commands/self.js +++ b/src/cli/commands/self.js @@ -83,13 +83,11 @@ async function fetchReleases( return releases; } -async function fetchBundle(config: Config, asset: ReleaseAsset): Promise { - const data: Buffer = await config.requestManager.request({ - url: asset.browser_download_url, +function fetchBundle(config: Config, url: string): Promise { + return config.requestManager.request({ + url, buffer: true, }); - - return data.toString(); } export function hasWrapper(flags: Object, args: Array): boolean { @@ -98,26 +96,51 @@ export function hasWrapper(flags: Object, args: Array): boolean { const {run, setFlags, examples} = buildSubCommands('self', { async set(config: Config, reporter: Reporter, flags: Object, args: Array): Promise { - const releases = await fetchReleases(config); + let range = args[0] || 'latest'; + let allowRc = flags.rc; - const release = releases.find(release => { - // $FlowFixMe - return semver.satisfies(release.version, args[0]); - }); + if (range === 'rc') { + range = 'latest'; + allowRc = true; + } - if (!release) { - throw new Error(`Release not found: ${args[0]}`); + if (range === 'latest') { + range = '*'; } - const asset = getBundleAsset(release); - invariant(asset, 'The bundle asset should exist'); + let bundleUrl; + let bundleVersion; + + if (range === 'nightly' || range === 'nightlies') { + bundleUrl = 'https://nightly.yarnpkg.com/latest.js'; + bundleVersion = 'nightly'; + } else { + const releases = await fetchReleases(config, { + includePrereleases: allowRc, + }); + + const release = releases.find(release => { + // $FlowFixMe + return semver.satisfies(release.version, range); + }); + + if (!release) { + throw new Error(`Release not found: ${range}`); + } + + const asset = getBundleAsset(release); + invariant(asset, 'The bundle asset should exist'); + + bundleUrl = asset.browser_download_url; + bundleVersion = release.version.version; + } - reporter.log(`Downloading ${chalk.green(asset.name)}...`); + reporter.log(`Downloading ${chalk.green(bundleUrl)}...`); - const bundle = await fetchBundle(config, asset); + const bundle = await fetchBundle(config, bundleUrl); const rc = getRcConfigForCwd(config.lockfileFolder, []); - const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/${release.version.version}.js`); + const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`); reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`); await fs.mkdirp(path.dirname(yarnPath)); await fs.writeFile(yarnPath, bundle); diff --git a/src/util/fs.js b/src/util/fs.js index 7877d0a63c..498b9707d2 100644 --- a/src/util/fs.js +++ b/src/util/fs.js @@ -28,7 +28,9 @@ export const lockQueue = new BlockingQueue('fs lock'); export const readFileBuffer = promisify(fs.readFile); export const open: (path: string, flags: string, mode?: number) => Promise> = promisify(fs.open); -export const writeFile: (path: string, data: string, options?: Object) => Promise = promisify(fs.writeFile); +export const writeFile: (path: string, data: string | Buffer, options?: Object) => Promise = promisify( + fs.writeFile, +); export const readlink: (path: string, opts: void) => Promise = promisify(fs.readlink); export const realpath: (path: string, opts: void) => Promise = promisify(fs.realpath); export const readdir: (path: string, opts: void) => Promise> = promisify(fs.readdir); From f2c7d17bc4b32cd47d9db9d33877746e7b90573e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 13 Nov 2018 17:45:56 +0000 Subject: [PATCH 3/4] Updates changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bef6d3b90..f4ed965834 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Please add one entry in this file for each change in Yarn's behavior. Use the sa ## Master +- Implements `yarn self set [range]`. Check [the documentation]() for usage & tips. + + [#6673](https://github.com/yarnpkg/yarn/pull/6673) - [**Maƫl Nison**](https://twitter.com/arcanis) + ## 1.12.3 **Important:** This release contains a cache bump. It will cause the very first install following the upgrade to take slightly more time, especially if you don't use the [Offline Mirror](https://yarnpkg.com/blog/2016/11/24/offline-mirror/) feature. After that everything will be back to normal. From 445fc65b75468b1b67eb5a33a5a30eb655c82cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 20 Nov 2018 15:58:26 +0000 Subject: [PATCH 4/4] Renames "yarn self set" into "yarn policies set-version" --- src/cli/commands/index.js | 4 ++-- src/cli/commands/{self.js => policies.js} | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) rename src/cli/commands/{self.js => policies.js} (93%) diff --git a/src/cli/commands/index.js b/src/cli/commands/index.js index 1380b9c5cb..2a8f6a852f 100644 --- a/src/cli/commands/index.js +++ b/src/cli/commands/index.js @@ -32,10 +32,10 @@ import * as node from './node.js'; import * as outdated from './outdated.js'; import * as owner from './owner.js'; import * as pack from './pack.js'; +import * as policies from './policies.js'; import * as publish from './publish.js'; import * as remove from './remove.js'; import * as run from './run.js'; -import * as self from './self.js'; import * as tag from './tag.js'; import * as team from './team.js'; import * as unplug from './unplug.js'; @@ -79,11 +79,11 @@ const commands = { outdated, owner, pack, + policies, prune: buildUseless("The prune command isn't necessary. `yarn install` will prune extraneous packages."), publish, remove, run, - self, tag, team, unplug, diff --git a/src/cli/commands/self.js b/src/cli/commands/policies.js similarity index 93% rename from src/cli/commands/self.js rename to src/cli/commands/policies.js index 3062abbd2b..595f42b5aa 100644 --- a/src/cli/commands/self.js +++ b/src/cli/commands/policies.js @@ -94,11 +94,13 @@ export function hasWrapper(flags: Object, args: Array): boolean { return false; } -const {run, setFlags, examples} = buildSubCommands('self', { - async set(config: Config, reporter: Reporter, flags: Object, args: Array): Promise { +const {run, setFlags, examples} = buildSubCommands('policies', { + async setVersion(config: Config, reporter: Reporter, flags: Object, args: Array): Promise { let range = args[0] || 'latest'; let allowRc = flags.rc; + reporter.log(`Resolving ${chalk.yellow(range)} to a url...`); + if (range === 'rc') { range = 'latest'; allowRc = true;