Skip to content

Commit e485851

Browse files
authoredJul 15, 2020
Merge pull request #389 from coderoad/feature/reset
Feature/reset
2 parents 613bce8 + 2183755 commit e485851

File tree

11 files changed

+182
-27
lines changed

11 files changed

+182
-27
lines changed
 

‎src/channel/index.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { openWorkspace, checkWorkspaceEmpty } from '../services/workspace'
1818
import { showOutput } from '../services/testRunner/output'
1919
import { exec } from '../services/node'
2020
import { WORKSPACE_ROOT, TUTORIAL_URL } from '../environment'
21+
import reset from '../services/reset'
22+
import getLastCommitHash from '../services/reset/lastHash'
2123

2224
const readFileAsync = promisify(readFile)
2325

@@ -320,17 +322,29 @@ class Channel implements Channel {
320322
case 'EDITOR_RUN_TEST':
321323
vscode.commands.executeCommand(COMMANDS.RUN_TEST, action?.payload)
322324
return
323-
case 'EDITOR_RUN_RESET_SCRIPT':
325+
case 'EDITOR_RUN_RESET':
326+
// reset to timeline
324327
const tutorial: TT.Tutorial | null = this.context.tutorial.get()
328+
const position: T.Position = this.context.position.get()
329+
330+
// get last pass commit
331+
const hash = getLastCommitHash(position, tutorial?.levels || [])
332+
333+
const branch = tutorial?.config.repo.branch
334+
335+
if (!branch) {
336+
console.error('No repo branch found for tutorial')
337+
return
338+
}
339+
340+
// load timeline until last pass commit
341+
reset({ branch, hash })
342+
325343
// if tutorial.config.reset.command, run it
326344
if (tutorial?.config?.reset?.command) {
327345
await exec({ command: tutorial.config.reset.command })
328346
}
329347
return
330-
case 'EDITOR_RUN_RESET_TO_LAST_PASS':
331-
return
332-
case 'EDITOR_RUN_RESET_TO_TIMELINE':
333-
return
334348
default:
335349
logger(`No match for action type: ${actionType}`)
336350
return

‎src/services/git/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import * as TT from 'typings/tutorial'
22
import { exec, exists } from '../node'
33
import logger from '../logger'
4-
import { stringify } from 'querystring'
54

6-
const gitOrigin = 'coderoad'
5+
export const gitOrigin = 'coderoad'
76

87
const stashAllFiles = async (): Promise<never | void> => {
98
// stash files including untracked (eg. newly created file)

‎src/services/git/lastPass.ts

-12
This file was deleted.

‎src/services/node/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { promisify } from 'util'
55
import { WORKSPACE_ROOT } from '../../environment'
66

77
const asyncExec = promisify(cpExec)
8+
const asyncRemoveFile = promisify(fs.unlink)
89

910
interface ExecParams {
1011
command: string
@@ -19,3 +20,7 @@ export const exec = (params: ExecParams): Promise<{ stdout: string; stderr: stri
1920
export const exists = (...paths: string[]): boolean | never => {
2021
return fs.existsSync(join(WORKSPACE_ROOT, ...paths))
2122
}
23+
24+
export const removeFile = (...paths: string[]) => {
25+
return asyncRemoveFile(join(WORKSPACE_ROOT, ...paths))
26+
}

‎src/services/reset/index.ts

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { exec, removeFile } from '../node'
2+
3+
interface Input {
4+
hash: string
5+
branch: string
6+
}
7+
8+
const ignoreError = () => {}
9+
10+
// note: attempted to do this as a bash script
11+
// but requires the bash script has git permissions
12+
const reset = async ({ branch, hash }: Input): Promise<void> => {
13+
const remote = 'coderoad'
14+
15+
try {
16+
// if no git init, will initialize
17+
// otherwise re-initializes git
18+
await exec({ command: 'git init' }).catch(console.log)
19+
20+
// capture current branch
21+
const hasBranch = await exec({ command: 'git branch --show-current' })
22+
const localBranch = hasBranch.stdout
23+
// check if coderoad remote exists
24+
const hasRemote = await exec({ command: 'git remote -v' }).catch(console.warn)
25+
if (!hasRemote || !hasRemote.stdout || !hasRemote.stdout.length) {
26+
throw new Error('No remote found')
27+
} else if (!hasRemote.stdout.match(new RegExp(remote))) {
28+
throw new Error(`No "${remote}" remote found`)
29+
}
30+
31+
// switch to an empty branch
32+
await exec({
33+
command: 'git checkout --orphan reset-orphan-branch',
34+
})
35+
// stash any current work
36+
await exec({
37+
command: 'git stash',
38+
}).catch(ignoreError)
39+
40+
// remove any other files
41+
await exec({
42+
command: 'git rm -rf .',
43+
}).catch(ignoreError)
44+
await removeFile('.gitignore').catch(ignoreError)
45+
46+
await exec({
47+
command: `git branch -D ${localBranch}`,
48+
})
49+
await exec({
50+
command: `git checkout -b ${localBranch}`,
51+
})
52+
53+
// load git timeline
54+
await exec({
55+
command: `git fetch coderoad ${branch}`,
56+
})
57+
await exec({
58+
command: `git merge coderoad/${branch}`,
59+
})
60+
// reset to target commit hash
61+
await exec({
62+
command: `git reset --hard ${hash}`,
63+
})
64+
} catch (error) {
65+
console.error('Error resetting')
66+
console.error(error.message)
67+
}
68+
}
69+
70+
export default reset

‎src/services/reset/lastHash.test.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as TT from '../../../typings/tutorial'
2+
import * as T from '../../../typings'
3+
import getLastCommitHash from './lastHash'
4+
5+
describe('lastHash', () => {
6+
it('should grab the last passing hash from a step', () => {
7+
const position: T.Position = { levelId: '1', stepId: '1.2' }
8+
const levels: TT.Level[] = [
9+
{
10+
id: '1',
11+
title: '',
12+
summary: '',
13+
content: '',
14+
steps: [
15+
{
16+
id: '1.1',
17+
content: '',
18+
setup: { commits: ['abcdef1'] },
19+
},
20+
{
21+
id: '1.2',
22+
content: '',
23+
setup: { commits: ['abcdef2'] },
24+
},
25+
],
26+
},
27+
]
28+
const result = getLastCommitHash(position, levels)
29+
expect(result).toBe('abcdef2')
30+
})
31+
it('should grab the last passing hash from a step with several commits', () => {
32+
const position: T.Position = { levelId: '1', stepId: '1.2' }
33+
const levels: TT.Level[] = [
34+
{
35+
id: '1',
36+
title: '',
37+
summary: '',
38+
content: '',
39+
steps: [
40+
{
41+
id: '1.1',
42+
content: '',
43+
setup: { commits: ['abcdef1'] },
44+
},
45+
{
46+
id: '1.2',
47+
content: '',
48+
setup: { commits: ['abcdef2', 'abcdef3'] },
49+
},
50+
],
51+
},
52+
]
53+
const result = getLastCommitHash(position, levels)
54+
expect(result).toBe('abcdef3')
55+
})
56+
})

‎src/services/reset/lastHash.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as TT from '../../../typings/tutorial'
2+
import * as T from '../../../typings'
3+
4+
const getLastCommitHash = (position: T.Position, levels: TT.Level[]) => {
5+
// get previous position
6+
const { levelId, stepId } = position
7+
8+
const level: TT.Level | undefined = levels.find((l) => levelId === l.id)
9+
if (!level) {
10+
throw new Error(`No level found matching ${levelId}`)
11+
}
12+
const step = level.steps.find((s) => stepId === s.id)
13+
if (!step) {
14+
throw new Error(`No step found matching ${stepId}`)
15+
}
16+
const commits = step.setup.commits
17+
if (!commits.length) {
18+
throw new Error(`No commits found on step ${stepId}`)
19+
}
20+
return commits[commits.length - 1]
21+
}
22+
23+
export default getLastCommitHash

‎typings/tutorial.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export type Step = {
3434
id: string
3535
content: string
3636
setup: StepActions
37-
solution: Maybe<StepActions>
37+
solution?: Maybe<StepActions>
3838
hints?: string[]
3939
subtasks?: string[]
4040
}

‎web-app/src/containers/Tutorial/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import StepProgress from './components/StepProgress'
1212
import { DISPLAY_RUN_TEST_BUTTON } from '../../environment'
1313
import formatLevels from './formatLevels'
1414
// import SettingsPage from './containers/Settings'
15-
// import Reset from './components/Reset'
15+
import Reset from './components/Reset'
1616

1717
const styles = {
1818
header: {
@@ -95,7 +95,7 @@ const TutorialPage = (props: PageProps) => {
9595
}
9696

9797
const onReset = (): void => {
98-
// TODO
98+
props.send({ type: 'RUN_RESET' })
9999
}
100100

101101
const [menuVisible, setMenuVisible] = React.useState(false)
@@ -148,7 +148,7 @@ const TutorialPage = (props: PageProps) => {
148148

149149
{/* Center */}
150150
<div css={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
151-
{/* <Reset onReset={onReset} disabled={processes.length > 0} /> */}
151+
<Reset onReset={onReset} disabled={processes.length > 0} />
152152
</div>
153153

154154
{/* Right */}

‎web-app/src/services/state/actions/editor.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,9 @@ export default (editorSend: any) => ({
117117
payload: { position: context.position },
118118
})
119119
},
120-
runResetScript() {
120+
runReset() {
121121
editorSend({
122-
type: 'EDITOR_RUN_RESET_SCRIPT',
122+
type: 'EDITOR_RUN_RESET',
123123
})
124124
},
125125
})

‎web-app/src/services/state/machine.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ export const createMachine = (options: any) => {
168168
RUN_TEST: {
169169
actions: ['runTest'],
170170
},
171-
RESET_SCRIPT: {
172-
actions: ['runResetScript'],
171+
RUN_RESET: {
172+
actions: ['runReset'],
173173
},
174174
},
175175
},

0 commit comments

Comments
 (0)
Failed to load comments.