From ceb0a2bf8b0cb5db32c35f6e9e7b9244c63ca0d3 Mon Sep 17 00:00:00 2001 From: 417-72KI <417.72ki@gmail.com> Date: Wed, 19 Jun 2024 17:30:06 +0900 Subject: [PATCH 1/3] create release.swift --- scripts/release.swift | 140 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100755 scripts/release.swift diff --git a/scripts/release.swift b/scripts/release.swift new file mode 100755 index 0000000..ea51931 --- /dev/null +++ b/scripts/release.swift @@ -0,0 +1,140 @@ +#!/usr/bin/env swift + +import Foundation + +do { + let args = CommandLine.arguments + guard args.count == 3 else { + throw "Usage: \(args[0]) " + } + + let projectName = args[1] + let tag = args[2] + let isDebug = sh("git symbolic-ref --short HEAD") != "main" + if isDebug { + print("\u{001B}[33m⚠️ Release job is enabled only in main. Run in debug mode\u{001B}[m") + } + + let rootPath = sh("git rev-parse --show-toplevel") + FileManager.default.changeCurrentDirectoryPath(rootPath) + + guard let _ = tag.wholeMatch(of: #/^([0-9]+)\.([0-9]+)\.([0-9]+)$/#) else { + throw "Invalid version format: \(tag)" + } + + let localChanges = sh("git diff --name-only HEAD") + .split(separator: "\n") + + let makefile = "Makefile" + + switch localChanges { + case []: break + case ["Makefile"]: + let makefileDiff = sh("git diff -U0 \(makefile)") + .split(separator: "\n") + .filter { $0.prefixMatch(of: #/^[+-]((?!(-- a|\+\+ b)))/#) != nil } + .first { $0.prefixMatch(of: #/^[+-]ver = [0-9]*\.[0-9]*\.[0-9]*$/#) == nil } + guard makefileDiff == nil else { + throw "`ver` line is only accepted as a local change." + } + default: + throw "There are some local changes." + } + + guard sh("git fetch --tags && git tag | grep \"\(tag)\"").isEmpty else { + throw "Tag `\(tag)` already exists." + } + + // Update version + let readmeFile = "README.md" + try String(decoding: try Data(contentsOf: URL(fileURLWithPath: readmeFile)), as: UTF8.self) + .replacing(try! Regex(#"(?\.package\(url: \".*\#(projectName)\.git\", from: \").*(?\"\),?)"#)) { match in + if let values = match.output.extractValues(as: (Substring, prefix: Substring, suffix: Substring).self) { + "\(values.prefix)\(tag)\(values.suffix)" + } else { + "" + } + } + .write(toFile: readmeFile, atomically: true, encoding: .utf8) + + let packageFile = "Package.swift" + let packageContent = String(decoding: try Data(contentsOf: URL(fileURLWithPath: packageFile)), as: UTF8.self) + try packageContent.replacing(#/(let isRelease = )(true|false)/#) { match in + "\(match.output.1)true" + } + .write(toFile: packageFile, atomically: true, encoding: .utf8) + + // Update podspec + let macOSVersion = packageContent.firstMatch(of: #/macOS\(\.v([0-9]+)\)/#)!.output.1.paddingCommaZero() + let iOSVersion = packageContent.firstMatch(of: #/iOS\(\.v([0-9]+)\)/#)!.output.1.paddingCommaZero() + let tvOSVersion = packageContent.firstMatch(of: #/tvOS\(\.v([0-9]+)\)/#)!.output.1.paddingCommaZero() + + let podspecFile = "\(projectName).podspec" + try String(decoding: try Data(contentsOf: URL(fileURLWithPath: podspecFile)), as: UTF8.self) + .replacing(#/(spec\.version *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + "\(match.output.1)\"\(tag)\"" + } + .replacing(#/(spec\.osx\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + "\(match.output.1)\"\(macOSVersion)\"" + } + .replacing(#/(spec\.ios\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + "\(match.output.1)\"\(iOSVersion)\"" + } + .replacing(#/(spec\.tvos\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + "\(match.output.1)\"\(tvOSVersion)\"" + } + .write(toFile: podspecFile, atomically: true, encoding: .utf8) + + // Commit and release + let commitOption = isDebug ? "--dry-run" : "" + sh(#"git commit \#(commitOption) -m "Bump version to \#(tag)" \#(readmeFile) \#(packageFile) \#(podspecFile) \#(makefile)"#) + + if isDebug { + print("\u{001B}[34mDry run mode. Skip pushing and releasing.\u{001B}[m") + } else { + sh("git push origin main") + sh("gh release create \(tag) --target main --title \(tag) --generate-notes") + } + + // Revert to develop mode + try packageContent.write(toFile: packageFile, atomically: true, encoding: .utf8) + sh(#"git commit \#(commitOption) -m 'switch release flag to false' \#(packageFile)"#) + if !isDebug { + sh("git push origin main") + } +} catch { + print("\u{001B}[31m⛔️ \(error)\u{001B}[m") + exit(1) +} + +// MARK: - functions +@discardableResult +func sh(_ command: String) -> String { + // print("\u{001B}[34m\(command)\u{001B}[m") + let task = Process() + let stdout = Pipe() + task.launchPath = "/bin/sh" + task.standardOutput = stdout + task.arguments = ["-c", command] + task.launch() + task.waitUntilExit() + let data = stdout.fileHandleForReading.readDataToEndOfFile() + return String(decoding: data, as: UTF8.self) + .trimmingCharacters(in: .whitespacesAndNewlines) +} + +// MARK: - Error +extension String: LocalizedError { + public var errorDescription: String? { self } +} + +// MARK: - Regex +extension StringProtocol { + func paddingCommaZero() -> String { + if contains(".") { + String(self) + } else { + "\(self).0" + } + } +} \ No newline at end of file From dfcd0afb24d83586c245249700b48240295ac1c9 Mon Sep 17 00:00:00 2001 From: 417-72KI <417.72ki@gmail.com> Date: Wed, 19 Jun 2024 17:52:06 +0900 Subject: [PATCH 2/3] replace executing file --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0995032..8e078c3 100644 --- a/Makefile +++ b/Makefile @@ -15,4 +15,4 @@ test: xed test_output/test_result.xcresult release: - @scripts/release.sh ${PROJECT_NAME} ${ver} + @scripts/release.swift ${PROJECT_NAME} ${ver} From 02e207872699486be2a3a312e5c38c7f48eafb93 Mon Sep 17 00:00:00 2001 From: 417-72KI <417.72ki@gmail.com> Date: Wed, 19 Jun 2024 17:56:05 +0900 Subject: [PATCH 3/3] format by Xcode --- scripts/release.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/release.swift b/scripts/release.swift index ea51931..54978a2 100755 --- a/scripts/release.swift +++ b/scripts/release.swift @@ -48,21 +48,21 @@ do { // Update version let readmeFile = "README.md" try String(decoding: try Data(contentsOf: URL(fileURLWithPath: readmeFile)), as: UTF8.self) - .replacing(try! Regex(#"(?\.package\(url: \".*\#(projectName)\.git\", from: \").*(?\"\),?)"#)) { match in + .replacing(try! Regex(#"(?\.package\(url: \".*\#(projectName)\.git\", from: \").*(?\"\),?)"#)) { match in if let values = match.output.extractValues(as: (Substring, prefix: Substring, suffix: Substring).self) { "\(values.prefix)\(tag)\(values.suffix)" - } else { + } else { "" } } .write(toFile: readmeFile, atomically: true, encoding: .utf8) - + let packageFile = "Package.swift" let packageContent = String(decoding: try Data(contentsOf: URL(fileURLWithPath: packageFile)), as: UTF8.self) try packageContent.replacing(#/(let isRelease = )(true|false)/#) { match in - "\(match.output.1)true" - } - .write(toFile: packageFile, atomically: true, encoding: .utf8) + "\(match.output.1)true" + } + .write(toFile: packageFile, atomically: true, encoding: .utf8) // Update podspec let macOSVersion = packageContent.firstMatch(of: #/macOS\(\.v([0-9]+)\)/#)!.output.1.paddingCommaZero() @@ -71,16 +71,16 @@ do { let podspecFile = "\(projectName).podspec" try String(decoding: try Data(contentsOf: URL(fileURLWithPath: podspecFile)), as: UTF8.self) - .replacing(#/(spec\.version *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + .replacing(#/(spec\.version *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in "\(match.output.1)\"\(tag)\"" } - .replacing(#/(spec\.osx\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + .replacing(#/(spec\.osx\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in "\(match.output.1)\"\(macOSVersion)\"" } - .replacing(#/(spec\.ios\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + .replacing(#/(spec\.ios\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in "\(match.output.1)\"\(iOSVersion)\"" } - .replacing(#/(spec\.tvos\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in + .replacing(#/(spec\.tvos\.deployment_target *= )\"([0-9]*\.[0-9]*(\.[0-9]*)?)\"/#) { match in "\(match.output.1)\"\(tvOSVersion)\"" } .write(toFile: podspecFile, atomically: true, encoding: .utf8) @@ -131,10 +131,10 @@ extension String: LocalizedError { // MARK: - Regex extension StringProtocol { func paddingCommaZero() -> String { - if contains(".") { + if contains(".") { String(self) } else { "\(self).0" } } -} \ No newline at end of file +}