Skip to content

Commit 6763560

Browse files
Source Links Fix (#440)
Changes the working of source_code_linker. It now correctly identifies the repository the source link & test links came from, either in local or combo build. - KNOWN_GOOD_JSON is required in combo builds now - In combo builds it will take the URL from the KNOWN_GOOD_JSON.
1 parent 4f39de2 commit 6763560

31 files changed

Lines changed: 3491 additions & 494 deletions

docs.bzl

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,30 @@ def _rewrite_needs_json_to_sourcelinks(labels):
6969
out.append(s)
7070
return out
7171

72-
def _merge_sourcelinks(name, sourcelinks):
72+
def _merge_sourcelinks(name, sourcelinks, known_good = None):
7373
"""Merge multiple sourcelinks JSON files into a single file.
7474
7575
Args:
7676
name: Name for the merged sourcelinks target
7777
sourcelinks: List of sourcelinks JSON file targets
7878
"""
7979

80+
extra_srcs = []
81+
known_good_arg = ""
82+
if known_good != None:
83+
extra_srcs = [known_good]
84+
known_good_arg = "--known_good $(location %s)" % known_good
85+
8086
native.genrule(
8187
name = name,
82-
srcs = sourcelinks,
88+
srcs = sourcelinks + extra_srcs,
8389
outs = [name + ".json"],
8490
cmd = """
8591
$(location @score_docs_as_code//scripts_bazel:merge_sourcelinks) \
8692
--output $@ \
93+
{known_good_arg} \
8794
$(SRCS)
88-
""",
95+
""".format(known_good_arg = known_good_arg),
8996
tools = ["@score_docs_as_code//scripts_bazel:merge_sourcelinks"],
9097
)
9198

@@ -120,7 +127,7 @@ def _missing_requirements(deps):
120127
fail(msg)
121128
fail("This case should be unreachable?!")
122129

123-
def docs(source_dir = "docs", data = [], deps = [], scan_code = []):
130+
def docs(source_dir = "docs", data = [], deps = [], scan_code = [], known_good = None):
124131
"""Creates all targets related to documentation.
125132
126133
By using this function, you'll get any and all updates for documentation targets in one place.
@@ -175,34 +182,45 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = []):
175182

176183
data_with_docs_sources = _rewrite_needs_json_to_docs_sources(data)
177184
additional_combo_sourcelinks = _rewrite_needs_json_to_sourcelinks(data)
178-
_merge_sourcelinks(name = "merged_sourcelinks", sourcelinks = [":sourcelinks_json"] + additional_combo_sourcelinks)
185+
_merge_sourcelinks(name = "merged_sourcelinks", sourcelinks = [":sourcelinks_json"] + additional_combo_sourcelinks, known_good = known_good)
186+
docs_data = data + [":sourcelinks_json"]
187+
combo_data = data_with_docs_sources + [":merged_sourcelinks"]
188+
189+
docs_env = {
190+
"SOURCE_DIRECTORY": source_dir,
191+
"DATA": str(data),
192+
"SCORE_SOURCELINKS": "$(location :sourcelinks_json)",
193+
}
194+
docs_sources_env = {
195+
"SOURCE_DIRECTORY": source_dir,
196+
"DATA": str(data_with_docs_sources),
197+
"SCORE_SOURCELINKS": "$(location :merged_sourcelinks)",
198+
}
199+
if known_good:
200+
docs_env["KNOWN_GOOD_JSON"] = "$(location "+ known_good + ")"
201+
docs_sources_env["KNOWN_GOOD_JSON"] = "$(location "+ known_good + ")"
202+
docs_data.append(known_good)
203+
combo_data.append(known_good)
204+
205+
docs_env["ACTION"] = "incremental"
179206

180207
py_binary(
181208
name = "docs",
182209
tags = ["cli_help=Build documentation:\nbazel run //:docs"],
183210
srcs = ["@score_docs_as_code//src:incremental.py"],
184-
data = data + [":sourcelinks_json"],
211+
data = docs_data,
185212
deps = deps,
186-
env = {
187-
"SOURCE_DIRECTORY": source_dir,
188-
"DATA": str(data),
189-
"ACTION": "incremental",
190-
"SCORE_SOURCELINKS": "$(location :sourcelinks_json)",
191-
},
213+
env = docs_env
192214
)
193215

216+
docs_sources_env["ACTION"] = "incremental"
194217
py_binary(
195218
name = "docs_combo",
196219
tags = ["cli_help=Build full documentation with all dependencies:\nbazel run //:docs_combo"],
197220
srcs = ["@score_docs_as_code//src:incremental.py"],
198-
data = data_with_docs_sources + [":merged_sourcelinks"],
221+
data = combo_data,
199222
deps = deps,
200-
env = {
201-
"SOURCE_DIRECTORY": source_dir,
202-
"DATA": str(data_with_docs_sources),
203-
"ACTION": "incremental",
204-
"SCORE_SOURCELINKS": "$(location :merged_sourcelinks)",
205-
},
223+
env = docs_sources_env
206224
)
207225

208226
native.alias(
@@ -211,59 +229,44 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = []):
211229
deprecation = "Target '//:docs_combo_experimental' is deprecated. Use '//:docs_combo' instead.",
212230
)
213231

232+
docs_env["ACTION"] = "linkcheck"
214233
py_binary(
215234
name = "docs_link_check",
216235
tags = ["cli_help=Verify Links inside Documentation:\nbazel run //:link_check\n (Note: this could take a long time)"],
217236
srcs = ["@score_docs_as_code//src:incremental.py"],
218-
data = data,
237+
data = docs_data,
219238
deps = deps,
220-
env = {
221-
"SOURCE_DIRECTORY": source_dir,
222-
"DATA": str(data),
223-
"ACTION": "linkcheck",
224-
},
239+
env = docs_env
225240
)
226241

242+
docs_env["ACTION"] = "check"
227243
py_binary(
228244
name = "docs_check",
229245
tags = ["cli_help=Verify documentation:\nbazel run //:docs_check"],
230246
srcs = ["@score_docs_as_code//src:incremental.py"],
231-
data = data + [":sourcelinks_json"],
247+
data = docs_data,
232248
deps = deps,
233-
env = {
234-
"SOURCE_DIRECTORY": source_dir,
235-
"DATA": str(data),
236-
"ACTION": "check",
237-
"SCORE_SOURCELINKS": "$(location :sourcelinks_json)",
238-
},
249+
env = docs_env
239250
)
240251

252+
docs_env["ACTION"] = "live_preview"
241253
py_binary(
242254
name = "live_preview",
243255
tags = ["cli_help=Live preview documentation in the browser:\nbazel run //:live_preview"],
244256
srcs = ["@score_docs_as_code//src:incremental.py"],
245-
data = data + [":sourcelinks_json"],
257+
data = docs_data,
246258
deps = deps,
247-
env = {
248-
"SOURCE_DIRECTORY": source_dir,
249-
"DATA": str(data),
250-
"ACTION": "live_preview",
251-
"SCORE_SOURCELINKS": "$(location :sourcelinks_json)",
252-
},
259+
env = docs_env
253260
)
254261

262+
docs_sources_env["ACTION"] = "live_preview"
255263
py_binary(
256264
name = "live_preview_combo_experimental",
257265
tags = ["cli_help=Live preview full documentation with all dependencies in the browser:\nbazel run //:live_preview_combo_experimental"],
258266
srcs = ["@score_docs_as_code//src:incremental.py"],
259-
data = data_with_docs_sources + [":merged_sourcelinks"],
267+
data = combo_data,
260268
deps = deps,
261-
env = {
262-
"SOURCE_DIRECTORY": source_dir,
263-
"DATA": str(data_with_docs_sources),
264-
"ACTION": "live_preview",
265-
"SCORE_SOURCELINKS": "$(location :merged_sourcelinks)",
266-
},
269+
env = docs_sources_env
267270
)
268271

269272
score_virtualenv(

scripts_bazel/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ py_binary(
3333
py_binary(
3434
name = "merge_sourcelinks",
3535
srcs = ["merge_sourcelinks.py"],
36+
deps= [ "//src/extensions/score_source_code_linker"],
3637
main = "merge_sourcelinks.py",
3738
visibility = ["//visibility:public"],
3839
)

scripts_bazel/generate_sourcelinks_cli.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,43 @@
2525
from src.extensions.score_source_code_linker.generate_source_code_links_json import (
2626
_extract_references_from_file, # pyright: ignore[reportPrivateUsage] TODO: move it out of the extension and into this script
2727
)
28+
from src.extensions.score_source_code_linker.helpers import parse_repo_name_from_path
2829
from src.extensions.score_source_code_linker.needlinks import (
29-
store_source_code_links_json,
30+
DefaultMetaData,
31+
store_source_code_links_with_metadata_json,
3032
)
3133

3234
logging.basicConfig(level=logging.INFO, format="%(message)s")
3335
logger = logging.getLogger(__name__)
3436

3537

38+
def clean_external_prefix(path: Path) -> Path:
39+
"""
40+
As it can be in combo builds that the path is:
41+
`external/score_docs_as_code+/....` or similar
42+
We have to remove this prefix from the file
43+
before we pass it to the extract function. Otherwise we have
44+
this prefix inside the `file` attribute leading to wrong links
45+
"""
46+
if not str(path).startswith("external/"):
47+
return path
48+
# This allows for files / folders etc. to have `external` in their name too.
49+
path_raw = str(path).removeprefix("external/")
50+
filepath_split = str(path_raw).split("/", maxsplit=1)
51+
return Path("".join(filepath_split[1:]))
52+
53+
3654
def main():
3755
parser = argparse.ArgumentParser(
3856
description="Generate source code links JSON from source files"
3957
)
40-
parser.add_argument(
58+
_ = parser.add_argument(
4159
"--output",
4260
required=True,
4361
type=Path,
4462
help="Output JSON file path",
4563
)
46-
parser.add_argument(
64+
_ = parser.add_argument(
4765
"files",
4866
nargs="*",
4967
type=Path,
@@ -53,15 +71,23 @@ def main():
5371
args = parser.parse_args()
5472

5573
all_need_references = []
74+
75+
metadata = DefaultMetaData()
76+
metadata_set = False
5677
for file_path in args.files:
78+
if "known_good.json" not in str(file_path) and not metadata_set:
79+
metadata["repo_name"] = parse_repo_name_from_path(file_path)
80+
metadata_set = True
5781
abs_file_path = file_path.resolve()
5882
assert abs_file_path.exists(), abs_file_path
83+
clean_path = clean_external_prefix(file_path)
5984
references = _extract_references_from_file(
60-
abs_file_path.parent, Path(abs_file_path.name)
85+
abs_file_path.parent, Path(abs_file_path.name), clean_path
6186
)
6287
all_need_references.extend(references)
63-
64-
store_source_code_links_json(args.output, all_need_references)
88+
store_source_code_links_with_metadata_json(
89+
file=args.output, metadata=metadata, needlist=all_need_references
90+
)
6591
logger.info(
6692
f"Found {len(all_need_references)} need references in {len(args.files)} files"
6793
)

scripts_bazel/merge_sourcelinks.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import sys
2222
from pathlib import Path
2323

24+
from src.extensions.score_source_code_linker.helpers import parse_info_from_known_good
25+
2426
logging.basicConfig(level=logging.INFO, format="%(message)s")
2527
logger = logging.getLogger(__name__)
2628

@@ -29,28 +31,57 @@ def main():
2931
parser = argparse.ArgumentParser(
3032
description="Merge multiple sourcelinks JSON files into one"
3133
)
32-
parser.add_argument(
34+
_ = parser.add_argument(
3335
"--output",
3436
required=True,
3537
type=Path,
3638
help="Output merged JSON file path",
3739
)
38-
parser.add_argument(
40+
_ = parser.add_argument(
41+
"--known_good",
42+
required=True,
43+
help="Path to a required 'known good' JSON file (provided by Bazel).",
44+
)
45+
_ = parser.add_argument(
3946
"files",
4047
nargs="*",
4148
type=Path,
4249
help="Input JSON files to merge",
4350
)
4451

4552
args = parser.parse_args()
53+
all_files = [x for x in args.files if "known_good.json" not in str(x)]
4654

4755
merged = []
48-
for json_file in args.files:
56+
for json_file in all_files:
4957
with open(json_file) as f:
5058
data = json.load(f)
51-
assert isinstance(data, list), repr(data)
52-
merged.extend(data)
59+
# If the file is empty e.g. '[]' there is nothing to parse, we continue
60+
if not data:
61+
continue
62+
metadata = data[0]
63+
if not isinstance(metadata, dict) or "repo_name" not in metadata:
64+
logger.warning(
65+
f"Unexpected schema in sourcelinks file '{json_file}': "
66+
"expected first element to be a metadata dict "
67+
"with a 'repo_name' key. "
68+
)
69+
# As we can't deal with bad JSON structure we just skip it
70+
continue
71+
if metadata["repo_name"] and metadata["repo_name"] != "local_repo":
72+
hash, repo = parse_info_from_known_good(
73+
known_good_json=args.known_good, repo_name=metadata["repo_name"]
74+
)
75+
metadata["hash"] = hash
76+
metadata["url"] = repo
77+
# In the case that 'metadata[repo_name]' is 'local_module'
78+
# hash & url are already existing and empty inside of 'metadata'
79+
# Therefore all 3 keys will be written to needlinks in each branch
5380

81+
for d in data[1:]:
82+
d.update(metadata)
83+
assert isinstance(data, list), repr(data)
84+
merged.extend(data[1:])
5485
with open(args.output, "w") as f:
5586
json.dump(merged, f, indent=2, ensure_ascii=False)
5687

0 commit comments

Comments
 (0)