Skip to content

allow for separate coderoad directory #276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
},
{
"name": "npm",
"version": ">5"
"version": ">=5"
}
]
}
Expand All @@ -52,4 +52,42 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how

## [0.3.0]

- Validate the extension version against the tutorial config version. This should allow us to manage breaking changes in tutorial schema in upcoming versions
- Validate the extension version against the tutorial config version. This should allow us to manage breaking changes in tutorial schema in upcoming versions. See [node-semver](https://github.com/npm/node-semver#advanced-range-syntax) for possible version ranges and options.

```json
{
"config": {
"appVersions": {
"vscode": ">=0.2"
},
}
```

- Configure the CodeRoad to load and run in a different directory. The example below will:
- load a commit and run npm install to setup the test runner in its own folder.
- run "npm test" in the \$ROOT/coderoad directory on save

```json
{
"config": {
"testRunner": {
"command": "npm test", // runs in path location or root
"path": "coderoad",
"actions": {
"commits": ["a974aea"],
"commands": ["npm install"] // runs in path location or root
}
},
}
```

Resulting in a folder structure like the following:

```
- .vscode
- coderoad (test runner files only with their own setup)
- package.json
- tests
- package.json
- server.js
```
26 changes: 20 additions & 6 deletions src/actions/setupActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ import openFiles from './utils/openFiles'
import runCommands from './utils/runCommands'
import onError from '../services/sentry/onError'

const setupActions = async (
actions: TT.StepActions,
send: (action: T.Action) => void, // send messages to client
): Promise<void> => {
async function wait(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}

interface SetupActions {
actions: TT.StepActions
send: (action: T.Action) => void // send messages to client
path?: string
}

export const setupActions = async ({ actions, send, path }: SetupActions): Promise<void> => {
const { commands, commits, files, watchers } = actions

// 1. run commits
Expand All @@ -26,8 +35,13 @@ const setupActions = async (
// 3. start file watchers
loadWatchers(watchers || [])

await wait(1000)

// 4. run command
await runCommands(commands || [], send).catch(onError)
await runCommands({ commands: commands || [], send, path }).catch(onError)
}

export default setupActions
export const solutionActions = async (params: SetupActions): Promise<void> => {
await git.clear()
return setupActions(params).catch(onError)
}
12 changes: 0 additions & 12 deletions src/actions/solutionActions.ts

This file was deleted.

11 changes: 9 additions & 2 deletions src/actions/utils/runCommands.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import * as T from 'typings'
import { exec } from '../../services/node'

const runCommands = async (commands: string[], send: (action: T.Action) => void) => {
interface RunCommands {
commands: string[]
send: (action: T.Action) => void
path?: string
}

const runCommands = async ({ commands, send, path }: RunCommands) => {
if (!commands.length) {
return
}
Expand All @@ -13,7 +19,8 @@ const runCommands = async (commands: string[], send: (action: T.Action) => void)
send({ type: 'COMMAND_START', payload: { process: { ...process, status: 'RUNNING' } } })
let result: { stdout: string; stderr: string }
try {
result = await exec(command)
result = await exec({ command, path })
console.log(result)
} catch (error) {
console.log(`Test failed: ${error.message}`)
send({ type: 'COMMAND_FAIL', payload: { process: { ...process, status: 'FAIL' } } })
Expand Down
7 changes: 3 additions & 4 deletions src/channel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import * as E from 'typings/error'
import * as vscode from 'vscode'
import { satisfies } from 'semver'
import saveCommit from '../actions/saveCommit'
import setupActions from '../actions/setupActions'
import solutionActions from '../actions/solutionActions'
import { setupActions, solutionActions } from '../actions/setupActions'
import tutorialConfig from '../actions/tutorialConfig'
import { COMMANDS } from '../editor/commands'
import logger from '../services/logger'
Expand Down Expand Up @@ -287,11 +286,11 @@ class Channel implements Channel {
// load step actions (git commits, commands, open files)
case 'SETUP_ACTIONS':
await vscode.commands.executeCommand(COMMANDS.SET_CURRENT_STEP, action.payload)
setupActions(action.payload, this.send)
setupActions({ actions: action.payload, send: this.send })
return
// load solution step actions (git commits, commands, open files)
case 'SOLUTION_ACTIONS':
await solutionActions(action.payload, this.send)
await solutionActions({ actions: action.payload, send: this.send })
// run test following solution to update position
vscode.commands.executeCommand(COMMANDS.RUN_TEST, action.payload)
return
Expand Down
8 changes: 7 additions & 1 deletion src/editor/commands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as TT from 'typings/tutorial'
import * as vscode from 'vscode'
import createTestRunner, { Payload } from '../services/testRunner'
import { setupActions } from '../actions/setupActions'
import createWebView from '../webview'

export const COMMANDS = {
Expand Down Expand Up @@ -47,7 +48,12 @@ export const createCommands = ({ extensionPath, workspaceState }: CreateCommandP
// setup 1x1 horizontal layout
webview.createOrShow()
},
[COMMANDS.CONFIG_TEST_RUNNER]: (config: TT.TutorialTestRunner) => {
[COMMANDS.CONFIG_TEST_RUNNER]: async (config: TT.TutorialTestRunnerConfig) => {
if (config.actions) {
// setup tutorial test runner commits
// assumes git already exists
await setupActions({ actions: config.actions, send: webview.send, path: config.path })
}
testRunner = createTestRunner(config, {
onSuccess: (payload: Payload) => {
// send test pass message back to client
Expand Down
2 changes: 1 addition & 1 deletion src/services/dependencies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const semverRegex = /(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)

export const version = async (name: string): Promise<string | null> => {
try {
const { stdout, stderr } = await exec(`${name} --version`)
const { stdout, stderr } = await exec({ command: `${name} --version` })
if (!stderr) {
const match = stdout.match(semverRegex)
if (match) {
Expand Down
18 changes: 9 additions & 9 deletions src/services/git/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const gitOrigin = 'coderoad'

const stashAllFiles = async (): Promise<never | void> => {
// stash files including untracked (eg. newly created file)
const { stdout, stderr } = await exec(`git stash --include-untracked`)
const { stdout, stderr } = await exec({ command: `git stash --include-untracked` })
if (stderr) {
console.error(stderr)
throw new Error('Error stashing files')
Expand All @@ -21,7 +21,7 @@ const cherryPickCommit = async (commit: string, count = 0): Promise<never | void
try {
// cherry-pick pulls commits from another branch
// -X theirs merges and accepts incoming changes over existing changes
const { stdout } = await exec(`git cherry-pick -X theirs ${commit}`)
const { stdout } = await exec({ command: `git cherry-pick -X theirs ${commit}` })
if (!stdout) {
throw new Error('No cherry-pick output')
}
Expand All @@ -47,7 +47,7 @@ export function loadCommit(commit: string): Promise<never | void> {
*/

export async function saveCommit(message: string): Promise<never | void> {
const { stdout, stderr } = await exec(`git commit -am '${message}'`)
const { stdout, stderr } = await exec({ command: `git commit -am '${message}'` })
if (stderr) {
console.error(stderr)
throw new Error('Error saving progress to Git')
Expand All @@ -58,7 +58,7 @@ export async function saveCommit(message: string): Promise<never | void> {
export async function clear(): Promise<Error | void> {
try {
// commit progress to git
const { stderr } = await exec('git reset HEAD --hard && git clean -fd')
const { stderr } = await exec({ command: 'git reset HEAD --hard && git clean -fd' })
if (!stderr) {
return
}
Expand All @@ -70,7 +70,7 @@ export async function clear(): Promise<Error | void> {
}

async function init(): Promise<Error | void> {
const { stderr } = await exec('git init')
const { stderr } = await exec({ command: 'git init' })
if (stderr) {
throw new Error('Error initializing Git')
}
Expand All @@ -85,13 +85,13 @@ export async function initIfNotExists(): Promise<never | void> {

export async function checkRemoteConnects(repo: TT.TutorialRepo): Promise<never | void> {
// check for git repo
const externalRepoExists = await exec(`git ls-remote --exit-code --heads ${repo.uri}`)
const externalRepoExists = await exec({ command: `git ls-remote --exit-code --heads ${repo.uri}` })
if (externalRepoExists.stderr) {
// no repo found or no internet connection
throw new Error(externalRepoExists.stderr)
}
// check for git repo branch
const { stderr, stdout } = await exec(`git ls-remote --exit-code --heads ${repo.uri} ${repo.branch}`)
const { stderr, stdout } = await exec({ command: `git ls-remote --exit-code --heads ${repo.uri} ${repo.branch}` })
if (stderr) {
throw new Error(stderr)
}
Expand All @@ -101,7 +101,7 @@ export async function checkRemoteConnects(repo: TT.TutorialRepo): Promise<never
}

export async function addRemote(repo: string): Promise<never | void> {
const { stderr } = await exec(`git remote add ${gitOrigin} ${repo} && git fetch ${gitOrigin}`)
const { stderr } = await exec({ command: `git remote add ${gitOrigin} ${repo} && git fetch ${gitOrigin}` })
if (stderr) {
const alreadyExists = stderr.match(`${gitOrigin} already exists.`)
const successfulNewBranch = stderr.match('new branch')
Expand All @@ -116,7 +116,7 @@ export async function addRemote(repo: string): Promise<never | void> {

export async function checkRemoteExists(): Promise<boolean> {
try {
const { stdout, stderr } = await exec('git remote -v')
const { stdout, stderr } = await exec({ command: 'git remote -v' })
if (stderr) {
return false
}
Expand Down
12 changes: 8 additions & 4 deletions src/services/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import { WORKSPACE_ROOT } from '../../environment'

const asyncExec = promisify(cpExec)

export const exec = (cmd: string): Promise<{ stdout: string; stderr: string }> | never => {
return asyncExec(cmd, {
cwd: WORKSPACE_ROOT,
})
interface ExecParams {
command: string
path?: string
}

export const exec = (params: ExecParams): Promise<{ stdout: string; stderr: string }> | never => {
const cwd = join(WORKSPACE_ROOT, params.path || '')
return asyncExec(params.command, { cwd })
}

export const exists = (...paths: string[]): boolean | never => {
Expand Down
9 changes: 3 additions & 6 deletions src/services/testRunner/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TutorialTestRunnerConfig } from 'typings/tutorial'
import { exec } from '../node'
import logger from '../logger'
import parser from './parser'
Expand All @@ -17,14 +18,10 @@ interface Callbacks {
onError(payload: Payload): void
}

interface TestRunnerConfig {
command: string
}

const failChannelName = 'CodeRoad (Tests)'
const logChannelName = 'CodeRoad (Logs)'

const createTestRunner = (config: TestRunnerConfig, callbacks: Callbacks) => {
const createTestRunner = (config: TutorialTestRunnerConfig, callbacks: Callbacks) => {
return async (payload: Payload, onSuccess?: () => void): Promise<void> => {
const startTime = throttle()
// throttle time early
Expand All @@ -39,7 +36,7 @@ const createTestRunner = (config: TestRunnerConfig, callbacks: Callbacks) => {

let result: { stdout: string | undefined; stderr: string | undefined }
try {
result = await exec(config.command)
result = await exec({ command: config.command, path: config.path })
} catch (err) {
result = { stdout: err.stdout, stderr: err.stack }
}
Expand Down
6 changes: 4 additions & 2 deletions typings/tutorial.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export type Maybe<T> = T | null

export type TutorialConfig = {
appVersions: TutorialAppVersions
testRunner: TutorialTestRunner
testRunner: TutorialTestRunnerConfig
repo: TutorialRepo
dependencies?: TutorialDependency[]
}
Expand Down Expand Up @@ -51,8 +51,10 @@ export type StepActions = {
watchers: string[]
}

export interface TutorialTestRunner {
export interface TutorialTestRunnerConfig {
command: string
path?: string
actions?: StepActions
}

export interface TutorialRepo {
Expand Down