From d687ebc0b3ac2a8e4cda198dfe364879d7b2415e Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Sat, 20 Aug 2022 20:35:38 +0000 Subject: [PATCH 1/3] dev: Setup an automated release process --- .deny.toml | 49 ++++++++++++++++++++++++ .devcontainer/devcontainer.json | 2 +- .github/dependabot.yml | 15 ++++++++ .github/workflows/actions.yml | 19 +++++++++ .github/workflows/release.yml | 68 +++++++++++++++++++++++++++++++++ .github/workflows/rust.yml | 39 +++++++++++++++++++ Cargo.toml | 19 +++++---- LICENCE | 1 + README.md | 9 +++-- justfile | 52 +++++++++++++++++++++++++ src/main.rs | 64 ++++++++++++++++++++++++------- 11 files changed, 311 insertions(+), 26 deletions(-) create mode 100644 .deny.toml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/actions.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/rust.yml create mode 100644 justfile diff --git a/.deny.toml b/.deny.toml new file mode 100644 index 0000000..ce44f19 --- /dev/null +++ b/.deny.toml @@ -0,0 +1,49 @@ +targets = [{ triple = "x86_64-unknown-linux-musl" }] + +[advisories] +db-path = "~/.cargo/advisory-db" +db-urls = ["https://github.com/rustsec/advisory-db"] +vulnerability = "deny" +unmaintained = "warn" +yanked = "deny" +notice = "warn" +ignore = [] + +[licenses] +unlicensed = "deny" +allow = ["Apache-2.0", "MIT", "ISC"] +deny = [] +copyleft = "deny" +allow-osi-fsf-free = "neither" +default = "deny" +confidence-threshold = 0.8 +exceptions = [ + # The Unicode-DFS--2016 license is necessary for unicode-ident because they + # use data from the unicode tables to generate the tables which are + # included in the application. We do not distribute those data files so + # this is not a problem for us. See https://github.com/dtolnay/unicode-ident/pull/9/files + # for more details. + { allow = [ + "MIT", + "Apache-2.0", + "Unicode-DFS-2016", + ], name = "unicode-ident" }, +] + + +[bans] +multiple-versions = "deny" +wildcards = "deny" +highlight = "all" +deny = [] +skip = [] +skip-tree = [] + +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] + +[sources.allow-org] +github = [] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8814efb..4c592bd 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "j52j", + "name": "j5j", "image": "ghcr.io/linkerd/dev:v30", "extensions": [ "DavidAnson.vscode-markdownlint", diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b0ef2e7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: daily + allow: + - dependency-type: "all" + ignore: + - dependency-name: "clap_derive" + + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml new file mode 100644 index 0000000..2802805 --- /dev/null +++ b/.github/workflows/actions.yml @@ -0,0 +1,19 @@ +name: Actions + +on: + pull_request: + paths: + - .github/workflows/* + +permissions: + contents: read + +jobs: + action-lint: + timeout-minutes: 5 + runs-on: ubuntu-latest + container: ghcr.io/linkerd/dev:v30-tools + steps: + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + - run: just fetch + - run: just action-lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a82b819 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,68 @@ +name: Release + +on: + pull_request: + paths: + - .github/workflows/release.yml + push: + tags: + - 'v*' + +permissions: + contents: write + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + RUSTFLAGS: "-D warnings" + RUSTUP_MAX_RETRIES: 10 + +jobs: + meta: + timeout-minutes: 5 + runs-on: ubuntu-latest + container: ghcr.io/linkerd/dev:v30-rust + steps: + - id: meta + shell: bash + run: | + set -eu + shopt -s extglob + ref="${{ github.ref }}" + if [[ "$ref" == refs/tags/v+([0-9]).+([0-9]).+([0-9])?(-+([a-z0-9-])) ]]; then + tv=${ref##refs/tags/} + cv=$(just --evaluate version) + if [ "${tv#v}" != "$cv" ]; then + echo "::error ::Crate version v${cv} does not match tag version ${tv}" + exit 1 + fi + echo ::set-output name=version::"$tv" + echo ::set-output name=publish::true + else + sha="${{ github.sha }}" + echo ::set-output name=version::"test-${sha:0:7}" + fi + outputs: + publish: ${{ steps.meta.outputs.publish }} + version: ${{ steps.meta.outputs.version }} + + package: + needs: [meta] + timeout-minutes: 10 + runs-on: ubuntu-latest + container: ghcr.io/linkerd/dev:v30-rust + steps: + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + - run: just fetch + - run: just build + - run: just package + + - if: needs.meta.outputs.publish == 'true' + uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: ${{ needs.meta.outputs.version }} + files: target/j5j-${{ needs.meta.outputs.version }}-*.tar.gz + generate_release_notes: true + diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..d359551 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,39 @@ +name: Rust + +on: + pull_request: + paths: + - .github/workflows/lint.yml + - Cargo.lock + - '**/*.rs' + +permissions: + contents: read + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + RUSTFLAGS: "-D warnings" + RUSTUP_MAX_RETRIES: 10 + +jobs: + clippy: + timeout-minutes: 5 + runs-on: ubuntu-latest + container: ghcr.io/linkerd/dev:v30-rust + steps: + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + - run: just fetch + - run: just clippy + + run: + timeout-minutes: 5 + runs-on: ubuntu-latest + container: ghcr.io/linkerd/dev:v30-rust + steps: + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + - run: just fetch + - run: | + [ "$(cargo run --frozen --quiet -- .devcontainer/devcontainer.json | jq -r .name)" = j5j ] + - run: | + [ "$(cargo run --frozen --quiet -- - <.devcontainer/devcontainer.json | jq -r .name)" = j5j ] diff --git a/Cargo.toml b/Cargo.toml index 8504e9b..2f72c9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,17 @@ [package] -name = "json5-to-json" -version = "0.1.2" -authors = ["Callum Oakley "] +name = "j5j" +version = "0.2.0" description = "A bare-bones tool for converting JSON5 to plain JSON." license = "ISC" -repository = "https://github.com/callum-oakley/json5-to-json" +repository = "https://github.com/olix0r/j5j" readme = "README.md" -keywords = ["json5", "to", "json", "translate", "convert"] -edition = "2018" +keywords = ["json5", "json", "translate", "convert"] +edition = "2021" [dependencies] -json5 = "0.2" -serde_json = "1.0" +json5 = "0.4" +serde_json = "1" + +[dependencies.clap] +version = "3" +features = ["derive", "env", "std"] diff --git a/LICENCE b/LICENCE index 98b4977..0b4f8df 100644 --- a/LICENCE +++ b/LICENCE @@ -1,3 +1,4 @@ +Copyright 2022 Oliver Gould Copyright 2018 Callum Oakley Permission to use, copy, modify, and/or distribute this software for any diff --git a/README.md b/README.md index 3e96700..624574d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ -Reads [JSON5][] on stdin, writes plain JSON on stdout. A thin wrapper around -[json5-rs][] and [Serde JSON][]. +# j5j + +Reads [JSON5] from one or more files and prints it as plain old JSON. + +Based on `json5-to-json by @callum-oakley. $ cargo install json5-to-json - $ echo "{ hello: 'world' }" | json5-to-json + $ echo "{ hello: 'world' }" | j5j {"hello":"world"} [JSON5]: https://json5.org/ diff --git a/justfile b/justfile new file mode 100644 index 0000000..a93c3c7 --- /dev/null +++ b/justfile @@ -0,0 +1,52 @@ + +lint: action-lint clippy + +## +## Rust +## + +# If we're running in github actions and cargo-action-fmt is installed, then add +# a command suffix that formats errors. +_fmt := if env_var_or_default("GITHUB_ACTIONS", "") != "true" { "" } else { + ``` + if command -v cargo-action-fmt >/dev/null 2>&1; then + echo "--message-format=json | cargo-action-fmt" + fi + ``` +} + +toolchain := "" + +_cargo := "cargo" + if toolchain != "" { " +" + toolchain } else { "" } + +fetch: + {{ _cargo }} fetch --locked + +clippy: fetch + {{ _cargo }} clippy --frozen {{ _fmt }} + +build: fetch + {{ _cargo }} build --frozen --release --target x86_64-unknown-linux-musl {{ _fmt }} + +version := ``` + cargo metadata --format-version=1 \ + | jq -r '.packages[] | select(.name == "j5j") | .version' \ + | head -n 1 + ``` + +package: build + tar -czf target/j5j-v{{ version }}-x86_64-unknown-linux-musl.tar.gz \ + -C target/x86_64-unknown-linux-musl/release j5j + +## +## Etc +## + +# Format actionlint output for Github Actions if running in CI. +_actionlint-fmt := if env_var_or_default("GITHUB_ACTIONS", "") != "true" { "" } else { + '{{range $err := .}}::error file={{$err.Filepath}},line={{$err.Line}},col={{$err.Column}}::{{$err.Message}}%0A```%0A{{replace $err.Snippet "\\n" "%0A"}}%0A```\n{{end}}' +} + +# Lints all GitHub Actions workflows +action-lint: + actionlint {{ if _actionlint-fmt != '' { "-format '" + _actionlint-fmt + "'" } else { "" } }} .github/workflows/* diff --git a/src/main.rs b/src/main.rs index b35a85f..43f7a75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,58 @@ -use json5; -use serde_json::Value; -use std::error::Error; -use std::io::{self, Read}; -use std::process; +use clap::Parser; -fn run() -> Result> { - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; +#[derive(Clone, Debug, Parser)] +#[clap(about, version, arg_required_else_help(true))] +/// Reads JSON5 files and print them to stdout as plain old JSON. +struct Args { + #[clap(short, long, help = "Pretty-print JSON output")] + pretty: bool, - Ok(json5::from_str::(&buffer)?.to_string()) + #[clap(min_values(1), help = "Files to load JSON5 data from")] + files: Vec, } fn main() { - match run() { - Ok(s) => println!("{}", s), - Err(err) => { - eprintln!("json5-to-json: error: {}", err); - process::exit(1); + let Args { pretty, files } = Args::parse(); + + let mut failed = false; + for file in files { + let data = match read(&*file) { + Ok(data) => data, + Err(error) => { + eprintln!("Failed to read file: {}: {}", file, error); + failed = true; + continue; + } + }; + + let value = match json5::from_str::(&data) { + Ok(value) => value, + Err(error) => { + eprintln!("Failed to parse JSON: {}: {}", file, error); + failed = true; + continue; + } + }; + + if pretty { + println!("{:#}", value); + } else { + println!("{}", value); } } + + if failed { + std::process::exit(1); + } +} + +fn read(file: &str) -> Result { + if file == "-" { + use std::io::Read; + let mut buf = String::new(); + std::io::stdin().read_to_string(&mut buf)?; + Ok(buf) + } else { + std::fs::read_to_string(&file) + } } From abb2df8cf17316ecb79c37078606e1c520eb6c2e Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Sat, 20 Aug 2022 20:46:44 +0000 Subject: [PATCH 2/3] fiuxup --- .github/workflows/actions.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 2802805..3bb3191 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -15,5 +15,4 @@ jobs: container: ghcr.io/linkerd/dev:v30-tools steps: - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - - run: just fetch - run: just action-lint From be4112bfa561d4a22a340e9864d5ccf8ed5bd5b5 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Sat, 20 Aug 2022 21:03:43 +0000 Subject: [PATCH 3/3] readme Signed-off-by: Oliver Gould --- README.md | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 624574d..a73749c 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,40 @@ Reads [JSON5] from one or more files and prints it as plain old JSON. Based on `json5-to-json by @callum-oakley. - $ cargo install json5-to-json - $ echo "{ hello: 'world' }" | j5j - {"hello":"world"} +## Examples + +```console +:; j5j .devcontainer/devcontainer.json +{"extensions":["DavidAnson.vscode-markdownlint","kokakiwi.vscode-just","NathanRidley.autotrim","redhat.vscode-yaml","rust-lang.rust-analyzer","samverschueren.final-newline","tamasfe.even-better-toml"],"image":"ghcr.io/linkerd/dev:v30","mounts":["source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind"],"name":"j5j","overrideCommand":false,"remoteUser":"code","runArgs":["--init","--memory=12g","--memory-swap=12g","--net=host"]} +``` + +```console +:; j5j --pretty .devcontainer/devcontainer.json +{ + "extensions": [ + "DavidAnson.vscode-markdownlint", + "kokakiwi.vscode-just", + "NathanRidley.autotrim", + "redhat.vscode-yaml", + "rust-lang.rust-analyzer", + "samverschueren.final-newline", + "tamasfe.even-better-toml" + ], + "image": "ghcr.io/linkerd/dev:v30", + "mounts": [ + "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind" + ], + "name": "j5j", + "overrideCommand": false, + "remoteUser": "code", + "runArgs": [ + "--init", + "--memory=12g", + "--memory-swap=12g", + "--net=host" + ] +} +``` [JSON5]: https://json5.org/ [json5-rs]: https://github.com/callum-oakley/json5-rs