Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/design/build-mark.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ flowchart TD
- *Role*: Consumer — BuildMark queries GitHub for tags, commits, issues, pull requests, and
releases.
- *Contract*: `GitHubGraphQLClient` sends paginated queries to
`https://api.github.com/graphql` using `Authorization: bearer <token>`.
`https://api.github.com/graphql` by default; the endpoint is overridden by
`GitHubConnectorConfig.BaseUrl` when set, enabling GitHub Enterprise Server support
(e.g., `https://github.mycompany.com/api/graphql`). Authentication uses
`Authorization: bearer <token>`.
- *Constraints*: Authentication via `GH_TOKEN` or `GITHUB_TOKEN` environment variable, or
`gh auth token` CLI fallback; subject to GitHub API rate limits.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
### AzureDevOpsConnectorConfig

#### Purpose

`AzureDevOpsConnectorConfig` is an immutable record that carries Azure DevOps-specific connector
settings read from the `connector.azure-devops:` block of `.buildmark.yaml`. It allows operators
to override the organization URL, organization name, project, repository name, authentication
token variable, and area path that `AzureDevOpsRepoConnector` would otherwise auto-detect from
the environment or the git remote URL.

#### Data Model

**OrganizationUrl**: `string?` — Azure DevOps organization URL override (e.g.,
`https://dev.azure.com/myorg`). When set, replaces the organization URL parsed from the git
remote URL. When null, the organization URL is derived from the remote URL.

**Organization**: `string?` — Azure DevOps organization name override. When set, replaces
the organization name derived from the remote URL. When null, the organization name is
parsed from the remote URL.

**Project**: `string?` — Azure DevOps project name override. When set, replaces the project
name parsed from the git remote URL. When null, the project name is derived from the remote URL.

**Repository**: `string?` — Repository name override within the project. When set, replaces
the repository name parsed from the git remote URL. When null, the repository name is derived
from the remote URL.

**TokenVariable**: `string?` — Name of the environment variable that holds the Azure DevOps
access token. When set, `AzureDevOpsRepoConnector` reads the token exclusively from this
variable and does not fall back to well-known variable names (`AZURE_DEVOPS_PAT`,
`SYSTEM_ACCESSTOKEN`, etc.) or the `az` CLI. When the variable is absent or empty,
`InvalidOperationException` is thrown. The token is always treated as a Basic (PAT) credential
when loaded from a custom variable.

**AreaPath**: `string?` — Area path used to scope the known-issues WIQL query. When null,
the connector scopes the query to the ADO project name (using the project root area path).
When set to an explicit value (e.g., `MyProject\MyRepo`), the query uses
`[System.AreaPath] UNDER '{AreaPath}'` to include the specified area and all descendants.
When set to an empty string, area-path filtering is disabled and all bugs in the project are
queried.

#### Key Methods

N/A — `AzureDevOpsConnectorConfig` is an immutable configuration data record with no methods
beyond those auto-generated by C#.

#### Error Handling

N/A — Immutable data record. All parsing and validation of the `connector.azure-devops:` YAML
block is performed by `BuildMarkConfigReader`; this type holds only the results. Token
resolution errors are raised by `AzureDevOpsRepoConnector` when `TokenVariable` is set and the
named variable is absent or empty.

#### Dependencies

N/A — `AzureDevOpsConnectorConfig` has no dependencies on other local software items.

#### Callers

- **BuildMarkConfigReader** — parses the `connector.azure-devops:` YAML block and creates
instances.
- **ConnectorConfig** — holds an `AzureDevOpsConnectorConfig` instance in its `AzureDevOps`
property.
- **RepoConnectorFactory** — forwards `config?.AzureDevOps` to `AzureDevOpsRepoConnector` at
construction.
- **AzureDevOpsRepoConnector** — reads `OrganizationUrl`, `Organization`, `Project`,
`Repository`, `TokenVariable`, and `AreaPath` properties to override auto-detected values
at runtime.
51 changes: 51 additions & 0 deletions docs/design/build-mark/configuration/github-connector-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
### GitHubConnectorConfig

#### Purpose

`GitHubConnectorConfig` is an immutable record that carries GitHub-specific connector settings
read from the `connector.github:` block of `.buildmark.yaml`. It allows operators to override
the owner, repository name, GraphQL base URL, and authentication token variable that
`GitHubRepoConnector` would otherwise auto-detect from the environment or the git remote URL.

#### Data Model

**Owner**: `string?` — Repository owner override. When non-empty, replaces the owner parsed from
the git remote URL. When null or whitespace, the owner is derived from the remote URL.

**Repo**: `string?` — Repository name override. When non-empty, replaces the repository name
parsed from the git remote URL. When null or whitespace, the repository name is derived from
the remote URL.

**BaseUrl**: `string?` — Optional GitHub GraphQL API base endpoint override. When set, it is
forwarded to `ResolveGraphQLEndpoint` to derive the correct GraphQL endpoint for the target
instance (supports GitHub Enterprise Server). When null or empty, the default public GitHub
endpoint (`https://api.github.com/graphql`) is used.

**TokenVariable**: `string?` — Name of the environment variable that holds the GitHub access
token. When set, `GitHubRepoConnector` reads the token exclusively from this variable and does
not fall back to `GH_TOKEN`, `GITHUB_TOKEN`, or `gh auth token`. If the variable is absent or
empty, `InvalidOperationException` is thrown with a message identifying the expected variable.

#### Key Methods

N/A — `GitHubConnectorConfig` is an immutable configuration data record with no methods beyond
those auto-generated by C#.

#### Error Handling

N/A — Immutable data record. All parsing and validation of the `connector.github:` YAML block
is performed by `BuildMarkConfigReader`; this type holds only the results. Token resolution
errors are raised by `GitHubRepoConnector.GetBuildInformationAsync` when `TokenVariable` is set
and the named variable is absent or empty.

#### Dependencies

N/A — `GitHubConnectorConfig` has no dependencies on other local software items.

#### Callers

- **BuildMarkConfigReader** — parses the `connector.github:` YAML block and creates instances.
- **ConnectorConfig** — holds a `GitHubConnectorConfig` instance in its `GitHub` property.
- **RepoConnectorFactory** — forwards `config?.GitHub` to `GitHubRepoConnector` at construction.
- **GitHubRepoConnector** — reads `Owner`, `Repo`, `BaseUrl`, and `TokenVariable` properties
to override auto-detected values at runtime.
5 changes: 4 additions & 1 deletion docs/design/build-mark/repo-connectors/github.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ All other types in the subsystem are internal.
1. Read the git remote URL and current commit hash via `RunCommandAsync` (inherited from
`RepoConnectorBase`).
2. Determine the owner and repository name from `GitHubConnectorConfig` or by parsing the remote
URL.
URL. The URL parser accepts github.com, GitHub Enterprise Cloud (`*.ghe.com`), and GitHub
Enterprise Server (on-premises) hosts in both SSH (`git@<host>:owner/repo.git`) and HTTPS
(`https://<host>/owner/repo`) formats; the hostname is never validated so any host is accepted
uniformly.
3. Resolve a GitHub authentication token (`GH_TOKEN`, `GITHUB_TOKEN`, or `gh auth token`).
4. Create a `GitHubGraphQLClient` with the resolved token, using `GitHubConnectorConfig.BaseUrl`
as the GraphQL endpoint when set (supports GitHub Enterprise Server); fetch tags, releases,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ and `security` are preserved as the label name; unlabeled items default to `"oth

Steps: (1) get repository URL, branch, and current commit hash from Git via `RunCommandAsync`
(inherited from `RepoConnectorBase`); (2) determine owner and repository name from `_config` or
by parsing the remote URL; (3) resolve the GitHub authentication token; (4) create a
by calling `ParseGitHubUrl` on the remote URL — host-agnostic parsing accepts github.com, GitHub
Enterprise Cloud (`*.ghe.com`), and GitHub Enterprise Server (on-premises) instances for both SSH
(`git@<host>:owner/repo.git`) and HTTPS (`https://<host>/owner/repo`) URL formats; (3) resolve
the GitHub authentication token; (4) create a
`GitHubGraphQLClient` with the resolved token, using `_config.BaseUrl` as the GraphQL endpoint
when set (supports GitHub Enterprise Server); (5) fetch tags, releases, commits, pull requests
(with `body`), and issues (with `body`) via GraphQL; (6) determine the target version — if a
Expand All @@ -57,6 +60,64 @@ affected versions); (10) if routing rules are configured, call `ApplyRules` to p
`BuildInformation.RoutedSections`; otherwise use legacy `Changes`, `Bugs`, and `KnownIssues`
lists; (11) generate the changelog URL; (12) return the assembled `BuildInformation`.

**ParseGitHubUrl** (private static): Extracts owner and repository name from a git remote URL.

- *Parameters*: `url` (string) — SSH or HTTPS remote URL; leading and trailing whitespace is
trimmed.
- *Returns*: `(string owner, string repo)` — owner and repository name with `.git` suffix removed.
- *Algorithm*: Dispatches on URL prefix. For SSH format (`git@<host>:owner/repo.git`), the path
segment after the colon is forwarded to `ParseOwnerRepo`. For HTTPS format
(`https://<host>/owner/repo[.git]`), the host is stripped and the last two non-empty
slash-separated path segments are forwarded to `ParseOwnerRepo`. The hostname is never validated,
so github.com, GitHub Enterprise Cloud (`*.ghe.com`), and GitHub Enterprise Server (on-premises)
remotes are accepted uniformly.
- *Postconditions*: Returns a `(owner, repo)` tuple on success; throws `ArgumentException` when
the URL is not a recognized SSH or HTTPS remote.

**ParseOwnerRepo** (private static): Strips the `.git` suffix from a path segment and splits it
into owner and repository name at the `/` separator.

- *Parameters*: `path` (string) — path in `owner/repo[.git]` form.
- *Returns*: `(string owner, string repo)`.
- *Postconditions*: Throws `ArgumentException` when the path does not split into exactly two
slash-separated components.

**ResolveGraphQLEndpoint** (private static): Maps a configured base URL to the correct GraphQL
API endpoint for the target GitHub instance.

- *Parameters*: `baseUrl` (string?) — the configured base URL from `GitHubConnectorConfig.BaseUrl`.
- *Returns*: string? — the resolved GraphQL endpoint URL, or `null` when `baseUrl` is null or
empty (meaning the client should use its built-in default of `https://api.github.com/graphql`).
- *Routing branches*:
1. Null or blank `baseUrl` → returns `null` (use GitHub.com default).
2. URL already ends with `/graphql` (case-insensitive) → returns the trimmed URL unchanged
(explicit passthrough for callers that supply the full endpoint directly).
3. URL contains `api.github.com` → appends `/graphql` to the trimmed URL.
4. Otherwise (GitHub Enterprise Server) → appends `/api/graphql` to the trimmed URL.
- *Postconditions*: Returns a fully qualified GraphQL endpoint URL or `null`; never throws.

**GenerateGitHubChangelogLink** (private static): Builds a GitHub compare URL between two version
tags and wraps it in a `WebLink` record.

- *Parameters*:
- `owner` (string) — repository owner.
- `repo` (string) — repository name.
- `oldTag` (string?) — the baseline tag; pass `null` when there is no baseline.
- `newTag` (string) — the target (current) tag.
- `branchTagNames` (HashSet\<string\>) — set of tag names present on the current branch.
- `webBaseUrl` (string) — the scheme-and-host portion of the repository's web URL
(e.g., `https://github.com` or `https://github.mycompany.com`), derived by
`DeriveWebBaseUrlFromConfig` or `DeriveWebBaseUrl`.
- *Returns*: `WebLink?` — a `WebLink` whose `TargetUrl` is
`{webBaseUrl}/{owner}/{repo}/compare/{oldTag}...{newTag}` and whose label is
`{oldTag}...{newTag}`; returns `null` when `oldTag` is `null` or when either tag is
absent from `branchTagNames`.
- *Algorithm*: (1) return `null` if `oldTag` is `null`; (2) return `null` if either tag is
not in `branchTagNames`; (3) construct comparison label and URL from `webBaseUrl`; (4) return
`new WebLink(label, url)`.
- *Postconditions*: Returns a non-null `WebLink` only when a meaningful comparison range
exists on the current branch; never throws.

##### Error Handling

`GetBuildInformationAsync` throws `InvalidOperationException` when no GitHub token can be resolved,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ bypassing environment-variable checks.
`github.com`; `GitHubRepoConnector` as the default when `remoteUrl` is null or unrecognized.
- *Preconditions*: None.
- *Postconditions*: Returns a non-null `IRepoConnector`.
- *Note*: GitHub Enterprise Cloud (`*.ghe.com`) and GitHub Enterprise Server (on-premises)
remotes do not match the `github.com` substring check and therefore fall through to the
default `GitHubRepoConnector`. This is correct and expected behavior: `GitHubRepoConnector`
is host-agnostic and handles any GitHub remote regardless of hostname; the factory default
ensures that enterprise remotes are processed by the same connector as public GitHub.

Exposed internally so that unit tests can exercise URL-based detection logic without requiring a
real git process.
Expand Down
6 changes: 4 additions & 2 deletions docs/design/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ BuildMark (System)
│ └── Validation (Unit)
├── Utilities (Subsystem)
│ ├── PathHelpers (Unit)
│ └── ProcessRunner (Unit)
│ ├── ProcessRunner (Unit)
│ └── TemporaryDirectory (Unit)
├── Version (Subsystem)
│ ├── VersionComparable (Unit)
│ ├── VersionSemantic (Unit)
Expand Down Expand Up @@ -110,7 +111,8 @@ src/DemaConsulting.BuildMark/
│ └── Validation.cs - self-validation test runner
├── Utilities/
│ ├── PathHelpers.cs - safe path combination utilities
│ └── ProcessRunner.cs - process runner for Git commands
│ ├── ProcessRunner.cs - process runner for Git commands
│ └── TemporaryDirectory.cs - temporary directory lifecycle management
├── Version/
│ ├── VersionComparable.cs - core integer-based version comparison
│ ├── VersionSemantic.cs - semantic version with build metadata
Expand Down
12 changes: 12 additions & 0 deletions docs/reqstream/build-mark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ sections:
- BuildMark-RepoConnectors-GitHub
- BuildMark-Version-Subsystem

- id: BuildMark-GitHub-EnterpriseSupport
title: GitHub Enterprise support
justification: >
The tool shall support GitHub Enterprise Cloud and GitHub Enterprise Server
repositories by accepting SSH and HTTPS remote URLs with any hostname,
enabling owner and repository name resolution regardless of whether the host
is github.com, a GitHub Enterprise Cloud domain (*.ghe.com), or a GitHub
Enterprise Server on-premises instance.
children:
- BuildMark-GitHub-ParseUrl-SSH
- BuildMark-GitHub-ParseUrl-HTTPS

- title: Azure DevOps Integration
requirements:
- id: BuildMark-AzureDevOps-BuildNotes
Expand Down
3 changes: 2 additions & 1 deletion docs/reqstream/build-mark/repo-connectors/azure-devops.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ sections:
- AzureDevOps_GetBuildInformation_WithOpenWorkItems_IdentifiesKnownIssues
- AzureDevOps_GetBuildInformation_ReleaseVersion_SkipsPreReleases
children:
- BuildMark-AzureDevOps-UrlParsing
- BuildMark-AzureDevOps-UrlParsing-HTTPS
- BuildMark-AzureDevOps-UrlParsing-SSH
- BuildMark-AzureDevOps-ConnectorConfig
- BuildMark-AzureDevOps-BuildInformation
- BuildMark-AzureDevOps-ItemControls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,34 @@ sections:
sections:
- title: AzureDevOpsRepoConnector Requirements
requirements:
- id: BuildMark-AzureDevOps-UrlParsing
- id: BuildMark-AzureDevOps-UrlParsing-HTTPS
title: >-
The AzureDevOpsRepoConnector class shall parse organization URL, project, and repository
from Azure DevOps Services and on-premises Azure DevOps Server git remote URLs.
from Azure DevOps HTTPS remote URLs, supporting Azure DevOps Services (dev.azure.com,
visualstudio.com) and on-premises Azure DevOps Server instances.
justification: |
The connector must support both cloud Azure DevOps Services (dev.azure.com,
visualstudio.com) and on-premises Azure DevOps Server instances. A unified URL
parser using the _git path segment anchor handles all HTTPS URL formats, while
SSH URLs use the git@ssh.dev.azure.com:v3 format.
The connector must support HTTPS URL formats used by cloud Azure DevOps Services
(dev.azure.com, visualstudio.com) and on-premises Azure DevOps Server instances.
A unified URL parser using the _git path segment anchor handles all HTTPS URL formats.
tests:
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_DevAzureComHttps_ReturnsCorrectComponents
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_DevAzureComWithGitSuffix_StripsGitSuffix
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_VisualStudioComHttps_ReturnsCorrectComponents
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_SshUrl_ReturnsCorrectComponents
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_OnPremServer_ReturnsCorrectComponents
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_OnPremWithPort_ReturnsCorrectComponents
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_OnPremWithGitSuffix_StripsGitSuffix
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_UnsupportedFormat_ThrowsArgumentException

- id: BuildMark-AzureDevOps-UrlParsing-SSH
title: >-
The AzureDevOpsRepoConnector class shall parse organization URL, project, and repository
from Azure DevOps SSH remote URLs (git@ssh.dev.azure.com:v3 format).
justification: |
The connector must support the SSH URL format used by Azure DevOps Services
(git@ssh.dev.azure.com:v3/org/project/repo) in addition to HTTPS formats.
tests:
- AzureDevOpsRepoConnector_ParseAzureDevOpsUrl_SshUrl_ReturnsCorrectComponents

- id: BuildMark-AzureDevOps-ConnectorConfig
title: >-
The AzureDevOpsRepoConnector class shall accept an optional AzureDevOpsConnectorConfig
Expand Down
2 changes: 2 additions & 0 deletions docs/reqstream/build-mark/repo-connectors/github.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ sections:
- BuildMark-GitHub-GraphQLClient
- BuildMark-GitHub-Rules
- BuildMark-GitHub-TokenVariable
- BuildMark-GitHub-ParseUrl-SSH
- BuildMark-GitHub-ParseUrl-HTTPS
Loading
Loading