Compare commits

..

41 commits
v0.1.0 ... main

Author SHA1 Message Date
dependabot[bot]
83972e3b8e
Github Actions(deps): Bump untitaker/hyperlink from 0.1.43 to 0.1.44 (#16)
Some checks failed
Oranda / Build and deploy site and docs (push) Has been cancelled
Bumps [untitaker/hyperlink](https://github.com/untitaker/hyperlink) from 0.1.43 to 0.1.44.
- [Release notes](https://github.com/untitaker/hyperlink/releases)
- [Commits](https://github.com/untitaker/hyperlink/compare/0.1.43...0.1.44)

---
updated-dependencies:
- dependency-name: untitaker/hyperlink
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-03 16:43:04 +00:00
1ab9291899
dist: fix artifact build for aarch64-pc-windows-msvc 2024-12-19 12:26:00 +01:00
0d7b3850b7
oranda: generate install instuctions and use hacker theme 2024-12-19 12:19:17 +01:00
d6eb0faf47
dist: upgrade to v0.26.1 and add ARM targets 2024-12-19 12:18:39 +01:00
1585a60ce8
fix array enumeration of complex items resulting in unformatted JSON values 2024-12-19 11:51:37 +01:00
f25c8f31fd
update usage instructions 2024-11-10 10:50:52 +01:00
beed1f613d
remove install instructions 2024-11-08 13:01:11 +01:00
53e991875e
update lockfile 2024-11-08 12:54:22 +01:00
610e24729d
split into lib module 2024-11-08 12:54:19 +01:00
d116a49b11
add array parsing tests 2024-11-08 12:54:15 +01:00
15d7ee5dcc
improve parser ergonomics 2024-11-08 12:54:12 +01:00
8cefe25d62
allow choice of array separator and enumeration 2024-11-08 12:54:02 +01:00
dependabot[bot]
19843b8d08
Github Actions(deps): Bump untitaker/hyperlink from 0.1.40 to 0.1.43 (#15)
Bumps [untitaker/hyperlink](https://github.com/untitaker/hyperlink) from 0.1.40 to 0.1.43.
- [Release notes](https://github.com/untitaker/hyperlink/releases)
- [Commits](https://github.com/untitaker/hyperlink/compare/0.1.40...0.1.43)

---
updated-dependencies:
- dependency-name: untitaker/hyperlink
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 16:14:17 +00:00
d5dee1191d
dist: upgrade to v0.24.1 2024-10-30 23:05:13 +01:00
dependabot[bot]
46d8223136
Github Actions(deps): Bump untitaker/hyperlink from 0.1.32 to 0.1.40 (#14)
Bumps [untitaker/hyperlink](https://github.com/untitaker/hyperlink) from 0.1.32 to 0.1.40.
- [Release notes](https://github.com/untitaker/hyperlink/releases)
- [Commits](https://github.com/untitaker/hyperlink/compare/0.1.32...0.1.40)

---
updated-dependencies:
- dependency-name: untitaker/hyperlink
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-28 16:10:09 +00:00
77dd32e5d0
remove anyhow dependency 2024-05-16 17:48:44 +02:00
7086225b40
add toolchain file 2024-05-16 17:32:53 +02:00
4d5da55227
release v0.2.0 2024-03-09 11:38:36 +01:00
cfa0b5c8a2
lock stdout for faster writes 2024-03-09 11:38:36 +01:00
c42a606930
convert env var to tuple struct 2024-03-09 11:38:36 +01:00
dfe64bc1da
ci: update cargo-dist 2024-03-09 11:38:15 +01:00
bbd53fc570
ci: add manual triggers 2024-02-15 08:59:44 +01:00
bf24de0d04
add explicit separator default value 2024-02-15 08:59:44 +01:00
246231332f
pass refs to the parser 2024-02-15 08:59:44 +01:00
5b6736b1fd
fix errors for missing input or output 2024-02-15 08:59:20 +01:00
33aec78b16
use buffered writes & readers 2024-02-14 23:35:41 +01:00
5983d9b587
do not clone EnvVar fields for Display 2024-02-14 23:35:41 +01:00
dependabot[bot]
709aa82b3e
Cargo(deps): bump clap from 4.4.0 to 4.5.0 (#11)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.4.0...clap_complete-v4.5.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 17:51:45 +01:00
dependabot[bot]
578f507508
Github Actions(deps): bump actions/upload-artifact from 3 to 4 (#10)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 18:41:31 +01:00
dependabot[bot]
55d0ed265b
Github Actions(deps): bump actions/download-artifact from 3 to 4 (#9)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 18:40:48 +01:00
dependabot[bot]
fa458db00d
Github Actions(deps): bump untitaker/hyperlink from 0.1.31 to 0.1.32 (#8)
Bumps [untitaker/hyperlink](https://github.com/untitaker/hyperlink) from 0.1.31 to 0.1.32.
- [Release notes](https://github.com/untitaker/hyperlink/releases)
- [Commits](https://github.com/untitaker/hyperlink/compare/0.1.31...0.1.32)

---
updated-dependencies:
- dependency-name: untitaker/hyperlink
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 18:16:01 +01:00
dependabot[bot]
b6ca453666
Github Actions(deps): bump untitaker/hyperlink from 0.1.29 to 0.1.31 (#5)
Bumps [untitaker/hyperlink](https://github.com/untitaker/hyperlink) from 0.1.29 to 0.1.31.
- [Release notes](https://github.com/untitaker/hyperlink/releases)
- [Commits](https://github.com/untitaker/hyperlink/compare/0.1.29...0.1.31)

---
updated-dependencies:
- dependency-name: untitaker/hyperlink
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 11:10:21 +02:00
dependabot[bot]
d270849cae
Github Actions(deps): bump JamesIves/github-pages-deploy-action (#6)
Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.4.1 to 4.4.3.
- [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases)
- [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.4.1...v4.4.3)

---
updated-dependencies:
- dependency-name: JamesIves/github-pages-deploy-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 11:09:54 +02:00
dependabot[bot]
3dfcbd43cd
Github Actions(deps): bump actions/checkout from 3 to 4 (#7)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 11:09:24 +02:00
7d6ecaf144 fix link check with path_prefix 2023-10-24 11:04:29 +02:00
910845d934 add oranda configuration 2023-10-24 11:04:29 +02:00
ad96ff65a1 fix oranda ci hyperlink check 2023-10-24 11:04:29 +02:00
dc8d2ba245 make dependabot update github-actions 2023-10-24 11:04:23 +02:00
2baeabd2c1 add oranda integration 2023-10-18 12:41:18 +02:00
237143a8ad update cargo-dist integration 2023-10-18 12:00:33 +02:00
dependabot[bot]
dc6b9374e3
Cargo(deps): bump clap from 4.3.4 to 4.4.0 (#4)
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.4 to 4.4.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.4...clap_complete-v4.4.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-29 00:08:16 +02:00
12 changed files with 844 additions and 527 deletions

View file

@ -1,6 +1,6 @@
version: 2
updates:
- package-ecosystem: "cargo" # See documentation for possible values
- package-ecosystem: "cargo"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
@ -8,6 +8,15 @@ updates:
prefix: "Cargo"
include: "scope"
ignore:
# ignore patch updates
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "Github Actions"
include: "scope"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-minor"]

102
.github/workflows/oranda.yml vendored Normal file
View file

@ -0,0 +1,102 @@
# Workflow to build your docs with oranda (and mdbook)
# and deploy them to Github Pages
name: Oranda
# We're going to push to the gh-pages branch, so we need that permission
permissions:
contents: write
# What situations do we want to build docs in?
# All of these work independently and can be removed / commented out
# if you don't want oranda/mdbook running in that situation
on:
# Check that a PR didn't break docs!
#
# Note that the "Deploy to Github Pages" step won't run in this mode,
# so this won't have any side-effects. But it will tell you if a PR
# completely broke oranda/mdbook. Sadly we don't provide previews (yet)!
pull_request:
# Whenever something gets pushed to main, update the docs!
# This is great for getting docs changes live without cutting a full release.
#
# Note that if you're using cargo-dist, this will "race" the Release workflow
# that actually builds the Github Release that oranda tries to read (and
# this will almost certainly complete first). As a result you will publish
# docs for the latest commit but the oranda landing page won't know about
# the latest release. The workflow_run trigger below will properly wait for
# cargo-dist, and so this half-published state will only last for ~10 minutes.
#
# If you only want docs to update with releases, disable this, or change it to
# a "release" branch. You can, of course, also manually trigger a workflow run
# when you want the docs to update.
push:
branches:
- main
# Whenever a workflow called "Release" completes, update the docs!
#
# If you're using cargo-dist, this is recommended, as it will ensure that
# oranda always sees the latest release right when it's available. Note
# however that Github's UI is wonky when you use workflow_run, and won't
# show this workflow as part of any commit. You have to go to the "actions"
# tab for your repo to see this one running (the gh-pages deploy will also
# only show up there).
workflow_run:
workflows: [ "Release" ]
types:
- completed
workflow_dispatch:
# Alright, let's do it!
jobs:
oranda:
name: Build and deploy site and docs
runs-on: ubuntu-latest
steps:
# Setup
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@stable
- uses: swatinem/rust-cache@v2
# If you use any mdbook plugins, here's the place to install them!
# Install and run oranda (and mdbook)
# This will write all output to ./public/ (including copying mdbook's output to there)
- name: Install and run oranda
run: |
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/latest/download/oranda-installer.sh | sh
oranda build
- name: Prepare HTML for link checking
# untitaker/hyperlink supports no site prefixes, move entire site into
# a subfolder
run: |
mkdir /tmp/public/ && cp -R public /tmp/public/json-to-env
cp public/index.html /tmp/public
- name: Check HTML for broken internal links
uses: untitaker/hyperlink@0.1.44
with:
args: /tmp/public
# Deploy to our gh-pages branch (creating it if it doesn't exist)
# the "public" dir that oranda made above will become the root dir
# of this branch.
#
# Note that once the gh-pages branch exists, you must
# go into repo's settings > pages and set "deploy from branch: gh-pages"
# the other defaults work fine.
- name: Deploy to Github Pages
uses: JamesIves/github-pages-deploy-action@v4.4.3
# ONLY if we're on main (so no PRs or feature branches allowed!)
if: ${{ github.ref == 'refs/heads/main' }}
with:
branch: gh-pages
# Gotta tell the action where to find oranda's output
folder: public
token: ${{ secrets.GITHUB_TOKEN }}
single-commit: true

View file

@ -1,140 +1,290 @@
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
#
# Copyright 2022-2024, axodotdev
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
#
# * checks for a Git Tag that looks like a release
# * creates a Github Release™ and fills in its text
# * builds artifacts with cargo-dist (executable-zips, installers)
# * uploads those artifacts to the Github Release™
# * builds artifacts with dist (archives, installers, hashes)
# * uploads those artifacts to temporary workflow zip
# * on success, uploads the artifacts to a GitHub Release
#
# Note that the Github Release™ will be created before the artifacts,
# so there will be a few minutes where the release has no artifacts
# and then they will slowly trickle in, possibly failing. To make
# this more pleasant we mark the release as a "draft" until all
# artifacts have been successfully uploaded. This allows you to
# choose what to do with partial successes and avoids spamming
# anyone with notifications before the release is actually ready.
name: Release
# Note that the GitHub Release will be created with a generated
# title/body based on your changelogs.
name: Release
permissions:
contents: write
"contents": "write"
# This task will run whenever you push a git tag that looks like a version
# like "v1", "v1.2.0", "v0.1.0-prerelease01", "my-app-v1.0.0", etc.
# The version will be roughly parsed as ({PACKAGE_NAME}-)?v{VERSION}, where
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
# must be a Cargo-style SemVer Version.
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
#
# If PACKAGE_NAME is specified, then we will create a Github Release™ for that
# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
# If PACKAGE_NAME is specified, then the announcement will be for that
# package (erroring out if it doesn't have the given version or isn't dist-able).
#
# If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
# (cargo-dist-able) packages in the workspace with that version (this is mode is
# If PACKAGE_NAME isn't specified, then the announcement will be for all
# (dist-able) packages in the workspace with that version (this mode is
# intended for workspaces with only one dist-able package, or with all dist-able
# packages versioned/released in lockstep).
#
# If you push multiple tags at once, separate instances of this workflow will
# spin up, creating an independent Github Release™ for each one.
# spin up, creating an independent announcement for each one. However, GitHub
# will hard limit this to 3 tags per commit, as it will assume more tags is a
# mistake.
#
# If there's a prerelease-style suffix to the version then the Github Release™
# If there's a prerelease-style suffix to the version, then the release(s)
# will be marked as a prerelease.
on:
push:
tags:
- '*-?v[0-9]+*'
- '**[0-9]+.[0-9]+.[0-9]+*'
jobs:
# Create the Github Release™ so the packages have something to be uploaded to
create-release:
runs-on: ubuntu-latest
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
runs-on: "ubuntu-20.04"
outputs:
has-releases: ${{ steps.create-release.outputs.has-releases }}
val: ${{ steps.plan.outputs.manifest }}
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
publishing: ${{ !github.event.pull_request }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update 1.70.0 --no-self-update && rustup default 1.70.0
- name: Install cargo-dist
run: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.sh | sh
- id: create-release
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dist
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.26.1/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
# sure would be cool if github gave us proper conditionals...
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
# functionality based on whether this is a pull_request, and whether it's from a fork.
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
# but also really annoying to build CI around when it needs secrets to work right.)
- id: plan
run: |
cargo dist plan --tag=${{ github.ref_name }} --output-format=json > dist-manifest.json
echo "dist plan ran successfully"
cat dist-manifest.json
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "dist ran successfully"
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v4
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
# Create the Github Release™ based on what cargo-dist thinks it should be
ANNOUNCEMENT_TITLE=$(jq --raw-output ".announcement_title" dist-manifest.json)
IS_PRERELEASE=$(jq --raw-output ".announcement_is_prerelease" dist-manifest.json)
jq --raw-output ".announcement_github_body" dist-manifest.json > new_dist_announcement.md
gh release create ${{ github.ref_name }} --draft --prerelease="$IS_PRERELEASE" --title="$ANNOUNCEMENT_TITLE" --notes-file=new_dist_announcement.md
echo "created announcement!"
# Upload the manifest to the Github Release™
gh release upload ${{ github.ref_name }} dist-manifest.json
echo "uploaded manifest!"
# Disable all the upload-artifacts tasks if we have no actual releases
HAS_RELEASES=$(jq --raw-output ".releases != null" dist-manifest.json)
echo "has-releases=$HAS_RELEASES" >> "$GITHUB_OUTPUT"
# Build and packages all the things
upload-artifacts:
# Build and packages all the platform-specific things
build-local-artifacts:
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
# Let the initial task tell us to not run (currently very blunt)
needs: create-release
if: ${{ needs.create-release.outputs.has-releases == 'true' }}
needs:
- plan
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
strategy:
matrix:
# For these target platforms
include:
- os: ubuntu-20.04
dist-args: --artifacts=global
install-dist: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.sh | sh
- os: macos-11
dist-args: --artifacts=local --target=aarch64-apple-darwin --target=x86_64-apple-darwin
install-dist: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.sh | sh
- os: ubuntu-20.04
dist-args: --artifacts=local --target=x86_64-unknown-linux-gnu
install-dist: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.sh | sh
- os: windows-2019
dist-args: --artifacts=local --target=x86_64-pc-windows-msvc
install-dist: irm https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.ps1 | iex
runs-on: ${{ matrix.os }}
fail-fast: false
# Target platforms/runners are computed by dist in create-release.
# Each member of the matrix has the following arguments:
#
# - runner: the github runner
# - dist-args: cli flags to pass to dist
# - install-dist: expression to run to install dist on the runner
#
# Typically there will be:
# - 1 "global" task that builds universal installers
# - N "local" tasks that build each platform's binaries and platform-specific installers
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container && matrix.container.image || null }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
steps:
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update 1.70.0 --no-self-update && rustup default 1.70.0
- name: Install cargo-dist
run: ${{ matrix.install-dist }}
- name: Run cargo-dist
# This logic is a bit janky because it's trying to be a polyglot between
# powershell and bash since this will run on windows, macos, and linux!
# The two platforms don't agree on how to talk about env vars but they
# do agree on 'cat' and '$()' so we use that to marshal values between commands.
- name: enable windows longpaths
run: |
git config --global core.longpaths true
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust non-interactively if not already installed
if: ${{ matrix.container }}
run: |
if ! command -v cargo > /dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
fi
- name: Install dist
run: ${{ matrix.install_dist.run }}
# Get the dist-manifest
- name: Fetch local artifacts
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- name: Install dependencies
run: |
${{ matrix.packages_install }}
- name: Build artifacts
run: |
# Actually do builds and make zips and whatnot
cargo dist build --tag=${{ github.ref_name }} --output-format=json ${{ matrix.dist-args }} > dist-manifest.json
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
echo "dist ran successfully"
- id: cargo-dist
name: Post-build
# We force bash here just because github makes it really hard to get values up
# to "real" actions without writing to env-vars, and writing to env-vars has
# inconsistent syntax between shell and powershell.
shell: bash
run: |
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v4
with:
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Build and package all the platform-agnostic(ish) things
build-global-artifacts:
needs:
- plan
- build-local-artifacts
runs-on: "ubuntu-20.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: cargo-dist
shell: bash
run: |
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "dist ran successfully"
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v4
with:
name: artifacts-build-global
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Determines if we should publish/announce
host:
needs:
- plan
- build-local-artifacts
- build-global-artifacts
# Only run if we're "publishing", and only if local and global didn't fail (skipped is fine)
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: "ubuntu-20.04"
outputs:
val: ${{ steps.host.outputs.manifest }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v4
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: host
shell: bash
run: |
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
echo "artifacts uploaded and released successfully"
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v4
with:
# Overwrite the previous copy
name: artifacts-dist-manifest
path: dist-manifest.json
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@v4
with:
pattern: artifacts-*
path: artifacts
merge-multiple: true
- name: Cleanup
run: |
# Remove the granular manifests
rm -f artifacts/*-dist-manifest.json
- name: Create GitHub Release
env:
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
RELEASE_COMMIT: "${{ github.sha }}"
run: |
# Write and read notes from a file to avoid quoting breaking things
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
# Parse out what we just built and upload it to the Github Release™
jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json > uploads.txt
echo "uploading..."
cat uploads.txt
gh release upload ${{ github.ref_name }} $(cat uploads.txt)
echo "uploaded!"
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
# Mark the Github Release™ as a non-draft now that everything has succeeded!
publish-release:
# Only run after all the other tasks, but it's ok if upload-artifacts was skipped
needs: [create-release, upload-artifacts]
if: ${{ always() && needs.create-release.result == 'success' && (needs.upload-artifacts.result == 'skipped' || needs.upload-artifacts.result == 'success') }}
runs-on: ubuntu-latest
announce:
needs:
- plan
- host
# use "always() && ..." to allow us to wait for all publish jobs while
# still allowing individual publish jobs to skip themselves (for prereleases).
# "host" however must run to completion, no skipping allowed!
if: ${{ always() && needs.host.result == 'success' }}
runs-on: "ubuntu-20.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- name: mark release as non-draft
run: |
gh release edit ${{ github.ref_name }} --draft=false
- uses: actions/checkout@v4
with:
submodules: recursive

3
.gitignore vendored
View file

@ -1 +1,4 @@
/target
# Generated by `oranda generate ci`
public/

251
Cargo.lock generated
View file

@ -1,103 +1,83 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "anstream"
version = "0.3.2"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.0"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.0"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "clap"
version = "4.3.4"
version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed"
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.4"
version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636"
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.3.2"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [
"heck",
"proc-macro2",
@ -107,171 +87,115 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.5.0"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
version = "1.0.0"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "heck"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.1"
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.6"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "json2env"
version = "0.1.0"
version = "0.3.1"
dependencies = [
"anyhow",
"clap",
"serde_json",
]
[[package]]
name = "libc"
version = "0.2.146"
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "proc-macro2"
version = "1.0.59"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.28"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.13"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.163"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.97"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "strsim"
version = "0.10.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.18"
version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [
"proc-macro2",
"quote",
@ -280,34 +204,35 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.9"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "utf8parse"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.48.0"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
@ -316,42 +241,48 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -1,6 +1,6 @@
[package]
name = "json2env"
version = "0.1.0"
version = "0.3.1"
edition = "2021"
authors = ["Marcello Lamonaca <marcello@lamonaca.eu>"]
description = "JSON to Env Var converter"
@ -10,24 +10,10 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.71"
clap = { version = "4.3.4", features = ["derive", "color"] }
clap = { version = "4.5.0", features = ["derive", "color"] }
serde_json = "1.0.97"
# The profile that 'cargo dist' will build with
[profile.dist]
inherits = "release"
lto = "thin"
# Config for 'cargo dist'
[workspace.metadata.dist]
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.0.7"
# The preferred Rust toolchain to use in CI (rustup toolchain syntax)
rust-toolchain-version = "1.67.1"
# CI backends to support (see 'cargo dist generate-ci')
ci = ["github"]
# The installers to generate for each app
installers = ["shell", "powershell"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "aarch64-apple-darwin"]

View file

@ -1,32 +1,20 @@
# json2env
Convert valid JSON to environment variables or an `.env`-line file.
## Usage
```sh
JSON to Env Var converter
Usage: json2env.exe [OPTIONS]
Options:
-i, --input <FILE> Input file, defaults to STDIN if not specified
-o, --output <FILE> Output file, defaults to STDOUT if not specified
-s, --separator <STRING> Separator for nested keys
-h, --help Print help
-V, --version Print version
```
## Installation
You can either install the tool with `cargo`:
```sh
cargo install --path <path/to/repo>
```
or build the executable with (output in `target/release`):
```sh
cargo build --release
```
# json2env
Convert valid JSON to environment variables or an `.env`-line file.
## Usage
```sh
JSON to Env Var converter
Usage: json2env.exe [OPTIONS]
Options:
-i, --input <FILE> Input file, defaults to STDIN if not specified
-o, --output <FILE> Output file, defaults to STDOUT if not specified
-s, --key-separator <STRING> Separator for nested keys [default: __]
-S, --array-separator <STRING> Separator for array elements [default: ,]
-e, --enumerate-array Separate array elements in multiple environment variables
-h, --help Print help
-V, --version Print version
```

19
dist-workspace.toml Normal file
View file

@ -0,0 +1,19 @@
[workspace]
members = ["cargo:."]
# Config for 'dist'
[dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.26.1"
# CI backends to support
ci = "github"
# The installers to generate for each app
installers = ["shell", "powershell"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-pc-windows-msvc", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
# Which actions to run on pull requests
pr-run-mode = "skip"
# Path that installers should place binaries in
install-path = "CARGO_HOME"
# Whether to install an updater program
install-updater = false

15
oranda.json Normal file
View file

@ -0,0 +1,15 @@
{
"$schema": "https://github.com/axodotdev/oranda/releases/latest/download/oranda-config-schema.json",
"build": {
"path_prefix": "json-to-env"
},
"styles": {
"theme": "hacker"
},
"components": {
"changelog": true,
"artifacts": {
"cargo_dist": true
}
}
}

2
rust-toolchain.toml Normal file
View file

@ -0,0 +1,2 @@
[toolchain]
channel = "stable"

242
src/lib.rs Normal file
View file

@ -0,0 +1,242 @@
use std::fmt::Display;
use serde_json::Value;
#[derive(Debug, Clone)]
pub struct ParseOptions {
key_separator: String,
array_separator: String,
enumerate_array: bool,
}
impl ParseOptions {
pub fn new(key_separator: String, array_separator: String, enumerate_array: bool) -> Self {
Self {
key_separator,
array_separator,
enumerate_array,
}
}
}
#[derive(Debug, Clone)]
pub struct JsonParser {
options: ParseOptions,
}
impl JsonParser {
pub fn new(options: ParseOptions) -> Self {
Self { options }
}
pub fn parse(&mut self, json: &Value) -> Vec<EnvVar> {
Self::parse_value("", json, &self.options)
}
fn parse_value(key: &str, value: &Value, options: &ParseOptions) -> Vec<EnvVar> {
match value {
Value::Array(array) => {
let has_complex_values = array
.iter()
.any(|value| value.is_object() || value.is_array());
// complex (nested) values cannot be part of an array enumeration, skip just this array
if options.enumerate_array || has_complex_values {
let mut values = Vec::with_capacity(array.len());
for (index, item) in array.iter().enumerate() {
let key = Self::build_key(key, &index.to_string(), &options.key_separator);
values.push(Self::parse_value(&key, item, options));
}
values.into_iter().flatten().collect()
} else {
let value = array
.iter()
.map(|value| value.to_string().replace(['\\', '"'], ""))
.collect::<Vec<_>>()
.join(&options.array_separator);
let value = serde_json::Value::String(value);
Self::parse_value(key, &value, options)
}
}
Value::Object(object) => {
let mut values = Vec::with_capacity(object.len());
for (name, value) in object.iter() {
let key = Self::build_key(key, name, &options.key_separator);
values.push(Self::parse_value(&key, value, options));
}
values.into_iter().flatten().collect()
}
_ => vec![EnvVar(key.trim().to_owned(), value.clone())],
}
}
fn build_key(prefix: &str, key: &str, separator: &str) -> String {
match prefix.is_empty() {
true => key.to_string(),
false => format!("{prefix}{separator}{key}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnvVar(String, Value);
impl Display for EnvVar {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.1 {
Value::Null => write!(fmt, "{key}=null", key = self.0),
Value::Bool(bool) => write!(fmt, "{key}={bool}", key = self.0),
Value::Number(ref number) => write!(fmt, "{key}={number}", key = self.0),
Value::String(ref string) => write!(
fmt,
r#"{key}="{value}""#,
key = self.0,
value = string.replace('"', r#"\""#)
),
_ => write!(fmt, ""),
}
}
}
#[cfg(test)]
mod tests {
use serde_json::{json, Value};
use crate::{EnvVar, JsonParser, ParseOptions};
const KEY: &str = r#""key""#;
#[test]
fn build_key_should_leave_key_unchanged_when_prefix_is_empty() {
// ARRANGE
let separator = "";
let input = KEY.to_owned();
let expected = KEY;
// ACT
let result = JsonParser::build_key("", &input, separator);
// ASSERT
assert_eq!(result, expected);
}
#[test]
fn build_key_should_leave_prepend_prefix_with_separator() {
// ARRANGE
let separator = "_";
let input = KEY.to_owned();
let expected = format!("prefix{separator}{KEY}");
// ACT
let actual = JsonParser::build_key("prefix", &input, separator);
// ASSERT
assert_eq!(actual, expected);
}
#[test]
fn bool_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar(KEY.to_owned(), json!(true));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, r#""key"=true"#)
}
#[test]
fn numeric_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar(KEY.to_owned(), json!(1.0));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, r#""key"=1.0"#)
}
#[test]
fn string_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar(KEY.to_owned(), json!("hello"));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, r#""key"="hello""#)
}
#[test]
fn array_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar(KEY.to_owned(), json!([1, 2]));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, "")
}
#[test]
fn object_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar(KEY.to_owned(), json!({ "key": "value" }));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, "")
}
#[test]
fn parse_array_not_enumerated() {
// ARRANGE
let json = json!({ "array": [1, 2, 3] });
let options = ParseOptions::new("__".to_string(), ",".to_string(), false);
let mut parser = JsonParser::new(options);
// ACT
let environ = parser.parse(&json);
// ASSERT
assert_eq!(
*environ,
vec![EnvVar(
"array".to_string(),
Value::String("1,2,3".to_string())
)]
)
}
#[test]
fn parse_array_enumerated() {
// ARRANGE
let json = json!({ "array": [1, 2, 3] });
let options = ParseOptions::new("__".to_string(), ",".to_string(), true);
let mut parser = JsonParser::new(options);
// ACT
let environ = parser.parse(&json);
// ASSERT
assert_eq!(
*environ,
vec![
EnvVar("array__0".to_string(), Value::Number(1.into())),
EnvVar("array__1".to_string(), Value::Number(2.into())),
EnvVar("array__2".to_string(), Value::Number(3.into()))
]
)
}
}

View file

@ -1,219 +1,89 @@
use std::{
fmt::Display,
fs::File,
io::{Read, Write},
};
use anyhow::{Context, Result};
use clap::Parser;
use serde_json::Value;
fn main() -> Result<()> {
let args = Args::parse();
let mut reader: Box<dyn Read> = match args.input {
Some(ref filename) => Box::new(
File::open(filename).with_context(|| format!("Could not open file `{filename}`"))?,
),
None => Box::new(std::io::stdin()),
};
let mut buffer = String::new();
let filename = args.input.unwrap_or("STDIN".to_string());
reader
.read_to_string(&mut buffer)
.with_context(|| format!("Could not read `{filename}`"))?;
let json: Value = serde_json::from_str(&buffer)
.with_context(|| format!("`{filename}` does not contain valid JSON"))?;
let mut vars: Vec<EnvVar> = vec![];
let separator = args.separator.unwrap_or("__".to_string());
JsonParser::parse(&mut vars, "", json, &separator);
let environ = vars
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join("\n");
let mut writer: Box<dyn Write> = match args.output {
Some(ref filename) => Box::new(
File::create(filename).with_context(|| format!("Could not open file `{filename}`"))?,
),
None => Box::new(std::io::stdout()),
};
writer
.write_all(environ.as_bytes())
.with_context(|| "".to_string())?;
Ok(())
}
#[derive(Debug, Parser)]
#[command(name = "json2env", version, about)]
struct Args {
/// Input file, defaults to STDIN if not specified
#[arg(short, long, value_name = "FILE")]
input: Option<String>,
/// Output file, defaults to STDOUT if not specified
#[arg(short, long, value_name = "FILE")]
output: Option<String>,
/// Separator for nested keys, defaults to double underscore (__)
#[arg(short, long, value_name = "STRING")]
separator: Option<String>,
}
struct JsonParser;
impl JsonParser {
fn parse(lines: &mut Vec<EnvVar>, key: &str, value: Value, separator: &str) {
match value {
Value::Array(array) => {
for (index, item) in array.into_iter().enumerate() {
let key = Self::build_key(key, index.to_string(), separator);
Self::parse(lines, key.as_str(), item, separator)
}
}
Value::Object(object) => {
for (name, value) in object {
let key = Self::build_key(key, name, separator);
Self::parse(lines, key.as_str(), value, separator)
}
}
_ => lines.push(EnvVar::new(key.trim().to_owned(), value)),
}
}
fn build_key(prefix: &str, key: String, separator: &str) -> String {
match prefix.is_empty() {
true => key,
false => format!("{prefix}{separator}{key}"),
}
}
}
#[derive(Debug, PartialEq, Eq)]
struct EnvVar {
name: String,
value: Value,
}
impl EnvVar {
fn new(name: String, value: Value) -> Self {
Self { name, value }
}
}
impl Display for EnvVar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let key = self.name.clone();
match self.value.clone() {
Value::Null => write!(f, "{key}=null"),
Value::Bool(bool) => write!(f, "{key}={bool}",),
Value::Number(number) => write!(f, "{key}={number}"),
Value::String(string) => write!(f, r#"{key}="{}""#, string.replace('"', r#"\""#)),
_ => write!(f, ""),
}
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::{EnvVar, JsonParser};
const KEY: &str = r#""key""#;
#[test]
fn build_key_should_leave_key_unchanged_when_prefix_is_empty() {
// ARRANGE
let separator = "";
let input = KEY.to_owned();
let expected = KEY;
// ACT
let result = JsonParser::build_key("", input, separator);
// ASSERT
assert_eq!(result, expected);
}
#[test]
fn build_key_should_leave_prepend_prefix_with_separator() {
// ARRANGE
let separator = "_";
let input = KEY.to_owned();
let expected = format!("prefix{separator}{KEY}");
// ACT
let actual = JsonParser::build_key("prefix", input, separator);
// ASSERT
assert_eq!(actual, expected);
}
#[test]
fn bool_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar::new(KEY.to_owned(), json!(true));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, r#""key"=true"#)
}
#[test]
fn numeric_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar::new(KEY.to_owned(), json!(1.0));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, r#""key"=1.0"#)
}
#[test]
fn string_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar::new(KEY.to_owned(), json!("hello"));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, r#""key"="hello""#)
}
#[test]
fn array_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar::new(KEY.to_owned(), json!([1, 2]));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, "")
}
#[test]
fn object_env_var_should_be_formatted_correctly() {
// ARRANGE
let input = EnvVar::new(KEY.to_owned(), json!({ "key": "value" }));
// ACT
let result = input.to_string();
// ASSERT
assert_eq!(result, "")
}
}
use std::{
error::Error,
fs::File,
io::{BufRead, BufReader, BufWriter, Read, Write},
};
use clap::Parser;
use json2env::{JsonParser, ParseOptions};
use serde_json::Value;
fn main() -> Result<(), Box<dyn Error>> {
let args = Args::parse();
let mut reader: Box<dyn BufRead> = match args.input {
None => Box::new(std::io::stdin().lock()),
Some(ref filename) => {
let file = File::open(filename)
.inspect_err(|_| eprintln!("Error: Could not open file `{filename}`"))?;
Box::new(BufReader::new(file))
}
};
let mut buffer = String::new();
let input = args.input.unwrap_or("STDIN".to_string());
reader
.read_to_string(&mut buffer)
.inspect_err(|_| eprintln!("Error: Could not read `{input}`"))?;
let json: Value = serde_json::from_str(&buffer)
.inspect_err(|_| eprintln!("Error: `{input}` does not contain valid JSON"))?;
let options = ParseOptions::new(
args.key_separator,
args.array_separator,
args.enumerate_array,
);
let mut parser = JsonParser::new(options);
let keys = parser.parse(&json);
let environ = keys
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.join("\n");
let mut writer: Box<dyn Write> = match args.output {
None => Box::new(std::io::stdout().lock()),
Some(ref filename) => {
let file = File::create(filename)
.inspect_err(|_| eprintln!("Error: Could not open file `{filename}`"))?;
Box::new(BufWriter::new(file))
}
};
let output = args.output.unwrap_or("STDOUT".to_string());
writer
.write_all(environ.as_bytes())
.inspect_err(|_| eprintln!("Error: Could not write to `{output}`"))?;
Ok(())
}
#[derive(Debug, Parser)]
#[command(name = "json2env", version, about)]
struct Args {
/// Input file, defaults to STDIN if not specified
#[arg(short, long, value_name = "FILE")]
input: Option<String>,
/// Output file, defaults to STDOUT if not specified
#[arg(short, long, value_name = "FILE")]
output: Option<String>,
/// Separator for nested keys
#[arg(short = 's', long, value_name = "STRING", default_value = "__")]
key_separator: String,
/// Separator for array elements
#[arg(short = 'S', long, value_name = "STRING", default_value = ",")]
array_separator: String,
/// Separate array elements in multiple environment variables
#[arg(short, long)]
enumerate_array: bool,
}