Skip to content

Commit aedea05

Browse files
authored
fix(v1): disallow downgrade from v2 to v1 (#92)
1 parent 7c06f45 commit aedea05

File tree

4 files changed

+55
-58
lines changed

4 files changed

+55
-58
lines changed

src/commands/setup.ts

Lines changed: 28 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import util, { NPMRelease } from '../lib/util';
1+
import util from '../lib/util';
22
import { execSync } from 'child_process';
33
import os from 'os';
44
import chalk from 'chalk';
@@ -84,7 +84,7 @@ async function decideActionVersion(version: string, options: { update: boolean;
8484
let downgrade = false;
8585

8686
if (isUpdate) {
87-
const current = util.getCurrentNodeCGVersion();
87+
current = util.getCurrentNodeCGVersion();
8888

8989
if (semver.eq(target, current)) {
9090
console.log(
@@ -120,7 +120,12 @@ async function decideActionVersion(version: string, options: { update: boolean;
120120
}
121121

122122
if (semver.lt(target, 'v2.0.0')) {
123-
actionV1(current, target, isUpdate);
123+
if (current && semver.gte(current, 'v2.0.0')) {
124+
console.error(`You are attempting to downgrade NodeCG from v2.x to v1.x, which is not supported.`);
125+
return;
126+
}
127+
128+
await actionV1(current, target, isUpdate);
124129
} else if (semver.lt(target, 'v3.0.0')) {
125130
await actionV2(current, target, isUpdate);
126131
} else {
@@ -145,19 +150,16 @@ async function decideActionVersion(version: string, options: { update: boolean;
145150
}
146151
}
147152

148-
function actionV1(current: string | undefined, target: string, isUpdate: boolean) {
149-
if (isUpdate) {
153+
async function actionV1(current: string | undefined, target: string, isUpdate: boolean) {
154+
const isGitRepo = fs.existsSync('.git');
155+
if (isGitRepo && isUpdate) {
150156
process.stdout.write('Downloading latest release...');
151157
try {
152158
execSync('git fetch', { stdio: ['pipe', 'pipe', 'pipe'] });
153159
process.stdout.write(chalk.green('done!') + os.EOL);
154160
} catch (e) {
155-
/* istanbul ignore next */
156161
process.stdout.write(chalk.red('failed!') + os.EOL);
157-
/* istanbul ignore next */
158-
console.error(e.stack);
159-
/* istanbul ignore next */
160-
return;
162+
throw e;
161163
}
162164

163165
if (current) {
@@ -171,12 +173,8 @@ function actionV1(current: string | undefined, target: string, isUpdate: boolean
171173
execSync(`git clone ${NODECG_GIT_URL} .`, { stdio: ['pipe', 'pipe', 'pipe'] });
172174
process.stdout.write(chalk.green('done!') + os.EOL);
173175
} catch (e) {
174-
/* istanbul ignore next */
175176
process.stdout.write(chalk.red('failed!') + os.EOL);
176-
/* istanbul ignore next */
177-
console.error(e.stack);
178-
/* istanbul ignore next */
179-
return;
177+
throw e;
180178
}
181179

182180
// Check out the target version.
@@ -185,40 +183,22 @@ function actionV1(current: string | undefined, target: string, isUpdate: boolean
185183
execSync(`git checkout ${target}`, { stdio: ['pipe', 'pipe', 'pipe'] });
186184
process.stdout.write(chalk.green('done!') + os.EOL);
187185
} catch (e) {
188-
/* istanbul ignore next */
189186
process.stdout.write(chalk.red('failed!') + os.EOL);
190-
/* istanbul ignore next */
191-
console.error(e.stack);
187+
throw e;
192188
}
193189
}
194190
}
195191

196-
async function actionV2(current: string | undefined, target: string, _isUpdate: boolean) {
197-
let release: NPMRelease;
192+
async function actionV2(current: string | undefined, target: string, isUpdate: boolean) {
193+
if (isUpdate) {
194+
const deletingDirectories = ['.git', 'build', 'scripts', 'schemas'];
195+
await Promise.all(deletingDirectories.map((dir) => fs.promises.rm(dir, { recursive: true, force: true })));
196+
}
198197

199-
process.stdout.write('Downloading latest release...');
200-
try {
201-
release = await util.getLatestNodeCGRelease();
202-
// target is v1.2.3, release.version is 1.2.3
203-
if (!semver.satisfies(release.version, target)) {
204-
process.stdout.write(chalk.red('failed!') + os.EOL);
205-
console.error(
206-
`Expected latest npm release to be ${chalk.magenta(target)} but instead it was ${chalk.magenta(
207-
release.version,
208-
)}. Aborting.`,
209-
);
210-
return;
211-
}
198+
process.stdout.write(`Downloading ${target} from npm...`);
199+
const release = await util.getNodeCGRelease(target);
212200

213-
process.stdout.write(chalk.green('done!') + os.EOL);
214-
} catch (e) {
215-
/* istanbul ignore next */
216-
process.stdout.write(chalk.red('failed!') + os.EOL);
217-
/* istanbul ignore next */
218-
console.error(e.stack);
219-
/* istanbul ignore next */
220-
return;
221-
}
201+
process.stdout.write(chalk.green('done!') + os.EOL);
222202

223203
if (current) {
224204
logDownOrUpgradeMessage(current, target, semver.lt(target, current));
@@ -265,20 +245,13 @@ function gitCheckoutUpdate(target: string) {
265245
}
266246

267247
async function downloadAndExtractReleaseTarball(tarballUrl: string) {
268-
try {
269-
const res = await fetch(tarballUrl);
270-
if (!res.body) {
271-
throw new Error(`Failed to fetch release tarball from ${tarballUrl}, status code ${res.status}`);
272-
}
273-
274-
await stream.pipeline(res.body, tar.x({ strip: 1 }));
275-
process.stdout.write(chalk.green('done!') + os.EOL);
276-
} catch (e) {
277-
/* istanbul ignore next */
278-
process.stdout.write(chalk.red('failed!') + os.EOL);
279-
/* istanbul ignore next */
280-
console.error(e.stack);
248+
const res = await fetch(tarballUrl);
249+
if (!res.body) {
250+
throw new Error(`Failed to fetch release tarball from ${tarballUrl}, status code ${res.status}`);
281251
}
252+
253+
await stream.pipeline(res.body, tar.x({ strip: 1 }));
254+
process.stdout.write(chalk.green('done!') + os.EOL);
282255
}
283256

284257
function logDownOrUpgradeMessage(current: string, target: string, downgrade: boolean): void {

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const program = new Command('nodecg');
99
const packageVersion: string = require('../package.json').version;
1010

1111
// Check for updates, asynchronously, so as to not make the CLI startup time excessively slow
12-
util.getLatestNodeCGRelease()
12+
util.getLatestCLIRelease()
1313
.then((release) => {
1414
if (semver.gt(release.version, packageVersion)) {
1515
console.log(

src/lib/util.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'fs';
22
import path from 'path';
33
import fetch from 'node-fetch';
4+
import semver from 'semver';
45

56
export default {
67
/**
@@ -64,14 +65,28 @@ export default {
6465
/**
6566
* Gets the latest NodeCG release information from npm, including tarball download link.
6667
*/
67-
async getLatestNodeCGRelease(): Promise<NPMRelease> {
68-
const res = await fetch('http://registry.npmjs.org/nodecg/latest');
68+
async getNodeCGRelease(target: string): Promise<NPMRelease> {
69+
const targetVersion = semver.coerce(target)?.version;
70+
if (!targetVersion) {
71+
throw new Error(`Failed to determine target NodeCG version`);
72+
}
73+
74+
const res = await fetch(`http://registry.npmjs.org/nodecg/${targetVersion}`);
6975
if (res.status !== 200) {
7076
throw new Error(`Failed to fetch NodeCG release information from npm, status code ${res.status}`);
7177
}
7278

7379
return res.json() as Promise<NPMRelease>;
7480
},
81+
82+
async getLatestCLIRelease(): Promise<NPMRelease> {
83+
const res = await fetch('http://registry.npmjs.org/nodecg-cli/latest');
84+
if (res.status !== 200) {
85+
throw new Error(`Failed to fetch NodeCG release information from npm, status code ${res.status}`);
86+
}
87+
88+
return res.json();
89+
},
7590
};
7691

7792
export interface NPMRelease {

test/commands/setup.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ test('should print an error when the target version is the same as current', asy
7070
spy.mockRestore();
7171
});
7272

73+
test('should correctly handle and refuse when you try to downgrade from v2 to v1', async () => {
74+
chdir();
75+
jest.spyOn(inquirer, 'prompt').mockReturnValue(Promise.resolve({ installOlder: true }) as any);
76+
await program.runWith('setup 2.0.0 --skip-dependencies');
77+
expect(readPackageJson().version).toBe('2.0.0');
78+
await program.runWith('setup 1.9.0 -u --skip-dependencies');
79+
expect(readPackageJson().version).toBe('2.0.0');
80+
});
81+
7382
test("should print an error when the target version doesn't exist", async () => {
7483
const spy = jest.spyOn(console, 'error');
7584
await program.runWith('setup 0.0.99 -u --skip-dependencies');

0 commit comments

Comments
 (0)