Skip to content

Commit 6f586a1

Browse files
committed
Add support for checking out GitHub repositories
Fixes #183
1 parent b1a7ffb commit 6f586a1

13 files changed

+662
-322
lines changed

.conda/benchcab-dev.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ dependencies:
99
- pytest-cov
1010
- pyyaml
1111
- flatdict
12-
- cerberus>=1.3.5
12+
- cerberus>=1.3.5
13+
- gitpython

.conda/meta.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ requirements:
2727
- f90nml
2828
- flatdict
2929
- cerberus >=1.3.5
30+
- gitpython

benchcab/benchcab.py

+22-17
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from benchcab.model import Model
2323
from benchcab.utils.fs import mkdir, next_path
2424
from benchcab.utils.pbs import render_job_script
25+
from benchcab.utils.repo import SVNRepo, create_repo
2526
from benchcab.utils.subprocess import SubprocessWrapper, SubprocessWrapperInterface
2627
from benchcab.workdir import setup_fluxsite_directory_tree
2728

@@ -42,7 +43,7 @@ def __init__(
4243
self.validate_env = validate_env
4344

4445
self._config: Optional[dict] = None
45-
self._repos: list[Model] = []
46+
self._models: list[Model] = []
4647
self.tasks: list[Task] = [] # initialise fluxsite tasks lazily
4748

4849
def _validate_environment(self, project: str, modules: list):
@@ -99,18 +100,21 @@ def _get_config(self, config_path: str) -> dict:
99100
self._config = read_config(config_path)
100101
return self._config
101102

102-
def _get_repos(self, config: dict) -> list[Model]:
103-
if not self._repos:
104-
self._repos = [
105-
Model(**repo_config, repo_id=id)
106-
for id, repo_config in enumerate(config["realisations"])
107-
]
108-
return self._repos
103+
def _get_models(self, config: dict) -> list[Model]:
104+
if not self._models:
105+
for id, sub_config in enumerate(config["realisations"]):
106+
name = sub_config.get("name")
107+
repo = create_repo(
108+
spec=sub_config.pop("repo"),
109+
path=internal.SRC_DIR / name if name else internal.SRC_DIR,
110+
)
111+
self._models.append(Model(repo=repo, model_id=id, **sub_config))
112+
return self._models
109113

110114
def _initialise_tasks(self, config: dict) -> list[Task]:
111115
"""A helper method that initialises and returns the `tasks` attribute."""
112116
self.tasks = get_fluxsite_tasks(
113-
repos=self._get_repos(config),
117+
models=self._get_models(config),
114118
science_configurations=config.get(
115119
"science_configurations", internal.DEFAULT_SCIENCE_CONFIGURATIONS
116120
),
@@ -181,15 +185,16 @@ def checkout(self, config_path: str, verbose: bool):
181185

182186
print("Checking out repositories...")
183187
rev_number_log = ""
184-
for repo in self._get_repos(config):
185-
repo.checkout(verbose=verbose)
186-
rev_number_log += (
187-
f"{repo.name} last changed revision: "
188-
f"{repo.svn_info_show_item('last-changed-revision')}\n"
189-
)
188+
for model in self._get_models(config):
189+
model.repo.checkout(verbose=verbose)
190+
rev_number_log += f"{model.name}: {model.repo.get_revision()}\n"
190191

191192
# TODO(Sean) we should archive revision numbers for CABLE-AUX
192-
cable_aux_repo = Model(path=internal.CABLE_AUX_RELATIVE_SVN_PATH)
193+
cable_aux_repo = SVNRepo(
194+
svn_root=internal.CABLE_SVN_ROOT,
195+
branch_path=internal.CABLE_AUX_RELATIVE_SVN_PATH,
196+
path=internal.SRC_DIR / "CABLE-AUX",
197+
)
193198
cable_aux_repo.checkout(verbose=verbose)
194199

195200
rev_number_log_path = self.root_dir / next_path(
@@ -208,7 +213,7 @@ def build(self, config_path: str, verbose: bool):
208213
config = self._get_config(config_path)
209214
self._validate_environment(project=config["project"], modules=config["modules"])
210215

211-
for repo in self._get_repos(config):
216+
for repo in self._get_models(config):
212217
if repo.build_script:
213218
print(
214219
"Compiling CABLE using custom build script for "

benchcab/data/config-schema.yml

+26-5
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,38 @@ realisations:
2929
schema:
3030
type: "dict"
3131
schema:
32-
path:
33-
type: "string"
32+
repo:
33+
type: "dict"
34+
required: true
35+
schema:
36+
git:
37+
type: "dict"
38+
excludes: "svn"
39+
schema:
40+
branch:
41+
type: "string"
42+
required: true
43+
commit:
44+
type: "string"
45+
required: false
46+
url:
47+
type: "string"
48+
required: false
49+
svn:
50+
type: "dict"
51+
excludes: "git"
52+
schema:
53+
branch_path:
54+
type: "string"
55+
required: true
56+
revision:
57+
type: "integer"
3458
name:
3559
type: "string"
3660
required: false
3761
build_script:
3862
type: "string"
3963
required: false
40-
revision:
41-
type: "string"
42-
required: false
4364
patch:
4465
type: "dict"
4566
required: false

benchcab/data/test/config-valid.yml

+7-8
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@ project: w97
1919

2020
experiment: five-site-test
2121

22-
realisations: [
23-
{
24-
path: "trunk",
25-
},
26-
{
27-
path: "branches/Users/ccc561/v3.0-YP-changes",
28-
}
29-
]
22+
realisations:
23+
- repo:
24+
svn:
25+
branch_path: trunk
26+
- repo:
27+
svn:
28+
branch_path: branches/Users/ccc561/v3.0-YP-changes
3029

3130
modules: [
3231
intel-compiler/2021.1.1,

benchcab/data/test/integration.sh

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ project: $PROJECT
2020
experiment: AU-Tum
2121
2222
realisations:
23-
- path: trunk
24-
- path: branches/Users/sb8430/test-branch
23+
- repo:
24+
svn:
25+
branch_path: trunk
26+
- repo:
27+
svn:
28+
branch_path: branches/Users/sb8430/test-branch
2529
2630
modules: [
2731
intel-compiler/2021.1.1,

benchcab/fluxsite.py

+18-18
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,20 @@ class Task:
9595

9696
def __init__(
9797
self,
98-
repo: Model,
98+
model: Model,
9999
met_forcing_file: str,
100100
sci_conf_id: int,
101101
sci_config: dict,
102102
) -> None:
103-
self.repo = repo
103+
self.model = model
104104
self.met_forcing_file = met_forcing_file
105105
self.sci_conf_id = sci_conf_id
106106
self.sci_config = sci_config
107107

108108
def get_task_name(self) -> str:
109109
"""Returns the file name convention used for this task."""
110110
met_forcing_base_filename = self.met_forcing_file.split(".")[0]
111-
return f"{met_forcing_base_filename}_R{self.repo.repo_id}_S{self.sci_conf_id}"
111+
return f"{met_forcing_base_filename}_R{self.model.model_id}_S{self.sci_conf_id}"
112112

113113
def get_output_filename(self) -> str:
114114
"""Returns the file name convention used for the netcdf output file."""
@@ -188,19 +188,19 @@ def setup_task(self, verbose=False):
188188
print(f" Adding science configurations to CABLE namelist file {nml_path}")
189189
patch_namelist(nml_path, self.sci_config)
190190

191-
if self.repo.patch:
191+
if self.model.patch:
192192
if verbose:
193193
print(
194194
f" Adding branch specific configurations to CABLE namelist file {nml_path}"
195195
)
196-
patch_namelist(nml_path, self.repo.patch)
196+
patch_namelist(nml_path, self.model.patch)
197197

198-
if self.repo.patch_remove:
198+
if self.model.patch_remove:
199199
if verbose:
200200
print(
201201
f" Removing branch specific configurations from CABLE namelist file {nml_path}"
202202
)
203-
patch_remove_namelist(nml_path, self.repo.patch_remove)
203+
patch_remove_namelist(nml_path, self.model.patch_remove)
204204

205205
def clean_task(self, verbose=False):
206206
"""Cleans output files, namelist files, log files and cable executables if they exist."""
@@ -264,7 +264,7 @@ def fetch_files(self, verbose=False):
264264
self.root_dir / internal.NAMELIST_DIR, task_dir, dirs_exist_ok=True
265265
)
266266

267-
exe_src = self.repo.get_exe_path()
267+
exe_src = self.model.get_exe_path()
268268
exe_dest = task_dir / internal.CABLE_EXE
269269

270270
if verbose:
@@ -343,28 +343,28 @@ def add_provenance_info(self, verbose=False):
343343
).items()
344344
},
345345
**{
346-
"cable_branch": self.repo.svn_info_show_item("url"),
347-
"svn_revision_number": self.repo.svn_info_show_item("revision"),
346+
"cable_branch": self.model.repo.get_branch_name(),
347+
"svn_revision_number": self.model.repo.get_revision(),
348348
"benchcab_version": __version__,
349349
},
350350
}
351351
)
352352

353353

354354
def get_fluxsite_tasks(
355-
repos: list[Model],
355+
models: list[Model],
356356
science_configurations: list[dict],
357357
fluxsite_forcing_file_names: list[str],
358358
) -> list[Task]:
359359
"""Returns a list of fluxsite tasks to run."""
360360
tasks = [
361361
Task(
362-
repo=repo,
362+
model=model,
363363
met_forcing_file=file_name,
364364
sci_conf_id=sci_conf_id,
365365
sci_config=sci_config,
366366
)
367-
for repo in repos
367+
for model in models
368368
for file_name in fluxsite_forcing_file_names
369369
for sci_conf_id, sci_config in enumerate(science_configurations)
370370
]
@@ -403,14 +403,14 @@ def get_fluxsite_comparisons(
403403
output_dir / task_b.get_output_filename(),
404404
),
405405
task_name=get_comparison_name(
406-
task_a.repo, task_b.repo, task_a.met_forcing_file, task_a.sci_conf_id
406+
task_a.model, task_b.model, task_a.met_forcing_file, task_a.sci_conf_id
407407
),
408408
)
409409
for task_a in tasks
410410
for task_b in tasks
411411
if task_a.met_forcing_file == task_b.met_forcing_file
412412
and task_a.sci_conf_id == task_b.sci_conf_id
413-
and task_a.repo.repo_id < task_b.repo.repo_id
413+
and task_a.model.model_id < task_b.model.model_id
414414
# TODO(Sean): Review later - the following code avoids using a double
415415
# for loop to generate pair wise combinations, however we would have
416416
# to re-initialize task instances to get access to the output file path
@@ -425,14 +425,14 @@ def get_fluxsite_comparisons(
425425

426426

427427
def get_comparison_name(
428-
repo_a: Model,
429-
repo_b: Model,
428+
model_a: Model,
429+
model_b: Model,
430430
met_forcing_file: str,
431431
sci_conf_id: int,
432432
) -> str:
433433
"""Returns the naming convention used for bitwise comparisons."""
434434
met_forcing_base_filename = met_forcing_file.split(".")[0]
435435
return (
436436
f"{met_forcing_base_filename}_S{sci_conf_id}"
437-
f"_R{repo_a.repo_id}_R{repo_b.repo_id}"
437+
f"_R{model_a.model_id}_R{model_b.model_id}"
438438
)

benchcab/internal.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,17 @@
7575
FLUXSITE_DIRS["ANALYSIS"] = FLUXSITE_DIRS["RUN"] / "analysis"
7676

7777
# Relative path to directory that stores bitwise comparison results
78-
FLUXSITE_DIRS["BITWISE_CMP"] = (
79-
FLUXSITE_DIRS["ANALYSIS"] / "bitwise-comparisons"
80-
)
78+
FLUXSITE_DIRS["BITWISE_CMP"] = FLUXSITE_DIRS["ANALYSIS"] / "bitwise-comparisons"
8179

8280
# Path to met files:
8381
MET_DIR = Path("/g/data/ks32/CLEX_Data/PLUMBER2/v1-0/Met/")
8482

8583
# CABLE SVN root url:
8684
CABLE_SVN_ROOT = "https://trac.nci.org.au/svn/cable"
8785

86+
# CABLE GitHub URL:
87+
CABLE_GIT_URL = "https://github.com/CABLE-LSM/CABLE.git"
88+
8889
# CABLE executable file name:
8990
CABLE_EXE = "cable-mpi" if MPI else "cable"
9091

0 commit comments

Comments
 (0)