@@ -9,11 +9,12 @@ import tutorialConfig from '../actions/tutorialConfig'
99import { COMMANDS } from '../editor/commands'
1010import logger from '../services/logger'
1111import Context from './context'
12- import { version as gitVersion } from '../services/git '
12+ import { version , compareVersions } from '../services/dependencies '
1313import { openWorkspace , checkWorkspaceEmpty } from '../services/workspace'
1414import { readFile } from 'fs'
1515import { join } from 'path'
1616import { promisify } from 'util'
17+ import { compare } from 'semver'
1718
1819const readFileAsync = promisify ( readFile )
1920
@@ -94,6 +95,63 @@ class Channel implements Channel {
9495 // setup tutorial config (save watcher, test runner, etc)
9596 await this . context . setTutorial ( this . workspaceState , data )
9697
98+ // validate dependencies
99+ const dependencies = data . config . dependencies
100+ if ( dependencies && dependencies . length ) {
101+ for ( const dep of dependencies ) {
102+ // check dependency is installed
103+ const currentVersion : string | null = await version ( dep . name )
104+ if ( ! currentVersion ) {
105+ // use a custom error message
106+ const error = {
107+ type : 'MissingTutorialDependency' ,
108+ message : dep . message || `Process "${ dep . name } " is required but not found. It may need to be installed` ,
109+ actions : [
110+ {
111+ label : 'Check Again' ,
112+ transition : 'TRY_AGAIN' ,
113+ } ,
114+ ] ,
115+ }
116+ this . send ( { type : 'TUTORIAL_CONFIGURE_FAIL' , payload : { error } } )
117+ return
118+ }
119+
120+ // check dependency version
121+ const satisfiedDependency = await compareVersions ( currentVersion , dep . version )
122+
123+ if ( ! satisfiedDependency ) {
124+ const error = {
125+ type : 'UnmetTutorialDependency' ,
126+ message : `Expected ${ dep . name } to have version ${ dep . version } , but found version ${ currentVersion } ` ,
127+ actions : [
128+ {
129+ label : 'Check Again' ,
130+ transition : 'TRY_AGAIN' ,
131+ } ,
132+ ] ,
133+ }
134+ this . send ( { type : 'TUTORIAL_CONFIGURE_FAIL' , payload : { error } } )
135+ return
136+ }
137+
138+ if ( satisfiedDependency !== true ) {
139+ const error = satisfiedDependency || {
140+ type : 'UnknownError' ,
141+ message : `Something went wrong comparing dependency for ${ name } ` ,
142+ actions : [
143+ {
144+ label : 'Try Again' ,
145+ transition : 'TRY_AGAIN' ,
146+ } ,
147+ ] ,
148+ }
149+ this . send ( { type : 'TUTORIAL_CONFIGURE_FAIL' , payload : { error } } )
150+ return
151+ }
152+ }
153+ }
154+
97155 const error : E . ErrorMessage | void = await tutorialConfig ( { config : data . config } ) . catch ( ( error : Error ) => ( {
98156 type : 'UnknownError' ,
99157 message : `Location: tutorial config.\n\n${ error . message } ` ,
@@ -144,7 +202,7 @@ class Channel implements Channel {
144202 }
145203 // 2. check Git is installed.
146204 // Should wait for workspace before running otherwise requires access to root folder
147- const isGitInstalled = await gitVersion ( )
205+ const isGitInstalled = await version ( 'git' )
148206 if ( ! isGitInstalled ) {
149207 const error : E . ErrorMessage = {
150208 type : 'GitNotFound' ,
@@ -197,7 +255,7 @@ class Channel implements Channel {
197255
198256 if ( errorMarkdown ) {
199257 // add a clearer error message for the user
200- error . message = `${ errorMarkdown } \n${ error . message } `
258+ error . message = `${ errorMarkdown } \n\n ${ error . message } `
201259 }
202260 }
203261
0 commit comments