Skip to content

Commit ef85e20

Browse files
committed
Enable cross-PR testing
1 parent d82d736 commit ef85e20

File tree

4 files changed

+211
-10
lines changed

4 files changed

+211
-10
lines changed

.github/workflows/pull_request.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ jobs:
88
tests:
99
name: Test
1010
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
11-
soundness:
12-
name: Soundness
13-
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
1411
with:
15-
license_header_check_enabled: false
16-
license_header_check_project_name: "Swift.org"
12+
enable_windows_checks: false
13+
linux_pre_build_command: |
14+
swift cross-pr-checkout.swift
15+
# soundness:
16+
# name: Soundness
17+
# uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
18+
# with:
19+
# license_header_check_enabled: false
20+
# license_header_check_project_name: "Swift.org"

Sources/SwiftFormat/Rules/UseShorthandTypeNames.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
4848
switch node.name.text {
4949
case "Array":
5050
guard let argument = genericArgumentList.firstAndOnly,
51-
case .type(let typeArgument) = argument else {
51+
case .type(let typeArgument) = argument.argument else {
5252
newNode = nil
5353
break
5454
}
@@ -62,7 +62,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
6262
case "Dictionary":
6363
guard let arguments = exactlyTwoChildren(of: genericArgumentList),
6464
case .type(let type0Argument) = arguments.0.argument,
65-
caes .type(let type1Argument) = arguments.1.argument else {
65+
case .type(let type1Argument) = arguments.1.argument else {
6666
newNode = nil
6767
break
6868
}
@@ -79,7 +79,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
7979
break
8080
}
8181
guard let argument = genericArgumentList.firstAndOnly,
82-
case .type(let typeArgument) = argument else {
82+
case .type(let typeArgument) = argument.argument else {
8383
newNode = nil
8484
break
8585
}
@@ -143,7 +143,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
143143
switch expression.baseName.text {
144144
case "Array":
145145
guard let argument = genericArgumentList.firstAndOnly,
146-
case .type(let typeArgument) = argument else {
146+
case .type(let typeArgument) = argument.argument else {
147147
newNode = nil
148148
break
149149
}
@@ -172,7 +172,7 @@ public final class UseShorthandTypeNames: SyntaxFormatRule {
172172

173173
case "Optional":
174174
guard let argument = genericArgumentList.firstAndOnly,
175-
case .type(let typeArgument) = argument else {
175+
case .type(let typeArgument) = argument.argument else {
176176
newNode = nil
177177
break
178178
}

cross-pr-checkout.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import subprocess
2+
import pathlib
3+
import requests
4+
5+
class CrossRepoPR:
6+
org: str
7+
repo: str
8+
pr_num: str
9+
10+
def __init__(self, org: str, repo: str, pr_num: str) -> None:
11+
self.org = org
12+
self.repo = repo
13+
self.pr_num = pr_num
14+
15+
def cross_repo_prs() -> list[CrossRepoPR]:
16+
return [
17+
CrossRepoPR("swiftlang", "swift-syntax", "2859")
18+
]
19+
20+
def run(cmd: list[str], cwd: str|None = None):
21+
print(" ".join(cmd))
22+
subprocess.check_call(cmd, cwd=cwd)
23+
24+
def main():
25+
for cross_repo_pr in cross_repo_prs():
26+
run(["git", "clone", f"https://github.com/{cross_repo_pr.org}/{cross_repo_pr.repo}.git", f"{cross_repo_pr.repo}"], cwd="..")
27+
run(["git", "fetch", "origin", f"pull/{cross_repo_pr.pr_num}/merge:pr_merge"], cwd="../swift-syntax")
28+
run(["git", "checkout", "main"], cwd="../swift-syntax")
29+
run(["git", "reset", "--hard", "pr_merge"], cwd="../swift-syntax")
30+
run(["swift", "package", "config", "set-mirror", "--package-url", "https://github.com/swiftlang/swift-syntax.git", "--mirror-url", str(pathlib.Path("../swift-syntax").resolve())])
31+
32+
if __name__ == "__main__":
33+
main()

cross-pr-checkout.swift

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import Foundation
2+
3+
/// Provides convenience APIs for launching and gathering output from a subprocess
4+
public class ProcessRunner {
5+
private static let serialQueue = DispatchQueue(label: "\(ProcessRunner.self)")
6+
7+
let process: Process
8+
var launched = false
9+
10+
public init(
11+
launchPath: String,
12+
arguments: [String],
13+
workingDirectory: String?,
14+
environment: [String: String] = [:]
15+
) {
16+
process = Process()
17+
process.launchPath = launchPath
18+
process.arguments = arguments
19+
if let workingDirectory {
20+
process.currentDirectoryURL = URL(fileURLWithPath: workingDirectory)
21+
}
22+
process.environment = environment.merging(ProcessInfo.processInfo.environment) { (current, _) in current }
23+
}
24+
25+
public func run(
26+
input: String? = nil,
27+
captureStdout: Bool = true,
28+
captureStderr: Bool = true
29+
) -> ProcessResult {
30+
let group = DispatchGroup()
31+
32+
let inPipe = Pipe()
33+
if input != nil {
34+
process.standardInput = inPipe
35+
}
36+
37+
var outData = Data()
38+
if captureStdout {
39+
let outPipe = Pipe()
40+
process.standardOutput = outPipe
41+
addHandler(pipe: outPipe, group: group) { outData.append($0) }
42+
}
43+
44+
var errData = Data()
45+
if captureStderr {
46+
let errPipe = Pipe()
47+
process.standardError = errPipe
48+
addHandler(pipe: errPipe, group: group) { errData.append($0) }
49+
}
50+
51+
ProcessRunner.serialQueue.sync {
52+
process.launch()
53+
launched = true
54+
}
55+
56+
if let input = input {
57+
guard let data = input.data(using: .utf8) else {
58+
return ProcessResult(
59+
status: 1,
60+
stdout: Data(),
61+
stderr: "Invalid input".data(using: .utf8)!
62+
)
63+
}
64+
inPipe.fileHandleForWriting.write(data)
65+
inPipe.fileHandleForWriting.closeFile()
66+
}
67+
68+
process.waitUntilExit()
69+
if captureStdout || captureStderr {
70+
// Make sure we've received all stdout/stderr
71+
group.wait()
72+
}
73+
74+
return ProcessResult(
75+
status: process.terminationStatus,
76+
stdout: outData,
77+
stderr: errData
78+
)
79+
}
80+
81+
public func terminate() {
82+
ProcessRunner.serialQueue.sync {
83+
if launched {
84+
process.terminate()
85+
}
86+
}
87+
}
88+
89+
private func addHandler(
90+
pipe: Pipe,
91+
group: DispatchGroup,
92+
addData: @escaping (Data) -> Void
93+
) {
94+
group.enter()
95+
pipe.fileHandleForReading.readabilityHandler = { fileHandle in
96+
// Apparently using availableData can cause various issues
97+
let newData = fileHandle.readData(ofLength: Int.max)
98+
if newData.count == 0 {
99+
pipe.fileHandleForReading.readabilityHandler = nil;
100+
group.leave()
101+
} else {
102+
addData(newData)
103+
}
104+
}
105+
}
106+
}
107+
108+
/// The exit code and output (if redirected) from a subprocess that has
109+
/// terminated
110+
public struct ProcessResult {
111+
public let status: Int32
112+
public let stdout: Data
113+
public let stderr: Data
114+
115+
public var stdoutStr: String? {
116+
return String(data: stdout, encoding: .utf8)
117+
}
118+
public var stderrStr: String? {
119+
return String(data: stderr, encoding: .utf8)
120+
}
121+
}
122+
123+
func run(_ executable: String, _ arguments: String..., workingDirectory: String? = nil) {
124+
let runner = ProcessRunner(
125+
launchPath: executable,
126+
arguments: arguments,
127+
environment: [:],
128+
workingDirectory: workingDirectory
129+
)
130+
runner.run()
131+
}
132+
133+
struct CrossRepoPR {
134+
let org: String
135+
let repo: String
136+
let prNum: String
137+
}
138+
139+
let crossRepoPrs = [
140+
CrossRepoPR(org: "swiftlang", repo: "swift-syntax", prNum: "2859")
141+
]
142+
143+
for crossRepoPr in crossRepoPrs {
144+
run(
145+
"git",
146+
"clone",
147+
"https://github.com/\(crossRepoPr.org)/\(crossRepoPrs.repo).git",
148+
"\(crossRepoPr.repo)",
149+
workingDirectory: ".."
150+
)
151+
run("git", "fetch", "origin", "pull/\(crossRepoPr.prNum)/merge:pr_merge", workingDirectory: "../swift-syntax")
152+
run("git", "checkout", "main", workingDirectory: "../swift-syntax")
153+
run("git", "reset", "--hard", "pr_merge", workingDirectory: "../swift-syntax")
154+
run(
155+
"swift",
156+
"package",
157+
"config",
158+
"set-mirror",
159+
"--package-url",
160+
"https://github.com/swiftlang/swift-syntax.git",
161+
"--mirror-url",
162+
URL(fileURLWithPath: "../swift-syntax").resolveSymlinksInPath().path
163+
)
164+
}

0 commit comments

Comments
 (0)