diff --git a/.vscode/settings.json b/.vscode/settings.json index e0398fd9..39ce26e0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,8 @@ ".vscode-test/**": true }, "git.alwaysSignOff": true, - "typescript.tsdk": "./node_modules/typescript/lib" + "typescript.tsdk": "./node_modules/typescript/lib", + "cSpell.words": [ + "coderoad" + ] } diff --git a/src/actions/onTutorialConfigNew.ts b/src/actions/onTutorialConfigNew.ts index 171bee1a..5b3a4e55 100644 --- a/src/actions/onTutorialConfigNew.ts +++ b/src/actions/onTutorialConfigNew.ts @@ -4,7 +4,7 @@ import * as TT from 'typings/tutorial' import * as E from 'typings/error' import { satisfies } from 'semver' import { onEvent } from '../services/telemetry' -import { version, compareVersions } from '../services/dependencies' +import { getVersion, compareVersions } from '../services/dependencies' import Context from '../services/context/context' import tutorialConfig from './utils/tutorialConfig' import { send } from '../commands' @@ -46,8 +46,23 @@ const onTutorialConfigNew = async (action: T.Action, context: Context): Promise< if (dependencies && dependencies.length) { for (const dep of dependencies) { // check dependency is installed - const currentVersion: string | null = await version(dep.name) - if (!currentVersion) { + const { version, error: gitError } = await getVersion(dep.name) + if (gitError) { + // git config issue + const error: E.ErrorMessage = { + type: 'GitConfigError', + message: gitError.message, + actions: [ + { + label: 'Check Again', + transition: 'TRY_AGAIN', + }, + ], + } + send({ type: 'TUTORIAL_CONFIGURE_FAIL', payload: { error } }) + return + } + if (!version) { // use a custom error message const error: E.ErrorMessage = { type: 'MissingTutorialDependency', @@ -64,12 +79,12 @@ const onTutorialConfigNew = async (action: T.Action, context: Context): Promise< } // check dependency version - const satisfiedDependency = await compareVersions(currentVersion, dep.version) + const satisfiedDependency = await compareVersions(version, dep.version) if (!satisfiedDependency) { const error: E.ErrorMessage = { type: 'UnmetTutorialDependency', - message: `Expected ${dep.name} to have version ${dep.version}, but found version ${currentVersion}`, + message: `Expected ${dep.name} to have version ${dep.version}, but found version ${version}`, actions: [ { label: 'Check Again', @@ -116,7 +131,7 @@ const onTutorialConfigNew = async (action: T.Action, context: Context): Promise< // report back to the webview that setup is complete send({ type: 'TUTORIAL_CONFIGURED' }) - } catch (e) { + } catch (e: any) { const error = { type: 'UnknownError', message: `Location: EditorTutorialConfig.\n\n ${e.message}`, diff --git a/src/actions/onValidateSetup.ts b/src/actions/onValidateSetup.ts index 725784e2..d79c205f 100644 --- a/src/actions/onValidateSetup.ts +++ b/src/actions/onValidateSetup.ts @@ -1,5 +1,5 @@ import * as E from 'typings/error' -import { version } from '../services/dependencies' +import { getVersion } from '../services/dependencies' import { checkWorkspaceEmpty } from '../services/workspace' import { send } from '../commands' import { validateGitConfig } from '../services/git' @@ -28,8 +28,23 @@ const onValidateSetup = async (): Promise => { } // check Git is installed. // Should wait for workspace before running otherwise requires access to root folder - const isGitInstalled = await version('git') - if (!isGitInstalled) { + const { version, error: gitError } = await getVersion('git') + if (gitError) { + // git config issue + const error: E.ErrorMessage = { + type: 'GitConfigError', + message: gitError.message, + actions: [ + { + label: 'Check Again', + transition: 'TRY_AGAIN', + }, + ], + } + send({ type: 'VALIDATE_SETUP_FAILED', payload: { error } }) + return + } + if (!version) { const error: E.ErrorMessage = { type: 'GitNotFound', message: '', @@ -46,6 +61,9 @@ const onValidateSetup = async (): Promise => { const isGitUserNameConfigured = await validateGitConfig('user.name') const isGitUserEmailConfigured = await validateGitConfig('user.email') + console.log(`isGitUserNameConf: ${isGitUserNameConfigured}`) + console.log(`isGitUserEmailConf: ${isGitUserEmailConfigured}`) + if (!isGitUserNameConfigured || !isGitUserEmailConfigured) { let message = '' if (!isGitUserNameConfigured) message += 'Git user not configured.\n' @@ -65,9 +83,9 @@ const onValidateSetup = async (): Promise => { } send({ type: 'SETUP_VALIDATED' }) - } catch (e) { + } catch (e: any) { const error = { - type: 'UknownError', + type: 'UnknownError', message: e.message, } send({ type: 'VALIDATE_SETUP_FAILED', payload: { error } }) diff --git a/src/services/dependencies/index.ts b/src/services/dependencies/index.ts index 332455b9..361f24a0 100644 --- a/src/services/dependencies/index.ts +++ b/src/services/dependencies/index.ts @@ -1,21 +1,22 @@ import { satisfies } from 'semver' import { exec } from '../node' -const semverRegex = /(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?(\.windows.[0-9]+)?(?=$|\s)/gi +const semverRegex = + /(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?(\.windows.[0-9]+)?(?=$|\s)/gi -export const version = async (name: string): Promise => { +export const getVersion = async (name: string): Promise<{ version: string | null; error: Error | null }> => { try { const { stdout, stderr } = await exec({ command: `${name} --version` }) if (!stderr) { const match = stdout.match(semverRegex) if (match) { const parsedVersion = match[0].split('.').slice(0, 3).join('.') - return parsedVersion + return { version: parsedVersion, error: null } } } - return null - } catch (error) { - return null + return { version: null, error: null } + } catch (error: any) { + return { version: null, error } } } diff --git a/src/services/git/index.ts b/src/services/git/index.ts index 0e72e2d8..db2afc62 100644 --- a/src/services/git/index.ts +++ b/src/services/git/index.ts @@ -1,13 +1,13 @@ import * as TT from 'typings/tutorial' import { exec, exists } from '../node' -import { version, compareVersions } from '../dependencies' +import { getVersion, compareVersions } from '../dependencies' import logger from '../logger' export const gitOrigin = 'coderoad' const stashAllFiles = async (): Promise => { // stash files including untracked (eg. newly created file) - const { stdout, stderr } = await exec({ command: `git stash --include-untracked` }) + const { stderr } = await exec({ command: `git stash --include-untracked` }) if (stderr) { console.error(stderr) throw new Error('Error stashing files') @@ -71,7 +71,10 @@ export async function clear(): Promise { } async function init(): Promise { - const gitVersion = await version('git') + const { version: gitVersion, error: gitError } = await getVersion('git') + if (gitError) { + throw new Error(`Error: Git config error: ${gitError.message}`) + } if (!gitVersion) { throw new Error('Error: No git version found') } diff --git a/typings/error.d.ts b/typings/error.d.ts index 68f04b2c..2a10eec1 100644 --- a/typings/error.d.ts +++ b/typings/error.d.ts @@ -3,6 +3,7 @@ export type ErrorMessageView = 'FULL_PAGE' | 'NOTIFY' | 'NONE' export type ErrorMessageType = | 'FailedToConnectToGitRepo' | 'GitNotFound' + | 'GitConfigError' | 'GitUserNotConfigured' | 'GitProjectAlreadyExists' | 'GitRemoteAlreadyExists' diff --git a/web-app/src/services/errors/en.json b/web-app/src/services/errors/en.json index 09581899..ba88c9de 100644 --- a/web-app/src/services/errors/en.json +++ b/web-app/src/services/errors/en.json @@ -1,6 +1,7 @@ { "FailedToConnectToGitRepo": "### Failed to Connect to Git Repo\n\nThere are several possible causes:\n\n- you may not be connected to the internet or have an unstable connection.\n- you may not have access permission to the remote tutorial repo.\n- the remote tutorial repo may not exist at the provided location", "GitNotFound": "### Git Not Found\n\nMake sure you have Git installed.\n\nSee the [Git docs](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) for help.", + "GitConfigError": "### Git Config Error\n\n There may be an issue with your git configuration. Examine your config in ~/.gitconfig", "GitUserNotConfigured": "### Git User Not Configured\n\nThe first thing you should do when you install Git is to set your user name and email address. This is important because every Git commit uses this information, and it’s immutably baked into the commits you start creating:\n```shell\ngit config --global user.name \"John Doe\"\ngit config --global user.email johndoe@example.com\n```", "GitProjectAlreadyExists": "### Git Remote Already Exists\n\nHave you started this tutorial before in this workspace? The Git remote already exists.\n\nConsider deleting your `.git` folder and restarting.", "GitRemoteAlreadyExists": "### Git Project Already Exists\n\nCodeRoad requires an empty Git project.\n\nOpen a new workspace to start a tutorial.",