Add the Design Class Diagrams and plan MIL-005 (credentials asked, project .env) #40
@@ -21,6 +21,7 @@ document of a type. `Primary File` may contain a glob (e.g.
|
||||
| OC | Operation Contract | docs/uc-*/oc.md | 002 |
|
||||
| SD | Sequence Diagram | docs/uc-*/sd.md | 002 |
|
||||
| DM | Domain Model | docs/domain-model.md | 003 |
|
||||
| DCD | Design Class Diagram | docs/dcd.md | 003 |
|
||||
| DICT | Domain Dictionary (PO and IT terms) | docs/dictionary.md | 002 |
|
||||
| UCD | Use Case Diagram | docs/use-case-diagram.md | 002 |
|
||||
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 020 |
|
||||
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
# Design Class Diagram
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | DCD-002 |
|
||||
| CrossReference | [DCD-001], [DM-002], [UC-001], [OC-001], [SD-001], [DICT-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S02 | Initial version, from DCD-001 (UC-001) | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose and Scope
|
||||
|
||||
The consolidated design model of the project. Use-case models are scoped views; when one changes, this model is checked and updated in the same change. It currently covers [UC-001] "Create a new project" ([DCD-001]), which it was created from, and refines the concepts of [DM-002]. Class and attribute names are the IT terms of [DICT-001]. Every method traces to a contract in [OC-001] or a message in [SD-001].
|
||||
|
||||
The classes are design classes of a Bash program: a class is a group of functions in `src/lib/` with its data held in the shared state arrays (see "Implementation Mapping").
|
||||
|
||||
## Diagram
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
skinparam classAttributeIconSize 0
|
||||
hide empty members
|
||||
|
||||
enum Visibility {
|
||||
private
|
||||
public
|
||||
}
|
||||
|
||||
class ProjectCreator <<controller>> {
|
||||
+startProjectCreation() : PromptSet
|
||||
+provideProjectDetails(name : String, description : String, visibility : Visibility, giteaOwner : Owner, githubOwner : Owner [0..1], directory : Path, enablePlanGate : Boolean) : Summary
|
||||
}
|
||||
class ConfigLoader {
|
||||
+load(configFile : Path, envFile : Path) : Configuration
|
||||
}
|
||||
class ToolChecker {
|
||||
+check(tools : String [1..*]) : ToolCheck
|
||||
}
|
||||
class Preflight {
|
||||
+check(request : ProjectRequest) : PreflightResult
|
||||
}
|
||||
abstract class GitHost <<facade>> {
|
||||
-name : String
|
||||
-webAddress : String
|
||||
-apiAddress : String
|
||||
+verifyToken() : Boolean
|
||||
+ownerAccepts(owner : Owner) : Boolean
|
||||
+nameFree(name : String) : Boolean
|
||||
}
|
||||
class GiteaClient <<facade>> {
|
||||
+GiteaClient(configuration : Configuration)
|
||||
+hasLicense(key : String) : Boolean
|
||||
+createRepository(request : ProjectRequest, license : String [0..1]) : GiteaRepository
|
||||
+addPushMirror(source : GiteaRepository, target : GitHubRepository) : PushMirror
|
||||
-requestSync(mirror : PushMirror) : void
|
||||
}
|
||||
class GitHubClient <<facade>> {
|
||||
+GitHubClient(configuration : Configuration)
|
||||
+createEmptyRepository(request : ProjectRequest) : GitHubRepository
|
||||
}
|
||||
class LocalProjectBuilder {
|
||||
+build(directory : Path, source : GiteaRepository, target : GitHubRepository [0..1], sshPassed : Boolean) : LocalProject
|
||||
}
|
||||
class FrameworkInstaller {
|
||||
+install(project : LocalProject, enablePlanGate : Boolean) : InstallResult
|
||||
}
|
||||
class SummaryReport {
|
||||
+compose(request : ProjectRequest) : Summary
|
||||
}
|
||||
|
||||
class Run {
|
||||
-isApply : Boolean
|
||||
}
|
||||
class Configuration {
|
||||
-giteaUrl : String
|
||||
-giteaApiUrl : String
|
||||
-githubWebUrl : String
|
||||
-githubApiUrl : String
|
||||
-giteaSshPort : Integer
|
||||
-mirrorInterval : String
|
||||
-frameworkRepo : String
|
||||
-presetDetails : Map [0..1]
|
||||
}
|
||||
class Credential {
|
||||
-kind : String
|
||||
-value : String
|
||||
}
|
||||
class ToolCheck {
|
||||
-hasGit : Boolean
|
||||
-hasCurl : Boolean
|
||||
-hasJq : Boolean
|
||||
}
|
||||
class PromptSet {
|
||||
-prompts : String [1..*]
|
||||
}
|
||||
class ProjectRequest {
|
||||
-name : String
|
||||
-description : String
|
||||
-visibility : Visibility
|
||||
-directory : Path
|
||||
-enablePlanGate : Boolean
|
||||
}
|
||||
class Owner {
|
||||
-name : String
|
||||
-kind : String
|
||||
}
|
||||
class PreflightResult {
|
||||
-tokensWork : Boolean
|
||||
-ownersAccept : Boolean
|
||||
-nameIsFree : Boolean
|
||||
-licenseIsOffered : Boolean
|
||||
-sshPassed : Boolean
|
||||
}
|
||||
abstract class Repository {
|
||||
-name : String
|
||||
-description : String
|
||||
-visibility : Visibility
|
||||
-address : String
|
||||
}
|
||||
class GiteaRepository
|
||||
class GitHubRepository
|
||||
class LicenseFile {
|
||||
-key : String
|
||||
}
|
||||
class PushMirror {
|
||||
-interval : String
|
||||
-syncOnCommit : Boolean
|
||||
}
|
||||
class LocalProject {
|
||||
-directory : Path
|
||||
}
|
||||
class Remote {
|
||||
-name : String
|
||||
-address : String
|
||||
}
|
||||
class Submodule {
|
||||
-name : String
|
||||
-address : String
|
||||
}
|
||||
class HookSetup {
|
||||
-areSkillsInstalled : Boolean
|
||||
-areHooksInstalled : Boolean
|
||||
-isPlanGateEnabled : Boolean
|
||||
}
|
||||
class Template {
|
||||
-name : String
|
||||
-isCopied : Boolean
|
||||
}
|
||||
class InstallResult <<dto>>
|
||||
class Summary {
|
||||
-createdItems : String [0..*]
|
||||
-skippedItems : String [0..*]
|
||||
-nextSteps : String [0..*]
|
||||
}
|
||||
|
||||
ProjectCreator ..> ConfigLoader : creates
|
||||
ProjectCreator ..> ToolChecker : creates
|
||||
ProjectCreator ..> Preflight : creates
|
||||
ProjectCreator ..> GiteaClient : creates
|
||||
ProjectCreator ..> GitHubClient : creates [0..1]
|
||||
ProjectCreator ..> LocalProjectBuilder : creates
|
||||
ProjectCreator ..> FrameworkInstaller : creates
|
||||
ProjectCreator ..> SummaryReport : creates
|
||||
Preflight ..> GiteaClient : asks
|
||||
Preflight ..> GitHubClient : asks [0..1]
|
||||
GitHost <|-- GiteaClient
|
||||
GitHost <|-- GitHubClient
|
||||
GitHost "1" --> "0..*" Owner : has
|
||||
GiteaClient ..> Configuration
|
||||
GitHubClient ..> Configuration
|
||||
|
||||
ProjectCreator --> "1" Run
|
||||
Run *-- "1" Configuration
|
||||
Run *-- "1" ToolCheck
|
||||
Run *-- "0..1" ProjectRequest
|
||||
Run --> "1" PromptSet : returns
|
||||
Configuration *-- "1..2" Credential
|
||||
|
||||
ProjectRequest --> "1" Owner : giteaOwner
|
||||
ProjectRequest --> "0..1" Owner : githubOwner
|
||||
ProjectRequest *-- "0..1" PreflightResult
|
||||
ProjectRequest --> "0..1" GiteaRepository : stored in
|
||||
ProjectRequest --> "0..1" GitHubRepository : also stored in
|
||||
ProjectRequest --> "0..1" LocalProject : working copy
|
||||
Summary --> "1" ProjectRequest : reports on
|
||||
|
||||
Repository <|-- GiteaRepository
|
||||
Repository <|-- GitHubRepository
|
||||
Repository --> "1" Owner : owned by
|
||||
GiteaRepository *-- "0..1" LicenseFile
|
||||
PushMirror --> "1" GiteaRepository : source
|
||||
PushMirror --> "1" GitHubRepository : target
|
||||
PushMirror --> "1" Credential : authorised by
|
||||
|
||||
LocalProject *-- "1..2" Remote
|
||||
Remote --> "1" Repository : points to
|
||||
LocalProject *-- "1" Submodule
|
||||
LocalProject *-- "1" HookSetup
|
||||
LocalProject *-- "0..*" Template
|
||||
InstallResult --> "1" Submodule
|
||||
InstallResult --> "1" HookSetup
|
||||
InstallResult --> "0..*" Template
|
||||
|
||||
ProjectRequest --> Visibility
|
||||
Repository --> Visibility
|
||||
@enduml
|
||||
```
|
||||
|
||||
## Class Table
|
||||
|
||||
| Class | Refines (Domain Model concept) | Responsibility | Attributes | Operations |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `ProjectCreator` | none (controller for the system operations of [OC-001]) | Receives the two system operations, sequences the steps and stops on the first failure. | none | `startProjectCreation`, `provideProjectDetails` |
|
||||
| `ConfigLoader` | Configuration | Reads `config.env` and `.env` as plain text and validates every value, preset project details included. | none | `load` |
|
||||
| `ToolChecker` | none (system concept `ToolCheck`) | Detects the required and optional tools. | none | `check` |
|
||||
| `Preflight` | none (system concept `PreflightResult`) | Runs the read-only checks of both hosts before anything is created. | none | `check` |
|
||||
| `GitHost` | Git Host | The operations every host offers: check the token, check that an owner accepts new repositories, check that a name is free. | `name`, `webAddress`, `apiAddress` | `verifyToken`, `ownerAccepts`, `nameFree` |
|
||||
| `GiteaClient` | Git Host (Gitea) | Hides the Gitea API and its token; creates the repository and the push mirror. | none beyond `GitHost` (uses `Configuration`) | `GiteaClient`, `hasLicense`, `createRepository`, `addPushMirror`, `requestSync` |
|
||||
| `GitHubClient` | Git Host (GitHub) | Hides the GitHub API and its token; creates the empty repository. | none beyond `GitHost` (uses `Configuration`) | `GitHubClient`, `createEmptyRepository` |
|
||||
| `LocalProjectBuilder` | Local Project, Remote | Creates the project directory, its git repository and its credential-free remotes. | none | `build` |
|
||||
| `FrameworkInstaller` | Framework, Framework Setup, Template | Adds the framework submodule, installs skills and hooks once, and copies the templates without overwriting. | none | `install` |
|
||||
| `SummaryReport` | Summary | Composes the report of what was created, skipped or failed. | none | `compose` |
|
||||
| `Run` | none (system concept) | Holds the state of one execution. | `isApply` | none |
|
||||
| `Configuration` | Configuration | Holds the service addresses, the credentials and any preset project details. | `giteaUrl`, `giteaApiUrl`, `githubWebUrl`, `githubApiUrl`, `giteaSshPort`, `mirrorInterval`, `frameworkRepo`, `presetDetails` | none |
|
||||
| `Credential` | Access Token | Holds a secret in memory only; it never becomes part of an address or a message. | `kind`, `value` | none |
|
||||
| `ToolCheck` | none (system concept) | Records which tools are present. | `hasGit`, `hasCurl`, `hasJq` | none |
|
||||
| `PromptSet` | none (system concept) | The questions still to ask; a detail preset in `config.env` is not in it. | `prompts` | none |
|
||||
| `ProjectRequest` | Project | Holds the details of the project being created. | `name`, `description`, `visibility`, `directory`, `enablePlanGate` | none |
|
||||
| `Owner` | Owner | A user or organization on a host. | `name`, `kind` | none |
|
||||
| `PreflightResult` | none (system concept) | Records the outcome of the preflight checks. | `tokensWork`, `ownersAccept`, `nameIsFree`, `licenseIsOffered`, `sshPassed` | none |
|
||||
| `Repository` | Repository | Common data of a repository on a host. | `name`, `description`, `visibility`, `address` | none |
|
||||
| `GiteaRepository` | Gitea Repository | The source of truth. | none beyond `Repository` | none |
|
||||
| `GitHubRepository` | GitHub Repository | Receives its content from the mirror. | none beyond `Repository` | none |
|
||||
| `LicenseFile` | License | The `AGPL-3.0` file in the Gitea repository when GitHub is chosen. | `key` | none |
|
||||
| `PushMirror` | Mirror | The Gitea to GitHub push mirror. | `interval`, `syncOnCommit` | none |
|
||||
| `LocalProject` | Local Project | The project directory on the Maintainer's machine. | `directory` | none |
|
||||
| `Remote` | Remote | A named link to a repository (`origin`, `github`), without a credential. | `name`, `address` | none |
|
||||
| `Submodule` | Framework | The framework added to the local project. | `name`, `address` | none |
|
||||
| `HookSetup` | Framework Setup | Records the skills and hooks installed and the plan gate state. | `areSkillsInstalled`, `areHooksInstalled`, `isPlanGateEnabled` | none |
|
||||
| `Template` | Template | A framework file copied into the project. | `name`, `isCopied` | none |
|
||||
| `InstallResult` | none (carries the result of one operation) | Returns the submodule, the hook setup and the templates of `install`. | none | none |
|
||||
| `Summary` | Summary | The report returned to the Maintainer; it contains no credential. | `createdItems`, `skippedItems`, `nextSteps` | none |
|
||||
| `Visibility` | none (enumeration of a Project and Repository attribute) | The two allowed visibilities. | `private`, `public` | none |
|
||||
|
||||
## Method Traceability
|
||||
|
||||
| Method signature | Operation Contract / SD message |
|
||||
| --- | --- |
|
||||
| `ProjectCreator.startProjectCreation() : PromptSet` | [OC-001] `startProjectCreation`; [SD-001] `startProjectCreation()` |
|
||||
| `ProjectCreator.provideProjectDetails(name, description, visibility, giteaOwner, githubOwner, directory, enablePlanGate) : Summary` | [OC-001] `provideProjectDetails`; [SD-001] `provideProjectDetails(...)` |
|
||||
| `ConfigLoader.load(configFile, envFile) : Configuration` | [SD-001] `load(config.env, .env)`; [OC-001] `startProjectCreation` P2 |
|
||||
| `ToolChecker.check(tools) : ToolCheck` | [SD-001] `check(git, curl, jq)`; [OC-001] `startProjectCreation` P3 |
|
||||
| `Preflight.check(request) : PreflightResult` | [SD-001] `check(request)`; [OC-001] `provideProjectDetails` P2 |
|
||||
| `GiteaClient(configuration)` | [SD-001] `new(configuration)` to `GiteaClient` |
|
||||
| `GitHost.verifyToken() : Boolean` | [SD-001] `verifyToken()` from `Preflight` to either client; P2 |
|
||||
| `GitHost.ownerAccepts(owner) : Boolean` | [SD-001] `ownerAccepts(giteaOwner)` and `ownerAccepts(githubOwner)`; P2 |
|
||||
| `GitHost.nameFree(name) : Boolean` | [SD-001] `nameFree(name)` to either client; P2 |
|
||||
| `GiteaClient.hasLicense(key) : Boolean` | [SD-001] `hasLicense(AGPL-3.0)`; P2 |
|
||||
| `GiteaClient.createRepository(request, license) : GiteaRepository` | [SD-001] `createRepository(request, license)`; P3, P4 |
|
||||
| `GiteaClient.addPushMirror(source, target) : PushMirror` | [SD-001] `addPushMirror(giteaRepository, gitHubRepository)`; P6 |
|
||||
| `GiteaClient.requestSync(mirror) : void` | [SD-001] `requestSync(pushMirror)`; P6 |
|
||||
| `GitHubClient(configuration)` | [SD-001] `new(configuration)` to `GitHubClient` |
|
||||
| `GitHubClient.createEmptyRepository(request) : GitHubRepository` | [SD-001] `createEmptyRepository(request)`; P5 |
|
||||
| `LocalProjectBuilder.build(directory, source, target, sshPassed) : LocalProject` | [SD-001] `build(directory, giteaRepository, gitHubRepository, sshPassed)`; P7, P8, P9 |
|
||||
| `FrameworkInstaller.install(project, enablePlanGate) : InstallResult` | [SD-001] `install(localProject, enablePlanGate)`; P10, P11, P12 |
|
||||
| `SummaryReport.compose(request) : Summary` | [SD-001] `compose(projectRequest)`; P13 |
|
||||
|
||||
## Pattern Annotations
|
||||
|
||||
| Pattern | Classes | Rationale |
|
||||
| --- | --- | --- |
|
||||
| Controller (GRASP) | `ProjectCreator` | One entry for the system operations; coordinates and does no HTTP, git or file work itself |
|
||||
| Facade (GoF) | `GitHost`, `GiteaClient`, `GitHubClient` | Each client hides one host's HTTP API and keeps the token inside; no other class sees a credential. `GitHost` holds the operations both share |
|
||||
| Pure Fabrication (GRASP) | `ConfigLoader`, `ToolChecker`, `Preflight`, `LocalProjectBuilder`, `FrameworkInstaller`, `SummaryReport` | No domain concept owns these responsibilities; small units keep cohesion high |
|
||||
| Creator (GRASP) | `ConfigLoader` creates `Configuration`; `GiteaClient` creates `GiteaRepository` and `PushMirror` | The creating class holds the data needed to build the object |
|
||||
| Protection from variations (GRASP) | `GiteaClient`, `GitHubClient`, `ProjectRequest` | The optional GitHub path is decided by the controller; the clients do not know it |
|
||||
| Data Transfer Object (GoF-style) | `InstallResult` | Carries the three results of `install` in one return value |
|
||||
|
||||
## Dependency Check
|
||||
|
||||
No circular dependency. `ProjectCreator` depends on every helper class and no helper depends on it. `Preflight` depends on the two clients; the clients extend `GitHost` and depend only on `Configuration`. The data classes form a tree: `Run` holds `Configuration`, `ToolCheck` and `ProjectRequest`; `ProjectRequest` reaches the repositories and the `LocalProject`; `Summary` points at `ProjectRequest` and nothing points back at it. `Repository` is shared by `Remote` and `PushMirror` without a cycle.
|
||||
|
||||
SOLID check: no class has more than one reason to change (one host API, one kind of local work, one report); the clients can be replaced behind the same operations; the controller depends on the operations, not on how a host or git is called. `ProjectCreator` has two operations and no data, so it is not a god class.
|
||||
|
||||
## Implementation Mapping
|
||||
|
||||
| Design class | Where it lives in `src/` |
|
||||
| --- | --- |
|
||||
| `ProjectCreator` | `create-project.sh` (`main`), `lib/apply.sh` |
|
||||
| `ConfigLoader` | `lib/config.sh` (`load_configuration`), `lib/validate.sh` |
|
||||
| `ToolChecker` | `lib/tools.sh` |
|
||||
| `Preflight` | `lib/preflight.sh` |
|
||||
| `GitHost`, `GiteaClient`, `GitHubClient` | `lib/api.sh`, `lib/http.sh`, `lib/json.sh`, `lib/repositories.sh`, `lib/mirror.sh`, `lib/hosts.sh` |
|
||||
| `LocalProjectBuilder` | `lib/localproject.sh`, `lib/git.sh` |
|
||||
| `FrameworkInstaller` | `lib/framework.sh` |
|
||||
| `SummaryReport` | `lib/steps.sh`, `lib/plan.sh` |
|
||||
| `PromptSet`, `ProjectRequest` | `lib/project.sh`, `lib/prompts.sh` |
|
||||
| `Run`, `Configuration`, `Credential`, `PreflightResult` | the state arrays declared in `lib/constants.sh` |
|
||||
|
||||
---
|
||||
|
||||
[DCD-001]: ./uc-001/dcd.md
|
||||
[DM-002]: ./domain-model.md
|
||||
[UC-001]: ./uc-001/uc.md
|
||||
[OC-001]: ./uc-001/oc.md
|
||||
[SD-001]: ./uc-001/sd.md
|
||||
[DICT-001]: ./dictionary.md
|
||||
+23
-17
@@ -10,6 +10,7 @@
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-05 | Accepted | Jens Tirsvad Nielsen | S02 | Initial version, terms of UC-001 | [02875ae] |
|
||||
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S02 | The IT terms are also used in DCD-001 and DCD-002<br>InstallResult and Visibility named as design-only types | pending |
|
||||
|
||||
---
|
||||
|
||||
@@ -21,23 +22,23 @@ Maps each Product Owner (PO) term to its professional IT term. PO language: Engl
|
||||
|
||||
| PO term | Language | IT term | Definition | Used as PO term in | Used as IT term in |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Maintainer | en | Maintainer | The person who creates a new project. | DM, UC, US | OC, SD |
|
||||
| Project | en | ProjectRequest | The new software project being set up, with its name, description and visibility. | DM, UC, US | OC, SD |
|
||||
| Configuration | en | Configuration | The service addresses and access tokens set up before starting. | DM, UC | OC, SD |
|
||||
| Git Host | en | GitHost | A service that holds repositories: Gitea or GitHub. | DM, UC | OC, SD |
|
||||
| Access Token | en | Credential | A secret that lets the Maintainer act on a Git Host; never part of an address. | DM, UC | OC, SD |
|
||||
| Owner | en | Owner | The user or organization on a Git Host that owns repositories. | DM, UC | OC, SD |
|
||||
| Repository | en | Repository | A place on a Git Host that holds a project's history. | DM, UC | OC, SD |
|
||||
| Gitea Repository | en | GiteaRepository | The repository on Gitea; the source of truth. | DM, UC | OC, SD |
|
||||
| GitHub Repository | en | GitHubRepository | The repository on GitHub; it receives its content from the mirror. | DM, UC | OC, SD |
|
||||
| License | en | LicenseFile | The legal terms file (AGPL-3.0) added to the Gitea repository when GitHub is chosen. | DM, UC | OC, SD |
|
||||
| Mirror | en | PushMirror | The push mirror that copies a Gitea repository to a GitHub repository. | DM, UC | OC, SD |
|
||||
| Local Project | en | LocalProject | The project directory on the Maintainer's machine. | DM, UC | OC, SD |
|
||||
| Remote | en | Remote | A named link from a local project to a repository. | DM, UC | OC, SD |
|
||||
| Framework | en | Submodule | The SQA-QC-Framework added to a local project; the IT term names how it is attached. | DM, UC | OC, SD |
|
||||
| Framework Setup | en | HookSetup | The skills and git hooks installed from the framework, with the plan gate on or off. | DM, UC | OC, SD |
|
||||
| Template | en | Template | A framework file copied into a project. | DM, UC | OC, SD |
|
||||
| Summary | en | Summary | The report of what was created, skipped or failed and how to continue. | DM, UC | OC, SD |
|
||||
| Maintainer | en | Maintainer | The person who creates a new project. | DM, UC, US | OC, SD, DCD |
|
||||
| Project | en | ProjectRequest | The new software project being set up, with its name, description and visibility. | DM, UC, US | OC, SD, DCD |
|
||||
| Configuration | en | Configuration | The service addresses and access tokens set up before starting. | DM, UC | OC, SD, DCD |
|
||||
| Git Host | en | GitHost | A service that holds repositories: Gitea or GitHub. | DM, UC | OC, SD, DCD |
|
||||
| Access Token | en | Credential | A secret that lets the Maintainer act on a Git Host; never part of an address. | DM, UC | OC, SD, DCD |
|
||||
| Owner | en | Owner | The user or organization on a Git Host that owns repositories. | DM, UC | OC, SD, DCD |
|
||||
| Repository | en | Repository | A place on a Git Host that holds a project's history. | DM, UC | OC, SD, DCD |
|
||||
| Gitea Repository | en | GiteaRepository | The repository on Gitea; the source of truth. | DM, UC | OC, SD, DCD |
|
||||
| GitHub Repository | en | GitHubRepository | The repository on GitHub; it receives its content from the mirror. | DM, UC | OC, SD, DCD |
|
||||
| License | en | LicenseFile | The legal terms file (AGPL-3.0) added to the Gitea repository when GitHub is chosen. | DM, UC | OC, SD, DCD |
|
||||
| Mirror | en | PushMirror | The push mirror that copies a Gitea repository to a GitHub repository. | DM, UC | OC, SD, DCD |
|
||||
| Local Project | en | LocalProject | The project directory on the Maintainer's machine. | DM, UC | OC, SD, DCD |
|
||||
| Remote | en | Remote | A named link from a local project to a repository. | DM, UC | OC, SD, DCD |
|
||||
| Framework | en | Submodule | The SQA-QC-Framework added to a local project; the IT term names how it is attached. | DM, UC | OC, SD, DCD |
|
||||
| Framework Setup | en | HookSetup | The skills and git hooks installed from the framework, with the plan gate on or off. | DM, UC | OC, SD, DCD |
|
||||
| Template | en | Template | A framework file copied into a project. | DM, UC | OC, SD, DCD |
|
||||
| Summary | en | Summary | The report of what was created, skipped or failed and how to continue. | DM, UC | OC, SD, DCD |
|
||||
|
||||
## Rules
|
||||
|
||||
@@ -47,6 +48,10 @@ Maps each Product Owner (PO) term to its professional IT term. PO language: Engl
|
||||
- `Run`, `ToolCheck`, `PreflightResult` and `PromptSet` appear in [OC-001] but
|
||||
have no PO term: they are system concepts, not domain concepts, and are not
|
||||
in the Domain Model.
|
||||
- `InstallResult` and the enumeration `Visibility` appear only in [DCD-001]:
|
||||
`InstallResult` carries the three results of one operation, and `Visibility`
|
||||
is the type of the Project and Repository attribute of the same name. Neither
|
||||
is a domain concept.
|
||||
- A new concept in a Domain Model gets a row here in the same change.
|
||||
|
||||
---
|
||||
@@ -56,4 +61,5 @@ Maps each Product Owner (PO) term to its professional IT term. PO language: Engl
|
||||
[DM-001]: ./uc-001/dm.md
|
||||
[DM-002]: ./domain-model.md
|
||||
[OC-001]: ./uc-001/oc.md
|
||||
[DCD-001]: ./uc-001/dcd.md
|
||||
[02875ae]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/02875aee5f2953473924074eea0056eb31af6b7a
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-05 | Proposed | Jens Tirsvad Nielsen | S02 | Initial version, UC-001 artifacts and baseline | [02875ae] |
|
||||
| 2026-10-05 | Proposed | Jens Tirsvad Nielsen | S02 | Added MIL-004 and RC-018 | [2a6bb8e] |
|
||||
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S02 | Added DCD-001 and DCD-002 | pending |
|
||||
|
||||
---
|
||||
|
||||
@@ -35,16 +35,18 @@ updated whenever an artifact instance is created or reviewed.
|
||||
| [US-001] | US | [BC-001], [UCD-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004] | [UC-001] | [RC-001] |
|
||||
| [UC-001] | UC | [UCD-001], [US-001], [SA-001] | [SSD-001], [DM-001] | [RC-002] |
|
||||
| [SSD-001] | SSD | [UC-001] | [OC-001] | [RC-003] |
|
||||
| [DM-001] | DM | [UC-001], [SSD-001] | [DM-002], [DICT-001], [OC-001] | [RC-004] |
|
||||
| [DM-002] | DM | [DM-001] | [DICT-001] | [RC-005] |
|
||||
| [DM-001] | DM | [UC-001], [SSD-001] | [DM-002], [DICT-001], [OC-001], [DCD-001] | [RC-004] |
|
||||
| [DM-002] | DM | [DM-001] | [DICT-001], [DCD-001], [DCD-002] | [RC-005] |
|
||||
| [DICT-001] | DICT | [BC-001], [SA-001], [DM-001], [DM-002] | [OC-001], [SD-001] | [RC-008] |
|
||||
| [OC-001] | OC | [SSD-001], [DM-001] | [SD-001] | [RC-006] |
|
||||
| [SD-001] | SD | [OC-001] | - | [RC-007] |
|
||||
| [SD-001] | SD | [OC-001] | [DCD-001] | [RC-007] |
|
||||
| [DCD-001] | DCD | [UC-001], [DM-001], [DM-002], [OC-001], [SD-001], [DICT-001] | [DCD-002] | - |
|
||||
| [DCD-002] | DCD | [DCD-001], [DM-002], [DICT-001] | - | - |
|
||||
|
||||
## Coverage Notes
|
||||
|
||||
- Reviewed so far: every artifact in the project (see the Last Reviewed column).
|
||||
- No Design Class Diagram, ERD, KPI, BMC or BPMN exists yet. `-` in Downstream means nothing is built on the artifact yet.
|
||||
- [DCD-001] and [DCD-002] are proposed and not reviewed yet. No ERD, KPI, BMC or BPMN exists yet. `-` in Downstream means nothing is built on the artifact yet.
|
||||
|
||||
---
|
||||
|
||||
@@ -57,6 +59,8 @@ updated whenever an artifact instance is created or reviewed.
|
||||
[MIL-004]: ../milestones/mil-004-configurable-details.md
|
||||
[RC-018]: ./reviews/rc-018-mil-004.md
|
||||
[RC-019]: ./reviews/rc-019-mil-004-code.md
|
||||
[DCD-001]: ../uc-001/dcd.md
|
||||
[DCD-002]: ../dcd.md
|
||||
[UCD-001]: ../use-case-diagram.md
|
||||
[US-001]: ../user-stories.md
|
||||
[UC-001]: ../uc-001/uc.md
|
||||
@@ -83,5 +87,4 @@ updated whenever an artifact instance is created or reviewed.
|
||||
[RC-015]: ./reviews/rc-015-mil-003.md
|
||||
[RC-016]: ./reviews/rc-016-create-project-sh.md
|
||||
[RC-017]: ./reviews/rc-017-e2e-security-review.md
|
||||
[02875ae]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/02875aee5f2953473924074eea0056eb31af6b7a
|
||||
[2a6bb8e]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/2a6bb8e8afadfe6ca4a621da30e44a372898ca62
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
# Design Class Diagram (UC-001)
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | DCD-001 |
|
||||
| CrossReference | [UC-001], [DM-001], [DM-002], [OC-001], [SD-001], [DICT-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S02 | Initial version | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose and Scope
|
||||
|
||||
Covers [UC-001] "Create a new project". It refines the concepts of [DM-001] into design classes and turns the messages of [SD-001] into method signatures, so that every method traces to a contract in [OC-001] or to a message in [SD-001]. Class and attribute names are the IT terms of [DICT-001]. The project-level model that consolidates all use cases is [DCD-002].
|
||||
|
||||
The classes are design classes of a Bash program: a class is a group of functions in `src/lib/` with its data held in the shared state arrays (see "Implementation Mapping"). There is no object-oriented runtime, but the responsibilities, associations and dependencies below are the ones the code keeps.
|
||||
|
||||
Failure handling (the exceptions of [OC-001]) is one rule of `ProjectCreator`, stop and report, and is not drawn.
|
||||
|
||||
## Diagram
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
skinparam classAttributeIconSize 0
|
||||
hide empty members
|
||||
|
||||
enum Visibility {
|
||||
private
|
||||
public
|
||||
}
|
||||
|
||||
class ProjectCreator <<controller>> {
|
||||
+startProjectCreation() : PromptSet
|
||||
+provideProjectDetails(name : String, description : String, visibility : Visibility, giteaOwner : Owner, githubOwner : Owner [0..1], directory : Path, enablePlanGate : Boolean) : Summary
|
||||
}
|
||||
class ConfigLoader {
|
||||
+load(configFile : Path, envFile : Path) : Configuration
|
||||
}
|
||||
class ToolChecker {
|
||||
+check(tools : String [1..*]) : ToolCheck
|
||||
}
|
||||
class Preflight {
|
||||
+check(request : ProjectRequest) : PreflightResult
|
||||
}
|
||||
abstract class GitHost <<facade>> {
|
||||
-name : String
|
||||
-webAddress : String
|
||||
-apiAddress : String
|
||||
+verifyToken() : Boolean
|
||||
+ownerAccepts(owner : Owner) : Boolean
|
||||
+nameFree(name : String) : Boolean
|
||||
}
|
||||
class GiteaClient <<facade>> {
|
||||
+GiteaClient(configuration : Configuration)
|
||||
+hasLicense(key : String) : Boolean
|
||||
+createRepository(request : ProjectRequest, license : String [0..1]) : GiteaRepository
|
||||
+addPushMirror(source : GiteaRepository, target : GitHubRepository) : PushMirror
|
||||
-requestSync(mirror : PushMirror) : void
|
||||
}
|
||||
class GitHubClient <<facade>> {
|
||||
+GitHubClient(configuration : Configuration)
|
||||
+createEmptyRepository(request : ProjectRequest) : GitHubRepository
|
||||
}
|
||||
class LocalProjectBuilder {
|
||||
+build(directory : Path, source : GiteaRepository, target : GitHubRepository [0..1], sshPassed : Boolean) : LocalProject
|
||||
}
|
||||
class FrameworkInstaller {
|
||||
+install(project : LocalProject, enablePlanGate : Boolean) : InstallResult
|
||||
}
|
||||
class SummaryReport {
|
||||
+compose(request : ProjectRequest) : Summary
|
||||
}
|
||||
|
||||
class Run {
|
||||
-isApply : Boolean
|
||||
}
|
||||
class Configuration {
|
||||
-giteaUrl : String
|
||||
-giteaApiUrl : String
|
||||
-githubWebUrl : String
|
||||
-githubApiUrl : String
|
||||
-giteaSshPort : Integer
|
||||
-mirrorInterval : String
|
||||
-frameworkRepo : String
|
||||
-presetDetails : Map [0..1]
|
||||
}
|
||||
class Credential {
|
||||
-kind : String
|
||||
-value : String
|
||||
}
|
||||
class ToolCheck {
|
||||
-hasGit : Boolean
|
||||
-hasCurl : Boolean
|
||||
-hasJq : Boolean
|
||||
}
|
||||
class PromptSet {
|
||||
-prompts : String [1..*]
|
||||
}
|
||||
class ProjectRequest {
|
||||
-name : String
|
||||
-description : String
|
||||
-visibility : Visibility
|
||||
-directory : Path
|
||||
-enablePlanGate : Boolean
|
||||
}
|
||||
class Owner {
|
||||
-name : String
|
||||
-kind : String
|
||||
}
|
||||
class PreflightResult {
|
||||
-tokensWork : Boolean
|
||||
-ownersAccept : Boolean
|
||||
-nameIsFree : Boolean
|
||||
-licenseIsOffered : Boolean
|
||||
-sshPassed : Boolean
|
||||
}
|
||||
abstract class Repository {
|
||||
-name : String
|
||||
-description : String
|
||||
-visibility : Visibility
|
||||
-address : String
|
||||
}
|
||||
class GiteaRepository
|
||||
class GitHubRepository
|
||||
class LicenseFile {
|
||||
-key : String
|
||||
}
|
||||
class PushMirror {
|
||||
-interval : String
|
||||
-syncOnCommit : Boolean
|
||||
}
|
||||
class LocalProject {
|
||||
-directory : Path
|
||||
}
|
||||
class Remote {
|
||||
-name : String
|
||||
-address : String
|
||||
}
|
||||
class Submodule {
|
||||
-name : String
|
||||
-address : String
|
||||
}
|
||||
class HookSetup {
|
||||
-areSkillsInstalled : Boolean
|
||||
-areHooksInstalled : Boolean
|
||||
-isPlanGateEnabled : Boolean
|
||||
}
|
||||
class Template {
|
||||
-name : String
|
||||
-isCopied : Boolean
|
||||
}
|
||||
class InstallResult <<dto>>
|
||||
class Summary {
|
||||
-createdItems : String [0..*]
|
||||
-skippedItems : String [0..*]
|
||||
-nextSteps : String [0..*]
|
||||
}
|
||||
|
||||
ProjectCreator ..> ConfigLoader : creates
|
||||
ProjectCreator ..> ToolChecker : creates
|
||||
ProjectCreator ..> Preflight : creates
|
||||
ProjectCreator ..> GiteaClient : creates
|
||||
ProjectCreator ..> GitHubClient : creates [0..1]
|
||||
ProjectCreator ..> LocalProjectBuilder : creates
|
||||
ProjectCreator ..> FrameworkInstaller : creates
|
||||
ProjectCreator ..> SummaryReport : creates
|
||||
Preflight ..> GiteaClient : asks
|
||||
Preflight ..> GitHubClient : asks [0..1]
|
||||
GitHost <|-- GiteaClient
|
||||
GitHost <|-- GitHubClient
|
||||
GitHost "1" --> "0..*" Owner : has
|
||||
GiteaClient ..> Configuration
|
||||
GitHubClient ..> Configuration
|
||||
|
||||
ProjectCreator --> "1" Run
|
||||
Run *-- "1" Configuration
|
||||
Run *-- "1" ToolCheck
|
||||
Run *-- "0..1" ProjectRequest
|
||||
Run --> "1" PromptSet : returns
|
||||
Configuration *-- "1..2" Credential
|
||||
|
||||
ProjectRequest --> "1" Owner : giteaOwner
|
||||
ProjectRequest --> "0..1" Owner : githubOwner
|
||||
ProjectRequest *-- "0..1" PreflightResult
|
||||
ProjectRequest --> "0..1" GiteaRepository : stored in
|
||||
ProjectRequest --> "0..1" GitHubRepository : also stored in
|
||||
ProjectRequest --> "0..1" LocalProject : working copy
|
||||
Summary --> "1" ProjectRequest : reports on
|
||||
|
||||
Repository <|-- GiteaRepository
|
||||
Repository <|-- GitHubRepository
|
||||
Repository --> "1" Owner : owned by
|
||||
GiteaRepository *-- "0..1" LicenseFile
|
||||
PushMirror --> "1" GiteaRepository : source
|
||||
PushMirror --> "1" GitHubRepository : target
|
||||
PushMirror --> "1" Credential : authorised by
|
||||
|
||||
LocalProject *-- "1..2" Remote
|
||||
Remote --> "1" Repository : points to
|
||||
LocalProject *-- "1" Submodule
|
||||
LocalProject *-- "1" HookSetup
|
||||
LocalProject *-- "0..*" Template
|
||||
InstallResult --> "1" Submodule
|
||||
InstallResult --> "1" HookSetup
|
||||
InstallResult --> "0..*" Template
|
||||
|
||||
ProjectRequest --> Visibility
|
||||
Repository --> Visibility
|
||||
@enduml
|
||||
```
|
||||
|
||||
## Class Table
|
||||
|
||||
| Class | Refines (Domain Model concept) | Responsibility | Attributes | Operations |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `ProjectCreator` | none (controller for the system operations of [OC-001]) | Receives the two system operations, sequences the steps and stops on the first failure. | none | `startProjectCreation`, `provideProjectDetails` |
|
||||
| `ConfigLoader` | Configuration | Reads `config.env` and `.env` as plain text and validates every value, preset project details included. | none | `load` |
|
||||
| `ToolChecker` | none (system concept `ToolCheck`) | Detects the required and optional tools. | none | `check` |
|
||||
| `Preflight` | none (system concept `PreflightResult`) | Runs the read-only checks of both hosts before anything is created. | none | `check` |
|
||||
| `GitHost` | Git Host | The operations every host offers: check the token, check that an owner accepts new repositories, check that a name is free. | `name`, `webAddress`, `apiAddress` | `verifyToken`, `ownerAccepts`, `nameFree` |
|
||||
| `GiteaClient` | Git Host (Gitea) | Hides the Gitea API and its token; creates the repository and the push mirror. | none beyond `GitHost` (uses `Configuration`) | `GiteaClient`, `hasLicense`, `createRepository`, `addPushMirror`, `requestSync` |
|
||||
| `GitHubClient` | Git Host (GitHub) | Hides the GitHub API and its token; creates the empty repository. | none beyond `GitHost` (uses `Configuration`) | `GitHubClient`, `createEmptyRepository` |
|
||||
| `LocalProjectBuilder` | Local Project, Remote | Creates the project directory, its git repository and its credential-free remotes. | none | `build` |
|
||||
| `FrameworkInstaller` | Framework, Framework Setup, Template | Adds the framework submodule, installs skills and hooks once, and copies the templates without overwriting. | none | `install` |
|
||||
| `SummaryReport` | Summary | Composes the report of what was created, skipped or failed. | none | `compose` |
|
||||
| `Run` | none (system concept) | Holds the state of one execution. | `isApply` | none |
|
||||
| `Configuration` | Configuration | Holds the service addresses, the credentials and any preset project details. | `giteaUrl`, `giteaApiUrl`, `githubWebUrl`, `githubApiUrl`, `giteaSshPort`, `mirrorInterval`, `frameworkRepo`, `presetDetails` | none |
|
||||
| `Credential` | Access Token | Holds a secret in memory only; it never becomes part of an address or a message. | `kind`, `value` | none |
|
||||
| `ToolCheck` | none (system concept) | Records which tools are present. | `hasGit`, `hasCurl`, `hasJq` | none |
|
||||
| `PromptSet` | none (system concept) | The questions still to ask; a detail preset in `config.env` is not in it. | `prompts` | none |
|
||||
| `ProjectRequest` | Project | Holds the details of the project being created. | `name`, `description`, `visibility`, `directory`, `enablePlanGate` | none |
|
||||
| `Owner` | Owner | A user or organization on a host. | `name`, `kind` | none |
|
||||
| `PreflightResult` | none (system concept) | Records the outcome of the preflight checks. | `tokensWork`, `ownersAccept`, `nameIsFree`, `licenseIsOffered`, `sshPassed` | none |
|
||||
| `Repository` | Repository | Common data of a repository on a host. | `name`, `description`, `visibility`, `address` | none |
|
||||
| `GiteaRepository` | Gitea Repository | The source of truth. | none beyond `Repository` | none |
|
||||
| `GitHubRepository` | GitHub Repository | Receives its content from the mirror. | none beyond `Repository` | none |
|
||||
| `LicenseFile` | License | The `AGPL-3.0` file in the Gitea repository when GitHub is chosen. | `key` | none |
|
||||
| `PushMirror` | Mirror | The Gitea to GitHub push mirror. | `interval`, `syncOnCommit` | none |
|
||||
| `LocalProject` | Local Project | The project directory on the Maintainer's machine. | `directory` | none |
|
||||
| `Remote` | Remote | A named link to a repository (`origin`, `github`), without a credential. | `name`, `address` | none |
|
||||
| `Submodule` | Framework | The framework added to the local project. | `name`, `address` | none |
|
||||
| `HookSetup` | Framework Setup | Records the skills and hooks installed and the plan gate state. | `areSkillsInstalled`, `areHooksInstalled`, `isPlanGateEnabled` | none |
|
||||
| `Template` | Template | A framework file copied into the project. | `name`, `isCopied` | none |
|
||||
| `InstallResult` | none (carries the result of one operation) | Returns the submodule, the hook setup and the templates of `install`. | none | none |
|
||||
| `Summary` | Summary | The report returned to the Maintainer; it contains no credential. | `createdItems`, `skippedItems`, `nextSteps` | none |
|
||||
| `Visibility` | none (enumeration of a Project and Repository attribute) | The two allowed visibilities. | `private`, `public` | none |
|
||||
|
||||
## Method Traceability
|
||||
|
||||
| Method signature | Operation Contract / SD message |
|
||||
| --- | --- |
|
||||
| `ProjectCreator.startProjectCreation() : PromptSet` | [OC-001] `startProjectCreation`; [SD-001] `startProjectCreation()` |
|
||||
| `ProjectCreator.provideProjectDetails(name, description, visibility, giteaOwner, githubOwner, directory, enablePlanGate) : Summary` | [OC-001] `provideProjectDetails`; [SD-001] `provideProjectDetails(...)` |
|
||||
| `ConfigLoader.load(configFile, envFile) : Configuration` | [SD-001] `load(config.env, .env)`; [OC-001] `startProjectCreation` P2 |
|
||||
| `ToolChecker.check(tools) : ToolCheck` | [SD-001] `check(git, curl, jq)`; [OC-001] `startProjectCreation` P3 |
|
||||
| `Preflight.check(request) : PreflightResult` | [SD-001] `check(request)`; [OC-001] `provideProjectDetails` P2 |
|
||||
| `GiteaClient(configuration)` | [SD-001] `new(configuration)` to `GiteaClient` |
|
||||
| `GitHost.verifyToken() : Boolean` | [SD-001] `verifyToken()` from `Preflight` to either client; P2 |
|
||||
| `GitHost.ownerAccepts(owner) : Boolean` | [SD-001] `ownerAccepts(giteaOwner)` and `ownerAccepts(githubOwner)`; P2 |
|
||||
| `GitHost.nameFree(name) : Boolean` | [SD-001] `nameFree(name)` to either client; P2 |
|
||||
| `GiteaClient.hasLicense(key) : Boolean` | [SD-001] `hasLicense(AGPL-3.0)`; P2 |
|
||||
| `GiteaClient.createRepository(request, license) : GiteaRepository` | [SD-001] `createRepository(request, license)`; P3, P4 |
|
||||
| `GiteaClient.addPushMirror(source, target) : PushMirror` | [SD-001] `addPushMirror(giteaRepository, gitHubRepository)`; P6 |
|
||||
| `GiteaClient.requestSync(mirror) : void` | [SD-001] `requestSync(pushMirror)`; P6 |
|
||||
| `GitHubClient(configuration)` | [SD-001] `new(configuration)` to `GitHubClient` |
|
||||
| `GitHubClient.createEmptyRepository(request) : GitHubRepository` | [SD-001] `createEmptyRepository(request)`; P5 |
|
||||
| `LocalProjectBuilder.build(directory, source, target, sshPassed) : LocalProject` | [SD-001] `build(directory, giteaRepository, gitHubRepository, sshPassed)`; P7, P8, P9 |
|
||||
| `FrameworkInstaller.install(project, enablePlanGate) : InstallResult` | [SD-001] `install(localProject, enablePlanGate)`; P10, P11, P12 |
|
||||
| `SummaryReport.compose(request) : Summary` | [SD-001] `compose(projectRequest)`; P13 |
|
||||
|
||||
## Pattern Annotations
|
||||
|
||||
| Pattern | Classes | Rationale |
|
||||
| --- | --- | --- |
|
||||
| Controller (GRASP) | `ProjectCreator` | One entry for the system operations; coordinates and does no HTTP, git or file work itself |
|
||||
| Facade (GoF) | `GitHost`, `GiteaClient`, `GitHubClient` | Each client hides one host's HTTP API and keeps the token inside; no other class sees a credential. `GitHost` holds the operations both share |
|
||||
| Pure Fabrication (GRASP) | `ConfigLoader`, `ToolChecker`, `Preflight`, `LocalProjectBuilder`, `FrameworkInstaller`, `SummaryReport` | No domain concept owns these responsibilities; small units keep cohesion high |
|
||||
| Creator (GRASP) | `ConfigLoader` creates `Configuration`; `GiteaClient` creates `GiteaRepository` and `PushMirror` | The creating class holds the data needed to build the object |
|
||||
| Protection from variations (GRASP) | `GiteaClient`, `GitHubClient`, `ProjectRequest` | The optional GitHub path is decided by the controller; the clients do not know it |
|
||||
| Data Transfer Object (GoF-style) | `InstallResult` | Carries the three results of `install` in one return value |
|
||||
|
||||
## Dependency Check
|
||||
|
||||
No circular dependency. `ProjectCreator` depends on every helper class and no helper depends on it. `Preflight` depends on the two clients; the clients extend `GitHost` and depend only on `Configuration`. The data classes form a tree: `Run` holds `Configuration`, `ToolCheck` and `ProjectRequest`; `ProjectRequest` reaches the repositories and the `LocalProject`; `Summary` points at `ProjectRequest` and nothing points back at it. `Repository` is shared by `Remote` and `PushMirror` without a cycle.
|
||||
|
||||
SOLID check: no class has more than one reason to change (one host API, one kind of local work, one report); the clients can be replaced behind the same operations; the controller depends on the operations, not on how a host or git is called. `ProjectCreator` has two operations and no data, so it is not a god class.
|
||||
|
||||
## Implementation Mapping
|
||||
|
||||
| Design class | Where it lives in `src/` |
|
||||
| --- | --- |
|
||||
| `ProjectCreator` | `create-project.sh` (`main`), `lib/apply.sh` |
|
||||
| `ConfigLoader` | `lib/config.sh` (`load_configuration`), `lib/validate.sh` |
|
||||
| `ToolChecker` | `lib/tools.sh` |
|
||||
| `Preflight` | `lib/preflight.sh` |
|
||||
| `GitHost`, `GiteaClient`, `GitHubClient` | `lib/api.sh`, `lib/http.sh`, `lib/json.sh`, `lib/repositories.sh`, `lib/mirror.sh`, `lib/hosts.sh` |
|
||||
| `LocalProjectBuilder` | `lib/localproject.sh`, `lib/git.sh` |
|
||||
| `FrameworkInstaller` | `lib/framework.sh` |
|
||||
| `SummaryReport` | `lib/steps.sh`, `lib/plan.sh` |
|
||||
| `PromptSet`, `ProjectRequest` | `lib/project.sh`, `lib/prompts.sh` |
|
||||
| `Run`, `Configuration`, `Credential`, `PreflightResult` | the state arrays declared in `lib/constants.sh` |
|
||||
|
||||
---
|
||||
|
||||
[UC-001]: ./uc.md
|
||||
[DM-001]: ./dm.md
|
||||
[DM-002]: ../domain-model.md
|
||||
[OC-001]: ./oc.md
|
||||
[SD-001]: ./sd.md
|
||||
[DICT-001]: ../dictionary.md
|
||||
[DCD-002]: ../dcd.md
|
||||
+15
-16
@@ -4,17 +4,17 @@
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | SD-001 |
|
||||
| CrossReference | [OC-001] |
|
||||
| CrossReference | [OC-001], [DCD-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-05 | Accepted | Jens Tirsvad Nielsen | S02 | Initial version | [02875ae] |
|
||||
| 2026-10-05 | Accepted | Jens Tirsvad Nielsen | S02 | Note: preset project details are read by ConfigLoader | [2a6bb8e] |
|
||||
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S02 | Messages aligned with the method signatures of DCD-001<br>Cited DCD-001 | pending |
|
||||
|
||||
---
|
||||
|
||||
Design objects are conceptual; in `create-project.sh` each becomes a small function group. No Design Class Diagram exists yet.
|
||||
Design objects are conceptual; in `create-project.sh` each becomes a small function group. [DCD-001] gives each object its class and turns each message below into a method signature.
|
||||
|
||||
## Sequence: startProjectCreation
|
||||
|
||||
@@ -77,7 +77,7 @@ destroy TC
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
actor Maintainer
|
||||
'actor Maintainer
|
||||
participant ":ProjectCreator" as PC
|
||||
participant ":Preflight" as PF
|
||||
participant ":GiteaClient" as GT
|
||||
@@ -86,7 +86,7 @@ participant ":LocalProjectBuilder" as LB
|
||||
participant ":FrameworkInstaller" as FI
|
||||
participant ":SummaryReport" as SR
|
||||
|
||||
Maintainer -> PC : provideProjectDetails(name, description, visibility, giteaOwner, githubOwner, directory, enablePlanGate)
|
||||
-> PC : provideProjectDetails(name, description, visibility, giteaOwner, githubOwner, directory, enablePlanGate)
|
||||
activate PC
|
||||
create GT
|
||||
PC -> GT : new(configuration)
|
||||
@@ -105,16 +105,16 @@ PF --> PC : preflightResult
|
||||
deactivate PF
|
||||
|
||||
opt githubOwner present
|
||||
PC -> GH : createEmptyRepository(githubOwner, name)
|
||||
PC -> GH : createEmptyRepository(request)
|
||||
activate GH
|
||||
GH --> PC : gitHubRepository
|
||||
deactivate GH
|
||||
end
|
||||
|
||||
alt githubOwner present
|
||||
PC -> GT : createRepository(giteaOwner, name, license=AGPL-3.0)
|
||||
PC -> GT : createRepository(request, license=AGPL-3.0)
|
||||
else no GitHub
|
||||
PC -> GT : createRepository(giteaOwner, name, license=none)
|
||||
PC -> GT : createRepository(request, license=none)
|
||||
end
|
||||
activate GT
|
||||
GT --> PC : giteaRepository
|
||||
@@ -123,7 +123,7 @@ deactivate GT
|
||||
opt githubOwner present
|
||||
PC -> GT : addPushMirror(giteaRepository, gitHubRepository)
|
||||
activate GT
|
||||
GT -> GT : requestSync()
|
||||
GT -> GT : requestSync(pushMirror)
|
||||
GT --> PC : pushMirror
|
||||
deactivate GT
|
||||
end
|
||||
@@ -141,9 +141,8 @@ FI --> PC : submodule, hookSetup, templates
|
||||
deactivate FI
|
||||
|
||||
create SR
|
||||
PC -> SR : compose(all results)
|
||||
PC -> SR : compose(request)
|
||||
SR --> PC : summary
|
||||
PC --> Maintainer : summary
|
||||
deactivate PC
|
||||
destroy PF
|
||||
destroy GT
|
||||
@@ -169,17 +168,17 @@ destroy SR
|
||||
| --- | --- |
|
||||
| P1 ProjectRequest created | `provideProjectDetails` received by `ProjectCreator` |
|
||||
| P2 PreflightResult created | `check(request)` |
|
||||
| P3 GiteaRepository created | `createRepository(giteaOwner, name, license)` |
|
||||
| P3 GiteaRepository created | `createRepository(request, license)` |
|
||||
| P4 LicenseFile when GitHub chosen, otherwise empty | `createRepository(..., license=AGPL-3.0)` and the `alt` branch `license=none` |
|
||||
| P5 empty GitHubRepository when chosen | `createEmptyRepository(githubOwner, name)` |
|
||||
| P6 PushMirror and first sync | `addPushMirror(...)` and `requestSync()` |
|
||||
| P5 empty GitHubRepository when chosen | `createEmptyRepository(request)` |
|
||||
| P6 PushMirror and first sync | `addPushMirror(...)` and `requestSync(pushMirror)` |
|
||||
| P7 LocalProject created, history from Gitea when not empty | `build(directory, ...)` |
|
||||
| P8 origin remote (SSH if the test passed, else HTTPS) | `build(..., sshPassed)` |
|
||||
| P9 github remote when chosen | `build(...)` |
|
||||
| P10 framework Submodule | `install(localProject, ...)` |
|
||||
| P11 HookSetup, plan gate if chosen | `install(localProject, enablePlanGate)` |
|
||||
| P12 AGENTS.md and registry copied or kept | `install(...)` returning `templates` |
|
||||
| P13 Summary created and returned | `compose(all results)` and the final return |
|
||||
| P13 Summary created and returned | `compose(request)` and the final return |
|
||||
|
||||
### Responsibility Check
|
||||
|
||||
@@ -188,5 +187,5 @@ destroy SR
|
||||
---
|
||||
|
||||
[OC-001]: ./oc.md
|
||||
[02875ae]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/02875aee5f2953473924074eea0056eb31af6b7a
|
||||
[DCD-001]: ./dcd.md
|
||||
[2a6bb8e]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/2a6bb8e8afadfe6ca4a621da30e44a372898ca62
|
||||
|
||||
Reference in New Issue
Block a user