clrf cleanup
This commit is contained in:
+6
-15
@@ -1,14 +1,10 @@
|
|||||||
# Release model
|
# Release model
|
||||||
|
|
||||||
Each app release gets one document that keeps its version, build information,
|
Each app release gets one document that keeps its version, build information, and public changelog together in one place.
|
||||||
and public changelog together in one place. We are building this new release
|
|
||||||
model alongside the existing production workflows so we can try it safely
|
|
||||||
before using it for a real release.
|
|
||||||
|
|
||||||
## Identity
|
## Identity
|
||||||
|
|
||||||
The workflow accepts one version in strict `x.y.z` format and derives all other
|
The workflow accepts one version in strict `x.y.z` format and derives all other identifiers from it:
|
||||||
identifiers from it:
|
|
||||||
|
|
||||||
| Resource | Format |
|
| Resource | Format |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -22,8 +18,7 @@ Callers must not supply these derived identifiers independently.
|
|||||||
|
|
||||||
## Prepared state
|
## Prepared state
|
||||||
|
|
||||||
The preparation workflow creates the document before freezing the native
|
The preparation workflow creates the document before freezing the native candidate. At this stage, only `releaseVersion` is required:
|
||||||
candidate. At this stage, only `releaseVersion` is required:
|
|
||||||
|
|
||||||
```md
|
```md
|
||||||
---
|
---
|
||||||
@@ -43,8 +38,7 @@ releaseVersion: 1.131.1
|
|||||||
|
|
||||||
## Final state
|
## Final state
|
||||||
|
|
||||||
After both native builds succeed, the workflow records the frozen source and
|
After both native builds succeed, the workflow records the frozen source and artifact-derived build numbers. A finalized document requires every field:
|
||||||
artifact-derived build numbers. A finalized document requires every field:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
releaseVersion: 1.131.1
|
releaseVersion: 1.131.1
|
||||||
@@ -54,9 +48,6 @@ iosBuildNumber: 1662
|
|||||||
androidVersionCode: 1110
|
androidVersionCode: 1110
|
||||||
```
|
```
|
||||||
|
|
||||||
`sourceTag` must equal `releaseVersion`, `sourceSha` must be a full Git object
|
`sourceTag` must equal `releaseVersion`, `sourceSha` must be a full Git object ID, and both build numbers must be positive integers.
|
||||||
ID, and both build numbers must be positive integers.
|
|
||||||
|
|
||||||
Each successful OTA adds exactly one contiguous section (`OTA 1`, `OTA 2`, and
|
Each successful OTA adds exactly one contiguous section (`OTA 1`, `OTA 2`, and so on) inside the public changelog delimiters. GitHub Release text is extracted only from those delimiters; operational frontmatter is never published.
|
||||||
so on) inside the public changelog delimiters. GitHub Release text is extracted
|
|
||||||
only from those delimiters; operational frontmatter is never published.
|
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ function fail(message) {
|
|||||||
throw new ReleaseError(message)
|
throw new ReleaseError(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeLineEndings(markdown) {
|
||||||
|
return markdown.replace(/\r\n?/g, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
export function assertReleaseVersion(version) {
|
export function assertReleaseVersion(version) {
|
||||||
if (typeof version !== 'string' || !VERSION_PATTERN.test(version)) {
|
if (typeof version !== 'string' || !VERSION_PATTERN.test(version)) {
|
||||||
fail(`Release version must use strict x.y.z format; found '${version}'.`)
|
fail(`Release version must use strict x.y.z format; found '${version}'.`)
|
||||||
@@ -49,7 +53,7 @@ export function deriveReleaseIdentity(version) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseFrontmatter(markdown) {
|
function parseFrontmatter(markdown) {
|
||||||
const normalized = markdown.replace(/\r\n/g, '\n')
|
const normalized = normalizeLineEndings(markdown)
|
||||||
if (!normalized.startsWith('---\n')) {
|
if (!normalized.startsWith('---\n')) {
|
||||||
fail('Release file must start with YAML frontmatter.')
|
fail('Release file must start with YAML frontmatter.')
|
||||||
}
|
}
|
||||||
@@ -249,7 +253,8 @@ export function extractPublicChangelog(markdown) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function appendOtaChangelog(markdown, sequence, changelog) {
|
export function appendOtaChangelog(markdown, sequence, changelog) {
|
||||||
const parsed = parseReleaseDocument(markdown)
|
const normalized = normalizeLineEndings(markdown)
|
||||||
|
const parsed = parseReleaseDocument(normalized)
|
||||||
const expectedSequence = parsed.sections.length
|
const expectedSequence = parsed.sections.length
|
||||||
if (!Number.isSafeInteger(sequence) || sequence !== expectedSequence) {
|
if (!Number.isSafeInteger(sequence) || sequence !== expectedSequence) {
|
||||||
fail(`Next OTA sequence must be ${expectedSequence}; found '${sequence}'.`)
|
fail(`Next OTA sequence must be ${expectedSequence}; found '${sequence}'.`)
|
||||||
@@ -258,22 +263,29 @@ export function appendOtaChangelog(markdown, sequence, changelog) {
|
|||||||
if (!content) fail('OTA changelog cannot be empty.')
|
if (!content) fail('OTA changelog cannot be empty.')
|
||||||
|
|
||||||
const delimiter = `\n\n${PUBLIC_CHANGELOG_END}`
|
const delimiter = `\n\n${PUBLIC_CHANGELOG_END}`
|
||||||
|
if (!normalized.includes(delimiter)) {
|
||||||
|
fail('Could not find the public changelog end delimiter to append the OTA.')
|
||||||
|
}
|
||||||
const replacement = `\n\n## OTA ${sequence}\n\n${content}${delimiter}`
|
const replacement = `\n\n## OTA ${sequence}\n\n${content}${delimiter}`
|
||||||
const updated = markdown.replace(delimiter, replacement)
|
const updated = normalized.replace(delimiter, replacement)
|
||||||
parseReleaseDocument(updated)
|
parseReleaseDocument(updated)
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
export function finalizeReleaseDocument(markdown, finalMetadata) {
|
export function finalizeReleaseDocument(markdown, finalMetadata) {
|
||||||
const parsed = parseReleaseDocument(markdown)
|
const normalized = normalizeLineEndings(markdown)
|
||||||
|
const parsed = parseReleaseDocument(normalized)
|
||||||
const metadata = {...parsed.metadata, ...finalMetadata}
|
const metadata = {...parsed.metadata, ...finalMetadata}
|
||||||
validateMetadata(metadata, 'final')
|
validateMetadata(metadata, 'final')
|
||||||
|
|
||||||
const frontmatter = FRONTMATTER_KEYS.map(
|
const frontmatter = FRONTMATTER_KEYS.map(
|
||||||
key => `${key}: ${metadata[key]}`,
|
key => `${key}: ${metadata[key]}`,
|
||||||
).join('\n')
|
).join('\n')
|
||||||
const updated = markdown.replace(
|
const frontmatterPattern = /^---\n[\s\S]*?\n---\n/
|
||||||
/^---\n[\s\S]*?\n---\n/,
|
if (!frontmatterPattern.test(normalized))
|
||||||
|
fail('Could not replace the release frontmatter.')
|
||||||
|
const updated = normalized.replace(
|
||||||
|
frontmatterPattern,
|
||||||
`---\n${frontmatter}\n---\n`,
|
`---\n${frontmatter}\n---\n`,
|
||||||
)
|
)
|
||||||
parseReleaseDocument(updated, {stage: 'final'})
|
parseReleaseDocument(updated, {stage: 'final'})
|
||||||
|
|||||||
Reference in New Issue
Block a user