Skip to content

Commit 6242861

Browse files
authored
Merge pull request #990 from ahoppen/merge-main-6.2-2025-04-10
Merge `main` into `release/6.2`
2 parents 101b3aa + adb01db commit 6242861

File tree

6 files changed

+178
-16
lines changed

6 files changed

+178
-16
lines changed

.github/workflows/automerge.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Create PR to merge main into release branch
2+
3+
# In the first period after branching the release branch, we typically want to include all changes from `main` also in the release branch. This workflow automatically creates a PR every Monday to merge main into the release branch.
4+
# Later in the release cycle we should stop this practice to avoid landing risky changes by disabling this workflow. To do so, disable the workflow as described in https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow
5+
6+
on:
7+
schedule:
8+
- cron: '0 0 * * MON'
9+
workflow_dispatch:
10+
11+
jobs:
12+
create_merge_pr:
13+
name: Create PR to merge main into release branch
14+
runs-on: ubuntu-latest
15+
if: (github.event_name == 'schedule' && github.repository == 'swiftlang/swift-format') || (github.event_name != 'schedule') # Ensure that we don't run this on a schedule in a fork
16+
steps:
17+
- name: Set up variables
18+
id: variables
19+
run: |
20+
echo "release_branch=release/6.2" >> "$GITHUB_OUTPUT"
21+
echo "pr_branch=automerge/merge-main-$(date +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
22+
- name: Checkout repository
23+
uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
- name: Create merge commit
27+
id: create_merge_commit
28+
run: |
29+
# Without this, we can't perform git operations in GitHub actions.
30+
git config --global --add safe.directory "$(realpath .)"
31+
git config --local user.name 'swift-ci'
32+
git config --local user.email '[email protected]'
33+
34+
git checkout ${{ steps.variables.outputs.release_branch }}
35+
git merge main
36+
37+
if [[ "$(git rev-parse HEAD)" = "$(git rev-parse main)" ]]; then
38+
echo "has_merged_commits=true" >> "$GITHUB_OUTPUT"
39+
else
40+
echo "has_merged_commits=false" >> "$GITHUB_OUTPUT"
41+
fi
42+
- name: Push branch and create PR
43+
id: push_branch
44+
if: ${{ steps.create_merge_commit.outputs.has_merged_commits }}
45+
env:
46+
GH_TOKEN: ${{ github.token }}
47+
run: |
48+
git checkout -b "${{ steps.variables.outputs.pr_branch }}"
49+
git push --set-upstream origin "${{ steps.variables.outputs.pr_branch }}"
50+
51+
gh pr create -B "${{ steps.variables.outputs.release_branch }}" -H "${{ steps.variables.outputs.pr_branch }}" \
52+
--title 'Merge `main` into `${{ steps.variables.outputs.release_branch }}`' \
53+
--body 'This PR was automatically opened by a GitHub action. Review the changes included in this PR and determine if they should be included in the release branch. If yes, merge the PR. Otherwise revert changes that should not be included on this branch.'

.github/workflows/pull_request.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
pull_request:
55
types: [opened, reopened, synchronize]
66

7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.ref }}
9+
cancel-in-progress: true
10+
711
jobs:
812
tests:
913
name: Test

Documentation/Configuration.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,30 @@ switch someValue {
204204
---
205205

206206
### `reflowMultilineStringLiterals`
207-
**type:** `string`
207+
208+
> [!NOTE]
209+
> This setting should be specified as a string value (e.g. `"never"`)
210+
> For backward compatibility with swift-format version 601.0.0, the configuration also accepts the legacy object format where the setting is specified as an object with a single key (e.g., ⁠`{ "never": {} }`).
211+
212+
**type:** `string` or `object` (legacy)
213+
214+
**example:**
215+
216+
For all versions above 601.0.0, the configuration should be specified as a string, for example:
217+
```json
218+
{
219+
"reflowMultilineStringLiterals": "never"
220+
}
221+
```
222+
223+
For version 601.0.0, the configuration should be specified as an object, for example:
224+
```json
225+
{
226+
"reflowMultilineStringLiterals": {
227+
"never": {}
228+
}
229+
}
230+
```
208231

209232
**description:** Determines how multiline string literals should reflow when formatted.
210233

Sources/SwiftFormat/API/Configuration.swift

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public struct Configuration: Codable, Equatable {
197197
public var multiElementCollectionTrailingCommas: Bool
198198

199199
/// Determines how multiline string literals should reflow when formatted.
200-
public enum MultilineStringReflowBehavior: Codable {
200+
public enum MultilineStringReflowBehavior: String, Codable {
201201
/// Never reflow multiline string literals.
202202
case never
203203
/// Reflow lines in string literal that exceed the maximum line length. For example with a line length of 10:
@@ -241,20 +241,36 @@ public struct Configuration: Codable, Equatable {
241241
case always
242242

243243
var isNever: Bool {
244-
switch self {
245-
case .never:
246-
return true
247-
default:
248-
return false
249-
}
244+
self == .never
250245
}
251246

252247
var isAlways: Bool {
248+
self == .always
249+
}
250+
}
251+
252+
/// A private enum created to maintain backward compatibility with swift-format version 601.0.0,
253+
/// which had a `MultilineStringReflowBehavior` enum without a String raw type.
254+
///
255+
/// In version 601.0.0, the `reflowMultilineStringLiterals` configuration was encoded as an object
256+
/// with a single key (e.g., `{ "never": {} }`) rather than as a string (e.g., `"never"`). This
257+
/// enum allows decoding from both formats:
258+
/// - First, we attempt to decode as a String using `MultilineStringReflowBehavior`
259+
/// - If that fails, we fall back to this legacy format
260+
/// - If both attempts fail, an error will be thrown
261+
///
262+
/// This approach preserves compatibility without requiring a configuration version bump.
263+
private enum LegacyMultilineStringReflowBehavior: Codable {
264+
case never
265+
case onlyLinesOverLength
266+
case always
267+
268+
/// Converts this legacy enum to the corresponding `MultilineStringReflowBehavior` value.
269+
func toMultilineStringReflowBehavior() -> MultilineStringReflowBehavior {
253270
switch self {
254-
case .always:
255-
return true
256-
default:
257-
return false
271+
case .never: .never
272+
case .always: .always
273+
case .onlyLinesOverLength: .onlyLinesOverLength
258274
}
259275
}
260276
}
@@ -371,9 +387,31 @@ public struct Configuration: Codable, Equatable {
371387
)
372388
?? defaults.multiElementCollectionTrailingCommas
373389

374-
self.reflowMultilineStringLiterals =
375-
try container.decodeIfPresent(MultilineStringReflowBehavior.self, forKey: .reflowMultilineStringLiterals)
376-
?? defaults.reflowMultilineStringLiterals
390+
self.reflowMultilineStringLiterals = try {
391+
// Try to decode `reflowMultilineStringLiterals` as a string
392+
// This handles configurations using the String raw value format (e.g. "never").
393+
// If an error occurs, we'll silently bypass it and fall back to the legacy behavior.
394+
if let behavior = try? container.decodeIfPresent(
395+
MultilineStringReflowBehavior.self,
396+
forKey: .reflowMultilineStringLiterals
397+
) {
398+
return behavior
399+
}
400+
401+
// If the modern format fails, try to decode as an object with a single key.
402+
// This handles configurations from swift-format v601.0.0 (e.g. { "never": {} }).
403+
// If an error occurs in this step, we'll propagate it to the caller.
404+
if let legacyBehavior = try container.decodeIfPresent(
405+
LegacyMultilineStringReflowBehavior.self,
406+
forKey: .reflowMultilineStringLiterals
407+
) {
408+
return legacyBehavior.toMultilineStringReflowBehavior()
409+
}
410+
411+
// If the key is not present in the configuration at all, use the default value.
412+
return defaults.reflowMultilineStringLiterals
413+
}()
414+
377415
self.indentBlankLines =
378416
try container.decodeIfPresent(
379417
Bool.self,

Tests/SwiftFormatTests/API/ConfigurationTests.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,44 @@ final class ConfigurationTests: XCTestCase {
6464
let path = #"\\mount\test.swift"#
6565
XCTAssertNil(Configuration.url(forConfigurationFileApplyingTo: URL(fileURLWithPath: path)))
6666
}
67+
68+
func testDecodingReflowMultilineStringLiteralsAsString() throws {
69+
let testCases: [String: Configuration.MultilineStringReflowBehavior] = [
70+
"never": .never,
71+
"always": .always,
72+
"onlyLinesOverLength": .onlyLinesOverLength,
73+
]
74+
75+
for (jsonString, expectedBehavior) in testCases {
76+
let jsonData = """
77+
{
78+
"reflowMultilineStringLiterals": "\(jsonString)"
79+
}
80+
""".data(using: .utf8)!
81+
82+
let config = try JSONDecoder().decode(Configuration.self, from: jsonData)
83+
XCTAssertEqual(config.reflowMultilineStringLiterals, expectedBehavior)
84+
}
85+
}
86+
87+
func testDecodingReflowMultilineStringLiteralsAsObject() throws {
88+
89+
let testCases: [String: Configuration.MultilineStringReflowBehavior] = [
90+
"{ \"never\": {} }": .never,
91+
"{ \"always\": {} }": .always,
92+
"{ \"onlyLinesOverLength\": {} }": .onlyLinesOverLength,
93+
]
94+
95+
for (jsonString, expectedBehavior) in testCases {
96+
let jsonData = """
97+
{
98+
"reflowMultilineStringLiterals": \(jsonString)
99+
}
100+
""".data(using: .utf8)!
101+
102+
let config = try JSONDecoder().decode(Configuration.self, from: jsonData)
103+
XCTAssertEqual(config.reflowMultilineStringLiterals, expectedBehavior)
104+
}
105+
}
106+
67107
}

api-breakages.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ API breakage: enum Finding.Severity has been removed
1313
API breakage: var Finding.severity has been removed
1414
API breakage: var FindingCategorizing.defaultSeverity has been removed
1515
API breakage: var FindingCategorizing.defaultSeverity has been removed
16-
API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:)
16+
API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:)
17+
API breakage: func Configuration.MultilineStringReflowBehavior.hash(into:) has been removed
18+
API breakage: func Configuration.MultilineStringReflowBehavior.encode(to:) has been removed
19+
API breakage: var Configuration.MultilineStringReflowBehavior.hashValue has been removed
20+
API breakage: constructor Configuration.MultilineStringReflowBehavior.init(from:) has been removed

0 commit comments

Comments
 (0)