Skip to content
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

Fix cocopods release #64

Merged
merged 6 commits into from Nov 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 0 additions & 31 deletions .github/workflows/release.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -12,3 +12,4 @@ muter_logs
Pods
vendor
fastlane/report.xml
*.bak
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -241,6 +241,12 @@ If you would like Restler to do something else, create an issue with a feature r
4. Open the project in the folder `Restler-Example`. You can do it from the terminal: `open Restler-Example/Restler-Example.xcodeproj`
5. Run tests to be sure everything works properly.

### Releasing

1. Open the project root directory.
2. `cd Scripts/releaseTool`
3. `swift run ReleaseTool release ../..`

### Dependencies

#### Gems
Expand Down
4 changes: 2 additions & 2 deletions Restler.podspec
Expand Up @@ -19,12 +19,12 @@ Pod::Spec.new do |s|

# Core
s.subspec 'Core' do |ss|
ss.dependency 'RestlerCore'
ss.dependency 'RestlerCore', '~> 1.0.1'
end

# RxRestler
s.subspec 'Rx' do |ss|
ss.dependency 'Restler/Core'
ss.dependency 'RxRestler'
ss.dependency 'RxRestler', '~> 1.0.1'
end
end
2 changes: 1 addition & 1 deletion RxRestler.podspec
Expand Up @@ -16,6 +16,6 @@ Pod::Spec.new do |s|
s.swift_versions = '5.2'

s.source_files = 'Sources/RxRestler/**/*.swift'
s.dependency 'RestlerCore'
s.dependency 'RestlerCore', '~> 1.0.1'
s.dependency 'RxSwift', '~> 5.1.1'
end
2 changes: 1 addition & 1 deletion Scripts/pod_lib_lint.rb
Expand Up @@ -18,7 +18,7 @@ def usage()
--ignore-local-podspecs: list of podspecs that should not be added to
"--include-podspecs" list. If not specified, then all podspec
dependencies will be passed to "--include-podspecs".
Example: --ignore-local-podspecs=FirebaseInstanceID.podspec,GoogleDataTransport.podspec
Example: --ignore-local-podspecs=RxRestler.podspec
EOF
end

Expand Down
5 changes: 5 additions & 0 deletions Scripts/releaseTool/.gitignore
@@ -0,0 +1,5 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Scripts/releaseTool/Package.resolved
@@ -0,0 +1,25 @@
{
"object": {
"pins": [
{
"package": "console-kit",
"repositoryURL": "https://github.com/vapor/console-kit.git",
"state": {
"branch": null,
"revision": "7454e839bc5ae0e75c3946d2613e442fd342fe6b",
"version": "4.2.4"
}
},
{
"package": "swift-log",
"repositoryURL": "https://github.com/apple/swift-log.git",
"state": {
"branch": null,
"revision": "173f567a2dfec11d74588eea82cecea555bdc0bc",
"version": "1.4.0"
}
}
]
},
"version": 1
}
19 changes: 19 additions & 0 deletions Scripts/releaseTool/Package.swift
@@ -0,0 +1,19 @@
// swift-tools-version:5.3
import PackageDescription

let package = Package(
name: "ReleaseTool",
platforms: [
.macOS(.v10_15),
],
dependencies: [
.package(url: "https://github.com/vapor/console-kit.git", .upToNextMajor(from: "4.2.4")),
],
targets: [
.target(
name: "ReleaseTool",
dependencies: [
.product(name: "ConsoleKit", package: "console-kit")
]),
]
)
3 changes: 3 additions & 0 deletions Scripts/releaseTool/README.md
@@ -0,0 +1,3 @@
# ReleaseTool

A description of this package.
@@ -0,0 +1,45 @@
import ConsoleKit
import Foundation

final class ReleaseCommand: Command {

struct Signature: CommandSignature {
@Flag(
name: "dry-run",
help: "Prints all commands without calling them. Useful for testing.")
var dryRun: Bool

@Argument(
name: "git-root",
help: "A root directory of the project repository.")
var gitRoot: String

init() {}
}

var help: String {
"A command to release the Restler framework to CocoaPods trunk."
}

func run(using context: CommandContext, signature: Signature) throws {
let executor = Executor(dryRun: signature.dryRun)
let gitRoot = URL(fileURLWithPath: signature.gitRoot)
context.console.info(gitRoot.path)
let manifest = Manifest.shared

context.console.info("Changing versions in podspecs...")
PodspecVersionManager(
manifest: manifest,
gitRoot: gitRoot,
executor: executor,
dryRun: signature.dryRun)
.updateVersions()

context.console.info("Pushing pods to trunk...")
PodspecPusher(
manifest: manifest,
executor: executor,
gitRoot: gitRoot)
.pushToTrunk()
}
}
46 changes: 46 additions & 0 deletions Scripts/releaseTool/Sources/ReleaseTool/Doers/Executor.swift
@@ -0,0 +1,46 @@
import Foundation

final class Executor {
let dryRun: Bool

// MARK: - Initialization
init(dryRun: Bool) {
self.dryRun = dryRun
}

// MARK: - Internal
@discardableResult
func execute(_ command: [String], workingDir: URL? = nil) -> Int32 {
let command = command.joined(separator: " ")
return execute(command, workingDir: workingDir)
}

@discardableResult
func execute(_ command: String, workingDir: URL? = nil) -> Int32 {
console.info(command)
guard !self.dryRun else { return 0 }
let task = createTask(forCommand: command, workingDir: workingDir)
return run(task: task)
}
}

// MARK: - Private
extension Executor {
private func createTask(forCommand command: String, workingDir: URL?) -> Process {
let task = Process()
task.executableURL = URL(fileURLWithPath: "/usr/bin/env")
task.arguments = command.split(separator: " ").map { String($0) }
if let workingDir = workingDir {
task.currentDirectoryURL = workingDir
task.currentDirectoryPath = workingDir.path
}
return task
}

private func run(task: Process) -> Int32 {
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
}

108 changes: 108 additions & 0 deletions Scripts/releaseTool/Sources/ReleaseTool/Doers/FileReader.swift
@@ -0,0 +1,108 @@
import Foundation

final class FileReader {
private let fileURL: URL
private let dryRun: Bool
private let fileManager: FileManager = .init()

// MARK: - Initialization
init(
fileURL: URL,
dryRun: Bool
) {
self.fileURL = fileURL
self.dryRun = dryRun

guard dryRun else { return }
console.info("FileReader: \(fileURL.path)")
}

// MARK: - Internal
func replaceAll(regex: String, with text: String) {
guard var content = readFile() else { return }
var newContent = content
repeat {
content = newContent
newContent = replace(firstRegex: regex, with: text, in: content)
} while content != newContent
writeToFile(text: newContent)
}

func replace(firstRegex: String, with text: String) {
guard var content = readFile() else { return }
content = replace(firstRegex: firstRegex, with: text, in: content)
writeToFile(text: content)
}

func writeToFile(text: String?) {
if fileExists() {
removeFile()
}
createFile(content: text)
}

func readFile() -> String? {
readString(encoding: .utf8)
}

func removeFile() {
guard !dryRun else {
console.info("removeFile")
return
}
do {
try fileManager.removeItem(at: fileURL)
} catch {
fatalError("File removing error: \(error)")
}
}

func fileExists() -> Bool {
return fileManager.fileExists(atPath: fileURL.path)
}

func copy(to: URL) {
guard !dryRun else {
console.info("copyTo: \(to.path)")
return
}
do {
if fileManager.fileExists(atPath: to.path) {
try fileManager.removeItem(at: to)
}
try fileManager.copyItem(at: fileURL, to: to)
} catch {
fatalError("Error occured while copying file: \(error)")
}
}
}

// MARK: - Private
extension FileReader {
private func replace(firstRegex: String, with newText: String, in content: String) -> String {
guard let regexRange = content.range(of: firstRegex, options: .regularExpression) else { return content }
var newContent = content
newContent.removeSubrange(regexRange)
newContent.insert(contentsOf: newText, at: regexRange.lowerBound)
return newContent
}

private func createFile(content: String?) {
guard !dryRun else {
console.info("writeToFile: \(text ?? "<nil>")")
return
}
fileManager.createFile(
atPath: fileURL.path,
contents: content?.data(using: .utf8))
}

private func readString(encoding: String.Encoding) -> String? {
guard let data = readData() else { return nil }
return String(data: data, encoding: encoding)
}

private func readData() -> Data? {
fileManager.contents(atPath: fileURL.path)
}
}
27 changes: 27 additions & 0 deletions Scripts/releaseTool/Sources/ReleaseTool/Doers/PodspecPusher.swift
@@ -0,0 +1,27 @@
import Foundation

final class PodspecPusher {
private let manifest: Manifest
private let executor: Executor
private let gitRoot: URL

// MARK: - Initialization
init(
manifest: Manifest,
executor: Executor,
gitRoot: URL
) {
self.manifest = manifest
self.executor = executor
self.gitRoot = gitRoot
}

// MARK: - Internal
func pushToTrunk() {
manifest.pods.filter(\.releasing).forEach { pod in
let warningsOK = pod.allowWarnings ? "--allow-warnings" : ""
let command: String = "pod trunk push --skip-tests --synchronous \(warningsOK) \(pod.name).podspec"
executor.execute(command, workingDir: gitRoot)
}
}
}