@@ -2,12 +2,16 @@ import * as yamlParser from "js-yaml";
22import * as path from "path" ;
33import * as _ from "lodash" ;
44import * as fs from "fs" ;
5- import * as T from "../typings/tutorial " ;
5+ import * as util from "util " ;
66import { parse } from "./utils/parse" ;
7- // import validate from './validator';
7+ import { getArg } from "./utils/args" ;
8+ import { getCommits } from "./utils/commits" ;
9+ import * as T from "../typings/tutorial" ;
10+
11+ const write = util . promisify ( fs . writeFile ) ;
12+ const read = util . promisify ( fs . readFile ) ;
813
914// import not working
10- const simpleGit = require ( "simple-git/promise" ) ;
1115
1216const workingDir = "tmp" ;
1317
@@ -56,143 +60,84 @@ async function cleanupFiles(workingDir: string) {
5660}
5761}
5862
59- export type BuildOptions = {
60- repo :string ; // Git url to the repo. It should finish with .git
61- codeBranch :string ; // The branch containing the tutorial code
62- setupBranch :string ; // The branch containing the tutorialuration files
63- isLocal :boolean ; // define if the repo is local or remote
64- output :string ;
63+ export type BuildConfigOptions = {
64+ text :string ; // text document from markdown
65+ config :T . Tutorial ; // yaml config file converted to json
66+ commits :{ [ key :string ] :string [ ] } ;
6567} ;
6668
67- async function build ( { repo, codeBranch, setupBranch, isLocal} :BuildOptions ) {
68- let git :any ;
69- let isSubModule = false ;
70- let localPath :string ;
71-
72- if ( isLocal ) {
73- git = simpleGit ( repo ) ;
74- localPath = repo ;
75- } else {
76- const gitTest = simpleGit ( process . cwd ( ) ) ;
77- const isRepo = await gitTest . checkIsRepo ( ) ;
78- localPath = path . join ( process . cwd ( ) , workingDir ) ;
69+ async function generateConfig ( { text, config, commits} :BuildConfigOptions ) {
70+ const tutorial = parse ( text , config ) ;
7971
80- if ( isRepo ) {
81- await gitTest . submoduleAdd ( repo , workingDir ) ;
82-
83- isSubModule = true ;
84- } else {
85- await gitTest . clone ( repo , localPath ) ;
86- }
87-
88- git = simpleGit ( localPath ) ;
89- }
90-
91- await git . fetch ( ) ;
72+ // const isValid = validate(tutorial);
9273
93- // checkout the branch to load tutorialuration and content branch
94- await git . checkout ( setupBranch ) ;
74+ // if (!isValid) {
75+ // console.log(JSON.stringify(validate.errors, null, 2));
76+ // return;
77+ // }
9578
96- // Load files
97- const _content = fs . readFileSync ( path . join ( localPath , "TUTORIAL.md" ) , "utf8" ) ;
98- let _config = fs . readFileSync ( path . join ( localPath , "coderoad.yaml" ) , "utf8" ) ;
79+ return tutorial ;
80+ }
9981
100- const tutorial = parse ( _content , _config ) ;
82+ type BuildArgs = {
83+ dir :string ;
84+ markdown :string ;
85+ yaml :string ;
86+ output :string ;
87+ } ;
10188
102- // Checkout the code branches
103- await git . checkout ( codeBranch ) ;
89+ const parseArgs = ( args :string [ ] ) :BuildArgs => {
90+ // default .
91+ const dir = args [ 0 ] || "." ;
92+ // -o --output - default coderoad.json
93+ const output =
94+ getArg ( args , { name :"output" , alias :"o" } ) || "coderoad.json" ;
95+ // -m --markdown - default TUTORIAL.md
96+ const markdown =
97+ getArg ( args , { name :"markdown" , alias :"m" } ) || "TUTORIAL.md" ;
98+ // -y --yaml - default coderoad-config.yml
99+ const yaml =
100+ getArg ( args , { name :"coderoad-config.yml" , alias :"y" } ) ||
101+ "coderoad-config.yml" ;
102+
103+ return {
104+ dir,
105+ output,
106+ markdown,
107+ yaml,
108+ } ;
109+ } ;
104110
105- // Load all logs
106- const logs = await git . log ( ) ;
111+ async function build ( args : string [ ] ) {
112+ const options = parseArgs ( args ) ;
107113
108- //Filter relevant logs
109- const parts = new Set ( ) ;
114+ //path to run build from
115+ const localPath = path . join ( process . cwd ( ) , options . dir ) ;
110116
111- for ( const commit of logs . all ) {
112- const matches = commit . message . match (
113- / ^ (?< stepId > (?< levelId > L \d + ) S \d + ) (?< stepType > [ Q A ] ) ? /
114- ) ;
117+ // load files
118+ const [ _markdown , _yaml ] = await Promise . all ( [
119+ read ( path . join ( localPath , options . markdown ) , "utf8" ) ,
120+ read ( path . join ( localPath , options . yaml ) , "utf8" ) ,
121+ ] ) ;
115122
116- if ( matches && ! parts . has ( matches [ 0 ] ) ) {
117- // Uses a set to make sure only the latest commit is proccessed
118- parts . add ( matches [ 0 ] ) ;
123+ const config = yamlParser . load ( _yaml ) ;
119124
120- // Add the content and git hash to the tutorial
121- if ( matches . groups . stepId ) {
122- // If it's a step: add the content and the setup/solution hashes depending on the type
123- const level :T . Level | null =
124- tutorial . levels . find (
125- ( level :T . Level ) => level . id === matches . groups . levelId
126- ) || null ;
127- if ( ! level ) {
128- console . log ( `Level${ matches . groups . levelId } not found` ) ;
129- } else {
130- const theStep :T . Step | null =
131- level . steps . find (
132- ( step :T . Step ) => step . id === matches . groups . stepId
133- ) || null ;
134-
135- if ( ! theStep ) {
136- console . log ( `Step${ matches . groups . stepId } not found` ) ;
137- } else {
138- if ( matches . groups . stepType === "Q" ) {
139- theStep . setup . commits . push ( commit . hash . substr ( 0 , 7 ) ) ;
140- } else if (
141- matches . groups . stepType === "A" &&
142- theStep . solution &&
143- theStep . solution . commits
144- ) {
145- theStep . solution . commits . push ( commit . hash . substr ( 0 , 7 ) ) ;
146- }
147- }
148- }
149- } else {
150- // If it's level: add the commit hash (if the level has the commit key) and the content to the tutorial
151- const theLevel :T . Level | null =
152- tutorial . levels . find (
153- ( level :T . Level ) => level . id === matches . groups . levelId
154- ) || null ;
155-
156- if ( ! theLevel ) {
157- console . log ( `Level${ matches . groups . levelId } not found` ) ;
158- } else {
159- if ( _ . has ( theLevel , "tutorial.commits" ) ) {
160- if ( theLevel . setup ) {
161- theLevel . setup . commits . push ( commit . hash . substr ( 0 , 7 ) ) ;
162- }
163- }
164- }
165- }
166- }
167- }
125+ const commits = getCommits ( config . config . repo . branch ) ;
168126
169- // cleanup the submodules
170- if ( ! isLocal ) {
171- let cleanupErr ;
127+ // Otherwise, continue with the other options
128+ const tutorial :T . Tutorial = await generateConfig ( {
129+ text :_markdown ,
130+ config,
131+ commits,
132+ } ) ;
172133
173- if ( isSubModule ) {
174- cleanupErr = await cleanupFiles ( workingDir ) ;
134+ if ( tutorial ) {
135+ if ( options . output ) {
136+ await write ( options . output , JSON . stringify ( tutorial ) , "utf8" ) ;
175137} else {
176- cleanupErr = rmDir ( path . join ( process . cwd ( ) , workingDir ) ) ;
177- }
178-
179- if ( cleanupErr ) {
180- console . log (
181- `Error when deleting temporary files on${
182- isSubModule ?"module" :"folder"
183- } ${ workingDir } .`
184- ) ;
138+ console . log ( JSON . stringify ( tutorial , null , 2 ) ) ;
185139}
186140}
187-
188- // const isValid = validate(tutorial);
189-
190- // if (!isValid) {
191- // console.log(JSON.stringify(validate.errors, null, 2));
192- // return;
193- // }
194-
195- return tutorial ;
196141}
197142
198143export default build ;