electron / electron/clerk

Project: Release Notes Service

Open
#66 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
62
Forks
24
Avg merge
1h 49m
Merged PRs (30d)
5

Description

Clerk currently serves a single purpose, validating that every PR has a valid `Notes: .*` block in the PR body and then storing those notes on merge to ensure they are frozen.

As part of https://github.com/electron/electron/issues/30516 we need to extend Clerk to be the primary source of complete release notes generation. Implementation aside Clerk should implement a single API endpoint

### POST `/notes`

#### Request Body

```json
{
"startVersion": "9.1.2",
"endVersion": "10.0.1",
}
```

#### Response Body

##### Request Started: 201 Created

If the cache does not have notes between the given versions, we should queue notes generation and return a `201 Created` status code immediately with an empty body.

##### Request In Progress: 202 Accepted

If the cache does not have notes between the given versions and a notes generation for the given range is already queued or in progress we should return a `202 Accepted` status code with an empty response body.

##### Notes Complete: 200 OK

```typescript
type Change = {
type: "fix" | "feature" | "breaking_change" | "docs";
// Some notes are multi-line, this should be an array of each line
description: string[];
// PR that caused this change
prNumber: number;
// If the PR that caused this change was a backport, this field should be populated
// as the number of the original PR that landed to "main"
originalPrNumber: number;
// The version this change was first released in, in the context of this release notes request
// just because this change may have been released in other versions it doesn't matter
landedInVersion: string;
};

type Response = {
// Changes that landed between startVersion and endVersion
additions: Change[];
// Changes that were available in startVersion but were backported
// to a version in the endVersion major line AFTER the endVersion
// i.e. in order to retain the change you have to update to a newer
// patch version of endVersion
removals: Change[];
}
```

#### Algorithm for determining changes

Given a `startVersion` and `endVersion` each with known properties `major`, `minor`, `patch`, `preReleaseTag` and `preReleaseNumber`.

| Version | Major | Minor | Patch | Pre Release Tag | Pre Release Number |
|--|--|--|--|--|--|
| 12.0.0-alpha.1 | 12 | 0 | 0 | alpha | 1 |
| 13.1.2 | 13 | 1 | 2 | | |

Very pseudo code 😄
```python
changes = []
removals = []

# returns the last commit hash that the branch for the given major release
# shares with the "main" branch.
# This can be simplified as the "newest common ancestor between main and majorRelease"
def branchPoint(majorRelease);

# returns a "Change" object for the given commit or null if no change could be
# identified.
# This should follow normal logic of:
# --> Find PR that landed commit
# --> Pull notes from PR
# --> Check if PR was backport, if so find original PR
def buildChange(commit);

# returns True if no change in the given list matches / is identical to the provided
# change. This is determined via either the PR numbers matching (backport of th
# same PR) or the description strings being idential.
def noChangeMatches(changeList, change);

already_present_changes = []
duplicated_changes = []

for commit in range(branchPoint(startVersion.major), startVersion):
change = buildChange(commit)
if change is not None:
already_present_changes.append(change)

for commit in range(branchPoint(startVersion.major), endVersion):
change = buildChange(commit)
if change is not None:
# Only include the change if it wasn't already in the startVersion
if noChangeMatches(already_present_changes, change):
changes.append(change)
else:
duplicated_changes.append(change)

# Any change that was already present after the branch point but we did not find a duplicate of
# in the target version should be considered "removed"
removals = inverse_intersect(already_present_changes, duplicated_changes)
```

Every single part of this logic needs to be heavily cached in PSQL / Redis. We should only calculate the release notes between two versions **once** regardless of how many people request it.

All GitHub API requests should be heavily cached internally as well, requests for commit information can be cached indefinitely. PR information should not be cached. Notes should be cached indefinitely, if we need to cache bust we should be able to do so by re-deploying Clerk with an incremented cache key.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.