1
1
import * as path from "path" ;
2
- import * as fs from "fs" ;
3
- import util from "util" ;
2
+ import * as fs from "fs-extra" ;
4
3
import * as yamlParser from "js-yaml" ;
5
4
import { getArg } from "./utils/args" ;
6
5
import gitP , { SimpleGit } from "simple-git/promise" ;
6
+ import {
7
+ createCommandRunner ,
8
+ createCherryPick ,
9
+ createTestRunner ,
10
+ } from "./utils/exec" ;
7
11
import { getCommits , CommitLogObject } from "./utils/commits" ;
8
12
9
- const mkdir = util . promisify ( fs . mkdir ) ;
10
- const exists = util . promisify ( fs . exists ) ;
11
- const rmdir = util . promisify ( fs . rmdir ) ;
12
- const read = util . promisify ( fs . readFile ) ;
13
-
14
13
async function validate ( args : string [ ] ) {
15
14
// dir - default .
16
15
const dir = ! args . length || args [ 0 ] . match ( / ^ - / ) ? "." : args [ 0 ] ;
@@ -21,55 +20,146 @@ async function validate(args: string[]) {
21
20
yaml : getArg ( args , { name : "yaml" , alias : "y" } ) || "coderoad.yaml" ,
22
21
} ;
23
22
24
- const _yaml = await read ( path . join ( localDir , options . yaml ) , "utf8" ) ;
23
+ const _yaml = await fs . readFile ( path . join ( localDir , options . yaml ) , "utf8" ) ;
25
24
26
25
// parse yaml config
27
- let config ;
26
+ let skeleton ;
28
27
try {
29
- config = yamlParser . load ( _yaml ) ;
30
- // TODO: validate yaml
31
- if ( ! config || ! config . length ) {
28
+ skeleton = yamlParser . load ( _yaml ) ;
29
+
30
+ if ( ! skeleton ) {
32
31
throw new Error ( "Invalid yaml file contents" ) ;
33
32
}
34
33
} catch ( e ) {
35
34
console . error ( "Error parsing yaml" ) ;
36
35
console . error ( e . message ) ;
37
36
}
38
37
39
- const codeBranch : string = config . config . repo . branch ;
38
+ const codeBranch : string = skeleton . config . repo . branch ;
39
+
40
+ // validate commits
41
+ const commits : CommitLogObject = await getCommits ( { localDir, codeBranch } ) ;
42
+
43
+ // setup tmp dir
44
+ const tmpDir = path . join ( localDir , ".tmp" ) ;
40
45
41
- // VALIDATE SKELETON WITH COMMITS
42
- const commits = getCommits ( { localDir, codeBranch } ) ;
46
+ try {
47
+ if ( ! ( await fs . pathExists ( tmpDir ) ) ) {
48
+ await fs . emptyDir ( tmpDir ) ;
49
+ }
50
+ const tempGit : SimpleGit = gitP ( tmpDir ) ;
43
51
44
- // parse tutorial skeleton for order and commands
52
+ await tempGit . init ( ) ;
53
+ await tempGit . addRemote ( "origin" , skeleton . config . repo . uri ) ;
54
+ await tempGit . fetch ( "origin" , skeleton . config . repo . branch ) ;
55
+ // no js cherry pick implementation
56
+ const cherryPick = createCherryPick ( tmpDir ) ;
57
+ const runCommands = createCommandRunner ( tmpDir ) ;
58
+ const runTest = createTestRunner ( tmpDir , skeleton . config . testRunner ) ;
45
59
46
- // on error, warn missing level/step
60
+ // setup
61
+ console . info ( "* Setup" ) ;
62
+ if ( commits . INIT ) {
63
+ // load commits
64
+ console . info ( "-- Loading commits..." ) ;
65
+ await cherryPick ( commits . INIT ) ;
47
66
48
- // VALIDATE COMMIT ORDER
49
- // list all commits in order
50
- // validate that a level number doesn't come before another level
51
- // validate that a step falls within a level
52
- // validate that steps are in order
67
+ // run commands
68
+ if ( skeleton . config ?. testRunner ?. setup ?. commands ) {
69
+ console . info ( "-- Running commands..." ) ;
70
+
71
+ await runCommands (
72
+ skeleton . config ?. testRunner ?. setup ?. commands ,
73
+ // add optional setup directory
74
+ skeleton . config ?. testRunner ?. directory
75
+ ) ;
76
+ }
77
+ }
53
78
54
- // on error, show level/step out of order
79
+ for ( const level of skeleton . levels ) {
80
+ console . info ( `* ${ level . id } ` ) ;
81
+ if ( level ?. setup ) {
82
+ // load commits
83
+ if ( commits [ `${ level . id } ` ] ) {
84
+ console . log ( `-- Loading commits...` ) ;
85
+ await cherryPick ( commits [ level . id ] ) ;
86
+ }
87
+ // run commands
88
+ if ( level . setup ?. commands ) {
89
+ console . log ( `-- Running commands...` ) ;
90
+ await runCommands ( level . setup . commands ) ;
91
+ }
92
+ }
93
+ // steps
94
+ if ( level . steps ) {
95
+ for ( const step of level . steps ) {
96
+ console . info ( `** ${ step . id } ` ) ;
97
+ // load commits
98
+ const stepSetupCommits = commits [ `${ step . id } Q` ] ;
99
+ if ( stepSetupCommits ) {
100
+ console . info ( `--- Loading setup commits...` ) ;
101
+ await cherryPick ( stepSetupCommits ) ;
102
+ }
103
+ // run commands
104
+ if ( step . setup . commands ) {
105
+ console . info ( `--- Running setup commands...` ) ;
106
+ await runCommands ( step . setup . commands ) ;
107
+ }
55
108
56
- // VALIDATE TUTORIAL TESTS
57
- // load INIT commit(s)
58
- // run test runner setup command(s)
59
- // loop over commits:
60
- // - load level commit
61
- // - run level setup command(s)
62
- // - load step setup commit(s)
63
- // - run step setup command(s)
64
- // - if next solution:
65
- // - run test - expect fail
66
- // - if solution
67
- // - run test - expect pass
109
+ const stepSolutionCommits = commits [ `${ step . id } A` ] ;
110
+ const hasSolution = step . solution || stepSolutionCommits ;
68
111
69
- // log level/step
70
- // on error, show level/step & error message
112
+ // ignore running tests on steps with no solution
113
+ if ( hasSolution ) {
114
+ // run test
115
+ console . info ( "--- Running setup test..." ) ;
116
+ // expect fail
117
+ const { stdout, stderr } = await runTest ( ) ;
118
+ if ( stdout ) {
119
+ console . error (
120
+ `--- Expected ${ step . id } setup tests to fail, but passed`
121
+ ) ;
122
+ // log tests
123
+ console . log ( stdout ) ;
124
+ }
125
+ }
71
126
72
- // CLEANUP
127
+ if ( stepSolutionCommits ) {
128
+ console . info ( `--- Loading solution commits...` ) ;
129
+ await cherryPick ( stepSolutionCommits ) ;
130
+ }
131
+
132
+ // run commands
133
+ if ( step ?. solution ?. commands ) {
134
+ console . info ( `--- Running solution commands...` ) ;
135
+ await runCommands ( step . solution . commands ) ;
136
+ }
137
+
138
+ if ( hasSolution ) {
139
+ // run test
140
+ console . info ( "--- Running solution test..." ) ;
141
+ // expect pass
142
+ const { stdout, stderr } = await runTest ( ) ;
143
+ if ( stderr ) {
144
+ console . error (
145
+ `--- Expected ${ step . id } solution tests to pass, but failed`
146
+ ) ;
147
+ // log tests
148
+ console . log ( stderr ) ;
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ console . info ( `\n✔ Success!` ) ;
156
+ } catch ( e ) {
157
+ console . error ( "\n✘ Fail!" ) ;
158
+ console . error ( e . message ) ;
159
+ } finally {
160
+ // cleanup
161
+ await fs . emptyDir ( tmpDir ) ;
162
+ }
73
163
}
74
164
75
165
export default validate ;
0 commit comments