Skip to content

Commit 2db0d8c

Browse files
authored
Merge pull request sparkle-project#1571 from D0miH/feature/1569-download-url-prefix-option
Prefix for download url can be provided to generate_appcast
2 parents 01b48d4 + 0d16b45 commit 2db0d8c

File tree

2 files changed

+144
-45
lines changed

2 files changed

+144
-45
lines changed

generate_appcast/ArchiveItem.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class ArchiveItem: CustomStringConvertible {
4646

4747
var dsaSignature: String?;
4848
var edSignature: String?;
49+
var downloadUrlPrefix: URL?;
4950

5051
init(version: String, shortVersion: String?, feedURL: URL?, minimumSystemVersion: String?, publicEdKey: String?, supportsDSA: Bool, appPath: URL, archivePath: URL) throws {
5152
self.version = version;
@@ -125,8 +126,11 @@ class ArchiveItem: CustomStringConvertible {
125126
guard let escapedFilename = self.archivePath.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
126127
return nil;
127128
}
128-
if let relative = self.feedURL {
129-
return URL(string: escapedFilename, relativeTo: relative)
129+
if let downloadUrlPrefix = self.downloadUrlPrefix {
130+
// if a download url prefix was given use this one
131+
return URL(string: escapedFilename, relativeTo: downloadUrlPrefix)
132+
} else if let relativeFeedUrl = self.feedURL {
133+
return URL(string: escapedFilename, relativeTo: relativeFeedUrl)
130134
}
131135
return URL(string: escapedFilename)
132136
}

generate_appcast/main.swift

Lines changed: 138 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ func printUsage() {
2525
)
2626
}
2727

28+
func printHelp() {
29+
let command = URL(fileURLWithPath: CommandLine.arguments.first!).lastPathComponent;
30+
print(
31+
"Usage: \(command) [OPTIONS] [ARCHIVES_FOLDER]\n",
32+
"Options:\n",
33+
"\t-f: provide the path to the private DSA key\n",
34+
"\t-n: provide the name of the private DSA key. This option has to be used together with `-k`\n",
35+
"\t-k: provide the name of the keychain. This option has to be used together with `-n`\n",
36+
"\t-s: provide the path to the private EdDSA key\n",
37+
"\t--download-url-prefix: provide a static url that will be used as prefix for the url from where updates will be downloaded\n"
38+
)
39+
}
40+
2841
func loadPrivateKeys(_ privateDSAKey: SecKey?, _ privateEdString: String?) -> PrivateKeys {
2942
var privateEdKey: Data?;
3043
var publicEdKey: Data?;
@@ -62,75 +75,157 @@ func loadPrivateKeys(_ privateDSAKey: SecKey?, _ privateEdString: String?) -> Pr
6275
return PrivateKeys(privateDSAKey: privateDSAKey, privateEdKey: privateEdKey, publicEdKey: publicEdKey);
6376
}
6477

65-
func main() {
66-
let args = CommandLine.arguments;
67-
if args.count < 2 {
68-
printUsage()
78+
/**
79+
* Parses all possible command line options and returns the values in a tuple.
80+
*/
81+
func parseCommandLineOptions(argumentList: [String]) -> (privateDSAKey: SecKey?, privateEdString: String?, downloadUrlPrefix: URL?, archivesSourceDir: URL) {
82+
// if the option `-h` is in the argument list print the help dialog
83+
if argumentList.contains("-h") {
84+
printHelp()
6985
exit(1)
7086
}
7187

72-
var privateDSAKey: SecKey? = nil;
73-
var privateEdString: String? = nil;
74-
75-
// this was typical usage for DSA keys
76-
if args.count == 3 || (args.count == 4 && args[1] == "-f") {
77-
// private key specified by filename
78-
let privateKeyURL = URL(fileURLWithPath: args.count == 3 ? args[1] : args[2])
88+
// make a mutable copy of the argument list
89+
var arguments = argumentList
90+
// remove the first element since this is the path to executable which we don't need
91+
arguments.removeFirst()
92+
93+
// define the variables for the possible argument values
94+
var privateDSAKey: SecKey? = nil
95+
var privateEdString: String? = nil
96+
var downloadUrlPrefix: URL? = nil
97+
var archivesSourceDir: URL
98+
99+
// check if the private dsa key option is present
100+
if let privateDSAKeyOptionIndex = arguments.firstIndex(of: "-f") {
101+
// check that when accessing the value of the option we don't get out of bounds
102+
if privateDSAKeyOptionIndex + 1 >= arguments.count {
103+
print("Too few arguments were given")
104+
exit(1)
105+
}
79106

107+
// get the private DSA key
108+
let privateKeyUrl = URL(fileURLWithPath: arguments[privateDSAKeyOptionIndex + 1])
80109
do {
81-
privateDSAKey = try loadPrivateDSAKey(at: privateKeyURL)
110+
privateDSAKey = try loadPrivateDSAKey(at: privateKeyUrl)
82111
} catch {
83-
print("Unable to load DSA private key from", privateKeyURL.path, "\n", error)
112+
print("Unable to load DSA private key from", privateKeyUrl.path, "\n", error)
84113
exit(1)
85114
}
115+
116+
// remove the already parsed arguments
117+
arguments.remove(at: privateDSAKeyOptionIndex)
118+
arguments.remove(at: privateDSAKeyOptionIndex + 1)
86119
}
87-
// this is legacy for DSA keychain; probably very rarely used
88-
else if args.count == 6 && (args[1] == "-n" || args[1] == "-k") {
89-
// private key specified by keychain + key name
90-
let keyName: String
91-
let keychainURL: URL
120+
121+
// check if the private dsa sould be loaded using the keyname and the name of the keychain
122+
if let keyNameOptionIndex = arguments.firstIndex(of: "-n"), let keychainNameOptionIndex = arguments.firstIndex(of: "-k") {
123+
// check that when accessing one of the values of the options we don't get out of bounds
124+
if keyNameOptionIndex + 1 >= arguments.count || keychainNameOptionIndex + 1 >= arguments.count {
125+
print("Too few arguments were given")
126+
exit(1)
127+
}
92128

93-
if args[1] == "-n" {
94-
if args[3] != "-k" {
95-
printUsage()
96-
exit(1)
97-
}
98-
99-
keyName = args[2]
100-
keychainURL = URL(fileURLWithPath: args[4])
129+
// get the keyname and the keychain url to load the private DSA key
130+
let keyName: String = arguments[keyNameOptionIndex + 1]
131+
let keychainUrl: URL = URL(fileURLWithPath: arguments[keychainNameOptionIndex + 1])
132+
do {
133+
privateDSAKey = try loadPrivateDSAKey(named: keyName, fromKeychainAt: keychainUrl)
134+
} catch {
135+
print("Unable to load DSA private key '\(keyName)' from keychain at", keychainUrl.path, "\n", error)
136+
exit(1)
101137
}
102-
else {
103-
if args[3] != "-n" {
104-
printUsage()
105-
exit(1)
106-
}
107-
108-
keyName = args[4]
109-
keychainURL = URL(fileURLWithPath: args[2])
138+
139+
// remove the already parsed arguments
140+
arguments.remove(at: keyNameOptionIndex + 1)
141+
arguments.remove(at: keyNameOptionIndex)
142+
arguments.remove(at: arguments.firstIndex(of: "-k")! + 1)
143+
arguments.remove(at: keychainNameOptionIndex)
144+
}
145+
146+
// check if the private EdDSA key string was given as an argument
147+
if let privateEdDSAKeyOptionIndex = arguments.firstIndex(of: "-s") {
148+
// check that when accessing the value of the option we don't get out of bounds
149+
if privateEdDSAKeyOptionIndex + 1 >= arguments.count {
150+
print("Too few arguments were given")
151+
exit(1)
110152
}
111-
153+
154+
// get the private EdDSA key string
155+
privateEdString = arguments[privateEdDSAKeyOptionIndex + 1]
156+
157+
// remove the already parsed argument
158+
arguments.remove(at: privateEdDSAKeyOptionIndex + 1)
159+
arguments.remove(at: privateEdDSAKeyOptionIndex)
160+
}
161+
162+
// check if a prefix for the download url of the archives was given
163+
if let downloadUrlPrefixOptionIndex = arguments.firstIndex(of: "--download-url-prefix") {
164+
// check that when accessing the value of the option we don't get out of bounds
165+
if downloadUrlPrefixOptionIndex + 1 >= arguments.count {
166+
print("Too few arguments were given")
167+
exit(1)
168+
}
169+
170+
// get the download url prefix
171+
downloadUrlPrefix = URL(string: arguments[downloadUrlPrefixOptionIndex + 1])
172+
173+
// remove the parsed argument
174+
arguments.remove(at: downloadUrlPrefixOptionIndex + 1)
175+
arguments.remove(at: downloadUrlPrefixOptionIndex)
176+
}
177+
178+
// now that all command line options have been removed from the arguments array
179+
// there should only be the path to the private DSA key (if provided) path to the archives dir left
180+
if arguments.count == 2 {
181+
// if there are two arguments left they are the private DSA key and the path to the archives directory (in this order)
182+
// first get the private DSA key
183+
let privateKeyUrl = URL(fileURLWithPath: arguments[0])
112184
do {
113-
privateDSAKey = try loadPrivateDSAKey(named: keyName, fromKeychainAt: keychainURL)
185+
privateDSAKey = try loadPrivateDSAKey(at: privateKeyUrl)
114186
} catch {
115-
print("Unable to load DSA private key '\(keyName)' from keychain at", keychainURL.path, "\n", error)
187+
print("Unable to load DSA private key from", privateKeyUrl.path, "\n", error)
116188
exit(1)
117189
}
190+
191+
// remove the parsed path to the DSA key
192+
arguments.removeFirst()
118193
}
119-
// private + public EdDSA keys are provided via command line argument
120-
else if args.count == 4 && args[1] == "-s" {
121-
privateEdString = args[2]
122-
}
123-
else if args.count != 2 {
194+
195+
// now only the archives source dir is left
196+
archivesSourceDir = URL(fileURLWithPath: arguments[0], isDirectory: true)
197+
198+
return (privateDSAKey, privateEdString, downloadUrlPrefix, archivesSourceDir)
199+
}
200+
201+
func main() {
202+
let args = CommandLine.arguments;
203+
if args.count < 2 {
124204
printUsage()
125205
exit(1)
126206
}
127207

128-
let archivesSourceDir = URL(fileURLWithPath: args.last!, isDirectory: true)
208+
var privateDSAKey: SecKey? = nil;
209+
var privateEdString: String? = nil;
210+
var downloadUrlPrefix: URL? = nil;
211+
var archivesSourceDir: URL
212+
213+
(privateDSAKey, privateEdString, downloadUrlPrefix, archivesSourceDir) = parseCommandLineOptions(argumentList: args)
214+
129215
let keys = loadPrivateKeys(privateDSAKey, privateEdString)
130216

131217
do {
132218
let allUpdates = try makeAppcast(archivesSourceDir: archivesSourceDir, keys: keys, verbose:verbose);
133219

220+
// if a download url prefix was provided set it for each archive item
221+
if downloadUrlPrefix != nil {
222+
for (_, archiveItems) in allUpdates {
223+
for archiveItem in archiveItems {
224+
archiveItem.downloadUrlPrefix = downloadUrlPrefix
225+
}
226+
}
227+
}
228+
134229
for (appcastFile, updates) in allUpdates {
135230
let appcastDestPath = URL(fileURLWithPath: appcastFile, relativeTo: archivesSourceDir);
136231
try writeAppcast(appcastDestPath:appcastDestPath, updates:updates);

0 commit comments

Comments
 (0)