Skip to content

Commit

Permalink
Merge pull request #21 from lucka-me/query-metadata-before-update
Browse files Browse the repository at this point in the history
Query metadata before update
  • Loading branch information
lucka-me authored Mar 5, 2024
2 parents b2ceafa + dc30a4a commit 1ad210d
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 41 deletions.
85 changes: 65 additions & 20 deletions App/Shared/Data/ScriptManager+Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ extension ScriptManager {
}

extension ScriptManager {
func updateScripts(reporting progress: Progress) async throws {
defer {
progress.completedUnitCount = 0
}

func updateScripts(reporting progress: Progress, currentMainScriptVersion: String?) async throws {
try Self.ensureInternalDirectories()

let internalPlugins = try internalPluginNames
Expand All @@ -41,7 +37,7 @@ extension ScriptManager {
let channel = UserDefaults.shared.buildChannel
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask {
try await Self.downloadMainScript(from: channel)
try await Self.downloadMainScript(from: channel, currentVersion: currentMainScriptVersion)
await MainActor.run { progress.completedUnitCount += 1 }
}

Expand Down Expand Up @@ -82,13 +78,23 @@ extension ScriptManager {
}
}

static func downloadMainScript(from channel: BuildChannel) async throws {
let downloadURL = Self.websiteBuildURL
static func downloadMainScript(from channel: BuildChannel, currentVersion: String?) async throws {
let scriptURL = Self.websiteBuildURL
.appending(path: channel.rawValue)
.appending(path: FileConstants.mainScriptFilename)
.appendingPathExtension("user.js")
if let currentVersion {
// Check version from metadata
guard await checkUpdate(
from: scriptURL.appendingPathExtension(FileConstants.scriptMetadataExtension),
currentVersion: currentVersion
) else {
return
}
}

let temporaryURL = try await URLSession.shared.download(from: downloadURL)
let temporaryURL = try await URLSession.shared.download(
from: scriptURL.appendingPathExtension(FileConstants.userScriptExtension)
)

let fileManager = FileManager.default
var succeed = false
Expand All @@ -110,12 +116,30 @@ extension ScriptManager {
}

func downloadInternalPlugin(_ filename: String, from channel: BuildChannel) async throws {
let downloadURL = Self.websiteBuildURL
let predicate = #Predicate<Plugin> {
$0.isInternal && $0.filename == filename
}
var descriptor = FetchDescriptor(predicate: predicate)
descriptor.fetchLimit = 1
let item = try modelContext.fetch(descriptor).first

let pluginURL = Self.websiteBuildURL
.appending(path: channel.rawValue)
.appending(path: "plugins")
.appending(path: filename)
.appendingPathExtension("user.js")
let temporaryURL = try await URLSession.shared.download(from: downloadURL)
if let currentVersion = item?.version {
// Check version from metadata
guard await Self.checkUpdate(
from: pluginURL.appendingPathExtension(FileConstants.scriptMetadataExtension),
currentVersion: currentVersion
) else {
return
}
}

let temporaryURL = try await URLSession.shared.download(
from: pluginURL.appendingPathExtension(FileConstants.userScriptExtension)
)

let fileManager = FileManager.default
var succeed = false
Expand All @@ -136,12 +160,7 @@ extension ScriptManager {
}
try fileManager.moveItem(at: temporaryURL, to: destinationURL)

let predicate = #Predicate<Plugin> {
$0.isInternal && $0.filename == filename
}
var descriptor = FetchDescriptor(predicate: predicate)
descriptor.fetchLimit = 1
if let item = try modelContext.fetch(descriptor).first {
if let item {
item.update(from: metadata)
} else {
let item = Plugin(metadata: metadata, isInternal: true, filename: filename)
Expand Down Expand Up @@ -171,7 +190,13 @@ fileprivate extension ScriptManager {
static var websiteBuildURL: URL { .init(string: "https://iitc.app/build/")! }

private func updateExternal(plugin: Plugin, in externalURL: URL) async throws {
// TODO: Use updateURL to fetch metadata
if let currentVersion = plugin.version, let updateURL = plugin.updateURL {
// Check version from update URL
guard await Self.checkUpdate(from: updateURL, currentVersion: currentVersion) else {
return
}
}

guard let downloadURL = plugin.downloadURL else { return }
let temporaryURL = try await URLSession.shared.download(from: downloadURL)

Expand Down Expand Up @@ -210,3 +235,23 @@ fileprivate extension ScriptManager {
}
}
}

fileprivate extension ScriptManager {
static func checkUpdate(from url: URL, currentVersion: String) async -> Bool {
// Allow all errors
guard
let data = try? await URLSession.shared.data(from: url),
let content = String(data: data, encoding: .utf8),
let updateVersion = try? UserScriptMetadataDecoder()
.decode(VersionedMetadata.self, from: content)
.version
else {
return false
}
return updateVersion != currentVersion
}
}

fileprivate struct VersionedMetadata : Decodable {
var version: String
}
5 changes: 4 additions & 1 deletion App/Shared/IntelStackApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,12 @@ struct IntelStackApp: App {
@MainActor
private func updatePlugins() async throws {
guard updateStatus == .idle else { return }
updateProgress.completedUnitCount = 0
updateStatus = .updating
defer { updateStatus = .idle }
try await scriptManager.updateScripts(reporting: updateProgress)
try await scriptManager.updateScripts(
reporting: updateProgress, currentMainScriptVersion: mainScriptVersion
)
updateMainScriptVersion()
}
}
4 changes: 1 addition & 3 deletions App/Shared/Views/CommunityPluginListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,7 @@ struct CommunityPluginListView: View {
}
}

if
let homepageURLString = preview.metadata.homepageURL,
let homepageURL = URL(string: homepageURLString) {
if let homepageURL = preview.metadata.homepageURL {
Link(destination: homepageURL) {
Label("CommunityPluginListView.Homepage", systemImage: "house")
.capsule(.green)
Expand Down
22 changes: 7 additions & 15 deletions Shared/Data/PluginMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ struct PluginMetadata : Decodable {

var author: String?
var description: String?
var downloadURL: String?
var updateURL: String?
var downloadURL: URL?
var updateURL: URL?
var version: String?

var homepageURL: String?
var homepageURL: URL?
}

extension Plugin {
Expand All @@ -34,12 +34,8 @@ extension Plugin {
self.author = metadata.author
self.scriptDescription = metadata.description
self.version = metadata.version
if let downloadURL = metadata.downloadURL {
self.downloadURL = URL(string: downloadURL)
}
if let updateURL = metadata.updateURL {
self.updateURL = URL(string: updateURL)
}
self.downloadURL = metadata.downloadURL
self.updateURL = metadata.updateURL
}

func update(from metadata: PluginMetadata) {
Expand All @@ -48,11 +44,7 @@ extension Plugin {
self.author = metadata.author
self.scriptDescription = metadata.description
self.version = metadata.version
if let downloadURL = metadata.downloadURL {
self.downloadURL = URL(string: downloadURL)
}
if let updateURL = metadata.updateURL {
self.updateURL = URL(string: updateURL)
}
self.downloadURL = metadata.downloadURL
self.updateURL = metadata.updateURL
}
}
13 changes: 12 additions & 1 deletion Shared/Data/UserScriptMetadataDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ fileprivate struct KeyedContainer<Key: CodingKey> : KeyedDecodingContainerProtoc
try value(of: key)
}

func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
func decode<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
let rawValue = try rawValue(of: key)
switch type {
case let aType as LosslessStringConvertible.Type:
Expand All @@ -185,6 +185,17 @@ fileprivate struct KeyedContainer<Key: CodingKey> : KeyedDecodingContainerProtoc
)
}
return value as! T
case let aType as URL.Type:
guard let value = aType.init(string: rawValue) else {
throw DecodingError.typeMismatch(
type,
.init(
codingPath: [ key ],
debugDescription: "The raw value \(rawValue) can not be converted into \(type)"
)
)
}
return value as! T
// TODO: Add more cases if needed
default:
throw DecodingError.typeMismatch(
Expand Down
2 changes: 1 addition & 1 deletion Tests/UserScriptMetadataDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ final class UserScriptMetadataDecoderTests: XCTestCase {
XCTAssertEqual(metadata.author, author)
XCTAssertEqual(metadata.description, description)
XCTAssertNil(metadata.downloadURL)
XCTAssertEqual(metadata.updateURL, updateURL)
XCTAssertEqual(metadata.updateURL, .init(string: updateURL)!)
XCTAssertEqual(metadata.version, version)
}

Expand Down

0 comments on commit 1ad210d

Please sign in to comment.