Skip to content

Commit 158da6c

Browse files
authored
Implements "yarn self set" (#6673)
* Implements "yarn self set" * Adds support for nightlies * Updates changelog * Renames "yarn self set" into "yarn policies set-version"
1 parent 7f41910 commit 158da6c

File tree

5 files changed

+175
-3
lines changed

5 files changed

+175
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Please add one entry in this file for each change in Yarn's behavior. Use the sa
44

55
## Master
66

7+
- Implements `yarn policies set-version [range]`. Check [the documentation]() for usage & tips.
8+
9+
[#6673](https://github.com/yarnpkg/yarn/pull/6673) - [**Maël Nison**](https://twitter.com/arcanis)
10+
711
- Fixes a resolution issue when a package had an invalid `main` entry
812

913
[#6682](https://github.com/yarnpkg/yarn/pull/6682) - [**Maël Nison**](https://twitter.com/arcanis)

src/cli/commands/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import * as node from './node.js';
3232
import * as outdated from './outdated.js';
3333
import * as owner from './owner.js';
3434
import * as pack from './pack.js';
35+
import * as policies from './policies.js';
3536
import * as publish from './publish.js';
3637
import * as remove from './remove.js';
3738
import * as run from './run.js';
@@ -78,6 +79,7 @@ const commands = {
7879
outdated,
7980
owner,
8081
pack,
82+
policies,
8183
prune: buildUseless("The prune command isn't necessary. `yarn install` will prune extraneous packages."),
8284
publish,
8385
remove,

src/cli/commands/policies.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/* @flow */
2+
3+
import type {Reporter} from '../../reporters/index.js';
4+
import type Config from '../../config.js';
5+
import buildSubCommands from './_build-sub-commands.js';
6+
import {getRcConfigForCwd} from '../../rc.js';
7+
import * as fs from '../../util/fs.js';
8+
import {stringify} from '../../lockfile';
9+
10+
const chalk = require('chalk');
11+
const invariant = require('invariant');
12+
const path = require('path');
13+
const semver = require('semver');
14+
15+
type ReleaseAsset = {|
16+
id: any,
17+
18+
name: string,
19+
browser_download_url: string,
20+
|};
21+
22+
type Release = {|
23+
id: any,
24+
25+
draft: boolean,
26+
prerelease: boolean,
27+
28+
tag_name: string,
29+
version: {|
30+
version: string,
31+
|},
32+
33+
assets: Array<ReleaseAsset>,
34+
|};
35+
36+
function getBundleAsset(release: Release): ?ReleaseAsset {
37+
return release.assets.find(asset => {
38+
return asset.name.match(/^yarn-[0-9]+\.[0-9]+\.[0-9]+\.js$/);
39+
});
40+
}
41+
42+
type FetchReleasesOptions = {|
43+
includePrereleases: boolean,
44+
|};
45+
46+
async function fetchReleases(
47+
config: Config,
48+
{includePrereleases = false}: FetchReleasesOptions = {},
49+
): Promise<Array<Release>> {
50+
const request: Array<Release> = await config.requestManager.request({
51+
url: `https://api.github.com/repos/yarnpkg/yarn/releases`,
52+
json: true,
53+
});
54+
55+
const releases = request.filter(release => {
56+
if (release.draft) {
57+
return false;
58+
}
59+
60+
if (release.prerelease && !includePrereleases) {
61+
return false;
62+
}
63+
64+
// $FlowFixMe
65+
release.version = semver.coerce(release.tag_name);
66+
67+
if (!release.version) {
68+
return false;
69+
}
70+
71+
if (!getBundleAsset(release)) {
72+
return false;
73+
}
74+
75+
return true;
76+
});
77+
78+
releases.sort((a, b) => {
79+
// $FlowFixMe
80+
return -semver.compare(a.version, b.version);
81+
});
82+
83+
return releases;
84+
}
85+
86+
function fetchBundle(config: Config, url: string): Promise<Buffer> {
87+
return config.requestManager.request({
88+
url,
89+
buffer: true,
90+
});
91+
}
92+
93+
export function hasWrapper(flags: Object, args: Array<string>): boolean {
94+
return false;
95+
}
96+
97+
const {run, setFlags, examples} = buildSubCommands('policies', {
98+
async setVersion(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> {
99+
let range = args[0] || 'latest';
100+
let allowRc = flags.rc;
101+
102+
reporter.log(`Resolving ${chalk.yellow(range)} to a url...`);
103+
104+
if (range === 'rc') {
105+
range = 'latest';
106+
allowRc = true;
107+
}
108+
109+
if (range === 'latest') {
110+
range = '*';
111+
}
112+
113+
let bundleUrl;
114+
let bundleVersion;
115+
116+
if (range === 'nightly' || range === 'nightlies') {
117+
bundleUrl = 'https://nightly.yarnpkg.com/latest.js';
118+
bundleVersion = 'nightly';
119+
} else {
120+
const releases = await fetchReleases(config, {
121+
includePrereleases: allowRc,
122+
});
123+
124+
const release = releases.find(release => {
125+
// $FlowFixMe
126+
return semver.satisfies(release.version, range);
127+
});
128+
129+
if (!release) {
130+
throw new Error(`Release not found: ${range}`);
131+
}
132+
133+
const asset = getBundleAsset(release);
134+
invariant(asset, 'The bundle asset should exist');
135+
136+
bundleUrl = asset.browser_download_url;
137+
bundleVersion = release.version.version;
138+
}
139+
140+
reporter.log(`Downloading ${chalk.green(bundleUrl)}...`);
141+
142+
const bundle = await fetchBundle(config, bundleUrl);
143+
const rc = getRcConfigForCwd(config.lockfileFolder, []);
144+
145+
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`);
146+
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
147+
await fs.mkdirp(path.dirname(yarnPath));
148+
await fs.writeFile(yarnPath, bundle);
149+
await fs.chmod(yarnPath, 0o755);
150+
151+
const rcPath = `${config.lockfileFolder}/.yarnrc`;
152+
reporter.log(`Updating ${chalk.magenta(rcPath)}...`);
153+
rc['yarn-path'] = path.relative(config.lockfileFolder, yarnPath);
154+
await fs.writeFilePreservingEol(rcPath, `${stringify(rc)}\n`);
155+
156+
reporter.log(`Done!`);
157+
},
158+
});
159+
160+
export {run, setFlags, examples};

src/registries/yarn-registry.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export const DEFAULTS = {
3131
'user-agent': [`yarn/${version}`, 'npm/?', `node/${process.version}`, process.platform, process.arch].join(' '),
3232
};
3333

34-
const RELATIVE_KEYS = ['yarn-offline-mirror', 'cache-folder', 'offline-cache-folder'];
34+
const RELATIVE_KEYS = ['yarn-offline-mirror', 'cache-folder', 'offline-cache-folder', 'yarn-path'];
35+
const FOLDER_KEY = ['yarn-offline-mirror', 'cache-folder', 'offline-cache-folder'];
3536

3637
const npmMap = {
3738
'version-git-sign': 'sign-git-tag',
@@ -96,7 +97,10 @@ export default class YarnRegistry extends NpmRegistry {
9697

9798
if (!this.config[key] && valueLoc) {
9899
const resolvedLoc = (config[key] = path.resolve(path.dirname(loc), valueLoc));
99-
await fs.mkdirp(resolvedLoc);
100+
101+
if (FOLDER_KEY.includes(key)) {
102+
await fs.mkdirp(resolvedLoc);
103+
}
100104
}
101105
}
102106

src/util/fs.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export const lockQueue = new BlockingQueue('fs lock');
2828

2929
export const readFileBuffer = promisify(fs.readFile);
3030
export const open: (path: string, flags: string, mode?: number) => Promise<Array<string>> = promisify(fs.open);
31-
export const writeFile: (path: string, data: string, options?: Object) => Promise<void> = promisify(fs.writeFile);
31+
export const writeFile: (path: string, data: string | Buffer, options?: Object) => Promise<void> = promisify(
32+
fs.writeFile,
33+
);
3234
export const readlink: (path: string, opts: void) => Promise<string> = promisify(fs.readlink);
3335
export const realpath: (path: string, opts: void) => Promise<string> = promisify(fs.realpath);
3436
export const readdir: (path: string, opts: void) => Promise<Array<string>> = promisify(fs.readdir);

0 commit comments

Comments
 (0)