Skip to content

Commit

Permalink
Merge branch 'release/0.0.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianKahnert committed Jan 17, 2020
2 parents 8261e75 + b8a00b3 commit a57d99f
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .chia.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ skippedProviders:
projectRootAppendix: Null

swiftLintConfig:
lintingRulesPath: https://PATH/TO/CONFIG/.swiftlint.yml
# lintingRulesPath: https://PATH/TO/CONFIG/.swiftlint.yml
# lintingRulesPath: /LOCAL/PATH/TO/CONFIG/.swiftlint.yml
17 changes: 17 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: GitHub Release

on:
push:
tags:
- '*.*.*'

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58 changes: 54 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,55 @@
# chia - Check It All
# 🌱 chia - Check It All

## Usage
`chia` is a command line tool that lets you run some checks.
It can be easily integrated into your CI process.

## 🕹 Usage

You can run `chia` in your terminal, but keep in mind to also install all **required dependencies**.
Otherwise, the check might fail!
```bash
# detect language and run all available tests
chia

# specify a config for chia (local/remote)
chia --config /PATH/TO/.chia.yml
chia --config https://PATH/TO/.chia.yml

# only detect and return the language of the project
chia --only-language-detection
```

Instead of keeping track of your dependencies, you can use our [Docker Image](https://hub.docker.com/r/worldiety/chia).
It contains all the required binaries and is ready to use:
```bash
# run docker container with all dependencies and mount the current folder for analysis
docker run -it -v ${PWD}:/project worldiety/chia:latest
docker run -it -v ${PWD}:/project worldiety/chia:latest
```

You can also add this to your [GitLab CI config](https://docs.gitlab.com/ce/ci/yaml/) ...
```yml
...

stages:
- lint
- build
- deploy

chia:
stage: lint
image: worldiety/chia:latest
allow_failure: false
script:
- chia

...
```

... or use our [:octocat: Github Action](https://github.com/marketplace/actions/github-action-for-chia).

## Installation
## ⌨️🖱Installation

There are 2 ways to install `chia`. Choose the one that fits your needs.

Using [Mint](https://github.com/yonaskolb/mint):
```bash
Expand All @@ -24,3 +62,15 @@ git clone https://github.com/worldiety/chia && cd chia
swift build --configuration release
mv `swift build --configuration release --show-bin-path`/chia /usr/local/bin
```


## :octocat: Contributions

All contributions are welcome!
Feel free to contribute to this project.
Submit pull requests or contribute tutorials - whatever you have to offer, it would be appreciated!

If a check is missing, the [`CheckProvider`](https://github.com/worldiety/chia/blob/master/Sources/chiaLib/Internal/CheckProvider.swift) is the right places to start.
Just add another implemention and have a look at all the [other checks](https://github.com/worldiety/chia/tree/master/Sources/chiaLib/Internal/CheckProviders).

If your favorite programming language is missing, have a look at the [`Language`](https://github.com/worldiety/chia/blob/master/Sources/chiaLib/API/Language.swift).
4 changes: 2 additions & 2 deletions Sources/TerminalLog/TerminalLog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public struct TerminalLog: LogHandler {
formedMessage += " -- " + combinedPrettyMetadata!
}

print("\(formedMessage, color: getColor(for: level))\n")
print("\(formedMessage, color: getColor(for: level))")
}

/// Add, remove, or change the logging metadata.
Expand Down Expand Up @@ -68,7 +68,7 @@ public struct TerminalLog: LogHandler {
color = .red
case .warning:
color = .yellow
case .info:
case .notice:
color = .green
default:
color = .default
Expand Down
77 changes: 66 additions & 11 deletions Sources/chiaLib/API/Chia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Foundation
import FoundationNetworking
#endif
import Logging
import ShellOut
import Yams

/// Main part of `Chia`.
Expand All @@ -28,6 +29,9 @@ public struct Chia {
/// `Logger` instance that gets injected.
private let logger: Logger?

/// Filename that will be searched if no config url will be provided.
private let localConfigFilename = ".chia.yml"

/// `ChiaConfig` that will be save in the `setConfig(from:)` method.
private var config: ChiaConfig?

Expand Down Expand Up @@ -67,8 +71,17 @@ public struct Chia {
config = try perform(YAMLDecoder().decode(ChiaConfig.self, from: encodedYAML),
msg: "YAML is not valid could not be decoded.",
errorTransform: { .yamlDecodingError($0) })
logger?.info("Using config from: \(url.path)")

} else {
config = ChiaConfig()
if let localConfigString = try? String(contentsOf: Folder.current.url.appendingPathComponent(localConfigFilename)),
let localConfig = try? YAMLDecoder().decode(ChiaConfig.self, from: localConfigString) {
logger?.info("Using local config from: \(localConfigFilename)")
config = localConfig
} else {
logger?.info("Using default chia config.")
config = ChiaConfig()
}
}

// append project root from config, if needed
Expand Down Expand Up @@ -96,29 +109,42 @@ public struct Chia {
public func runChecks() throws {

guard let config = self.config,
let projectRootFolder = self.projectRootFolder else { throw CheckError.configNotFound }
let projectRootFolder = self.projectRootFolder else { throw ChiaError.configNotFound }

// get project language
guard let detectedLanguags = Language.detect(at: projectRootFolder) else { throw LanguageError.languageDetectionFailed }

// get all check providers for the detected language or generic ones
let filteredProviders = Chia.providers.filter { ($0.type == detectedLanguags || $0.type == .generic) && !$0.isPart(of: config.skippedProviders ?? []) }
logger?.info("These checks will be used:\n\(filteredProviders.map { String(describing: $0) })")

var noCheckFailed = true
// run all checks
var results = [CheckResult]()
for provider in filteredProviders {
do {
try perform(provider.run(with: config, at: projectRootFolder),
msg: "Check Failed [\(provider)]",
errorTransform: { .checkFailed($0) })

// validate if all dependencies (e.g. "swiftlint") exist
for dependency in provider.dependencies {
try canFindDependency(binary: dependency)
}

// run the check
let checkResults = try provider.run(with: config, at: projectRootFolder)
results.append(contentsOf: checkResults)

} catch CheckError.checkFailed(let info) {
results.append(CheckResult(severity: .error, message: "CheckError: Failed with info: \(info.description)", metadata: ["checkProvider": .string(String(describing: provider))]))
} catch CheckError.dependencyNotFound(let dependency) {
results.append(CheckResult(severity: .error, message: "CheckError: Dependency '\(dependency)' not found.", metadata: ["checkProvider": .string(String(describing: provider))]))
} catch CheckError.configPathNotFound(let path) {
results.append(CheckResult(severity: .error, message: "CheckError: Config path invalid: '\(path)'", metadata: ["checkProvider": .string(String(describing: provider))]))
} catch {
logger?.error("\(error)")
noCheckFailed = false
results.append(CheckResult(severity: .error, message: "\(error.localizedDescription)", metadata: ["checkProvider": .string(String(describing: provider))]))
}
}

if noCheckFailed {
logger?.info("All checks successful. We used:\n\(filteredProviders)")
}
// log the output of all checks
log(results)
}

// MARK: - Helper Function
Expand All @@ -130,4 +156,33 @@ public struct Chia {
}
}

private func canFindDependency(binary: String) throws {
do {
try shellOut(to: "which", arguments: [binary])
} catch {
throw CheckError.dependencyNotFound(dependency: binary)
}
}

private func log(_ results: [CheckResult]) {

logger?.info("\n\nCheck Results:\n")
let warnings = results.filter { $0.severity == .warning }
for warning in warnings {
logger?.warning("WARNING: \(warning.message)", metadata: warning.metadata)
}

let errors = results.filter { $0.severity == .error }
for error in errors {
logger?.error("ERROR: \(error.message)", metadata: error.metadata)
}

if warnings.isEmpty && errors.isEmpty {
logger?.notice("\nAll checks successful.\n")
} else if errors.isEmpty {
logger?.warning("\nFound \(warnings.count) warnings.\n")
} else {
logger?.error("\nFound \(errors.count) errors and \(warnings.count) warnings.\n")
}
}
}
2 changes: 1 addition & 1 deletion Sources/chiaLib/API/ChiaError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ public enum ChiaError: Error {
case projectRootNotFound(Error)
case yamlReadingError(Error)
case yamlDecodingError(Error)
case checkFailed(Error)
case configNotFound
}
2 changes: 1 addition & 1 deletion Sources/chiaLib/API/Language.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public enum Language: String, Equatable, Codable {
// case java

static func detect(at projectRoot: Folder) -> Language? {
if projectRoot.containsFile(at: "Package.swift") || projectRoot.files.contains(where: { $0.name.hasSuffix("xcodeproj") || $0.name.hasSuffix("xcworkspace") }) {
if projectRoot.containsFile(at: "Package.swift") || projectRoot.subfolders.contains(where: { $0.name.hasSuffix("xcodeproj") || $0.name.hasSuffix("xcworkspace") }) {
return .swift
} else {
return nil
Expand Down
12 changes: 2 additions & 10 deletions Sources/chiaLib/Internal/CheckProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import ShellOut

protocol CheckProvider {
static var type: Language { get }
static func run(with config: ChiaConfig, at projectRoot: Folder) throws
static var dependencies: [String] { get }
static func run(with config: ChiaConfig, at projectRoot: Folder) throws -> [CheckResult]
}

extension CheckProvider {
Expand All @@ -24,15 +25,6 @@ extension CheckProvider {
Logger(label: "CheckProvider")
}

static func canFindDependency(binary: String) throws {
do {
try shellOut(to: "which", arguments: [binary])
} catch {
logger.error("Could not find the dependency '\(binary)'.\n\(error.localizedDescription)")
throw CheckError.dependencyNotFound(binary)
}
}

static func isPart(of providers: [String]) -> Bool {
let selfDescription = String(describing: Self.self).lowercased()
return providers.contains { selfDescription.contains($0.lowercased()) }
Expand Down
10 changes: 6 additions & 4 deletions Sources/chiaLib/Internal/CheckProviders/LicenseCheck.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import Files

struct LicenseCheck: CheckProvider {

static let type: Language = .generic
static let dependencies: [String] = []
private static let missingFileResult = [CheckResult(severity: .error, message: "LICENSE file could not be found.", metadata: nil)]

static func run(with config: ChiaConfig, at projectRoot: Folder) throws {
guard projectRoot.containsFile(at: "LICENSE") else {
throw CheckError.checkFailed(.init(folder: projectRoot, error: nil))
}
static func run(with config: ChiaConfig, at projectRoot: Folder) throws -> [CheckResult] {
let fileMissing = !projectRoot.containsFile(at: "LICENSE")
return fileMissing ? missingFileResult : []
}
}
10 changes: 6 additions & 4 deletions Sources/chiaLib/Internal/CheckProviders/ReadmeCheck.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import Files

struct ReadmeCheck: CheckProvider {

static let type: Language = .generic
static let dependencies: [String] = []
private static let missingFileResult = [CheckResult(severity: .error, message: "README.md file could not be found.", metadata: nil)]

static func run(with config: ChiaConfig, at projectRoot: Folder) throws {
guard projectRoot.containsFile(at: "README.md") else {
throw CheckError.checkFailed(.init(folder: projectRoot, error: nil))
}
static func run(with config: ChiaConfig, at projectRoot: Folder) throws -> [CheckResult] {
let fileMissing = !projectRoot.containsFile(at: "README.md")
return fileMissing ? missingFileResult : []
}
}
Loading

0 comments on commit a57d99f

Please sign in to comment.