Skip to content

Commit 992a723

Browse files
committed
Improve provenance for Omega PRs
1 parent 148f6e4 commit 992a723

File tree

2 files changed

+149
-3
lines changed

2 files changed

+149
-3
lines changed

polaris/provenance.py

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44

55

6-
def write(work_dir, tasks, config=None):
6+
def write(work_dir, tasks, config=None, machine=None, baseline_dir=None):
77
"""
88
Write a file with provenance, such as the git version, conda packages,
99
command, and tasks, to the work directory
@@ -16,9 +16,15 @@ def write(work_dir, tasks, config=None):
1616
tasks : dict
1717
A dictionary describing all of the tasks and their steps
1818
19-
config : polaris.config.PolarisConfigParser
19+
config : polaris.config.PolarisConfigParser, optional
2020
Configuration options for this task, a combination of user configs
2121
and the defaults for the machine and component
22+
23+
machine : str, optional
24+
The machine on which Polaris is being run
25+
26+
baseline_dir : str, optional
27+
The path to the baseline work directory, if any
2228
"""
2329
polaris_git_version = None
2430
if os.path.exists('.git'):
@@ -69,6 +75,14 @@ def write(work_dir, tasks, config=None):
6975
f'component git version: {component_git_version}\n\n'
7076
)
7177
provenance_file.write(f'command: {calling_command}\n\n')
78+
79+
# Add readily parsable, PR-friendly metadata discovered at setup time
80+
_write_meta(provenance_file, 'machine', machine)
81+
_write_scheduler_metadata(provenance_file, config)
82+
_write_meta(provenance_file, 'compiler', _get_compiler(config))
83+
_write_meta(provenance_file, 'work directory', work_dir)
84+
_write_meta(provenance_file, 'build directory', _get_build_dir(config))
85+
_write_meta(provenance_file, 'baseline work directory', baseline_dir)
7286
provenance_file.write('tasks:\n')
7387

7488
for _, task in tasks.items():
@@ -126,3 +140,129 @@ def _get_component_git_version(config):
126140
os.chdir(cwd)
127141

128142
return component_git_version
143+
144+
145+
def _get_system(config):
146+
if config is None:
147+
return None
148+
if config.has_option('parallel', 'system'):
149+
return config.get('parallel', 'system')
150+
return None
151+
152+
153+
def _resolve_scheduler_fields(config, system):
154+
"""Resolve partition/qos/constraint (slurm) or just constraint (pbs)."""
155+
partition = None
156+
qos = None
157+
constraint = None
158+
159+
if config is None:
160+
return partition, qos, constraint
161+
162+
if system == 'pbs':
163+
# PBS: only constraint is relevant for our purposes
164+
cons_val = (
165+
config.get('job', 'constraint')
166+
if config.has_option('job', 'constraint')
167+
else None
168+
)
169+
if cons_val and cons_val != '<<<default>>>':
170+
constraint = cons_val
171+
elif config.has_option('parallel', 'constraints'):
172+
cons_list = config.getlist('parallel', 'constraints')
173+
if cons_list:
174+
constraint = cons_list[0]
175+
return partition, qos, constraint
176+
177+
# Default to slurm-like resolution
178+
part_val = (
179+
config.get('job', 'partition')
180+
if config.has_option('job', 'partition')
181+
else None
182+
)
183+
if part_val and part_val != '<<<default>>>':
184+
partition = part_val
185+
elif config.has_option('parallel', 'partitions'):
186+
parts = config.getlist('parallel', 'partitions')
187+
if parts:
188+
partition = parts[0]
189+
190+
qos_val = (
191+
config.get('job', 'qos') if config.has_option('job', 'qos') else None
192+
)
193+
if qos_val and qos_val != '<<<default>>>':
194+
qos = qos_val
195+
elif config.has_option('parallel', 'qos'):
196+
qos_list = config.getlist('parallel', 'qos')
197+
if qos_list:
198+
qos = qos_list[0]
199+
200+
cons_val = (
201+
config.get('job', 'constraint')
202+
if config.has_option('job', 'constraint')
203+
else None
204+
)
205+
if cons_val and cons_val != '<<<default>>>':
206+
constraint = cons_val
207+
elif config.has_option('parallel', 'constraints'):
208+
cons_list = config.getlist('parallel', 'constraints')
209+
if cons_list:
210+
constraint = cons_list[0]
211+
212+
return partition, qos, constraint
213+
214+
215+
def _get_compiler(config):
216+
if config is None:
217+
return None
218+
if config.has_option('deploy', 'compiler'):
219+
val = config.get('deploy', 'compiler')
220+
return val or None
221+
return None
222+
223+
224+
def _get_build_dir(config):
225+
if config is None:
226+
return None
227+
if config.has_option('paths', 'component_path'):
228+
val = config.get('paths', 'component_path')
229+
return val or None
230+
return None
231+
232+
233+
def _write_meta(provenance_file, label, value):
234+
"""Write a simple 'label: value' line if value is provided."""
235+
if value is None:
236+
return
237+
if isinstance(value, str) and value.strip() == '':
238+
return
239+
provenance_file.write(f'{label}: {value}\n\n')
240+
241+
242+
def _write_scheduler_metadata(provenance_file, config):
243+
"""Write partition/qos/constraint metadata when available."""
244+
system = _get_system(config)
245+
partition, qos, constraint = _resolve_scheduler_fields(config, system)
246+
_write_meta(provenance_file, 'partition', partition)
247+
_write_meta(provenance_file, 'qos', qos)
248+
_write_meta(provenance_file, 'constraint', constraint)
249+
if config.has_option('paths', 'component_path'):
250+
component_path = config.get('paths', 'component_path')
251+
else:
252+
component_path = None
253+
254+
if component_path is None or not os.path.exists(component_path):
255+
return None
256+
257+
cwd = os.getcwd()
258+
os.chdir(component_path)
259+
260+
try:
261+
args = ['git', 'describe', '--tags', '--dirty', '--always']
262+
component_git_version = subprocess.check_output(args).decode('utf-8')
263+
component_git_version = component_git_version.strip('\n')
264+
except subprocess.CalledProcessError:
265+
component_git_version = None
266+
os.chdir(cwd)
267+
268+
return component_git_version

polaris/setup.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,13 @@ def setup_tasks(
115115
config_file, machine, component_path, component, model
116116
)
117117

118-
provenance.write(work_dir, tasks, config=basic_config)
118+
provenance.write(
119+
work_dir,
120+
tasks,
121+
config=basic_config,
122+
machine=machine,
123+
baseline_dir=baseline_dir,
124+
)
119125

120126
if clean:
121127
print('')

0 commit comments

Comments
 (0)