Skip to content

Commit

Permalink
Merge pull request #13 from andres97medrano/andresm/add-profile-name-…
Browse files Browse the repository at this point in the history
…parameter

Add optional --profile-name parameter to create-provisioning-profile subcommand
  • Loading branch information
tinder-maxwellelliott committed Jan 26, 2024
2 parents b4426cc + 6071d80 commit ec0886a
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 13 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ OPTIONS:
Path to the openssl executable, this is used to generate CSR signing artifacts that are required when creating certificates
--intermediary-apple-certificates <intermediary-apple-certificates>
Intermediary Apple Certificates that should also be added to the keychain (https://www.apple.com/certificateauthority/)
--profile-name <profile-name>
The name that you would like to assign to the created provisioning profile (optional)
--certificate-signing-request-subject <certificate-signing-request-subject>
Subject for the Certificate Signing Request when creating certificates.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
case opensslPath = "opensslPath"
case intermediaryAppleCertificates = "intermediaryAppleCertificates"
case certificateSigningRequestSubject = "certificateSigningRequestSubject"
case profileName = "profileName"
}

@Option(help: "The key identifier of the private key (https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests)")
Expand Down Expand Up @@ -165,6 +166,9 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
@Option(help: "Intermediary Apple Certificates that should also be added to the keychain (https://www.apple.com/certificateauthority/)")
internal var intermediaryAppleCertificates: [String] = []

@Option(help: "The name that you would like to assign to the created provisioning profile (optional)")
internal var profileName: String?

@Option(help: """
Subject for the Certificate Signing Request when creating certificates.
Expand Down Expand Up @@ -223,7 +227,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
intermediaryAppleCertificates: [String],
certificateSigningRequestSubject: String,
bundleIdentifierName: String?,
platform: String
platform: String,
profileName: String?
) {
self.files = files
self.log = log
Expand All @@ -246,6 +251,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
self.certificateSigningRequestSubject = certificateSigningRequestSubject
self.bundleIdentifierName = bundleIdentifierName
self.platform = platform
self.profileName = profileName
}

internal init(from decoder: Decoder) throws {
Expand Down Expand Up @@ -279,7 +285,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
intermediaryAppleCertificates: try container.decodeIfPresent([String].self, forKey: .intermediaryAppleCertificates) ?? [],
certificateSigningRequestSubject: try container.decode(String.self, forKey: .certificateSigningRequestSubject),
bundleIdentifierName: try container.decodeIfPresent(String.self, forKey: .bundleIdentifierName),
platform: try container.decode(String.self, forKey: .platform)
platform: try container.decode(String.self, forKey: .platform),
profileName: try container.decodeIfPresent(String.self, forKey: .profileName)
)
}

Expand Down Expand Up @@ -315,7 +322,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand {
),
certificateId: certificateId,
deviceIDs: deviceIDs,
profileType: profileType
profileType: profileType,
profileName: profileName
)
guard let profileData: Data = .init(base64Encoded: profileResponse.data.attributes.profileContent)
else {
Expand Down
8 changes: 5 additions & 3 deletions Sources/SignHereLibrary/Services/iTunesConnectService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ internal protocol iTunesConnectService {
bundleId: String,
certificateId: String,
deviceIDs: Set<String>,
profileType: String
profileType: String,
profileName: String?
) throws -> CreateProfileResponse
func deleteProvisioningProfile(
jsonWebToken: String,
Expand Down Expand Up @@ -352,7 +353,8 @@ internal class iTunesConnectServiceImp: iTunesConnectService {
bundleId: String,
certificateId: String,
deviceIDs: Set<String>,
profileType: String
profileType: String,
profileName: String? = nil
) throws -> CreateProfileResponse {
let urlString: String = "https://api.appstoreconnect.apple.com/v1/profiles"
guard let url: URL = .init(string: urlString)
Expand All @@ -364,7 +366,7 @@ internal class iTunesConnectServiceImp: iTunesConnectService {
request.setValue(Constants.applicationJSONHeaderValue, forHTTPHeaderField: Constants.contentTypeHeaderName)
request.setValue("Bearer \(jsonWebToken)", forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
let profileName: String = "\(certificateId)_\(profileType)_\(clock.now().timeIntervalSince1970)"
let profileName = profileName ?? "\(certificateId)_\(profileType)_\(clock.now().timeIntervalSince1970)"
var devices: CreateProfileRequest.CreateProfileRequestData.Relationships.Devices? = nil
// ME: App Store profiles cannot use UDIDs
if !["IOS_APP_STORE", "MAC_APP_STORE", "TVOS_APP_STORE", "MAC_CATALYST_APP_STORE"].contains(profileType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ final class CreateProvisioningProfileCommandTests: XCTestCase {
intermediaryAppleCertificates: ["/intermediaryAppleCertificate"],
certificateSigningRequestSubject: "certificateSigningRequestSubject",
bundleIdentifierName: "bundleIdentifierName",
platform: "platform"
platform: "platform",
profileName: "profileName"
)
isRecording = false
}
Expand Down Expand Up @@ -160,7 +161,8 @@ final class CreateProvisioningProfileCommandTests: XCTestCase {
"opensslPath": "/opensslPath",
"certificateSigningRequestSubject": "certificateSigningRequestSubject",
"bundleIdentifierName": "bundleIdentifierName",
"platform": "platform"
"platform": "platform",
"profileName": "profileName"
}
""".utf8)

Expand All @@ -180,6 +182,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase {
XCTAssertEqual(subject.outputPath, "/outputPath")
XCTAssertEqual(subject.bundleIdentifierName, "bundleIdentifierName")
XCTAssertEqual(subject.platform, "platform")
XCTAssertEqual(subject.profileName, "profileName")
}

func test_execute_alreadyActiveCertificate() throws {
Expand Down Expand Up @@ -210,7 +213,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase {
iTunesConnectService.createCertificateHandler = { _, _, _ in
self.createCreateCertificateResponse()
}
iTunesConnectService.createProfileHandler = { _, _, _, _, _ in
iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in
self.createCreateProfileResponse()
}

Expand Down Expand Up @@ -253,7 +256,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase {
iTunesConnectService.createCertificateHandler = { _, _, _ in
self.createCreateCertificateResponse()
}
iTunesConnectService.createProfileHandler = { _, _, _, _, _ in
iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in
self.createCreateProfileResponse()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
curl \
--request POST \
--header "Accept: application/json" \
--header "Authorization: Bearer jsonWebToken" \
--header "Content-Type: application/json" \
--data "{\"data\":{\"attributes\":{\"name\":\"mySpecialProfile\",\"profileType\":\"profileType\"},\"type\":\"profiles\",\"relationships\":{\"devices\":{\"data\":[{\"id\":\"deviceId\",\"type\":\"devices\"}]},\"certificates\":{\"data\":[{\"id\":\"certificateId\",\"type\":\"certificates\"}]},\"bundleId\":{\"data\":{\"id\":\"bundleId\",\"type\":\"bundleIds\"}}}}}" \
"https://api.appstoreconnect.apple.com/v1/profiles"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
▿ CreateProfileResponse
▿ data: CreateProfileResponseData
▿ attributes: Attributes
- createdDate: 1970-01-01T00:00:00Z
- expirationDate: 1970-01-01T00:01:40Z
- name: "createdProfileName"
- platform: "platform"
- profileContent: "dGVzdAo="
- profileState: "profileState"
- profileType: "profileType"
- uuid: "uuid"
- id: "createdProfileITCID"
- type: "type"
39 changes: 36 additions & 3 deletions Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,38 @@ final class iTunesConnectServiceTests: XCTestCase {
bundleId: "bundleId",
certificateId: "certificateId",
deviceIDs: .init(["deviceId"]),
profileType: "profileType"
profileType: "profileType",
profileName: nil
)

// THEN
for argValue in network.executeArgValues {
assertSnapshot(matching: argValue, as: .curl)
}
assertSnapshot(
matching: value,
as: .dump
)
}

func test_createProfile_withProfileName() throws {
// GIVEN
let jsonEncoder: JSONEncoder = createJSONEncoder()
var networkExecutes: [Data] = [
try jsonEncoder.encode(createCreateProfileResponse()),
]
network.executeHandler = { _ in
return networkExecutes.removeFirst()
}

// WHEN
let value: CreateProfileResponse = try subject.createProfile(
jsonWebToken: "jsonWebToken",
bundleId: "bundleId",
certificateId: "certificateId",
deviceIDs: .init(["deviceId"]),
profileType: "profileType",
profileName: "mySpecialProfile"
)

// THEN
Expand Down Expand Up @@ -596,7 +627,8 @@ final class iTunesConnectServiceTests: XCTestCase {
bundleId: "bundleId",
certificateId: "certificateId",
deviceIDs: .init(["deviceId"]),
profileType: "IOS_APP_STORE"
profileType: "IOS_APP_STORE",
profileName: nil
)

// THEN
Expand Down Expand Up @@ -624,7 +656,8 @@ final class iTunesConnectServiceTests: XCTestCase {
bundleId: "bundleId",
certificateId: "certificateId",
deviceIDs: .init(["deviceId"]),
profileType: "profileType"
profileType: "profileType",
profileName: nil
)) {
if case iTunesConnectServiceImp.Error.unableToDecodeResponse = $0 {
return
Expand Down

0 comments on commit ec0886a

Please sign in to comment.