-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig-extract.py
executable file
·128 lines (103 loc) · 3.63 KB
/
config-extract.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python3
"""
This script extracts values from a structured config file, i.e. JSON or YAML.
"""
import argparse
import contextlib
import json
import os
import pathlib
import jsonpath_ng
import yaml
def infer_format(path: pathlib.Path):
if path.suffix in {".yml", ".yaml"}:
print(f"[INFO ] inferred YAML format from extension")
return "yaml"
elif path.suffix == ".json":
print(f"[INFO ] inferred JSON format from extension")
return "json"
elif path.name == "Dockerfile":
print(f"[INFO ] inferred Dockerfile format from filename")
return "dockerfile"
else:
print(f"[ERROR] unable to infer format from filename - {path.name}")
raise SystemExit(1)
@contextlib.contextmanager
def dockerfile_data(path: pathlib.Path):
"""
For a Dockerfile, the 'data' is a dictionary of build ARGs.
"""
data = {}
print("[INFO ] reading build args")
with path.open() as fd:
for line in fd.readlines():
if not line.startswith("ARG "):
continue
parts = line.removeprefix("ARG ").strip().split("=", maxsplit = 1)
try:
name, value = parts
except ValueError:
name = parts[0]
value = None
data[name] = value
yield data
@contextlib.contextmanager
def json_data(path: pathlib.Path):
print("[INFO ] reading data")
with path.open() as fd:
yield json.load(fd)
@contextlib.contextmanager
def yaml_data(path: pathlib.Path):
print("[INFO ] reading data")
with path.open() as fd:
yield yaml.safe_load(fd)
CONFIG_DATA = { "dockerfile": dockerfile_data, "json": json_data, "yaml": yaml_data }
def produce_output(name, value):
output_path = os.environ.get("GITHUB_OUTPUT", "/dev/stdout")
with open(output_path, "a") as fd:
print(f"{name}={value}", file = fd)
def main():
parser = argparse.ArgumentParser(description = "Extracts values from a structured config file.")
parser.add_argument(
"path",
help = "The path to the file to update.",
type = pathlib.Path
)
parser.add_argument(
"format",
help = "The format of the file. If empty, the format is inferred from the path.",
choices = ["", "dockerfile", "json", "yaml"]
)
parser.add_argument(
"outputs",
help = "The set of outputs to produce, one per line in the format 'name=json-path-expr'."
)
args = parser.parse_args()
# Make sure that the YAML SafeLoader respects Ansible's !unsafe tag
yaml.SafeLoader.add_constructor("!unsafe", yaml.SafeLoader.construct_scalar)
print(f"[INFO ] extracting from config file {args.path}")
if args.format:
print(f"[INFO ] using specified format - {args.format}")
path_format = args.format
else:
path_format = infer_format(args.path)
print(f"[INFO ] extracting values")
with CONFIG_DATA[path_format](args.path) as data:
for output in args.outputs.splitlines():
# Ignore empty lines
if not output:
continue
name, jsonpath = output.split("=", maxsplit = 1)
print(f"[INFO ] extracting {jsonpath} as {name}")
jsonpath_expr = jsonpath_ng.parse(jsonpath)
try:
value = next(
match.value
for match in jsonpath_expr.find(data)
)
except StopIteration:
print(f"[WARN ] no value at {jsonpath}")
else:
produce_output(name, value)
if __name__ == "__main__":
main()