Skip to content

Commit abb6f93

Browse files
authored
Merge pull request #88 from rage/fix-refresh-course
redesign course-refresher
2 parents 453c31c + 1942635 commit abb6f93

File tree

6 files changed

+359
-1007
lines changed

6 files changed

+359
-1007
lines changed

tmc-langs-cli/src/app.rs

Lines changed: 9 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -206,86 +206,29 @@ pub fn create_app() -> App<'static, 'static> {
206206
.subcommand(SubCommand::with_name("refresh-course")
207207
.about("Refresh the given course")
208208
// .long_about(schema_leaked::<RefreshData>()) // can't format YAML mapping
209-
.arg(Arg::with_name("course-name")
210-
.help("The name of the course.")
211-
.long("course-name")
212-
.required(true)
213-
.takes_value(true))
214209
.arg(Arg::with_name("cache-path")
215210
.help("Path to the cached course.")
216211
.long("cache-path")
217212
.required(true)
218213
.takes_value(true))
219-
.arg(Arg::with_name("clone-path")
220-
.help("Path to the course clone.")
221-
.long("clone-path")
222-
.required(true)
223-
.takes_value(true))
224-
.arg(Arg::with_name("stub-path")
225-
.help("Path to the course stub.")
226-
.long("stub-path")
227-
.required(true)
228-
.takes_value(true))
229-
.arg(Arg::with_name("stub-zip-path")
230-
.help("Path to the directory where the stub zips will be created.")
231-
.long("stub-zip-path")
232-
.required(true)
233-
.takes_value(true))
234-
.arg(Arg::with_name("solution-path")
235-
.help("The name of the course solution.")
236-
.long("solution-path")
237-
.required(true)
238-
.takes_value(true))
239-
.arg(Arg::with_name("solution-zip-path")
240-
.help("Path to the directory where the solution zips will be created.")
241-
.long("solution-zip-path")
214+
.arg(Arg::with_name("cache-root")
215+
.help("The cache root.")
216+
.long("cache-root")
242217
.required(true)
243218
.takes_value(true))
244-
.arg(Arg::with_name("exercise")
245-
.help("An exercise. Takes 3 values: the exercise's name, its relative path, and a comma separated list of its available points. Multiple exercises can be given.")
246-
.long("exercise")
247-
.takes_value(true)
248-
.multiple(true)
249-
.number_of_values(3)
250-
.value_names(&["exercise name", "relative path", "available points"]))
251-
.arg(Arg::with_name("source-backend")
252-
.help("The version control used for the course.")
253-
.long("source-backend")
254-
.required(true)
255-
.takes_value(true)
256-
.possible_values(&["git"]))
257-
.arg(Arg::with_name("source-url")
258-
.help("Version control URL.")
259-
.long("source-url")
219+
.arg(Arg::with_name("course-name")
220+
.help("The name of the course.")
221+
.long("course-name")
260222
.required(true)
261223
.takes_value(true))
262224
.arg(Arg::with_name("git-branch")
263225
.help("Version control branch.")
264226
.long("git-branch")
265227
.required(true)
266228
.takes_value(true))
267-
.arg(Arg::with_name("no-directory-changes")
268-
.help("If set, the cache is not reset and the clone is not updated/cloned, and the solutions and stubs are not prepared.")
269-
.long("no-directory-changes"))
270-
.arg(Arg::with_name("no-background-operations")
271-
.help("If set, will not update available points.")
272-
.long("no-background-operations"))
273-
.arg(Arg::with_name("chmod-bits")
274-
.help("The unix file permission bits in octal notation used for the directories from the rails root to the cache root and the cache root's contents.")
275-
.long("chmod-bits")
276-
.takes_value(true))
277-
.arg(Arg::with_name("chgrp-uid")
278-
.help("The UID of the owner of the directories from the rails root to the cache root and the cache root's contents.")
279-
.long("chgrp-uid")
280-
.takes_value(true))
281-
.arg(Arg::with_name("cache-root")
282-
.help("The cache root.")
283-
.long("cache-root")
284-
.required(true)
285-
.takes_value(true))
286-
.arg(Arg::with_name("rails-root")
287-
.help("The Rails root directory.")
288-
.long("rails-root")
229+
.arg(Arg::with_name("source-url")
230+
.help("Version control URL.")
231+
.long("source-url")
289232
.required(true)
290233
.takes_value(true)))
291234

@@ -943,40 +886,12 @@ mod base_test {
943886
"path",
944887
"--cache-root",
945888
"path",
946-
"--chgrp-uid",
947-
"1234",
948-
"--chmod-bits",
949-
"1234",
950-
"--clone-path",
951-
"path",
952889
"--course-name",
953890
"name",
954-
"--exercise",
955-
"name",
956-
"path",
957-
"10,11,12",
958-
"--exercise",
959-
"second",
960-
"path",
961-
"20,21,22",
962891
"--git-branch",
963892
"main",
964-
"--no-background-operations",
965-
"--no-directory-changes",
966-
"--rails-root",
967-
"path",
968-
"--solution-path",
969-
"path",
970-
"--solution-zip-path",
971-
"path",
972-
"--source-backend",
973-
"git",
974893
"--source-url",
975894
"example.com",
976-
"--stub-path",
977-
"path",
978-
"--stub-zip-path",
979-
"path",
980895
]);
981896
}
982897

tmc-langs-cli/src/main.rs

Lines changed: 5 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ use tmc_client::{ClientError, FeedbackAnswer, TmcClient, Token};
3636
use tmc_langs_framework::{domain::StyleValidationResult, error::CommandError, file_util};
3737
use tmc_langs_util::{
3838
progress_reporter::ProgressReporter,
39-
task_executor::{
40-
self, Course, GroupBits, ModeBits, Options, RefreshExercise, SourceBackend, TmcParams,
41-
},
39+
task_executor::{self, TmcParams},
4240
Language, OutputFormat,
4341
};
4442
use toml::{map::Map as TomlMap, Value as TomlValue};
@@ -559,84 +557,16 @@ fn run_app(matches: ArgMatches, pretty: bool, warnings: &mut Vec<anyhow::Error>)
559557
("refresh-course", Some(matches)) => {
560558
let cache_path = matches.value_of("cache-path").unwrap();
561559
let cache_root = matches.value_of("cache-root").unwrap();
562-
let chgrp_uid = matches.value_of("chgrp-uid");
563-
let chmod_bits = matches.value_of("chmod-bits");
564-
let clone_path = matches.value_of("clone-path").unwrap();
565560
let course_name = matches.value_of("course-name").unwrap();
566-
567-
let exercise_args = matches.values_of("exercise");
568561
let git_branch = matches.value_of("git-branch").unwrap();
569-
let no_background_operations = matches.is_present("no-background-operations");
570-
let no_directory_changes = matches.is_present("no-directory-changes");
571-
let rails_root = matches.value_of("rails-root").unwrap();
572-
573-
let solution_path = matches.value_of("solution-path").unwrap();
574-
let solution_zip_path = matches.value_of("solution-zip-path").unwrap();
575-
let source_backend = matches.value_of("source-backend").unwrap();
576562
let source_url = matches.value_of("source-url").unwrap();
577-
let stub_path = matches.value_of("stub-path").unwrap();
578-
let stub_zip_path = matches.value_of("stub-zip-path").unwrap();
579-
580-
let mut exercises = vec![];
581-
if let Some(mut exercise_args) = exercise_args {
582-
while let Some(exercise_name) = exercise_args.next() {
583-
let relative_path = exercise_args.next().unwrap();
584-
let available_points: Vec<_> =
585-
exercise_args.next().unwrap().split(',').collect();
586-
exercises.push(RefreshExercise {
587-
name: exercise_name.to_string(),
588-
relative_path: PathBuf::from(relative_path),
589-
available_points: available_points
590-
.into_iter()
591-
.map(str::to_string)
592-
.filter(|s| !s.is_empty())
593-
.collect(),
594-
});
595-
}
596-
}
597-
let source_backend = match source_backend {
598-
"git" => SourceBackend::Git,
599-
_ => unreachable!("validation error"),
600-
};
601-
let course = Course {
602-
name: course_name.to_string(),
603-
cache_path: PathBuf::from(cache_path),
604-
clone_path: PathBuf::from(clone_path),
605-
stub_path: PathBuf::from(stub_path),
606-
stub_zip_path: PathBuf::from(stub_zip_path),
607-
solution_path: PathBuf::from(solution_path),
608-
solution_zip_path: solution_zip_path.into(),
609-
exercises,
610-
source_backend,
611-
source_url: source_url.to_string(),
612-
git_branch: git_branch.to_string(),
613-
};
614-
let options = Options {
615-
no_background_operations,
616-
no_directory_changes,
617-
};
618-
let chmod_bits = if let Some(chmod_bits) = chmod_bits {
619-
Some(ModeBits::from_str_radix(chmod_bits, 8).with_context(|| {
620-
format!("Failed to convert chmod bits to an integer: {}", chmod_bits,)
621-
})?)
622-
} else {
623-
None
624-
};
625-
let chgrp_uid = if let Some(chgrp_uid) = chgrp_uid {
626-
Some(GroupBits::from_str_radix(chgrp_uid, 10).with_context(|| {
627-
format!("Failed to convert chgrp UID to an integer: {}", chgrp_uid,)
628-
})?)
629-
} else {
630-
None
631-
};
632563

633564
let refresh_result = task_executor::refresh_course(
634-
course,
635-
options,
636-
chmod_bits,
637-
chgrp_uid,
565+
course_name.to_string(),
566+
PathBuf::from(cache_path),
567+
source_url.to_string(),
568+
git_branch.to_string(),
638569
PathBuf::from(cache_root),
639-
PathBuf::from(rails_root),
640570
move |update| {
641571
let output = Output::StatusUpdate(StatusUpdateData::None(update));
642572
print_output(&output, pretty, &[])?;

tmc-langs-framework/src/tmc_project_yml.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl TmcProjectYml {
4141
config_path.push(".tmcproject.yml");
4242

4343
if !config_path.exists() {
44-
log::debug!("no config found at {}", config_path.display());
44+
log::trace!("no config found at {}", config_path.display());
4545
return Ok(Self::default());
4646
}
4747
log::debug!("reading .tmcproject.yml from {}", config_path.display());

tmc-langs-util/src/error.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
//! Contains the crate error type
22
3-
#[cfg(unix)]
4-
use crate::task_executor::ModeBits;
53
use std::path::PathBuf;
64
use thiserror::Error;
75
use tmc_langs_framework::error::FileIo;
86

7+
use crate::task_executor::ModeBits;
8+
99
#[derive(Debug, Error)]
1010
pub enum UtilError {
1111
#[error("Failed to create temporary file")]
@@ -32,12 +32,8 @@ pub enum UtilError {
3232
#[error("Error while writing file to zip")]
3333
ZipWrite(#[source] std::io::Error),
3434

35-
#[error("Unsupported source backend")]
36-
UnsupportedSourceBackend,
3735
#[error("Path {0} contained a dash '-' which is currently not allowed")]
3836
InvalidDirectory(PathBuf),
39-
#[error("The cache path ({0}) must be inside the rails root path ({1})")]
40-
CacheNotInRailsRoot(PathBuf, PathBuf),
4137

4238
#[error(transparent)]
4339
TmcError(#[from] tmc_langs_framework::TmcError),

tmc-langs-util/src/task_executor.rs

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,10 @@ mod submission_processing;
66
mod tar_helper;
77
mod tmc_zip;
88

9-
pub use self::course_refresher::{
10-
Course, GroupBits, ModeBits, Options, RefreshData, RefreshExercise, SourceBackend,
11-
};
9+
pub use self::course_refresher::{refresh_course, ModeBits, RefreshData, RefreshExercise};
1210
pub use self::submission_packaging::{OutputFormat, TmcParams};
1311

1412
use crate::error::UtilError;
15-
use crate::progress_reporter::StatusUpdate;
1613
use crate::{ExerciseDesc, ExercisePackagingConfiguration, RunResult, StyleValidationResult};
1714
use std::path::{Path, PathBuf};
1815
use tmc_langs_csharp::CSharpPlugin;
@@ -213,8 +210,8 @@ pub fn find_exercise_directories(exercise_path: &Path) -> Result<Vec<PathBuf>, U
213210
let mut paths = vec![];
214211
for entry in WalkDir::new(exercise_path).into_iter().filter_entry(|e| {
215212
!submission_processing::is_hidden_dir(e)
216-
|| e.file_name() == "private"
217-
|| submission_processing::contains_tmcignore(e)
213+
&& e.file_name() != "private"
214+
&& !submission_processing::contains_tmcignore(e)
218215
}) {
219216
let entry = entry?;
220217
// TODO: Java implementation doesn't scan root directories
@@ -231,29 +228,6 @@ pub fn get_available_points(exercise_path: &Path) -> Result<Vec<String>, UtilErr
231228
Ok(points)
232229
}
233230

234-
pub fn refresh_course(
235-
course: Course,
236-
options: Options,
237-
chmod_bits: Option<ModeBits>,
238-
chgrp_uid: Option<GroupBits>,
239-
cache_root: PathBuf,
240-
rails_root: PathBuf,
241-
progress_reporter: impl 'static
242-
+ Sync
243-
+ Send
244-
+ Fn(StatusUpdate<()>) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>,
245-
) -> Result<RefreshData, UtilError> {
246-
course_refresher::refresh_course(
247-
course,
248-
options,
249-
chmod_bits,
250-
chgrp_uid,
251-
cache_root,
252-
rails_root,
253-
progress_reporter,
254-
)
255-
}
256-
257231
// enum containing all the plugins
258232
#[impl_enum::with_methods(
259233
fn clean(&self, path: &Path) -> Result<(), TmcError> {}
@@ -278,28 +252,56 @@ enum Plugin {
278252
// Get language plugin for the given path.
279253
fn get_language_plugin(path: &Path) -> Result<Plugin, TmcError> {
280254
if NoTestsPlugin::is_exercise_type_correct(path) {
281-
log::info!("Detected project as {}", NoTestsPlugin::PLUGIN_NAME);
255+
log::info!(
256+
"Detected project at {} as {}",
257+
path.display(),
258+
NoTestsPlugin::PLUGIN_NAME
259+
);
282260
Ok(Plugin::NoTests(NoTestsPlugin::new()))
283261
} else if CSharpPlugin::is_exercise_type_correct(path) {
284262
let csharp = CSharpPlugin::new();
285-
log::info!("Detected project as {}", CSharpPlugin::PLUGIN_NAME);
263+
log::info!(
264+
"Detected project at {} as {}",
265+
path.display(),
266+
CSharpPlugin::PLUGIN_NAME
267+
);
286268
Ok(Plugin::CSharp(csharp))
287269
} else if MakePlugin::is_exercise_type_correct(path) {
288270
let make = MakePlugin::new();
289-
log::info!("Detected project as {}", MakePlugin::PLUGIN_NAME);
271+
log::info!(
272+
"Detected project at {} as {}",
273+
path.display(),
274+
MakePlugin::PLUGIN_NAME
275+
);
290276
Ok(Plugin::Make(make))
291277
} else if Python3Plugin::is_exercise_type_correct(path) {
292-
log::info!("Detected project as {}", Python3Plugin::PLUGIN_NAME);
278+
log::info!(
279+
"Detected project at {} as {}",
280+
path.display(),
281+
Python3Plugin::PLUGIN_NAME
282+
);
293283
Ok(Plugin::Python3(Python3Plugin::new()))
294284
} else if RPlugin::is_exercise_type_correct(path) {
295-
log::info!("Detected project as {}", RPlugin::PLUGIN_NAME);
285+
log::info!(
286+
"Detected project at {} as {}",
287+
path.display(),
288+
RPlugin::PLUGIN_NAME
289+
);
296290
Ok(Plugin::R(RPlugin::new()))
297291
} else if MavenPlugin::is_exercise_type_correct(path) {
298-
log::info!("Detected project as {}", MavenPlugin::PLUGIN_NAME);
292+
log::info!(
293+
"Detected project at {} as {}",
294+
path.display(),
295+
MavenPlugin::PLUGIN_NAME
296+
);
299297
Ok(Plugin::Maven(MavenPlugin::new()?))
300298
} else if AntPlugin::is_exercise_type_correct(path) {
301299
// TODO: currently, ant needs to be last because any project with src and test are recognized as ant
302-
log::info!("Detected project as {}", AntPlugin::PLUGIN_NAME);
300+
log::info!(
301+
"Detected project at {} as {}",
302+
path.display(),
303+
AntPlugin::PLUGIN_NAME
304+
);
303305
Ok(Plugin::Ant(AntPlugin::new()?))
304306
} else {
305307
Err(TmcError::PluginNotFound(path.to_path_buf()))

0 commit comments

Comments
 (0)