track-cli 4.1.0-rc1 → 4.1.0-rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/src/action/frontend-template-switch.js +16 -19
- package/esm/src/action/lang.js +11 -2
- package/esm/src/action/run.js +6 -7
- package/esm/src/meta.d.ts +1 -1
- package/esm/src/meta.js +1 -1
- package/esm/src/shared/errors.d.ts +7 -1
- package/esm/src/shared/errors.js +16 -3
- package/esm/src/shared/file.d.ts +0 -1
- package/esm/src/shared/file.js +1 -33
- package/esm/src/shared/types.d.ts +1 -0
- package/esm/src/shared/types.js +1 -0
- package/esm/src/track/test.d.ts +2 -1
- package/esm/src/track/test.js +15 -8
- package/package.json +1 -2
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as dntShim from "../../_dnt.shims.js";
|
|
2
2
|
import { load as loadConfig, save as saveConfig, } from "../shared/config.js";
|
|
3
3
|
import { OtherError } from "../shared/errors.js";
|
|
4
|
+
import { archiveExistingChallengeFiles, chooseFrontendTemplate, downloadChallengeFilesTo, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
|
|
4
5
|
import { ChallengeStyle } from "../shared/types.js";
|
|
5
|
-
import { archiveExistingChallengeFiles, chooseFrontendTemplate, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
|
|
6
|
-
import { unzip } from "../shared/file.js";
|
|
7
6
|
export async function switchFrontendTemplate() {
|
|
8
7
|
const config = await loadConfig();
|
|
9
8
|
const api = trackClientFromConfig(config);
|
|
10
9
|
const [codingContext, challenge] = await getCommonChallengeContext(config, api);
|
|
11
10
|
if (challenge.style !== ChallengeStyle.Frontend) {
|
|
12
|
-
throw new OtherError("This challenge is not a
|
|
11
|
+
throw new OtherError("This challenge is not a Frontend Framework (VSCode) challenge." +
|
|
12
|
+
"\n\thint: Use 'track lang' to change programming language instead.");
|
|
13
13
|
}
|
|
14
14
|
// Get available frontend templates from Track
|
|
15
15
|
const allFrontendTemplates = await api.frontendTemplates();
|
|
@@ -29,24 +29,21 @@ export async function switchFrontendTemplate() {
|
|
|
29
29
|
const desiredTemplate = chooseFrontendTemplate(availableTemplates);
|
|
30
30
|
if (desiredTemplate.seriesCode ===
|
|
31
31
|
codingContext.selectedFrontendTemplate?.seriesCode) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (!templateDetails.downloadUrl) {
|
|
38
|
-
throw new OtherError("Template does not have a download URL available");
|
|
32
|
+
const shouldForceReset = dntShim.confirm(`You are already using the template '${desiredTemplate.seriesCode}'. Do you want to start-over from the template code anyway?`);
|
|
33
|
+
if (!shouldForceReset) {
|
|
34
|
+
// already using this template
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
39
37
|
}
|
|
40
|
-
const currentDir = dntShim.Deno.cwd();
|
|
41
38
|
await archiveExistingChallengeFiles(config.orgName);
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
await
|
|
39
|
+
// Ask the backend to reset the user's solution to the starter code for this template.
|
|
40
|
+
await api.updateFrontendTemplate(desiredTemplate.seriesCode);
|
|
41
|
+
// Re-download the challenge files to reflect the reset solution in the backend.
|
|
42
|
+
const currentDir = dntShim.Deno.cwd();
|
|
43
|
+
const newCodingContext = await api.context();
|
|
44
|
+
const showFileDiff = false;
|
|
45
|
+
const includeTarball = false;
|
|
46
|
+
await downloadChallengeFilesTo(newCodingContext, currentDir, api, showFileDiff, includeTarball);
|
|
50
47
|
const updatedConfig = {
|
|
51
48
|
...config,
|
|
52
49
|
frontendTemplate: desiredTemplate.seriesCode,
|
package/esm/src/action/lang.js
CHANGED
|
@@ -2,15 +2,24 @@ import * as dntShim from "../../_dnt.shims.js";
|
|
|
2
2
|
import { load as loadConfig, save as saveConfig, } from "../shared/config.js";
|
|
3
3
|
import { CantChooseLanguage } from "../shared/errors.js";
|
|
4
4
|
import { archiveExistingChallengeFiles, chooseProgrammingLanguage, downloadChallengeFilesTo, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
|
|
5
|
+
import { ChallengeStyle, } from "../shared/types.js";
|
|
5
6
|
export async function lang() {
|
|
6
7
|
const config = await loadConfig();
|
|
7
8
|
const api = trackClientFromConfig(config);
|
|
8
9
|
const [codingContext, challenge] = await getCommonChallengeContext(config, api);
|
|
9
10
|
if (challenge.programmingLanguages.length === 0) {
|
|
10
|
-
throw new CantChooseLanguage(
|
|
11
|
+
throw new CantChooseLanguage({
|
|
12
|
+
hint: challenge.style === ChallengeStyle.Frontend
|
|
13
|
+
? "Use 'track frontend-template' to change the frontend template for a Frontend Framework (VSCode) challenge."
|
|
14
|
+
: "",
|
|
15
|
+
});
|
|
11
16
|
}
|
|
12
17
|
const availableLanguages = (await api.languages())
|
|
13
|
-
.filter((pl) =>
|
|
18
|
+
.filter((pl) => {
|
|
19
|
+
return codingContext.programmingLanguages.find((n) => {
|
|
20
|
+
return n === pl.value;
|
|
21
|
+
});
|
|
22
|
+
});
|
|
14
23
|
console.log("Your existing code will be archived in this directory and the original challenge code for the new language will be downloaded");
|
|
15
24
|
const shouldContinue = dntShim.confirm("Are you sure you want to continue?");
|
|
16
25
|
if (!shouldContinue) {
|
package/esm/src/action/run.js
CHANGED
|
@@ -59,17 +59,16 @@ export async function run(options) {
|
|
|
59
59
|
const runId = dntShim.crypto.randomUUID();
|
|
60
60
|
// Run the initialize/build commands if they exist
|
|
61
61
|
let buildDoneData;
|
|
62
|
-
|
|
63
|
-
codingContext.settings.initialize
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.filter((s) => s && s.length > 0);
|
|
62
|
+
const buildCommands = []
|
|
63
|
+
.concat(codingContext.settings.initialize || [])
|
|
64
|
+
.concat(codingContext.answers.build || [])
|
|
65
|
+
.filter((s) => s && s.length > 0);
|
|
66
|
+
if (buildCommands.length > 0) {
|
|
68
67
|
const runCmd = {
|
|
69
68
|
id: `${runId}-build`,
|
|
70
69
|
imageName: envConfig.imageName,
|
|
71
70
|
workingDir: envConfig.workingDir ?? "",
|
|
72
|
-
shellCmd:
|
|
71
|
+
shellCmd: buildCommands.join(" && "),
|
|
73
72
|
attachVolume: true,
|
|
74
73
|
files,
|
|
75
74
|
envVars: {},
|
package/esm/src/meta.d.ts
CHANGED
package/esm/src/meta.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as colors from "../deps/deno.land/std@0.195.0/fmt/colors.js";
|
|
2
2
|
// @ts-ignore: has no exported member
|
|
3
3
|
import { CookieJar, fetch } from "node-fetch-cookies";
|
|
4
|
-
export const VERSION = "4.1.0-
|
|
4
|
+
export const VERSION = "4.1.0-rc2";
|
|
5
5
|
export const DESCRIPTION = "A CLI for interacting with tracks.run and running code tests on track's servers";
|
|
6
6
|
const VERSION_RE = /^(\d+)\.(\d+)\.(\d+)(?:-?(.*))$/;
|
|
7
7
|
function parseSemver(s) {
|
|
@@ -47,8 +47,14 @@ export declare class InvalidChallengeId extends TrackError {
|
|
|
47
47
|
challengeId: number;
|
|
48
48
|
constructor(challengeId: number);
|
|
49
49
|
}
|
|
50
|
+
export declare class UnsupportedChallenge extends TrackError {
|
|
51
|
+
challengeId: number;
|
|
52
|
+
constructor(challengeId: number);
|
|
53
|
+
}
|
|
50
54
|
export declare class CantChooseLanguage extends TrackError {
|
|
51
|
-
constructor(
|
|
55
|
+
constructor(p?: {
|
|
56
|
+
hint?: string;
|
|
57
|
+
});
|
|
52
58
|
}
|
|
53
59
|
export declare class NotASingleResponse extends TrackError {
|
|
54
60
|
constructor();
|
package/esm/src/shared/errors.js
CHANGED
|
@@ -119,7 +119,7 @@ export class ChallengeAlreadyFinished extends TrackError {
|
|
|
119
119
|
}
|
|
120
120
|
export class InvalidChallengeId extends TrackError {
|
|
121
121
|
constructor(challengeId) {
|
|
122
|
-
super(`The challenge id specified in your URL (${challengeId})
|
|
122
|
+
super(`The challenge id specified in your URL (${challengeId}) is invalid`);
|
|
123
123
|
Object.defineProperty(this, "challengeId", {
|
|
124
124
|
enumerable: true,
|
|
125
125
|
configurable: true,
|
|
@@ -129,9 +129,22 @@ export class InvalidChallengeId extends TrackError {
|
|
|
129
129
|
this.name = "InvalidChallengeId";
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
export class UnsupportedChallenge extends TrackError {
|
|
133
|
+
constructor(challengeId) {
|
|
134
|
+
super(`The challenge id specified in your URL (${challengeId}) is not supported by the 'track' CLI`);
|
|
135
|
+
Object.defineProperty(this, "challengeId", {
|
|
136
|
+
enumerable: true,
|
|
137
|
+
configurable: true,
|
|
138
|
+
writable: true,
|
|
139
|
+
value: challengeId
|
|
140
|
+
});
|
|
141
|
+
this.name = "UnsupportedChallenge";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
132
144
|
export class CantChooseLanguage extends TrackError {
|
|
133
|
-
constructor() {
|
|
134
|
-
super("This challenge does not support changing programming languages"
|
|
145
|
+
constructor(p = {}) {
|
|
146
|
+
super("This challenge does not support changing programming languages." +
|
|
147
|
+
(p.hint ? `\n\thint: ${p.hint}` : ""));
|
|
135
148
|
this.name = "CantChooseLanguage";
|
|
136
149
|
}
|
|
137
150
|
}
|
package/esm/src/shared/file.d.ts
CHANGED
|
@@ -24,7 +24,6 @@ export declare function getAllDirFiles(p: string): Promise<string[]>;
|
|
|
24
24
|
export declare function toNixStyle(p: string): string;
|
|
25
25
|
export declare function toNativeStyle(p: string): string;
|
|
26
26
|
export declare function untgz(compressed: Uint8Array, dest: string): Promise<void>;
|
|
27
|
-
export declare function unzip(zipData: Uint8Array, dest: string): Promise<void>;
|
|
28
27
|
export type GetUniqueDirNameOptions = {
|
|
29
28
|
root?: string;
|
|
30
29
|
};
|
package/esm/src/shared/file.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
-
import * as path from "../../deps/deno.land/std@0.195.0/path/mod.js";
|
|
3
2
|
import { Foras, gunzip } from "@hazae41/foras";
|
|
4
3
|
import tar from "tar";
|
|
5
4
|
import * as fs from "../../deps/deno.land/std@0.195.0/fs/mod.js";
|
|
5
|
+
import * as path from "../../deps/deno.land/std@0.195.0/path/mod.js";
|
|
6
6
|
export const _internals = {
|
|
7
7
|
SEP: path.SEP,
|
|
8
8
|
};
|
|
@@ -97,38 +97,6 @@ export async function untgz(compressed, dest) {
|
|
|
97
97
|
cwd: dest,
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
-
export async function unzip(zipData, dest) {
|
|
101
|
-
const JSZip = (await import("jszip")).default;
|
|
102
|
-
try {
|
|
103
|
-
// destination directory exists
|
|
104
|
-
await fs.ensureDir(dest);
|
|
105
|
-
const zip = new JSZip();
|
|
106
|
-
await zip.loadAsync(zipData);
|
|
107
|
-
const promises = [];
|
|
108
|
-
zip.forEach((relativePath, file) => {
|
|
109
|
-
if (!file.dir) {
|
|
110
|
-
// file
|
|
111
|
-
promises.push((async () => {
|
|
112
|
-
const content = await file.async("uint8array");
|
|
113
|
-
const filePath = path.join(dest, relativePath);
|
|
114
|
-
await fs.ensureFile(filePath);
|
|
115
|
-
await dntShim.Deno.writeFile(filePath, content);
|
|
116
|
-
})());
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
// directory
|
|
120
|
-
promises.push((async () => {
|
|
121
|
-
const dirPath = path.join(dest, relativePath);
|
|
122
|
-
await fs.ensureDir(dirPath);
|
|
123
|
-
})());
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
await Promise.all(promises);
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
throw new Error(`Failed to extract ZIP file: ${error}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
100
|
export async function getUniqueDirName(base, options) {
|
|
133
101
|
let counter = 1;
|
|
134
102
|
const root = options?.root ?? dntShim.Deno.cwd();
|
package/esm/src/shared/types.js
CHANGED
package/esm/src/track/test.d.ts
CHANGED
|
@@ -15,13 +15,14 @@ export declare class TestClient extends TrackClientBase implements TrackClient {
|
|
|
15
15
|
private ensureExamInProgress;
|
|
16
16
|
private getExamSession;
|
|
17
17
|
private getChallengeFromExamSession;
|
|
18
|
+
isChallengeSupported(c: ChallengeSession): boolean;
|
|
18
19
|
languages(): Promise<ProgrammingLanguageInfo[]>;
|
|
19
20
|
frontendTemplates(): Promise<FrontendTemplateInfo[]>;
|
|
20
21
|
frontendTemplate(code: string): Promise<FrontendTemplateInfo>;
|
|
21
22
|
start(): Promise<ChallengeResult>;
|
|
22
23
|
prepare(): Promise<ChallengeResult>;
|
|
23
24
|
updateLanguage(language: ProgrammingLanguage): Promise<void>;
|
|
24
|
-
updateFrontendTemplate(
|
|
25
|
+
updateFrontendTemplate(seriesCode: string): Promise<void>;
|
|
25
26
|
timeLeft(): Promise<number>;
|
|
26
27
|
context(): Promise<CodingContext>;
|
|
27
28
|
presigned(files: string[]): Promise<{
|
package/esm/src/track/test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExamCanceled, ExamExpired, ExamSubmitted, ExamUnread, InvalidChallengeId, OtherError, } from "../shared/errors.js";
|
|
1
|
+
import { ExamCanceled, ExamExpired, ExamSubmitted, ExamUnread, InvalidChallengeId, OtherError, UnsupportedChallenge, } from "../shared/errors.js";
|
|
2
2
|
import { HttpStatus } from "../shared/mod.js";
|
|
3
3
|
import { ChallengeStyle, } from "../shared/types.js";
|
|
4
4
|
import { FormType, TrackClientBase, } from "./client.js";
|
|
@@ -71,6 +71,9 @@ export class TestClient extends TrackClientBase {
|
|
|
71
71
|
if (!challenge) {
|
|
72
72
|
throw new InvalidChallengeId(this.challengeId);
|
|
73
73
|
}
|
|
74
|
+
else if (!this.isChallengeSupported(challenge)) {
|
|
75
|
+
throw new UnsupportedChallenge(this.challengeId);
|
|
76
|
+
}
|
|
74
77
|
this.applicantExamId = applicantExam.id;
|
|
75
78
|
this.resultId = result?.id || 0;
|
|
76
79
|
return challenge;
|
|
@@ -100,10 +103,13 @@ export class TestClient extends TrackClientBase {
|
|
|
100
103
|
return examSession.challengesSets
|
|
101
104
|
.flatMap((s) => s.challenges)
|
|
102
105
|
.map((esc) => toChallengeSession(esc, result))
|
|
103
|
-
.find((c) => c.challengeId === this.challengeId
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
.find((c) => c.challengeId === this.challengeId);
|
|
107
|
+
}
|
|
108
|
+
isChallengeSupported(c) {
|
|
109
|
+
return (c.style === ChallengeStyle.Development ||
|
|
110
|
+
c.style === ChallengeStyle.Algorithm ||
|
|
111
|
+
c.style === ChallengeStyle.Function ||
|
|
112
|
+
c.style === ChallengeStyle.Frontend);
|
|
107
113
|
}
|
|
108
114
|
async languages() {
|
|
109
115
|
return (await this._get(`/api/enum/challenges/programminglanguages`))
|
|
@@ -130,9 +136,10 @@ export class TestClient extends TrackClientBase {
|
|
|
130
136
|
programmingLanguage: language,
|
|
131
137
|
}); // ChallengeResult
|
|
132
138
|
}
|
|
133
|
-
async updateFrontendTemplate(
|
|
134
|
-
return await this.
|
|
135
|
-
|
|
139
|
+
async updateFrontendTemplate(seriesCode) {
|
|
140
|
+
return await this._post(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/frontend/switch-frontend-template`, FormType.JSON, {
|
|
141
|
+
seriesCode,
|
|
142
|
+
forceReset: true,
|
|
136
143
|
});
|
|
137
144
|
}
|
|
138
145
|
async timeLeft() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "track-cli",
|
|
3
|
-
"version": "4.1.0-
|
|
3
|
+
"version": "4.1.0-rc2",
|
|
4
4
|
"description": "A CLI for interacting with tracks.run and running code tests on track's servers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@hazae41/foras": "2.0.8",
|
|
21
21
|
"eventemitter3": "5.0.1",
|
|
22
|
-
"jszip": "3.10.1",
|
|
23
22
|
"node-fetch-cookies": "2.0.4",
|
|
24
23
|
"proxy-agent": "6.3.0",
|
|
25
24
|
"tar": "6.1.13",
|