Skip to content

Commit

Permalink
Enable cross-PR testing
Browse files Browse the repository at this point in the history
  • Loading branch information
ahoppen committed Oct 25, 2024
1 parent d82d736 commit ef85e20
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 10 deletions.
14 changes: 9 additions & 5 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ jobs:
tests:
name: Test
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
license_header_check_enabled: false
license_header_check_project_name: "Swift.org"
enable_windows_checks: false
linux_pre_build_command: |
swift cross-pr-checkout.swift
# soundness:
# name: Soundness
# uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
# with:
# license_header_check_enabled: false
# license_header_check_project_name: "Swift.org"
10 changes: 5 additions & 5 deletions Sources/SwiftFormat/Rules/UseShorthandTypeNames.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
switch node.name.text {
case "Array":
guard let argument = genericArgumentList.firstAndOnly,
case .type(let typeArgument) = argument else {
case .type(let typeArgument) = argument.argument else {
newNode = nil
break
}
Expand All @@ -62,7 +62,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
case "Dictionary":
guard let arguments = exactlyTwoChildren(of: genericArgumentList),
case .type(let type0Argument) = arguments.0.argument,
caes .type(let type1Argument) = arguments.1.argument else {
case .type(let type1Argument) = arguments.1.argument else {
newNode = nil
break
}
Expand All @@ -79,7 +79,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
break
}
guard let argument = genericArgumentList.firstAndOnly,
case .type(let typeArgument) = argument else {
case .type(let typeArgument) = argument.argument else {
newNode = nil
break
}
Expand Down Expand Up @@ -143,7 +143,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
switch expression.baseName.text {
case "Array":
guard let argument = genericArgumentList.firstAndOnly,
case .type(let typeArgument) = argument else {
case .type(let typeArgument) = argument.argument else {
newNode = nil
break
}
Expand Down Expand Up @@ -172,7 +172,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {

case "Optional":
guard let argument = genericArgumentList.firstAndOnly,
case .type(let typeArgument) = argument else {
case .type(let typeArgument) = argument.argument else {
newNode = nil
break
}
Expand Down
33 changes: 33 additions & 0 deletions cross-pr-checkout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import subprocess
import pathlib
import requests

class CrossRepoPR:
org: str
repo: str
pr_num: str

def __init__(self, org: str, repo: str, pr_num: str) -> None:
self.org = org
self.repo = repo
self.pr_num = pr_num

def cross_repo_prs() -> list[CrossRepoPR]:
return [
CrossRepoPR("swiftlang", "swift-syntax", "2859")
]

def run(cmd: list[str], cwd: str|None = None):
print(" ".join(cmd))
subprocess.check_call(cmd, cwd=cwd)

def main():
for cross_repo_pr in cross_repo_prs():
run(["git", "clone", f"https://github.com/{cross_repo_pr.org}/{cross_repo_pr.repo}.git", f"{cross_repo_pr.repo}"], cwd="..")
run(["git", "fetch", "origin", f"pull/{cross_repo_pr.pr_num}/merge:pr_merge"], cwd="../swift-syntax")
run(["git", "checkout", "main"], cwd="../swift-syntax")
run(["git", "reset", "--hard", "pr_merge"], cwd="../swift-syntax")
run(["swift", "package", "config", "set-mirror", "--package-url", "https://github.com/swiftlang/swift-syntax.git", "--mirror-url", str(pathlib.Path("../swift-syntax").resolve())])

if __name__ == "__main__":
main()
164 changes: 164 additions & 0 deletions cross-pr-checkout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import Foundation

/// Provides convenience APIs for launching and gathering output from a subprocess
public class ProcessRunner {
private static let serialQueue = DispatchQueue(label: "\(ProcessRunner.self)")

let process: Process
var launched = false

public init(
launchPath: String,
arguments: [String],
workingDirectory: String?,
environment: [String: String] = [:]
) {
process = Process()
process.launchPath = launchPath
process.arguments = arguments
if let workingDirectory {
process.currentDirectoryURL = URL(fileURLWithPath: workingDirectory)
}
process.environment = environment.merging(ProcessInfo.processInfo.environment) { (current, _) in current }
}

public func run(
input: String? = nil,
captureStdout: Bool = true,
captureStderr: Bool = true
) -> ProcessResult {
let group = DispatchGroup()

let inPipe = Pipe()
if input != nil {
process.standardInput = inPipe
}

var outData = Data()
if captureStdout {
let outPipe = Pipe()
process.standardOutput = outPipe
addHandler(pipe: outPipe, group: group) { outData.append($0) }
}

var errData = Data()
if captureStderr {
let errPipe = Pipe()
process.standardError = errPipe
addHandler(pipe: errPipe, group: group) { errData.append($0) }
}

ProcessRunner.serialQueue.sync {
process.launch()
launched = true
}

if let input = input {
guard let data = input.data(using: .utf8) else {
return ProcessResult(
status: 1,
stdout: Data(),
stderr: "Invalid input".data(using: .utf8)!
)
}
inPipe.fileHandleForWriting.write(data)
inPipe.fileHandleForWriting.closeFile()
}

process.waitUntilExit()
if captureStdout || captureStderr {
// Make sure we've received all stdout/stderr
group.wait()
}

return ProcessResult(
status: process.terminationStatus,
stdout: outData,
stderr: errData
)
}

public func terminate() {
ProcessRunner.serialQueue.sync {
if launched {
process.terminate()
}
}
}

private func addHandler(
pipe: Pipe,
group: DispatchGroup,
addData: @escaping (Data) -> Void
) {
group.enter()
pipe.fileHandleForReading.readabilityHandler = { fileHandle in
// Apparently using availableData can cause various issues
let newData = fileHandle.readData(ofLength: Int.max)
if newData.count == 0 {
pipe.fileHandleForReading.readabilityHandler = nil;
group.leave()
} else {
addData(newData)
}
}
}
}

/// The exit code and output (if redirected) from a subprocess that has
/// terminated
public struct ProcessResult {
public let status: Int32
public let stdout: Data
public let stderr: Data

public var stdoutStr: String? {
return String(data: stdout, encoding: .utf8)
}
public var stderrStr: String? {
return String(data: stderr, encoding: .utf8)
}
}

func run(_ executable: String, _ arguments: String..., workingDirectory: String? = nil) {
let runner = ProcessRunner(
launchPath: executable,
arguments: arguments,
environment: [:],
workingDirectory: workingDirectory
)
runner.run()
}

struct CrossRepoPR {
let org: String
let repo: String
let prNum: String
}

let crossRepoPrs = [
CrossRepoPR(org: "swiftlang", repo: "swift-syntax", prNum: "2859")
]

for crossRepoPr in crossRepoPrs {
run(
"git",
"clone",
"https://github.com/\(crossRepoPr.org)/\(crossRepoPrs.repo).git",
"\(crossRepoPr.repo)",
workingDirectory: ".."
)
run("git", "fetch", "origin", "pull/\(crossRepoPr.prNum)/merge:pr_merge", workingDirectory: "../swift-syntax")
run("git", "checkout", "main", workingDirectory: "../swift-syntax")
run("git", "reset", "--hard", "pr_merge", workingDirectory: "../swift-syntax")
run(
"swift",
"package",
"config",
"set-mirror",
"--package-url",
"https://github.com/swiftlang/swift-syntax.git",
"--mirror-url",
URL(fileURLWithPath: "../swift-syntax").resolveSymlinksInPath().path
)
}

0 comments on commit ef85e20

Please sign in to comment.