Skip to content

Commit

Permalink
Add SwiftFormat support
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelmeuli committed Jan 4, 2020
1 parent 4fe8fbc commit 574b45d
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 13 deletions.
21 changes: 10 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

# Go

- name: Install Go
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
Expand All @@ -38,12 +38,12 @@ jobs:
# Node.js

- name: Install Node.js and Yarn
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 12

- name: Install NPM dependencies
- name: Install Node.js dependencies
run: |
cd ./test/linters/projects/eslint
yarn install
Expand All @@ -56,22 +56,21 @@ jobs:
# Python

- name: Install Python and pip
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.8

- name: Install Black
run: pip install black

- name: Install Flake8
run: pip install flake8
- name: Install Python dependencies
run: pip install black flake8

# Swift (only on macOS)

- name: Install SwiftLint
- name: Install Swift dependencies
if: startsWith(matrix.os, 'macos')
run: brew install swiftlint
run: |
brew update
brew install swiftformat swiftlint
# Tests

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This action…
- [Black](https://black.readthedocs.io)
- [Flake8](http://flake8.pycqa.org)
- **Swift:**
- [SwiftFormat](https://github.com/nicklockwood/SwiftFormat)
- [SwiftLint](https://github.com/realm/SwiftLint)

## Usage
Expand Down Expand Up @@ -137,7 +138,7 @@ All linters are disabled by default. To enable a linter, simply set the option w
eslint: true # Enables ESLint checks
```

`[linter]` can be one of `black`, `eslint`, `flake8`, `gofmt`, `golint`, `prettier`, `stylelint`, and `swiftlint`:
`[linter]` can be one of `black`, `eslint`, `flake8`, `gofmt`, `golint`, `prettier`, `stylelint`, `swiftformat` and `swiftlint`:

- **`[linter]`:** Enables the linter in your repository. Default: `false`
- **`[linter]_extensions`:** Extensions of files to check with the linter. Example: `eslint_extensions: js,ts` to lint both JavaScript and TypeScript files with ESLint. Default: See [`action.yml`](./action.yml)
Expand Down
12 changes: 12 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ inputs:

# Swift

swiftformat:
description: Enable or disable SwiftFormat checks
required: false
default: false
swiftformat_extensions:
description: Extensions of files to check with SwiftFormat
required: false
default: "swift"
swiftformat_dir:
description: Directory where the SwiftFormat command should be run
required: false

swiftlint:
description: Enable or disable SwiftLint checks
required: false
Expand Down
77 changes: 77 additions & 0 deletions src/linters/swiftformat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const commandExists = require("../../vendor/command-exists");
const { run } = require("../utils/action");

const PARSE_REGEX = /^(.*):([0-9]+):[0-9]+: \w+: \((\w+)\) (.*)\.$/gm;

/**
* https://github.com/nicklockwood/SwiftFormat
*/
class SwiftFormat {
static get name() {
return "SwiftFormat";
}

/**
* Verifies that all required programs are installed. Exits the GitHub action if one of the
* programs is missing
*
* @param {string} dir: Directory to run the linting program in
*/
static async verifySetup(dir) {
// Verify that SwiftFormat is installed
if (!(await commandExists("swiftformat"))) {
throw new Error(`${this.name} is not installed`);
}
}

/**
* Runs the linting program and returns the command output
*
* @param {string} dir: Directory to run the linting program in
* @param {string[]} extensions: Array of file extensions which should be linted
* @param {boolean} fix: Whether the linter should attempt to fix code style issues automatically
* @returns {string}: Results of the linting process
*/
static lint(dir, extensions, fix = false) {
if (extensions.length !== 1 || extensions[0] !== "swift") {
throw new Error(`${this.name} error: File extensions are not configurable`);
}

return run(`swiftformat ${fix ? "" : "--lint"} "."`, {
dir,
ignoreErrors: true,
}).stderr;
}

/**
* Parses the results of the linting process and returns it as a processable array
*
* @param {string} dir: Directory in which the linting program has been run
* @param {string} results: Results of the linting process
* @returns {object[]}: Parsed results
*/
static parseResults(dir, results) {
const matches = results.matchAll(PARSE_REGEX);

// Parsed results: [notices, warnings, failures]
const resultsParsed = [[], [], []];

for (const match of matches) {
const [_, pathFull, line, rule, message] = match;
const path = pathFull.substring(dir.length + 1);
const lineNr = parseInt(line, 10);
// SwiftFormat only seems to use the "warning" level, which this action will therefore
// categorize as errors
resultsParsed[2].push({
path,
firstLine: lineNr,
lastLine: lineNr,
message: `${message} (${rule})`,
});
}

return resultsParsed;
}
}

module.exports = SwiftFormat;
3 changes: 2 additions & 1 deletion test/linters/linters.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const gofmtParams = require("./params/gofmt");
const golintParams = require("./params/golint");
const prettierParams = require("./params/prettier");
const stylelintParams = require("./params/stylelint");
const swiftformatParams = require("./params/swiftformat");
const swiftlintParams = require("./params/swiftlint");

const linterParams = [
Expand All @@ -24,7 +25,7 @@ const linterParams = [

// Only run Swift tests on macOS
if (process.platform === "darwin") {
linterParams.push(swiftlintParams);
linterParams.push(swiftformatParams, swiftlintParams);
}

describe.each(linterParams)(
Expand Down
65 changes: 65 additions & 0 deletions test/linters/params/swiftformat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const { join } = require("path");
const SwiftFormat = require("../../../src/linters/swiftformat");

const testName = "swiftformat";
const linter = SwiftFormat;
const extensions = ["swift"];

// Testing input/output for the Linter.lint function, with auto-fixing disabled
function getLintParams(dir) {
const warning1 = `${join(
dir,
"file1.swift",
)}:3:1: warning: (consecutiveBlankLines) Replace consecutive blank lines with a single blank line.`;
const warning2 = `${join(
dir,
"file1.swift",
)}:7:1: warning: (indent) Indent code in accordance with the scope level.`;
const warning3 = `${join(dir, "file2.swift")}:2:1: warning: (semicolons) Remove semicolons.`;
return {
// Strings that must be contained in the stdout of the lint command
stdoutParts: [warning1, warning2, warning3],
// Example output of the lint command, used to test the parsing function
parseInput: `Running SwiftFormat...\n(lint mode - no files will be changed.)\n${warning1}\n${warning2}\n${warning3}\nwarning: No swift version was specified, so some formatting features were disabled. Specify the version of swift you are using with the --swiftversion command line option, or by adding a .swift-version file to your project.\nSwiftFormat completed in 0.01s.\nSource input did not pass lint check.\n2/2 files require formatting.`,
// Expected output of the parsing function
parseResult: [
[],
[],
[
{
path: "file1.swift",
firstLine: 3,
lastLine: 3,
message:
"Replace consecutive blank lines with a single blank line (consecutiveBlankLines)",
},
{
path: "file1.swift",
firstLine: 7,
lastLine: 7,
message: "Indent code in accordance with the scope level (indent)",
},
{
path: "file2.swift",
firstLine: 2,
lastLine: 2,
message: "Remove semicolons (semicolons)",
},
],
],
};
}

// Testing input/output for the Linter.lint function, with auto-fixing enabled
function getFixParams(dir) {
return {
// stdout of the lint command
stdout: "",
// Example output of the lint command, used to test the parsing function
parseInput: "",
// Expected output of the parsing function
parseResult: [[], [], []],
};
}

module.exports = [testName, linter, extensions, getLintParams, getFixParams];
10 changes: 10 additions & 0 deletions test/linters/projects/swiftformat/file1.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let str = "world"

// "consecutiveBlankLines" warning


func main() {
print("hello \(str)") // "indent" warning
}

main()
2 changes: 2 additions & 0 deletions test/linters/projects/swiftformat/file2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// "semicolons" warning
print("hello \(str)");

0 comments on commit 574b45d

Please sign in to comment.