Skip to content

Commit 8a9a38f

Browse files
authored
Generate Workspace using Gita (#47)
* Generate Workspace using Gita This change adds the capability to generate a workspace directly from the known_good.json file. To achieve this, the known_good.json file is transformed into a configuration file for a git workspace management tool called gita, which is then used to check out all mentioned modules on the specific commit/branch that is given in known_good.json. In addition, the score_modules.MODULE.bazel is updated with local_path_overrides to the checked out modules. Hence, a bazel build will immediately work as expected. # Conflicts: # .gitignore # Conflicts: # .devcontainer/devcontainer.json * also generate .gitmodules metadata * Remove git submodules generation This does not work, since submodules require also commit tracking in the top-level repository. This would by default pollute reference_integration, which is certainly not what we want. * do not create a workspace by default * Add VSCode Tasks and documentation * Fix deprecation warning * mark as executable * clarify the devcontainer relation and which build targets should be working
1 parent 03eb3f1 commit 8a9a38f

File tree

9 files changed

+203
-5
lines changed

9 files changed

+203
-5
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "eclipse-s-core",
33
"image": "ghcr.io/eclipse-score/devcontainer:v1.1.0",
4+
"postCreateCommand": "bash .devcontainer/prepare_workspace.sh",
45
"postStartCommand": "ssh-keygen -f '/home/vscode/.ssh/known_hosts' -R '[localhost]:2222' || true"
56
}

.devcontainer/prepare_workspace.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# Install pipx
5+
sudo apt update
6+
sudo apt install -y pipx
7+
8+
# Install gita
9+
pipx install gita
10+
11+
# Enable bash autocompletion for gita
12+
echo "eval \"\$(register-python-argcomplete gita -s bash)\"" >> ~/.bashrc
13+
14+
# Set GITA_PROJECT_HOME environment variable
15+
echo "export GITA_PROJECT_HOME=$(pwd)/.gita" >> ~/.bashrc
16+
GITA_PROJECT_HOME=$(pwd)/.gita
17+
mkdir -p "$GITA_PROJECT_HOME"
18+
export GITA_PROJECT_HOME
19+
20+
# Generate workspace metadata files from known_good.json:
21+
# - .gita-workspace.csv
22+
python3 tools/known_good_to_workspace_metadata.py --known-good known_good.json --gita-workspace .gita-workspace.csv

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ __pycache__/
2222
/_build
2323
/docs/ubproject.toml
2424
/docs/_collections
25+
26+
# Workspace files
27+
/score_*/
28+
/.gita/
29+
/.gita-workspace.csv

.vscode/tasks.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "Update workspace metadata from known good",
6+
"type": "shell",
7+
"command": "python3",
8+
"args": [
9+
"tools/known_good_to_workspace_metadata.py"
10+
],
11+
"problemMatcher": []
12+
},
13+
{
14+
"label": "Switch Bazel modules to local_path_overrides",
15+
"type": "shell",
16+
"command": "python3",
17+
"args": [
18+
"tools/update_module_from_known_good.py",
19+
"--override-type",
20+
"local_path"
21+
],
22+
"problemMatcher": []
23+
},
24+
{
25+
"label": "Switch Bazel modules to git_overrides",
26+
"type": "shell",
27+
"command": "python3",
28+
"args": [
29+
"tools/update_module_from_known_good.py",
30+
"--override-type",
31+
"git"
32+
]
33+
},
34+
{
35+
"label": "Gita: Generate workspace",
36+
"type": "shell",
37+
"command": "gita",
38+
"args": [
39+
"clone",
40+
"--preserve-path",
41+
"--from-file",
42+
".gita-workspace.csv"
43+
],
44+
"problemMatcher": []
45+
}
46+
]
47+
}

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,47 @@ Execute `bazel query //feature_showcase/...` to obtain list of targets that You
5757
bazel build --config bl-x86_64-linux @score_orchestrator//src/... --verbose_failures
5858
```
5959

60+
## Workspace support
61+
62+
You can obtain a complete S-CORE workspace, i.e. a git checkout of all modules from `known_good.json`, on the specific branches / commits, integrated into one Bazel build.
63+
This helps with cross-module development, debugging, and generally "trying out things".
64+
65+
> [!NOTE]
66+
> The startup of the [S-CORE devcontainer](https://github.com/eclipse-score/devcontainer) [integrated in this repository](.devcontainer/) already installs supported workspace managers and generates the required metadata.
67+
> You can do this manually as well, of course (e.g. if you do not use the devcontainer).
68+
> Take a look at `.devcontainer/prepare_workspace.sh`, which contains the setup script.
69+
70+
> [!NOTE]
71+
> Not all Bazel targets are supported yet.
72+
> Running `./scripts/integration_test.sh` will work, though.
73+
> Take a look at the [Known Issues](#known-issues-️) below to see which Bazel targets are available and working.
74+
75+
The supported workspace managers are:
76+
77+
| Name | Description |
78+
|------|-------------|
79+
| [Gita](https://github.com/nosarthur/gita) | "a command-line tool to manage multiple git repos" |
80+
81+
A description of how to use these workspace managers, together with their advantages and drawbacks, is beyond the scope of this document.
82+
In case of doubt, choose the first.
83+
84+
### Initialization of the workspace
85+
86+
> [!WARNING]
87+
> This will change the file `score_modules.MODULE.bazel`.
88+
> Do **not** commit these changes!
89+
90+
1. Switch to local path overrides, using the VSCode Task (`Terminal`->`Run Task...`) "Switch Bazel modules to `local_path_overrides`".
91+
Note that you can switch back to `git_overrides` (the default) using the task "Switch Bazel modules to `git_overrides`"
92+
93+
2. Run VSCode Task "<Name>: Generate workspace", e.g. "Gita: Generate workspace".
94+
This will clone all modules using the chosen workspace manager.
95+
The modules will be in sub-directories starting with `score_`.
96+
Note that the usage of different workspace managers is mutually exclusive.
97+
98+
When you now run Bazel, it will use the local working copies of all modules and not download them from git remotes.
99+
You can make local changes to each module, which will be directly reflected in the next Bazel run.
100+
60101
## Known Issues ⚠️
61102

62103
### Orchestrator

tools/get_module_info.py

100644100755
File mode changed.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import json
4+
import csv
5+
6+
MODULES_CSV_HEADER = [
7+
"repo_url",
8+
"name",
9+
"workspace_path",
10+
"version",
11+
"hash",
12+
"branch"
13+
]
14+
15+
def main():
16+
parser = argparse.ArgumentParser(description="Convert known_good.json to workspace metadata files for gita and git submodules.")
17+
18+
parser.add_argument("--known-good", dest="known_good", default="known_good.json", help="Path to known_good.json")
19+
parser.add_argument("--gita-workspace", dest="gita_workspace", default=".gita-workspace.csv", help="File to output gita workspace metadata")
20+
args = parser.parse_args()
21+
22+
with open(args.known_good, "r") as f:
23+
data = json.load(f)
24+
25+
modules = data.get("modules", {})
26+
27+
gita_metadata = []
28+
for name, info in modules.items():
29+
repo_url = info.get("repo", "")
30+
if not repo_url:
31+
raise RuntimeError("repo must not be empty")
32+
33+
# default branch: main
34+
branch = info.get("branch", "main")
35+
36+
# if no hash is given, use branch
37+
hash_ = info.get("hash", branch)
38+
39+
# workspace_path is not available in known_good.json, default to name of repository
40+
workspace_path = name
41+
42+
# gita format: {url},{name},{path},{prop['type']},{repo_flags},{branch}
43+
row = [repo_url, name, workspace_path, "", "", hash_]
44+
gita_metadata.append(row)
45+
46+
with open(args.gita_workspace, "w", newline="") as f:
47+
writer = csv.writer(f)
48+
for row in gita_metadata:
49+
writer.writerow(row)
50+
51+
if __name__ == "__main__":
52+
main()

tools/update_module_from_known_good.py

100644100755
Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,24 @@ def generate_git_override_blocks(modules_dict: Dict[str, Any], repo_commit_dict:
9090

9191
return blocks
9292

93+
def generate_local_override_blocks(modules_dict: Dict[str, Any]) -> List[str]:
94+
"""Generate bazel_dep and local_path_override blocks for each module."""
95+
blocks = []
96+
97+
for name, module in modules_dict.items():
98+
block = (
99+
f'bazel_dep(name = "{name}")\n'
100+
'local_path_override(\n'
101+
f' module_name = "{name}",\n'
102+
f' path = "{name}",\n'
103+
')\n'
104+
)
105+
106+
blocks.append(block)
107+
108+
return blocks
93109

94-
def generate_file_content(modules: Dict[str, Any], repo_commit_dict: Dict[str, str], timestamp: Optional[str] = None) -> str:
110+
def generate_file_content(args: argparse.Namespace, modules: Dict[str, Any], repo_commit_dict: Dict[str, str], timestamp: Optional[str] = None) -> str:
95111
"""Generate the complete content for score_modules.MODULE.bazel."""
96112
# License header assembled with parenthesis grouping (no indentation preserved in output).
97113
header = (
@@ -117,7 +133,15 @@ def generate_file_content(modules: Dict[str, Any], repo_commit_dict: Dict[str, s
117133
"\n"
118134
)
119135

120-
blocks = generate_git_override_blocks(modules, repo_commit_dict)
136+
if args.override_type == "git":
137+
blocks = generate_git_override_blocks(modules, repo_commit_dict)
138+
else:
139+
header += (
140+
"# Note: This file uses local_path overrides. Ensure that local paths are set up correctly.\n"
141+
"\n"
142+
)
143+
blocks = generate_local_override_blocks(modules)
144+
121145

122146
if not blocks:
123147
raise SystemExit("No valid modules to generate git_override blocks")
@@ -149,6 +173,12 @@ def main() -> None:
149173
action="append",
150174
help="Override commit for a specific repo (format: <REPO_URL>@<COMMIT_SHA>)"
151175
)
176+
parser.add_argument(
177+
"--override-type",
178+
choices=["local_path", "git"],
179+
default="git",
180+
help="Type of override to use (default: git)"
181+
)
152182

153183
args = parser.parse_args()
154184

@@ -180,7 +210,7 @@ def main() -> None:
180210

181211
# Generate file content
182212
timestamp = data.get("timestamp") or datetime.now().isoformat()
183-
content = generate_file_content(modules, repo_commit_dict, timestamp)
213+
content = generate_file_content(args, modules, repo_commit_dict, timestamp)
184214

185215
if args.dry_run:
186216
print(f"Dry run: would write to {output_path}\n")
@@ -191,7 +221,7 @@ def main() -> None:
191221
else:
192222
with open(output_path, "w", encoding="utf-8") as f:
193223
f.write(content)
194-
print(f"Generated {output_path} with {len(modules)} git_override entries")
224+
print(f"Generated {output_path} with {len(modules)} {args.override_type}_override entries")
195225

196226

197227
if __name__ == "__main__":

tools/update_module_latest.py

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def load_known_good(path: str) -> dict:
119119

120120
def write_known_good(path: str, original: dict, modules: list[Module]) -> None:
121121
out = dict(original) # shallow copy
122-
out["timestamp"] = dt.datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
122+
out["timestamp"] = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat() + "Z"
123123
out["modules"] = {}
124124
for m in modules:
125125
mod_dict = {"repo": m.repo, "hash": m.hash}

0 commit comments

Comments
 (0)