-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from twof/ComputerReadableOutput
Create computer readable output for ingestion by CI
- Loading branch information
Showing
8 changed files
with
218 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,10 +16,19 @@ jobs: | |
uses: trilom/[email protected] | ||
with: | ||
output: ' ' | ||
- uses: pre-commit/[email protected] | ||
- id: pre_commit_action | ||
uses: pre-commit/[email protected] | ||
with: | ||
extra_args: downstream --files ${{ steps.file_changes.outputs.files}} | ||
- uses: JoseThen/comment-pr@v1 | ||
with: | ||
comment: 'This PR is the bees knees!' | ||
extra_args: downstream --files ${{ steps.file_changes.outputs.files }}; | ||
- id: get_changes | ||
run: | | ||
content="$(swift run downstream -o human ${{ steps.file_changes.outputs.files }})" | ||
content="${content//'%'/'%25'}" | ||
content="${content//$'\n'/'%0A'}" | ||
content="${content//$'\r'/'%0D'}" | ||
echo "::set-output name=test::$content" | ||
- uses: daohoangson/comment-on-github@v2 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
with: | ||
body: ${{ steps.get_changes.outputs.test }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
repos: | ||
- repo: https://github.com/twof/Downstream | ||
rev: 0.0.6 | ||
hooks: | ||
- id: downstream | ||
- repo: . | ||
rev: HEAD | ||
hooks: | ||
- id: downstream |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
# Downstream | ||
|
||
A pre-commit hook to alert users when files they're changing may cause docs to be out of date. Downstream is more or less a reverse dependency manager in that it's used to describe what relies on your code rather than what your code relies on. | ||
A tool to alert users when files they're changing may cause docs to be out of date. Downstream is more or less a reverse dependency manager in that it's used to describe what relies on your code rather than what your code relies on. | ||
|
||
## Why? | ||
|
||
|
@@ -15,11 +15,58 @@ In your `.pre-commit-config.yaml` add the following | |
```yaml | ||
repos: | ||
- repo: https://github.com/twof/Downstream | ||
rev: 0.0.3 | ||
rev: 0.0.7 | ||
hooks: | ||
- id: downstream | ||
``` | ||
### Github Actions Installation | ||
You can also have Github comment on PRs when file changes may necessitate other changes. Here is an example setup. | ||
```yaml | ||
name: pre-commit | ||
|
||
on: | ||
workflow_dispatch: | ||
pull_request: | ||
push: | ||
branches: [master] | ||
|
||
jobs: | ||
pre-commit: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v2 | ||
- id: file_changes | ||
uses: trilom/[email protected] | ||
with: | ||
output: ' ' | ||
- id: get_changes | ||
run: | | ||
content="$(swift run downstream -o human ${{ steps.file_changes.outputs.files }})" | ||
content="${content//'%'/'%25'}" | ||
content="${content//$'\n'/'%0A'}" | ||
content="${content//$'\r'/'%0D'}" | ||
echo "::set-output name=test::$content" | ||
- uses: daohoangson/comment-on-github@v2 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
with: | ||
body: ${{ steps.get_changes.outputs.test }} | ||
``` | ||
For some background, `file_changes` records a list of files that have been changed in this PR to `steps.file_changes.outputs.files`. It's basically the equivalent of `git diff --name-only`. | ||
|
||
These lines | ||
``` | ||
content="${content//'%'/'%25'}" | ||
content="${content//$'\n'/'%0A'}" | ||
content="${content//$'\r'/'%0D'}" | ||
``` | ||
are necessary due to (a bug in Github Actions)[https://github.community/t/set-output-truncates-multiline-strings/16852] that prevents multiple lines from being passed to `set-output`. | ||
### Project Structure | ||
You will need to put a file called `downstream.yml` in the directory with the file you'd like to attach documentation to. | ||
|
@@ -48,8 +95,26 @@ Downstream...............................................................Passed | |
- hook id: downstream | ||
- duration: 1.19s | ||
Our records indicate that you may need to update the docs at https://github.com/twof/Downstream/edit/main/README.md because changes were made to Associations.swift | ||
Due to changes made to Sources/downstream/main.swift, you may need to make updates to the following: | ||
https://github.com/twof/Downstream/blob/main/README.md | ||
[main b5db130] bumped pre-commit hook | ||
2 files changed, 1 insertion(+), 2 deletions(-) | ||
``` | ||
|
||
### Usage | ||
|
||
Beyond its usage as a pre-commit hook, Downstream can also be executed manually for integration with CI and whatnot like can be seen above with Github Actions. It can currently produce output based on the format requested by the user with the `-o` flag. Possible options are `human` for human friendly output like seen in the example above, `yaml`, `json`, and `list` which simply lists out all of the docs that may need updates in a format that's convenient for intake in a bash script. | ||
|
||
``` | ||
$ downstream -h | ||
USAGE: downstream-argument [--output-format <output-format>] [<files> ...] | ||
ARGUMENTS: | ||
<files> Input files | ||
OPTIONS: | ||
-o, --output-format <output-format> | ||
The format of the output | ||
-h, --help Show help information. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Alex Reilly on 10/24/20. | ||
// | ||
|
||
import ArgumentParser | ||
import Foundation | ||
import Files | ||
import Yams | ||
|
||
enum OutputFormat: String, ExpressibleByArgument { | ||
case json | ||
case yaml | ||
case list | ||
case humanFriendly = "human" | ||
} | ||
|
||
struct DownstreamArgument: ParsableCommand { | ||
@Option(name: .shortAndLong, help: "The format of the output") | ||
var outputFormat: OutputFormat? | ||
|
||
@Argument(help: "Input files") | ||
var files: [String] | ||
|
||
mutating func run() throws { | ||
let todoList = todos(fileList: self.files) | ||
let output = outputFactory(todos: todoList, format: outputFormat) | ||
|
||
print(output) | ||
} | ||
|
||
func todos(fileList: [String]) -> [String: [String]] { | ||
let decoder = YAMLDecoder() | ||
|
||
return fileList.reduce(into: [String: [String]]()) { (result, filePath) in | ||
let changedFile = try! File(path: filePath) | ||
|
||
if | ||
let parent = changedFile.parent, | ||
let downsteamYML = try? parent.file(named: "downstream.yml").read() | ||
{ | ||
guard let associationsFile = try? decoder.decode(AssociationsFile.self, from: downsteamYML) else { | ||
print("\(parent.path)downstream.yml could not be parsed") | ||
DownstreamArgument.exit() | ||
} | ||
let fileName = changedFile.name | ||
let newTodos = associationsFile.associations[fileName] | ||
result[filePath] = newTodos | ||
} | ||
} | ||
} | ||
|
||
/// Formats the todo list in the selected format type. Defaults to .humanFriendly if no format type is provided. | ||
/// - Parameters: | ||
/// - todos: Todo list in the form of changed file -> associated tasks | ||
/// - format: How the list ought to be formatted. Defaults to .humanFriendly if no format type is provided | ||
/// - Returns: Todo list formatted as desired. | ||
func outputFactory(todos: [String: [String]], format: OutputFormat?) -> String { | ||
let format = format ?? .humanFriendly | ||
|
||
switch format { | ||
case .humanFriendly: | ||
return humanReadableOutput(todos: todos) | ||
case .json: | ||
return jsonOutput(todos: todos) | ||
case .yaml: | ||
return yamlOutput(todos: todos) | ||
case .list: | ||
return listOutput(todos: todos) | ||
} | ||
} | ||
|
||
func humanReadableOutput(todos: [String: [String]]) -> String { | ||
return todos.map { (filePath, todos) in | ||
return "Due to changes made to \(filePath), you may need to make updates to the following: \n \(todos.joined(separator: "\n"))" | ||
}.joined(separator: "\n") | ||
} | ||
|
||
func jsonOutput(todos: [String: [String]]) -> String { | ||
let encoder = JSONEncoder() | ||
guard | ||
let jsonData = try? encoder.encode(todos), | ||
let jsonString = String(data: jsonData, encoding: .utf8) | ||
else { | ||
print("Todo list could not be encoded to JSON string. \n List: \(todos)") | ||
Self.exit() | ||
} | ||
|
||
return jsonString | ||
} | ||
|
||
func yamlOutput(todos: [String: [String]]) -> String { | ||
let encoder = YAMLEncoder() | ||
guard | ||
let yamlString = try? encoder.encode(todos) | ||
else { | ||
print("Todo list could not be encoded to YAML string. \n List: \(todos)") | ||
Self.exit() | ||
} | ||
|
||
return yamlString | ||
} | ||
|
||
func listOutput(todos: [String: [String]]) -> String { | ||
return Set(todos.values).map { $0.joined(separator: "\n") }.joined(separator: "\n") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters