-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Service client release notes #1780
Changes from all commits
dc80f19
e117102
eb4fed9
51d58ec
c8891ee
55146a6
956e8ff
48710f7
cedd3ea
21177ef
bda2b0e
4dc331d
f2c6d4f
4e1181e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// | ||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
import Foundation | ||
import AWSCLIUtils | ||
|
||
struct FeaturesReader: Decodable { | ||
private let requestFilePath: String | ||
private let mappingFilePath: String | ||
|
||
public init( | ||
requestFilePath: String = "../build-request.json", | ||
mappingFilePath: String = "../feature-service-id.json" | ||
) { | ||
self.requestFilePath = requestFilePath | ||
self.mappingFilePath = mappingFilePath | ||
} | ||
|
||
public func getFeaturesFromFile() throws -> Features { | ||
let fileContents = try FileManager.default.loadContents(atPath: requestFilePath) | ||
return try JSONDecoder().decode(Features.self, from: fileContents) | ||
} | ||
|
||
public func getFeaturesIDToServiceNameDictFromFile() throws -> [String: String] { | ||
let fileContents = try FileManager.default.loadContents(atPath: mappingFilePath) | ||
return try JSONDecoder().decode([String: String].self, from: fileContents) | ||
} | ||
} | ||
|
||
struct Features: Decodable { | ||
let features: [Feature] | ||
} | ||
|
||
struct Feature: Decodable { | ||
let releaseNotes: String | ||
let featureMetadata: FeatureMetadata | ||
|
||
struct FeatureMetadata: Decodable { | ||
let trebuchet: Trebuchet | ||
|
||
struct Trebuchet: Decodable { | ||
let featureId: String | ||
let featureType: String | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,24 +15,62 @@ struct ReleaseNotesBuilder { | |
let repoOrg: PrepareRelease.Org | ||
let repoType: PrepareRelease.Repo | ||
let commits: [String] | ||
|
||
var featuresReader: FeaturesReader = FeaturesReader() | ||
|
||
// MARK: - Build | ||
|
||
func build() -> String { | ||
let contents = [ | ||
"## What's Changed", | ||
buildCommits(), | ||
.newline, | ||
"**Full Changelog**: https://github.com/\(repoOrg.rawValue)/\(repoType.rawValue)/compare/\(previousVersion)...\(newVersion)" | ||
|
||
func build() throws -> String { | ||
let sdkChanges: [String] = buildSDKChangeSection() | ||
let serviceClientChanges = repoType == .awsSdkSwift ? (try buildServiceChangeSection()) : [] | ||
let fullCommitLogLink = [ | ||
"\n**Full Changelog**: https://github.com/\(repoOrg.rawValue)/\(repoType.rawValue)/compare/\(previousVersion)...\(newVersion)" | ||
] | ||
let contents = ["## What's Changed"] + serviceClientChanges + sdkChanges + fullCommitLogLink | ||
return contents.joined(separator: .newline) | ||
} | ||
|
||
// Adds a preceding `*` to each commit string | ||
// This renders the list of commits as a list in markdown | ||
func buildCommits() -> String { | ||
commits | ||
.map { "* \($0)"} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add new sections for release notes; one for service feature changes & one for service documentation update changes. |
||
func buildSDKChangeSection() -> [String] { | ||
let formattedCommits = commits | ||
.filter { $0.hasPrefix("feat") || $0.hasPrefix("fix") } | ||
.map { "* \($0)" } | ||
.joined(separator: .newline) | ||
if (!formattedCommits.isEmpty) { | ||
return ["### Miscellaneous", formattedCommits] | ||
} | ||
return [] | ||
} | ||
|
||
func buildServiceChangeSection() throws -> [String] { | ||
let features = try featuresReader.getFeaturesFromFile() | ||
let mapping = try featuresReader.getFeaturesIDToServiceNameDictFromFile() | ||
return buildServiceFeatureSection(features, mapping) + buildServiceDocSection(features, mapping) | ||
} | ||
|
||
private func buildServiceFeatureSection( | ||
_ features: Features, | ||
_ mapping: [String: String] | ||
) -> [String] { | ||
let formattedFeatures = features.features | ||
.filter { $0.featureMetadata.trebuchet.featureType == "NEW_FEATURE" } | ||
.map { "* **AWS \(mapping[$0.featureMetadata.trebuchet.featureId]!)**: \($0.releaseNotes)" } | ||
.joined(separator: .newline) | ||
if (!formattedFeatures.isEmpty) { | ||
return ["### Service Features", formattedFeatures] | ||
} | ||
return [] | ||
} | ||
|
||
private func buildServiceDocSection( | ||
_ features: Features, | ||
_ mapping: [String: String] | ||
) -> [String] { | ||
let formattedDocUpdates = features.features | ||
.filter { $0.featureMetadata.trebuchet.featureType == "DOC_UPDATE" } | ||
.map { "* **AWS \(mapping[$0.featureMetadata.trebuchet.featureId]!)**: \($0.releaseNotes)" } | ||
.joined(separator: .newline) | ||
if (!formattedDocUpdates.isEmpty) { | ||
return ["### Service Documentation", formattedDocUpdates] | ||
} | ||
return [] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,16 @@ class PrepareReleaseTests: CLITestCase { | |
createPackageVersion(previousVersion) | ||
createNextPackageVersion(newVersion) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creates files with bare minimum strings for making this test pass bc |
||
let buildRequest = """ | ||
{ | ||
"features": [] | ||
} | ||
""" | ||
FileManager.default.createFile(atPath: "build-request.json", contents: Data(buildRequest.utf8)) | ||
|
||
let mapping = "{}" | ||
FileManager.default.createFile(atPath: "feature-service-id.json", contents: Data(mapping.utf8)) | ||
|
||
let subject = PrepareRelease.mock(repoType: .awsSdkSwift, diffChecker: { _,_ in true }) | ||
try! subject.run() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,6 @@ class PackageManifestBuilderTests: XCTestCase { | |
|
||
let expected = """ | ||
<contents of prefix> | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this test expectation changed? This feature doesn't involve the manifest. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test was failing; I don't think this test was being run as part of CI with the package refactor task from before. |
||
// MARK: - Dynamic Content | ||
|
||
let clientRuntimeVersion: Version = "1.2.3" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regression tests for |
||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
@testable import AWSSDKSwiftCLI | ||
import AWSCLIUtils | ||
import XCTest | ||
|
||
/* | ||
* Regression tests for protection against change in generated release notes markdown content. | ||
*/ | ||
class ReleaseNotesBuilderTests: XCTestCase { | ||
/* Reusable feature strings */ | ||
|
||
// New feature 1 | ||
private let feature1 = """ | ||
{ | ||
"releaseNotes": "New feature description A.", | ||
"featureMetadata": { | ||
"trebuchet": { | ||
"featureId": "feature-id-a", | ||
"featureType": "NEW_FEATURE", | ||
} | ||
} | ||
} | ||
""" | ||
|
||
// New feature 2 | ||
private let feature2 = """ | ||
{ | ||
"releaseNotes": "New feature description B.", | ||
"featureMetadata": { | ||
"trebuchet": { | ||
"featureId": "feature-id-b", | ||
"featureType": "NEW_FEATURE", | ||
} | ||
} | ||
} | ||
""" | ||
|
||
// Documentation update | ||
private let feature3 = """ | ||
{ | ||
"releaseNotes": "Doc update description C.", | ||
"featureMetadata": { | ||
"trebuchet": { | ||
"featureId": "feature-id-c", | ||
"featureType": "DOC_UPDATE", | ||
} | ||
} | ||
} | ||
""" | ||
|
||
// Dictionary of feature ID to name of the service | ||
private let mapping = """ | ||
{ | ||
"feature-id-a": "Service 1", | ||
"feature-id-b": "Service 2", | ||
"feature-id-c": "Service 3" | ||
} | ||
""" | ||
|
||
func testAllSectionsPresent() throws { | ||
let buildRequest = """ | ||
{ "features": [\(feature1), \(feature2), \(feature3)] } | ||
""" | ||
setUpBuildRequestAndMappingJSONs(buildRequest, mapping) | ||
let builder = try setUpBuilder(testCommits: ["fix: Fix X", "feat: Feat Y"]) | ||
let releaseNotes = try builder.build() | ||
let expected = """ | ||
## What's Changed | ||
### Service Features | ||
* **AWS Service 1**: New feature description A. | ||
* **AWS Service 2**: New feature description B. | ||
### Service Documentation | ||
* **AWS Service 3**: Doc update description C. | ||
### Miscellaneous | ||
* fix: Fix X | ||
* feat: Feat Y | ||
|
||
**Full Changelog**: https://github.com/awslabs/aws-sdk-swift/compare/1.0.0...1.0.1 | ||
""" | ||
XCTAssertEqual(releaseNotes, expected) | ||
} | ||
|
||
func testNoServiceFeatureSectionPresent() throws { | ||
let buildRequest = """ | ||
{ "features": [\(feature3)] } | ||
""" | ||
setUpBuildRequestAndMappingJSONs(buildRequest, mapping) | ||
let builder = try setUpBuilder(testCommits: ["fix: Fix X", "feat: Feat Y"]) | ||
let releaseNotes = try builder.build() | ||
let expected = """ | ||
## What's Changed | ||
### Service Documentation | ||
* **AWS Service 3**: Doc update description C. | ||
### Miscellaneous | ||
* fix: Fix X | ||
* feat: Feat Y | ||
|
||
**Full Changelog**: https://github.com/awslabs/aws-sdk-swift/compare/1.0.0...1.0.1 | ||
""" | ||
XCTAssertEqual(releaseNotes, expected) | ||
} | ||
|
||
func testNoServiceDocSectionPresent() throws { | ||
let buildRequest = """ | ||
{ "features": [\(feature1), \(feature2)] } | ||
""" | ||
setUpBuildRequestAndMappingJSONs(buildRequest, mapping) | ||
let builder = try setUpBuilder(testCommits: ["fix: Fix X", "feat: Feat Y"]) | ||
let releaseNotes = try builder.build() | ||
let expected = """ | ||
## What's Changed | ||
### Service Features | ||
* **AWS Service 1**: New feature description A. | ||
* **AWS Service 2**: New feature description B. | ||
### Miscellaneous | ||
* fix: Fix X | ||
* feat: Feat Y | ||
|
||
**Full Changelog**: https://github.com/awslabs/aws-sdk-swift/compare/1.0.0...1.0.1 | ||
""" | ||
XCTAssertEqual(releaseNotes, expected) | ||
} | ||
|
||
func testNoSDKChangeSectionPresent() throws { | ||
let buildRequest = """ | ||
{ "features": [\(feature1), \(feature2), \(feature3)] } | ||
""" | ||
setUpBuildRequestAndMappingJSONs(buildRequest, mapping) | ||
let builder = try setUpBuilder() | ||
let releaseNotes = try builder.build() | ||
let expected = """ | ||
## What's Changed | ||
### Service Features | ||
* **AWS Service 1**: New feature description A. | ||
* **AWS Service 2**: New feature description B. | ||
### Service Documentation | ||
* **AWS Service 3**: Doc update description C. | ||
|
||
**Full Changelog**: https://github.com/awslabs/aws-sdk-swift/compare/1.0.0...1.0.1 | ||
""" | ||
XCTAssertEqual(releaseNotes, expected) | ||
} | ||
|
||
func testNoSectionsPresentAndIrrelevantCommitsAreFiltered() throws { | ||
let buildRequest = """ | ||
{ "features":[] } | ||
""" | ||
setUpBuildRequestAndMappingJSONs(buildRequest, mapping) | ||
let builder = try setUpBuilder(testCommits: ["chore: Commit A", "Update X"]) | ||
let releaseNotes = try builder.build() | ||
let expected = """ | ||
## What's Changed | ||
|
||
**Full Changelog**: https://github.com/awslabs/aws-sdk-swift/compare/1.0.0...1.0.1 | ||
""" | ||
XCTAssertEqual(releaseNotes, expected) | ||
} | ||
|
||
private func setUpBuildRequestAndMappingJSONs(_ buildRequest: String, _ mapping: String) { | ||
// In real scenario, the JSON files we need are located one level above, in the workspace directory. | ||
// For tests, due to sandboxing, the dummy files are created in current directory instead of | ||
// in parent directory. | ||
FileManager.default.createFile(atPath: "build-request.json", contents: Data(buildRequest.utf8)) | ||
FileManager.default.createFile(atPath: "feature-service-id.json", contents: Data(mapping.utf8)) | ||
} | ||
|
||
private func setUpBuilder(testCommits: [String] = []) throws -> ReleaseNotesBuilder { | ||
return try ReleaseNotesBuilder( | ||
previousVersion: Version("1.0.0"), | ||
newVersion: Version("1.0.1"), | ||
repoOrg: .awslabs, | ||
repoType: .awsSdkSwift, | ||
commits: testCommits, | ||
// Parametrize behavior of FeaturesReader with paths used to create JSON test files | ||
featuresReader: FeaturesReader( | ||
requestFilePath: "build-request.json", | ||
mappingFilePath: "feature-service-id.json" | ||
) | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The containers for decoding info we need from the JSON files available to use from internal build system
&
The reader struct for changing file paths for tests (can't go
../
due to sandboxing limits)