Merge remote-tracking branch 'origin/main'

# Conflicts:
#	.github/workflows/release.yml
This commit is contained in:
2025-09-15 07:50:12 -06:00
10 changed files with 384 additions and 316 deletions
+165 -283
View File
@@ -8,9 +8,9 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
bump_type: bump_type:
description: 'Specify the type of version bump' description: "Specify the type of version bump"
required: true required: true
default: 'patch' default: "patch"
type: choice type: choice
options: options:
- patch - patch
@@ -46,7 +46,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.10' python-version: "3.10"
- name: Install Commitizen - name: Install Commitizen
run: | run: |
@@ -126,9 +126,7 @@ jobs:
- name: Generate changelog for the version bump - name: Generate changelog for the version bump
id: changelog id: changelog
run: | run: |
changelog=$(conventional-changelog -p angular -i CHANGELOG.md -s --from ${{ env.prev_version }} --to ${{ env.version }}) conventional-changelog -p conventionalcommits -i CHANGELOG.md --from ${{ env.prev_version }} --to v${{ env.version }} > artifacts/changelog.md
echo "$changelog" > artifacts/changelog.md
echo "changelog_body=$(cat artifacts/changelog.md)" >> $GITHUB_ENV
- name: Push changes - name: Push changes
if: env.ACT != 'true' if: env.ACT != 'true'
@@ -151,30 +149,42 @@ jobs:
Cargo.toml Cargo.toml
Cargo.lock Cargo.lock
build-release-artifacts: publish-github-release:
name: build-release name: build-release
needs: [bump-version] needs: [bump-version]
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.os }}
env: env:
RUST_BACKTRACE: 1 RUST_BACKTRACE: 1
BUILD_CMD: cargo
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
# prettier-ignore include:
job: - target: aarch64-unknown-linux-musl
# - { name: "macOS-arm64", os: "macOS-latest", target: "aarch64-apple-darwin", artifact_suffix: "macos-arm64", use-cross: true } os: ubuntu-latest
# - { name: "macOS-amd64", os: "macOS-latest", target: "x86_64-apple-darwin", artifact_suffix: "macos" } use-cross: true
# - { name: "windows-amd64", os: "windows-latest", target: "x86_64-pc-windows-msvc", artifact_suffix: "windows" } cargo-flags: ""
# - { name: "windows-aarch64", os: "windows-latest", target: "aarch64-pc-windows-msvc", artifact_suffix: "windows-aarch64", use-cross: true } - target: aarch64-apple-darwin
- { name: "linux-gnu", os: "ubuntu-latest", target: "x86_64-unknown-linux-gnu", artifact_suffix: "linux" } os: macos-latest
- { name: "linux-musl", os: "ubuntu-latest", target: "x86_64-unknown-linux-musl", artifact_suffix: "linux-musl", use-cross: true, } use-cross: true
# - { name: "linux-aarch64-gnu", os: "ubuntu-latest", target: "aarch64-unknown-linux-gnu", artifact_suffix: "aarch64-gnu", use-cross: true, test-bin: "--bin gman" } cargo-flags: ""
# - { name: "linux-aarch64-musl", os: "ubuntu-latest", target: "aarch64-unknown-linux-musl", artifact_suffix: "aarch64-musl", use-cross: true, test-bin: "--bin gman" } - target: aarch64-pc-windows-msvc
# - { name: "linux-arm-gnu", os: "ubuntu-latest", target: "arm-unknown-linux-gnueabi", artifact_suffix: "armv6-gnu", use-cross: true, test-bin: "--bin gman" } os: windows-latest
# - { name: "linux-arm-musl", os: "ubuntu-latest", target: "arm-unknown-linux-musleabihf", artifact_suffix: "armv6-musl", use-cross: true, test-bin: "--bin gman" } use-cross: true
# - { name: "linux-armv7-gnu", os: "ubuntu-latest", target: "armv7-unknown-linux-gnueabihf", artifact_suffix: "armv7-gnu", use-cross: true, test-bin: "--bin gman" } cargo-flags: ""
# - { name: "linux-armv7-musl", os: "ubuntu-latest", target: "armv7-unknown-linux-musleabihf", artifact_suffix: "armv7-musl", use-cross: true, test-bin: "--bin gman" } - target: x86_64-apple-darwin
rust: [stable] os: macos-latest
cargo-flags: ""
- target: x86_64-pc-windows-msvc
os: windows-latest
cargo-flags: ""
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
use-cross: true
cargo-flags: ""
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
cargo-flags: ""
steps: steps:
- name: Check if actor is repository owner - name: Check if actor is repository owner
@@ -194,216 +204,6 @@ jobs:
git fetch --all git fetch --all
git pull git pull
- name: Get bumped Cargo files (Act)
if: env.ACT == 'true'
uses: actions/download-artifact@v4
with:
name: bumped-cargo-files
path: ${{ github.workspace }}
- uses: actions/cache@v3
name: Cache Cargo registry
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('Cargo.lock') }}
- uses: actions/cache@v3
if: startsWith(matrix.job.name, 'linux-')
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-cargo-bin-${{ hashFiles('.github/workflows/release.yml') }}
- uses: dtolnay/rust-toolchain@stable
name: Set Rust toolchain
with:
targets: ${{ matrix.job.target }}
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
# NB: sets CARGO_BUILD_TARGET evar - do not need --target flag in build
target: ${{ matrix.job.target }}
- uses: taiki-e/install-action@cross
if: ${{ matrix.job.use-cross }}
- name: Installing needed Ubuntu dependencies
if: matrix.job.os == 'ubuntu-latest'
shell: bash
run: |
rustup update
sudo apt-get -y update
case ${{ matrix.job.target }} in
arm*-linux-*) sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
aarch64-*-linux-*) sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
- name: Install clang + bindgen for musl targets
if: matrix.job.os == 'ubuntu-latest' && contains(matrix.job.target, 'musl')
shell: bash
run: |
set -euxo pipefail
sudo apt-get -y update
sudo apt-get -y install clang llvm-dev libclang-dev pkg-config cmake make build-essential musl-tools
# force install to avoid stale cache issues
cargo install --force --locked bindgen-cli
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
# help bindgen find libclang
echo "LIBCLANG_PATH=$(llvm-config --libdir)" >> "$GITHUB_ENV"
# quick visibility checks (fail early if missing)
which bindgen
bindgen --version
clang --version
- name: Configure bindgen target flags for musl cross-compile
if: matrix.job.os == 'ubuntu-latest' && contains(matrix.job.target, 'musl')
shell: bash
run: |
set -euo pipefail
triple='${{ matrix.job.target }}'
# Bindgen needs explicit target to avoid host header confusion
echo "BINDGEN_EXTRA_CLANG_ARGS_${triple//-/_}=--target=$triple" >> "$GITHUB_ENV"
# CC hints help any C sub-builds in the tree
case "$triple" in
x86_64-unknown-linux-musl)
echo "CC_x86_64_unknown_linux_musl=musl-gcc" >> "$GITHUB_ENV"
;;
aarch64-unknown-linux-musl)
echo "CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc" >> "$GITHUB_ENV"
;;
arm-unknown-linux-musleabihf)
echo "CC_arm_unknown_linux_musleabihf=arm-linux-musleabihf-gcc" >> "$GITHUB_ENV"
;;
armv7-unknown-linux-musleabihf)
echo "CC_armv7_unknown_linux_musleabihf=armv7-linux-musleabihf-gcc" >> "$GITHUB_ENV"
;;
esac
echo "PKG_CONFIG_ALLOW_CROSS=1" >> "$GITHUB_ENV"
- name: OpenSSL (vendored) toolchain for musl
if: startsWith(matrix.job.name, 'linux-') && contains(matrix.job.target, 'musl')
shell: bash
run: |
# Tools needed for building vendored OpenSSL
sudo apt-get -y update
sudo apt-get -y install musl-tools pkg-config perl make cmake
# Let openssl-sys know we're cross-compiling and want static
echo "OPENSSL_STATIC=1" >> $GITHUB_ENV
echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV
# Set the right C compiler per musl target (some provided by taiki-e/setup-cross-toolchain-action)
case "${{ matrix.job.target }}" in
x86_64-unknown-linux-musl)
echo "CC_x86_64_unknown_linux_musl=musl-gcc" >> $GITHUB_ENV
;;
aarch64-unknown-linux-musl)
# If your toolchain action installs aarch64-linux-musl-gcc, use that:
echo "CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc" >> $GITHUB_ENV
;;
arm-unknown-linux-musleabihf)
echo "CC_arm_unknown_linux_musleabihf=arm-linux-musleabihf-gcc" >> $GITHUB_ENV
;;
armv7-unknown-linux-musleabihf)
echo "CC_armv7_unknown_linux_musleabihf=armv7-linux-musleabihf-gcc" >> $GITHUB_ENV
;;
esac
- name: Build
shell: bash
run: |
set -euxo pipefail
if [[ "${{ matrix.job.use-cross || 'false' }}" == 'true' ]]; then
cross build --release --locked --target=${{ matrix.job.target }} --verbose
else
cargo build --release --locked --target=${{ matrix.job.target }} --verbose
fi
- name: Verify file
shell: bash
run: |
file target/${{ matrix.job.target }}/release/gman
- name: Test
if: matrix.job.target != 'aarch64-apple-darwin' && matrix.job.target != 'aarch64-pc-windows-msvc'
shell: bash
run: |
set -euxo pipefail
if [[ "${{ matrix.job.use-cross || 'false' }}" == 'true' ]]; then
cross test --release --locked --target=${{ matrix.job.target }} --verbose ${{ matrix.job.test-bin }}
else
cargo test --release --locked --target=${{ matrix.job.target }} --verbose ${{ matrix.job.test-bin }}
fi
- name: Test
if: matrix.job.target != 'aarch64-apple-darwin' && matrix.job.target != 'aarch64-pc-windows-msvc'
run: cargo test --release --verbose --target=${{ matrix.job.target }} ${{ matrix.job.test-bin }}
- name: Packaging final binary (Windows)
if: matrix.job.os == 'windows-latest'
shell: bash
run: |
cd target/${{ matrix.job.target }}/release
BINARY_NAME=gman.exe
if [ "${{ matrix.job.target }}" != "aarch64-pc-windows-msvc" ]; then
# strip the binary
strip $BINARY_NAME
fi
RELEASE_NAME=gman-${{ matrix.job.artifact_suffix }}
mkdir -p artifacts
tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME
# create sha checksum files
certutil -hashfile $RELEASE_NAME.tar.gz sha256 | grep -E [A-Fa-f0-9]{64} > $RELEASE_NAME.sha256
echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV
- name: Packaging final binary (macOS and Linux)
if: matrix.job.os != 'windows-latest'
shell: bash
run: |
# set the right strip executable
STRIP="strip";
case ${{ matrix.job.target }} in
arm*-linux-*) STRIP="arm-linux-gnueabihf-strip" ;;
aarch64-*-linux-*) STRIP="aarch64-linux-gnu-strip" ;;
esac;
cd target/${{ matrix.job.target }}/release
BINARY_NAME=gman
# strip the binary
"$STRIP" "$BINARY_NAME"
RELEASE_NAME=gman-${{ matrix.job.artifact_suffix }}
tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME
# create sha checksum files
shasum -a 256 $RELEASE_NAME.tar.gz > $RELEASE_NAME.sha256
echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV
- name: Add artifacts
run: |
mkdir -p artifacts
cp target/${{ matrix.job.target }}/release/${{ env.RELEASE_NAME }}.tar.gz artifacts/
cp target/${{ matrix.job.target }}/release/${{ env.RELEASE_NAME }}.sha256 artifacts/
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: artifacts-${{ env.RELEASE_NAME }}
path: artifacts
overwrite: true
publish-github-release:
name: publish-github-release
needs: [build-release-artifacts]
runs-on: ubuntu-latest
steps:
- name: Check if actor is repository owner
if: ${{ github.actor != github.repository_owner && env.ACT != 'true' }}
run: |
echo "You are not authorized to run this workflow."
exit 1
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Download all artifacts - name: Download all artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
@@ -417,60 +217,146 @@ jobs:
git pull git pull
- name: Set environment variables - name: Set environment variables
shell: bash
run: | run: |
release_version="$(cat ./artifacts/release-version)" release_version="$(cat ./artifacts/release-version)"
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
changelog_body="$(cat ./artifacts/changelog.md)"
echo "changelog_body=$(cat artifacts/changelog.md)" >> $GITHUB_ENV
- name: Validate release environment variables - name: Validate release environment variables
run: | run: |
echo "Release version: ${{ env.RELEASE_VERSION }}" echo "Release version: ${{ env.RELEASE_VERSION }}"
echo "Changelog body: ${{ env.changelog_body }}" echo "Changelog body: $(cat artifacts/changelog.md)"
- name: Create a GitHub Release - name: Get bumped Cargo files (Act)
if: env.ACT == 'true'
uses: actions/download-artifact@v4
with:
name: bumped-cargo-files
path: ${{ github.workspace }}
- uses: dtolnay/rust-toolchain@stable
name: Set Rust toolchain
with:
targets: ${{ matrix.target }}
- name: Install cross
if: matrix.use-cross
uses: taiki-e/install-action@v2
with:
tool: cross
- name: Overwrite build command env variable
if: matrix.use-cross
shell: bash
run: echo "BUILD_CMD=cross" >> $GITHUB_ENV
- name: Install latest LLVM/Clang
if: matrix.os == 'ubuntu-latest'
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
# omit the version to get the latest stable for your Ubuntu (24.04 "noble" on ubuntu-latest)
sudo ./llvm.sh all
# ensure libclang dev package is present (adjust the "22" if a newer major exists)
sudo apt-get update
sudo apt-get install -y libclang-20-dev libclang-dev
- name: Show Version Information (Rust, cargo, GCC)
shell: bash
run: |
gcc --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- name: Build
shell: bash
run: $BUILD_CMD build --locked --release --target=${{ matrix.target }} ${{ matrix.cargo-flags }}
- name: Verify file
shell: bash
run: |
file target/${{ matrix.target }}/release/gman
- name: Test
if: matrix.target != 'aarch64-apple-darwin' && matrix.target != 'aarch64-pc-windows-msvc'
shell: bash
run: |
set -euxo pipefail
if [[ "${{ matrix.use-cross || 'false' }}" == 'true' ]]; then
cross test --release --locked --target=${{ matrix.target }} --verbose
else
cargo test --release --locked --target=${{ matrix.target }} --verbose
fi
- name: Build Archive
shell: bash
id: package
env:
target: ${{ matrix.target }}
run: |
set -euxo pipefail
bin=${GITHUB_REPOSITORY##*/}
dist_dir=`pwd`/dist
name=$bin-$target
executable=target/$target/release/$bin
if [[ "$RUNNER_OS" == "Windows" ]]; then
executable=$executable.exe
fi
mkdir $dist_dir
cp $executable $dist_dir
cd $dist_dir
if [[ "$RUNNER_OS" == "Windows" ]]; then
archive=$dist_dir/$name.zip
sha=$dist_dir/$name.sha256
7z a $archive *
certutil -hashfile $archive sha256 | grep -E [A-Fa-f0-9]{64} > $sha
echo "archive=dist/$name.zip" >> $GITHUB_OUTPUT
echo "sha=dist/$name.sha256" >> $GITHUB_OUTPUT
else
archive=$dist_dir/$name.tar.gz
sha=$dist_dir/$name.sha256
tar -czf $archive *
shasum -a 256 $archive > $sha
echo "archive=dist/$name.tar.gz" >> $GITHUB_OUTPUT
echo "sha=dist/$name.sha256" >> $GITHUB_OUTPUT
fi
- name: Publish Archive and SHA
if: env.ACT != 'true' if: env.ACT != 'true'
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
files: | files: |
artifacts/gman-macos-arm64.tar.gz ${{ steps.package.outputs.archive }}
artifacts/gman-macos-arm64.sha256 ${{ steps.package.outputs.sha }}
artifacts/gman-macos.tar.gz
artifacts/gman-macos.sha256
artifacts/gman-windows.tar.gz
artifacts/gman-windows.sha256
artifacts/gman-windows-aarch64.tar.gz
artifacts/gman-windows-aarch64.sha256
artifacts/gman-linux.tar.gz
artifacts/gman-linux.sha256
artifacts/gman-linux-musl.tar.gz
artifacts/gman-linux-musl.sha256
artifacts/gman-aarch64-gnu.tar.gz
artifacts/gman-aarch64-gnu.sha256
artifacts/gman-aarch64-musl.tar.gz
artifacts/gman-aarch64-musl.sha256
artifacts/gman-armv6-gnu.tar.gz
artifacts/gman-armv6-gnu.sha256
artifacts/gman-armv6-musl.tar.gz
artifacts/gman-armv6-musl.sha256
artifacts/gman-armv7-gnu.tar.gz
artifacts/gman-armv7-gnu.sha256
artifacts/gman-armv7-musl.tar.gz
artifacts/gman-armv7-musl.sha256
tag_name: v${{ env.RELEASE_VERSION }} tag_name: v${{ env.RELEASE_VERSION }}
name: 'v${{ env.RELEASE_VERSION }}' name: "v${{ env.RELEASE_VERSION }}"
body: ${{ env.changelog_body }} body_path: artifacts/changelog.md
draft: false
prerelease: false prerelease: false
- name: Add artifacts
shell: bash
run: |
[[ -d artifacts ]] || mkdir -p artifacts
cp ${{ steps.package.outputs.archive }} artifacts/
cp ${{ steps.package.outputs.sha }} artifacts/
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: artifacts-v${{ env.RELEASE_VERSION }}-${{ matrix.target }}
path: artifacts path: artifacts
overwrite: true overwrite: true
publish-chocolatey-package: publish-chocolatey-package:
needs: [publish-github-release] needs: [publish-github-release]
name: Publish Chocolatey Package name: Publish Chocolatey Package
@@ -497,7 +383,7 @@ jobs:
shell: pwsh shell: pwsh
run: | run: |
# Read the first column from the SHA256 file # Read the first column from the SHA256 file
$windows_sha = Get-Content ./artifacts/gman-windows.sha256 | ForEach-Object { $_.Split(' ')[0] } $windows_sha = Get-Content ./artifacts/gman-x86_64-pc-windows-msvc.sha256 | ForEach-Object { $_.Split(' ')[0] }
Add-Content -Path $env:GITHUB_ENV -Value "WINDOWS_SHA=$windows_sha" Add-Content -Path $env:GITHUB_ENV -Value "WINDOWS_SHA=$windows_sha"
# Read the release version from the release-version file # Read the release version from the release-version file
@@ -520,10 +406,17 @@ jobs:
# Publish to Chocolatey # Publish to Chocolatey
cd ./deployment/chocolatey cd ./deployment/chocolatey
choco pack choco pack
echo y | choco install gman -dv -s . choco install gman --yes -dv -s .
$version = gman --version # Verify install succeeded
choco list --local-only | Select-String '^gman ' | Out-Null
# Run the EXE directly from the installed package folder
$exe = Get-ChildItem "$env:ChocolateyInstall\lib\gman\tools\gman*.exe" -ErrorAction Stop |
Select-Object -First 1 -ExpandProperty FullName
$version = & $exe --version
$version = $version -replace " ", "." $version = $version -replace " ", "."
choco push $version.nupkg -s https://push.chocolatey.org/ --api-key ${{ secrets.CHOCOLATEY_API_KEY }}; choco push "$version.nupkg" -s https://push.chocolatey.org/ --api-key ${{ secrets.CHOCOLATEY_API_KEY }}
publish-homebrew-formula: publish-homebrew-formula:
needs: [publish-github-release] needs: [publish-github-release]
@@ -551,11 +444,11 @@ jobs:
shell: bash shell: bash
run: | run: |
# Set environment variables # Set environment variables
macos_sha="$(cat ./artifacts/gman-macos.sha256 | awk '{print $1}')" macos_sha="$(cat ./artifacts/gman-x86_64-apple-darwin.sha256 | awk '{print $1}')"
echo "MACOS_SHA=$macos_sha" >> $GITHUB_ENV echo "MACOS_SHA=$macos_sha" >> $GITHUB_ENV
macos_sha_arm="$(cat ./artifacts/gman-macos-arm64.sha256 | awk '{print $1}')" macos_sha_arm="$(cat ./artifacts/gman-aarch64-apple-darwin.sha256 | awk '{print $1}')"
echo "MACOS_SHA_ARM=$macos_sha_arm" >> $GITHUB_ENV echo "MACOS_SHA_ARM=$macos_sha_arm" >> $GITHUB_ENV
linux_sha="$(cat ./artifacts/gman-linux-musl.sha256 | awk '{print $1}')" linux_sha="$(cat ./artifacts/gman-x86_64-unknown-linux-musl.sha256 | awk '{print $1}')"
echo "LINUX_SHA=$linux_sha" >> $GITHUB_ENV echo "LINUX_SHA=$linux_sha" >> $GITHUB_ENV
release_version="$(cat ./artifacts/release-version)" release_version="$(cat ./artifacts/release-version)"
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
@@ -618,17 +511,6 @@ jobs:
git fetch --all git fetch --all
git pull git pull
- uses: actions/cache@v3
name: Cache Cargo registry
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('Cargo.lock') }}
- uses: actions/cache@v3
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-cargo-bin-${{ hashFiles('.github/workflows/release.yml') }}
- name: Install Rust stable - name: Install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
+33 -1
View File
@@ -7,4 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.0.1] - 2025-09-10 ## [0.0.1] - 2025-09-10
### Other ## v0.1.0 (2025-09-15)
### Fix
- Pass the changelog to the GHA properly using a file
- Potential bug in changelog variable generation
- Revert back hacky stuff so I can test with act now
- Attempting to use pre-generated bindgens for the aws-lc-sys library
- Install openSSL differently to make this work
- Address edge case for unknown_musl targets
- Install LLVM prereqs for release flow
- Updated the release flow to install the external bindgen-cli
## v0.0.1 (2025-09-12)
### Feat
- Azure Key Vault support
- GCP Secret Manager support
- Full AWS SecretsManager support
- AWS Secrets Manager support
- Added two new flags to output where gman writes logs to and where it expects the config file to live
### Fix
- Made the vault file location more fault tolerant
- Attempting to maybe be a bit more explicit about config file handling to fix MacOS tests
### Refactor
- Refactor configuration structs directly into the provider definition to simplify validation, structs, and future extensions
- Made the creation of the log directories a bit more fault tolerant
- Renamed the provider field in a config file to type to make things a little easier to understand; also removed husky
Generated
+25 -15
View File
@@ -1501,9 +1501,9 @@ dependencies = [
[[package]] [[package]]
name = "gcloud-sdk" name = "gcloud-sdk"
version = "0.28.1" version = "0.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41dcccf7c0cc0986cb5f476854a5c63b95bab4835f12884704f6aa33ac7d14bc" checksum = "7fe603014c94ee883f514ea12a3df0ad99da67124472bd3c52ee7fed0ccd4ea1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@@ -1576,7 +1576,7 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]] [[package]]
name = "gman" name = "gman"
version = "0.0.1" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argon2", "argon2",
@@ -1605,7 +1605,6 @@ dependencies = [
"log", "log",
"log4rs", "log4rs",
"openssl", "openssl",
"openssl-sys",
"predicates", "predicates",
"pretty_assertions", "pretty_assertions",
"proptest", "proptest",
@@ -2206,9 +2205,9 @@ dependencies = [
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
@@ -3291,16 +3290,17 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.26" version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.219" version = "1.0.223"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac"
dependencies = [ dependencies = [
"serde_core",
"serde_derive", "serde_derive",
] ]
@@ -3315,10 +3315,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_core"
version = "1.0.219" version = "1.0.223"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" checksum = "20f57cbd357666aa7b3ac84a90b4ea328f1d4ddb6772b430caa5d9e1309bb9e9"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.223"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d428d07faf17e306e699ec1e91996e5a165ba5d6bce5b5155173e91a8a01a56"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3327,14 +3336,15 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.143" version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu", "ryu",
"serde", "serde",
"serde_core",
] ]
[[package]] [[package]]
+7 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "gman" name = "gman"
version = "0.0.1" version = "0.1.0"
edition = "2024" edition = "2024"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"] authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "Universal secret management and injection tool" description = "Universal secret management and injection tool"
@@ -58,10 +58,14 @@ gcloud-sdk = { version = "0.28.1", features = [
crc32c = "0.6.8" crc32c = "0.6.8"
azure_identity = "0.27.0" azure_identity = "0.27.0"
azure_security_keyvault_secrets = "0.6.0" azure_security_keyvault_secrets = "0.6.0"
openssl = { version = "0.10", features = ["vendored"] }
openssl-sys = { version = "0.9", features = ["vendored"] }
aws-lc-sys = { version = "0.31.0", features = ["bindgen"] } aws-lc-sys = { version = "0.31.0", features = ["bindgen"] }
[target.'cfg(all(target_os="linux", target_env="musl"))'.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
[target.'cfg(target_os="macos")'.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.1" pretty_assertions = "1.4.1"
proptest = "1.5.0" proptest = "1.5.0"
@@ -2,7 +2,7 @@ $ErrorActionPreference = 'Stop';
$PackageName = 'gman' $PackageName = 'gman'
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$url64 = 'https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-windows.tar.gz' $url64 = 'https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-x86_64-pc-windows-msvc.zip'
$checksum64 = '$hash_64' $checksum64 = '$hash_64'
$packageArgs = @{ $packageArgs = @{
@@ -16,5 +16,5 @@ $packageArgs = @{
} }
Install-ChocolateyZipPackage @packageArgs Install-ChocolateyZipPackage @packageArgs
$File = Get-ChildItem -File -Path $env:ChocolateyInstall\lib\$packageName\tools\ -Filter *.tar $File = Get-ChildItem -File -Path $env:ChocolateyInstall\lib\$packageName\tools\ -Filter *.zip
Get-ChocolateyUnzip -fileFullPath $File.FullName -destination $env:ChocolateyInstall\lib\$packageName\tools\ Get-ChocolateyUnzip -fileFullPath $File.FullName -destination $env:ChocolateyInstall\lib\$packageName\tools\
+2 -2
View File
@@ -33,9 +33,9 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe
<docsUrl>https://github.com/Dark-Alex-17/gman/blob/main/README.md</docsUrl> <docsUrl>https://github.com/Dark-Alex-17/gman/blob/main/README.md</docsUrl>
<bugTrackerUrl>https://github.com/Dark-Alex-17/gman/issues</bugTrackerUrl> <bugTrackerUrl>https://github.com/Dark-Alex-17/gman/issues</bugTrackerUrl>
<tags>cli cross-platform terminal credential-management secret-management rust</tags> <tags>cli cross-platform terminal credential-management secret-management rust</tags>
<summary>Universal command line credential management and injection tool</summary> <summary>Universal CLI credential management and injection tool</summary>
<description> <description>
Universal command line credential management and injection tool. Universal CLI credential management and injection tool.
**Usage** **Usage**
To get started, run `gman --help` in a terminal. To get started, run `gman --help` in a terminal.
+4 -4
View File
@@ -1,16 +1,16 @@
# Documentation: https://docs.brew.sh/Formula-Cookbook # Documentation: https://docs.brew.sh/Formula-Cookbook
# https://rubydoc.brew.sh/Formula # https://rubydoc.brew.sh/Formula
class GMan < Formula class Gman < Formula
desc "Universal command line credential management and injection tool" desc "Universal command line credential management and injection tool"
homepage "https://github.com/Dark-Alex-17/gman" homepage "https://github.com/Dark-Alex-17/gman"
if OS.mac? and Hardware::CPU.arm? if OS.mac? and Hardware::CPU.arm?
url "https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-macos-arm64.tar.gz" url "https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-aarch64-apple-darwin.tar.gz"
sha256 "$hash_mac_arm" sha256 "$hash_mac_arm"
elsif OS.mac? and Hardware::CPU.intel? elsif OS.mac? and Hardware::CPU.intel?
url "https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-macos.tar.gz" url "https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-x86_64-apple-darwin.tar.gz"
sha256 "$hash_mac" sha256 "$hash_mac"
else else
url "https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-linux-musl.tar.gz" url "https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-x86_64-unknown-linux-musl.tar.gz"
sha256 "$hash_linux" sha256 "$hash_linux"
end end
version "$version" version "$version"
+1
View File
@@ -144,6 +144,7 @@ impl ProviderConfig {
match &mut self.provider_type { match &mut self.provider_type {
SupportedProvider::Local { provider_def } => { SupportedProvider::Local { provider_def } => {
debug!("Using local secret provider"); debug!("Using local secret provider");
provider_def.runtime_provider_name = self.name.clone();
provider_def provider_def
} }
SupportedProvider::AwsSecretsManager { provider_def } => { SupportedProvider::AwsSecretsManager { provider_def } => {
+143 -6
View File
@@ -5,9 +5,9 @@ use std::path::{Path, PathBuf};
use std::{env, fs}; use std::{env, fs};
use zeroize::Zeroize; use zeroize::Zeroize;
use crate::config::Config; use crate::config::{Config, get_config_file_path, load_config};
use crate::providers::SecretProvider;
use crate::providers::git_sync::{SyncOpts, repo_name_from_url, sync_and_push}; use crate::providers::git_sync::{SyncOpts, repo_name_from_url, sync_and_push};
use crate::providers::{SecretProvider, SupportedProvider};
use crate::{ use crate::{
ARGON_M_COST_KIB, ARGON_P, ARGON_T_COST, HEADER, KDF, KEY_LEN, NONCE_LEN, SALT_LEN, VERSION, ARGON_M_COST_KIB, ARGON_P, ARGON_T_COST, HEADER, KDF, KEY_LEN, NONCE_LEN, SALT_LEN, VERSION,
}; };
@@ -54,6 +54,8 @@ pub struct LocalProvider {
#[validate(email)] #[validate(email)]
pub git_user_email: Option<String>, pub git_user_email: Option<String>,
pub git_executable: Option<PathBuf>, pub git_executable: Option<PathBuf>,
#[serde(skip)]
pub runtime_provider_name: Option<String>,
} }
impl Default for LocalProvider { impl Default for LocalProvider {
@@ -65,6 +67,7 @@ impl Default for LocalProvider {
git_user_name: None, git_user_name: None,
git_user_email: None, git_user_email: None,
git_executable: None, git_executable: None,
runtime_provider_name: None,
} }
} }
} }
@@ -169,7 +172,9 @@ impl SecretProvider for LocalProvider {
config_changed = true; config_changed = true;
debug!("Prompting user to set git_remote in config for sync"); debug!("Prompting user to set git_remote in config for sync");
let remote: String = Input::with_theme(&ColorfulTheme::default()) let remote: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Enter remote git URL to sync with") .with_prompt(
"Enter remote git URL to sync with (e.g. 'git@github.com:user/repo.git')",
)
.validate_with(|s: &String| { .validate_with(|s: &String| {
LocalProvider { LocalProvider {
git_remote_url: Some(s.clone()), git_remote_url: Some(s.clone()),
@@ -185,9 +190,7 @@ impl SecretProvider for LocalProvider {
} }
if config_changed { if config_changed {
debug!("Saving updated config"); self.persist_git_settings_to_config()?;
confy::store("gman", "config", &self)
.with_context(|| "failed to save updated config")?;
} }
let sync_opts = SyncOpts { let sync_opts = SyncOpts {
@@ -203,6 +206,52 @@ impl SecretProvider for LocalProvider {
} }
impl LocalProvider { impl LocalProvider {
fn persist_git_settings_to_config(&self) -> Result<()> {
debug!("Saving updated config (only current local provider)");
let mut cfg = load_config().with_context(|| "failed to load existing config")?;
let target_name = self.runtime_provider_name.clone();
let mut updated = false;
for pc in cfg.providers.iter_mut() {
if let SupportedProvider::Local { provider_def } = &mut pc.provider_type {
let matches_name = match (&pc.name, &target_name) {
(Some(n), Some(t)) => n == t,
(Some(_), None) => false,
_ => false,
};
if matches_name || target_name.is_none() {
provider_def.git_branch = self.git_branch.clone();
provider_def.git_remote_url = self.git_remote_url.clone();
updated = true;
if matches_name {
break;
}
}
}
}
if !updated {
bail!("unable to find matching local provider in config to update");
}
let path = get_config_file_path()?;
let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
if ext.eq_ignore_ascii_case("yml") || ext.eq_ignore_ascii_case("yaml") {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let s = serde_yaml::to_string(&cfg)?;
fs::write(&path, s).with_context(|| format!("failed to write {}", path.display()))?;
} else {
confy::store("gman", "config", &cfg)
.with_context(|| "failed to save updated config via confy")?;
}
Ok(())
}
fn repo_dir_for_config(&self) -> Result<Option<PathBuf>> { fn repo_dir_for_config(&self) -> Result<Option<PathBuf>> {
if let Some(remote) = &self.git_remote_url { if let Some(remote) = &self.git_remote_url {
let name = repo_name_from_url(remote); let name = repo_name_from_url(remote);
@@ -424,6 +473,7 @@ mod tests {
use super::*; use super::*;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use secrecy::{ExposeSecret, SecretString}; use secrecy::{ExposeSecret, SecretString};
use std::env as std_env;
use tempfile::tempdir; use tempfile::tempdir;
#[test] #[test]
@@ -458,9 +508,96 @@ mod tests {
fs::write(&file, "secretpw\n").unwrap(); fs::write(&file, "secretpw\n").unwrap();
let provider = LocalProvider { let provider = LocalProvider {
password_file: Some(file), password_file: Some(file),
runtime_provider_name: None,
..LocalProvider::default() ..LocalProvider::default()
}; };
let pw = provider.get_password().unwrap(); let pw = provider.get_password().unwrap();
assert_eq!(pw.expose_secret(), "secretpw"); assert_eq!(pw.expose_secret(), "secretpw");
} }
#[test]
fn persist_only_target_local_provider_git_settings() {
let td = tempdir().unwrap();
let xdg = td.path().join("xdg");
let app_dir = xdg.join("gman");
fs::create_dir_all(&app_dir).unwrap();
unsafe {
std_env::set_var("XDG_CONFIG_HOME", &xdg);
}
let initial_yaml = indoc::indoc! {
"---
default_provider: local
providers:
- name: local
type: local
password_file: /tmp/.gman_pass
git_branch: main
git_remote_url: null
git_user_name: null
git_user_email: null
git_executable: null
- name: other
type: local
git_branch: main
git_remote_url: git@github.com:someone/else.git
run_configs:
- name: echo
secrets: [API_KEY]
"
};
let cfg_path = app_dir.join("config.yml");
fs::write(&cfg_path, initial_yaml).unwrap();
let provider = LocalProvider {
password_file: None,
git_branch: Some("dev".into()),
git_remote_url: Some("git@github.com:user/repo.git".into()),
git_user_name: Some("Test User".into()),
git_user_email: Some("test@example.com".into()),
git_executable: Some(PathBuf::from("/usr/bin/git")),
runtime_provider_name: Some("local".into()),
};
provider
.persist_git_settings_to_config()
.expect("persist ok");
let content = fs::read_to_string(&cfg_path).unwrap();
let cfg: crate::config::Config = serde_yaml::from_str(&content).unwrap();
assert_eq!(cfg.default_provider.as_deref(), Some("local"));
assert!(cfg.run_configs.is_some());
assert_eq!(cfg.run_configs.as_ref().unwrap().len(), 1);
let p0 = &cfg.providers[0];
assert_eq!(p0.name.as_deref(), Some("local"));
match &p0.provider_type {
SupportedProvider::Local { provider_def } => {
assert_eq!(provider_def.git_branch.as_deref(), Some("dev"));
assert_eq!(
provider_def.git_remote_url.as_deref(),
Some("git@github.com:user/repo.git")
);
}
_ => panic!("expected local provider"),
}
let p1 = &cfg.providers[1];
assert_eq!(p1.name.as_deref(), Some("other"));
match &p1.provider_type {
SupportedProvider::Local { provider_def } => {
assert_eq!(provider_def.git_branch.as_deref(), Some("main"));
assert_eq!(
provider_def.git_remote_url.as_deref(),
Some("git@github.com:someone/else.git")
);
}
_ => panic!("expected local provider"),
}
unsafe {
std_env::remove_var("XDG_CONFIG_HOME");
}
}
} }
+2
View File
@@ -34,6 +34,7 @@ fn test_local_provider_valid() {
git_user_name: None, git_user_name: None,
git_user_email: Some("test@example.com".to_string()), git_user_email: Some("test@example.com".to_string()),
git_executable: None, git_executable: None,
runtime_provider_name: None,
}; };
assert!(provider.validate().is_ok()); assert!(provider.validate().is_ok());
@@ -48,6 +49,7 @@ fn test_local_provider_invalid_email() {
git_user_name: None, git_user_name: None,
git_user_email: Some("test".to_string()), git_user_email: Some("test".to_string()),
git_executable: None, git_executable: None,
runtime_provider_name: None,
}; };
assert!(config.validate().is_err()); assert!(config.validate().is_err());