Skip to content

Merge main into release/6.2 #990

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

Merged
merged 7 commits into from
Apr 11, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/automerge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Create PR to merge main into release branch

# 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.
# 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

on:
schedule:
- cron: '0 0 * * MON'
workflow_dispatch:

jobs:
create_merge_pr:
name: Create PR to merge main into release branch
runs-on: ubuntu-latest
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
steps:
- name: Set up variables
id: variables
run: |
echo "release_branch=release/6.2" >> "$GITHUB_OUTPUT"
echo "pr_branch=automerge/merge-main-$(date +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Create merge commit
id: create_merge_commit
run: |
# Without this, we can't perform git operations in GitHub actions.
git config --global --add safe.directory "$(realpath .)"
git config --local user.name 'swift-ci'
git config --local user.email '[email protected]'

git checkout ${{ steps.variables.outputs.release_branch }}
git merge main

if [[ "$(git rev-parse HEAD)" = "$(git rev-parse main)" ]]; then
echo "has_merged_commits=true" >> "$GITHUB_OUTPUT"
else
echo "has_merged_commits=false" >> "$GITHUB_OUTPUT"
fi
- name: Push branch and create PR
id: push_branch
if: ${{ steps.create_merge_commit.outputs.has_merged_commits }}
env:
GH_TOKEN: ${{ github.token }}
run: |
git checkout -b "${{ steps.variables.outputs.pr_branch }}"
git push --set-upstream origin "${{ steps.variables.outputs.pr_branch }}"

gh pr create -B "${{ steps.variables.outputs.release_branch }}" -H "${{ steps.variables.outputs.pr_branch }}" \
--title 'Merge `main` into `${{ steps.variables.outputs.release_branch }}`' \
--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.'
4 changes: 4 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ on:
pull_request:
types: [opened, reopened, synchronize]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
tests:
name: Test
Expand Down
25 changes: 24 additions & 1 deletion Documentation/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,30 @@ switch someValue {
---

### `reflowMultilineStringLiterals`
**type:** `string`

> [!NOTE]
> This setting should be specified as a string value (e.g. `"never"`)
> 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": {} }`).

**type:** `string` or `object` (legacy)

**example:**

For all versions above 601.0.0, the configuration should be specified as a string, for example:
```json
{
"reflowMultilineStringLiterals": "never"
}
```

For version 601.0.0, the configuration should be specified as an object, for example:
```json
{
"reflowMultilineStringLiterals": {
"never": {}
}
}
```

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

Expand Down
66 changes: 52 additions & 14 deletions Sources/SwiftFormat/API/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public struct Configuration: Codable, Equatable {
public var multiElementCollectionTrailingCommas: Bool

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

var isNever: Bool {
switch self {
case .never:
return true
default:
return false
}
self == .never
}

var isAlways: Bool {
self == .always
}
}

/// A private enum created to maintain backward compatibility with swift-format version 601.0.0,
/// which had a `MultilineStringReflowBehavior` enum without a String raw type.
///
/// In version 601.0.0, the `reflowMultilineStringLiterals` configuration was encoded as an object
/// with a single key (e.g., `{ "never": {} }`) rather than as a string (e.g., `"never"`). This
/// enum allows decoding from both formats:
/// - First, we attempt to decode as a String using `MultilineStringReflowBehavior`
/// - If that fails, we fall back to this legacy format
/// - If both attempts fail, an error will be thrown
///
/// This approach preserves compatibility without requiring a configuration version bump.
private enum LegacyMultilineStringReflowBehavior: Codable {
case never
case onlyLinesOverLength
case always

/// Converts this legacy enum to the corresponding `MultilineStringReflowBehavior` value.
func toMultilineStringReflowBehavior() -> MultilineStringReflowBehavior {
switch self {
case .always:
return true
default:
return false
case .never: .never
case .always: .always
case .onlyLinesOverLength: .onlyLinesOverLength
}
}
}
Expand Down Expand Up @@ -371,9 +387,31 @@ public struct Configuration: Codable, Equatable {
)
?? defaults.multiElementCollectionTrailingCommas

self.reflowMultilineStringLiterals =
try container.decodeIfPresent(MultilineStringReflowBehavior.self, forKey: .reflowMultilineStringLiterals)
?? defaults.reflowMultilineStringLiterals
self.reflowMultilineStringLiterals = try {
// Try to decode `reflowMultilineStringLiterals` as a string
// This handles configurations using the String raw value format (e.g. "never").
// If an error occurs, we'll silently bypass it and fall back to the legacy behavior.
if let behavior = try? container.decodeIfPresent(
MultilineStringReflowBehavior.self,
forKey: .reflowMultilineStringLiterals
) {
return behavior
}

// If the modern format fails, try to decode as an object with a single key.
// This handles configurations from swift-format v601.0.0 (e.g. { "never": {} }).
// If an error occurs in this step, we'll propagate it to the caller.
if let legacyBehavior = try container.decodeIfPresent(
LegacyMultilineStringReflowBehavior.self,
forKey: .reflowMultilineStringLiterals
) {
return legacyBehavior.toMultilineStringReflowBehavior()
}

// If the key is not present in the configuration at all, use the default value.
return defaults.reflowMultilineStringLiterals
}()

self.indentBlankLines =
try container.decodeIfPresent(
Bool.self,
Expand Down
40 changes: 40 additions & 0 deletions Tests/SwiftFormatTests/API/ConfigurationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,44 @@ final class ConfigurationTests: XCTestCase {
let path = #"\\mount\test.swift"#
XCTAssertNil(Configuration.url(forConfigurationFileApplyingTo: URL(fileURLWithPath: path)))
}

func testDecodingReflowMultilineStringLiteralsAsString() throws {
let testCases: [String: Configuration.MultilineStringReflowBehavior] = [
"never": .never,
"always": .always,
"onlyLinesOverLength": .onlyLinesOverLength,
]

for (jsonString, expectedBehavior) in testCases {
let jsonData = """
{
"reflowMultilineStringLiterals": "\(jsonString)"
}
""".data(using: .utf8)!

let config = try JSONDecoder().decode(Configuration.self, from: jsonData)
XCTAssertEqual(config.reflowMultilineStringLiterals, expectedBehavior)
}
}

func testDecodingReflowMultilineStringLiteralsAsObject() throws {

let testCases: [String: Configuration.MultilineStringReflowBehavior] = [
"{ \"never\": {} }": .never,
"{ \"always\": {} }": .always,
"{ \"onlyLinesOverLength\": {} }": .onlyLinesOverLength,
]

for (jsonString, expectedBehavior) in testCases {
let jsonData = """
{
"reflowMultilineStringLiterals": \(jsonString)
}
""".data(using: .utf8)!

let config = try JSONDecoder().decode(Configuration.self, from: jsonData)
XCTAssertEqual(config.reflowMultilineStringLiterals, expectedBehavior)
}
}

}
6 changes: 5 additions & 1 deletion api-breakages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ API breakage: enum Finding.Severity has been removed
API breakage: var Finding.severity has been removed
API breakage: var FindingCategorizing.defaultSeverity has been removed
API breakage: var FindingCategorizing.defaultSeverity has been removed
API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:)
API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:)
API breakage: func Configuration.MultilineStringReflowBehavior.hash(into:) has been removed
API breakage: func Configuration.MultilineStringReflowBehavior.encode(to:) has been removed
API breakage: var Configuration.MultilineStringReflowBehavior.hashValue has been removed
API breakage: constructor Configuration.MultilineStringReflowBehavior.init(from:) has been removed