Skip to content

Commit b67fe90

Browse files
committed
Remove add-workload and add-dt-project CLI
Both commands are superseded by `pia sync`, which covers everything they did and fixes what they could not: they only ever appended rows, so an authorization could be granted but never revoked or corrected, re-running one duplicated the entry, and the database was the only record of who was authorized — nothing to review, diff, or roll back. `sync` reconciles the whole state against a curated file that can be code-reviewed like any other change. Keeping them alongside `sync` would mean two sources of truth: a row added by `add-workload` is absent from the curated file, so the next `sync` would plan to delete it again. DependencyTrack projects were never created by `add-dt-project` either (it only resolved an existing one and stored its UUID), so nothing is lost there — `pia create-dt-projects` provisions them from the same file. Assisted-by: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Lukas Puehringer <lukas.puehringer@eclipse-foundation.org>
1 parent 7a49da4 commit b67fe90

3 files changed

Lines changed: 2 additions & 539 deletions

File tree

docs/DESIGN.md

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -412,36 +412,6 @@ idempotent: existing projects are resolved, not recreated. Requires `--dt-url` a
412412
than `sync`'s VIEW_PORTFOLIO, since it creates projects); it needs no
413413
`PIA_DATABASE_URL`.
414414

415-
#### `pia add-workload <ef_project_id> <url>`
416-
417-
Registers a new workload for an Eclipse Foundation project. The workload type
418-
is determined by the URL:
419-
420-
- **GitHub** (URL contains `github.com`): Parses `owner` and `repo` from the
421-
URL path. Queries the GitHub API (`GET https://api.github.com/users/{owner}`)
422-
to fetch the numeric `repo_owner_id`. Creates a `GitHubWorkload`.
423-
- **Jenkins** (URL starts with `https://ci.eclipse.org`): Uses the URL as
424-
`issuer`. Creates a `JenkinsWorkload`.
425-
426-
If the `EclipseFoundationProject` row for `ef_project_id` does not exist, it is
427-
created automatically.
428-
429-
#### `pia add-dt-project <ef_project_id> <dt_url> <parent_name> <project_name>`
430-
431-
Registers a DependencyTrack project for an Eclipse Foundation project. Looks up
432-
the project in DependencyTrack by name hierarchy:
433-
434-
1. Find exactly one root project by `parent_name`.
435-
2. Find exactly one child project by `project_name`
436-
3. Store `DependencyTrackProject(name=project_name, parent_uuid=<child project UUID>)`
437-
438-
The `dt_url` argument is the DependencyTrack base URL (e.g.
439-
`https://sbom.eclipse.org`). Authentication uses the `PIA_DEPENDENCY_TRACK_API_KEY`
440-
environment variable. The API key requires at least the `VIEW_PORTFOLIO` permission.
441-
442-
If the `EclipseFoundationProject` row for `ef_project_id` does not exist, it is
443-
created automatically.
444-
445415
## 6. Security Considerations
446416

447417
### 6.1 Token Validation

pia/cli.py

Lines changed: 2 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,25 @@
44
-----------
55
- sync: Reconcile all project authorizations from a curated file into the
66
database (create/update/delete).
7-
87
- create-dt-projects: Create the DependencyTrack projects referenced by a curated
98
file (provisioning only; no database access).
109
11-
- add-workload: Register a CI/CD workload that is allowed to upload SBOMs for an
12-
Eclipse Foundation project.
13-
14-
- add-dt-project: Register a DependencyTrack project as the upload target for a
15-
given Eclipse Foundation project.
16-
17-
Usage Examples
18-
--------------
19-
Reconcile All Authorizations From a Curated File:
20-
10+
Usage Example
11+
-------------
2112
PIA_DATABASE_URL=postgresql://user:secret@localhost:5432/pia \
2213
PIA_DEPENDENCY_TRACK_API_KEY=<API key with VIEW_PORTFOLIO permission> \
2314
PIA_GITHUB_TOKEN=<optional token with public read permission for rate limit> \
2415
uv run pia sync projects.yaml --dt-url https://sbom.eclipse.org --dry-run
2516
26-
Register GitHub Actions:
27-
28-
PIA_DATABASE_URL=postgresql://user:secret@localhost:5432/pia \
29-
uv run pia add-workload eclipse-foo \
30-
https://github.com/eclipse-foo/repo
31-
32-
Register Jenkins Instance:
33-
34-
PIA_DATABASE_URL=postgresql://user:secret@localhost:5432/pia \
35-
uv run pia add-workload eclipse-bar \
36-
https://ci.eclipse.org/eclipse-bar/oidc
37-
38-
Register DependencyTrack Project:
39-
40-
PIA_DATABASE_URL=postgresql://user:secret@localhost:5432/pia \
41-
PIA_DEPENDENCY_TRACK_API_KEY=<API key with VIEW_PORTFOLIO permission> \
42-
uv run pia add-dt-project eclipse-baz \
43-
https://sbom.eclipse.org "Eclipse Baz" baz-server
44-
4517
"""
4618

4719
import logging
4820
import os
49-
from typing import Any
50-
from urllib.parse import urlparse
5121

5222
import click
53-
import requests
5423
from sqlalchemy import create_engine
5524
from sqlalchemy.orm import Session, sessionmaker
5625

57-
from .models import (
58-
GITHUB_BASE_URL,
59-
JENKINS_ISSUER_BASE_URL,
60-
DependencyTrackProject,
61-
EclipseFoundationProject,
62-
GitHubWorkload,
63-
JenkinsWorkload,
64-
Workload,
65-
)
6626
from .sync import (
6727
apply_plan,
6828
build_desired,
@@ -73,8 +33,6 @@
7333
validate_projects_file,
7434
)
7535

76-
logger = logging.getLogger(__name__)
77-
7836

7937
@click.group()
8038
@click.option("-v", "--verbose", is_flag=True, help="Enable debug logging.")
@@ -91,173 +49,6 @@ def _make_session() -> Session:
9149
return sessionmaker(bind=engine)()
9250

9351

94-
def _get_dt_api_key() -> str:
95-
key = os.environ.get("PIA_DEPENDENCY_TRACK_API_KEY")
96-
if not key:
97-
raise click.ClickException("PIA_DEPENDENCY_TRACK_API_KEY is not set")
98-
return key
99-
100-
101-
def _create_ef_project_if_needed(session: Session, ef_project_id: str) -> None:
102-
if session.get(EclipseFoundationProject, ef_project_id) is None:
103-
logger.info(f"Creating EclipseFoundationProject {ef_project_id!r}")
104-
session.add(EclipseFoundationProject(id=ef_project_id))
105-
else:
106-
logger.info(f"Using existing EclipseFoundationProject {ef_project_id!r}")
107-
108-
109-
def _fetch_github_owner_id(owner: str) -> str:
110-
url = f"https://api.github.com/users/{owner}"
111-
logger.info(f"Fetching GitHub owner id from {url}")
112-
response = requests.get(url, headers={"Accept": "application/vnd.github+json"})
113-
response.raise_for_status()
114-
owner_id = str(response.json()["id"])
115-
logger.info(f"GitHub owner {owner!r} has id {owner_id}")
116-
return owner_id
117-
118-
119-
def _dt_find_root_project_by_name(
120-
dt_url: str, name: str, api_key: str
121-
) -> dict[str, Any]:
122-
"""Look up a root DependencyTrack project by name, asserting exactly one match."""
123-
url = f"{dt_url.rstrip('/')}/api/v1/project"
124-
logger.info(f"Querying DependencyTrack root projects at {url} for name={name!r}")
125-
response = requests.get(
126-
url,
127-
params={"name": name, "onlyRoot": "true"},
128-
headers={"X-Api-Key": api_key, "Accept": "application/json"},
129-
)
130-
response.raise_for_status()
131-
projects = response.json()
132-
if len(projects) != 1:
133-
raise click.ClickException(
134-
f"Expected exactly one root DependencyTrack project named {name!r}, "
135-
f"found {len(projects)}"
136-
)
137-
return projects[0]
138-
139-
140-
@cli.command("add-workload")
141-
@click.argument("ef_project_id")
142-
@click.argument("url")
143-
@click.option(
144-
"--dry-run",
145-
is_flag=True,
146-
help="Look up data and prepare the row, but do not commit.",
147-
)
148-
def add_workload(ef_project_id: str, url: str, dry_run: bool) -> None:
149-
"""Register a GitHub or Jenkins workload for an Eclipse Foundation project.
150-
151-
URL type is determined by its value: github.com URLs create a GitHubWorkload,
152-
ci.eclipse.org URLs create a JenkinsWorkload with the URL as issuer. Both are
153-
matched on the URL's scheme and host. Any other URL is rejected.
154-
"""
155-
# Fail early, before the GitHub lookup, rather than after it.
156-
if not os.environ.get("PIA_DATABASE_URL"):
157-
raise click.ClickException("PIA_DATABASE_URL is not set")
158-
159-
parsed = urlparse(url)
160-
base_url = f"{parsed.scheme}://{parsed.hostname}"
161-
if base_url == GITHUB_BASE_URL:
162-
path_parts = parsed.path.strip("/").split("/")
163-
if len(path_parts) != 2 or not all(path_parts):
164-
raise click.ClickException(f"GitHub URL must include owner/repo: {url}")
165-
owner, repo = path_parts[0], path_parts[1]
166-
owner_id = _fetch_github_owner_id(owner)
167-
workload: Workload = GitHubWorkload(
168-
ef_project_id=ef_project_id,
169-
repo_owner=owner,
170-
repo_name=repo,
171-
repo_owner_id=owner_id,
172-
)
173-
logger.info(
174-
f"Prepared GitHubWorkload(ef_project_id={ef_project_id!r}, "
175-
f"repo_owner={owner!r}, repo_name={repo!r}, repo_owner_id={owner_id})"
176-
)
177-
elif base_url == JENKINS_ISSUER_BASE_URL:
178-
workload = JenkinsWorkload(ef_project_id=ef_project_id, issuer=url)
179-
logger.info(
180-
f"Prepared JenkinsWorkload(ef_project_id={ef_project_id!r}, issuer={url!r})"
181-
)
182-
else:
183-
raise click.ClickException(
184-
f"URL must be a {GITHUB_BASE_URL} repo URL or a "
185-
f"{JENKINS_ISSUER_BASE_URL} issuer URL: {url}"
186-
)
187-
188-
with _make_session() as session:
189-
_create_ef_project_if_needed(session, ef_project_id)
190-
session.add(workload)
191-
if dry_run:
192-
logger.info("Dry-run: rolling back transaction")
193-
session.rollback()
194-
else:
195-
session.commit()
196-
logger.info("Committed workload")
197-
198-
199-
@cli.command("add-dt-project")
200-
@click.argument("ef_project_id")
201-
@click.argument("dt_url")
202-
@click.argument("parent_name")
203-
@click.argument("project_name")
204-
@click.option(
205-
"--dry-run",
206-
is_flag=True,
207-
help="Look up data and prepare the row, but do not commit.",
208-
)
209-
def add_dt_project(
210-
ef_project_id: str,
211-
dt_url: str,
212-
parent_name: str,
213-
project_name: str,
214-
dry_run: bool,
215-
) -> None:
216-
"""Register a DependencyTrack project for an Eclipse Foundation project.
217-
218-
Fetches the root project matching PARENT_NAME (asserting exactly one), then
219-
finds PROJECT_NAME among its children (asserting exactly one), and stores
220-
that child's UUID.
221-
"""
222-
# Fail early, before the DependencyTrack lookups, rather than after them.
223-
if not os.environ.get("PIA_DATABASE_URL"):
224-
raise click.ClickException("PIA_DATABASE_URL is not set")
225-
226-
api_key = _get_dt_api_key()
227-
228-
parent = _dt_find_root_project_by_name(dt_url, parent_name, api_key)
229-
logger.info(f"Resolved parent {parent_name!r} -> uuid={parent['uuid']}")
230-
231-
children = [c for c in parent.get("children", []) if c.get("name") == project_name]
232-
if len(children) != 1:
233-
raise click.ClickException(
234-
f"Expected exactly one child named {project_name!r} under {parent_name!r}, "
235-
f"found {len(children)}"
236-
)
237-
child_uuid = children[0]["uuid"]
238-
logger.info(f"Resolved child {project_name!r} -> uuid={child_uuid}")
239-
240-
dt_project = DependencyTrackProject(
241-
ef_project_id=ef_project_id,
242-
name=project_name,
243-
parent_uuid=child_uuid,
244-
)
245-
logger.info(
246-
f"Prepared DependencyTrackProject(ef_project_id={ef_project_id!r}, "
247-
f"name={project_name!r}, parent_uuid={child_uuid})"
248-
)
249-
250-
with _make_session() as session:
251-
_create_ef_project_if_needed(session, ef_project_id)
252-
session.add(dt_project)
253-
if dry_run:
254-
logger.info("Dry-run: rolling back transaction")
255-
session.rollback()
256-
else:
257-
session.commit()
258-
logger.info("Committed DependencyTrack project")
259-
260-
26152
@cli.command("sync")
26253
@click.argument("file", type=click.Path(exists=True, dir_okay=False))
26354
@click.option(

0 commit comments

Comments
 (0)