diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb25858..ebdf44f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,9 +8,9 @@ on: workflow_dispatch: inputs: bump_type: - description: 'Specify the type of version bump' + description: "Specify the type of version bump" required: true - default: 'patch' + default: "patch" type: choice options: - patch @@ -46,7 +46,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: "3.10" - name: Install Commitizen run: | @@ -126,9 +126,7 @@ jobs: - name: Generate changelog for the version bump id: changelog run: | - changelog=$(conventional-changelog -p angular -i CHANGELOG.md -s --from ${{ env.prev_version }} --to ${{ env.version }}) - echo "$changelog" > artifacts/changelog.md - echo "changelog_body=$(cat artifacts/changelog.md)" >> $GITHUB_ENV + conventional-changelog -p conventionalcommits -i CHANGELOG.md --from ${{ env.prev_version }} --to v${{ env.version }} > artifacts/changelog.md - name: Push changes if: env.ACT != 'true' @@ -151,30 +149,42 @@ jobs: Cargo.toml Cargo.lock - build-release-artifacts: + publish-github-release: name: build-release needs: [bump-version] - runs-on: ${{ matrix.job.os }} + runs-on: ${{ matrix.os }} env: RUST_BACKTRACE: 1 + BUILD_CMD: cargo strategy: fail-fast: true matrix: - # prettier-ignore - job: - # - { name: "macOS-arm64", os: "macOS-latest", target: "aarch64-apple-darwin", artifact_suffix: "macos-arm64", use-cross: true } - # - { name: "macOS-amd64", os: "macOS-latest", target: "x86_64-apple-darwin", artifact_suffix: "macos" } - # - { name: "windows-amd64", os: "windows-latest", target: "x86_64-pc-windows-msvc", artifact_suffix: "windows" } - # - { name: "windows-aarch64", os: "windows-latest", target: "aarch64-pc-windows-msvc", artifact_suffix: "windows-aarch64", use-cross: true } - - { name: "linux-gnu", os: "ubuntu-latest", target: "x86_64-unknown-linux-gnu", artifact_suffix: "linux" } - - { name: "linux-musl", os: "ubuntu-latest", target: "x86_64-unknown-linux-musl", artifact_suffix: "linux-musl", 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" } - # - { name: "linux-aarch64-musl", os: "ubuntu-latest", target: "aarch64-unknown-linux-musl", artifact_suffix: "aarch64-musl", use-cross: true, test-bin: "--bin gman" } - # - { name: "linux-arm-gnu", os: "ubuntu-latest", target: "arm-unknown-linux-gnueabi", artifact_suffix: "armv6-gnu", use-cross: true, test-bin: "--bin gman" } - # - { name: "linux-arm-musl", os: "ubuntu-latest", target: "arm-unknown-linux-musleabihf", artifact_suffix: "armv6-musl", use-cross: true, test-bin: "--bin gman" } - # - { name: "linux-armv7-gnu", os: "ubuntu-latest", target: "armv7-unknown-linux-gnueabihf", artifact_suffix: "armv7-gnu", use-cross: true, test-bin: "--bin gman" } - # - { name: "linux-armv7-musl", os: "ubuntu-latest", target: "armv7-unknown-linux-musleabihf", artifact_suffix: "armv7-musl", use-cross: true, test-bin: "--bin gman" } - rust: [stable] + include: + - target: aarch64-unknown-linux-musl + os: ubuntu-latest + use-cross: true + cargo-flags: "" + - target: aarch64-apple-darwin + os: macos-latest + use-cross: true + cargo-flags: "" + - target: aarch64-pc-windows-msvc + os: windows-latest + use-cross: true + cargo-flags: "" + - target: x86_64-apple-darwin + 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: - name: Check if actor is repository owner @@ -194,216 +204,6 @@ jobs: git fetch --all 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 uses: actions/download-artifact@v4 with: @@ -417,60 +217,146 @@ jobs: git pull - name: Set environment variables + shell: bash run: | release_version="$(cat ./artifacts/release-version)" 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 run: | 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' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: files: | - artifacts/gman-macos-arm64.tar.gz - artifacts/gman-macos-arm64.sha256 - 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 + ${{ steps.package.outputs.archive }} + ${{ steps.package.outputs.sha }} tag_name: v${{ env.RELEASE_VERSION }} - name: 'v${{ env.RELEASE_VERSION }}' - body: ${{ env.changelog_body }} - draft: false + name: "v${{ env.RELEASE_VERSION }}" + body_path: artifacts/changelog.md 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 uses: actions/upload-artifact@v4 with: + name: artifacts-v${{ env.RELEASE_VERSION }}-${{ matrix.target }} path: artifacts overwrite: true + publish-chocolatey-package: needs: [publish-github-release] name: Publish Chocolatey Package @@ -497,7 +383,7 @@ jobs: shell: pwsh run: | # 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" # Read the release version from the release-version file @@ -520,10 +406,17 @@ jobs: # Publish to Chocolatey cd ./deployment/chocolatey choco pack - echo y | choco install gman -dv -s . - $version = gman --version + choco install gman --yes -dv -s . + # 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 " ", "." - 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: needs: [publish-github-release] @@ -551,11 +444,11 @@ jobs: shell: bash run: | # 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 - 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 - 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 release_version="$(cat ./artifacts/release-version)" echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV @@ -618,17 +511,6 @@ jobs: git fetch --all 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 uses: dtolnay/rust-toolchain@stable diff --git a/CHANGELOG.md b/CHANGELOG.md index da43d4e..59a509c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,4 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [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 diff --git a/Cargo.lock b/Cargo.lock index e85ede4..544da2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1501,9 +1501,9 @@ dependencies = [ [[package]] name = "gcloud-sdk" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dcccf7c0cc0986cb5f476854a5c63b95bab4835f12884704f6aa33ac7d14bc" +checksum = "7fe603014c94ee883f514ea12a3df0ad99da67124472bd3c52ee7fed0ccd4ea1" dependencies = [ "async-trait", "bytes", @@ -1576,7 +1576,7 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gman" -version = "0.0.1" +version = "0.1.0" dependencies = [ "anyhow", "argon2", @@ -1605,7 +1605,6 @@ dependencies = [ "log", "log4rs", "openssl", - "openssl-sys", "predicates", "pretty_assertions", "proptest", @@ -2206,9 +2205,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ "bitflags", "libc", @@ -3291,16 +3290,17 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.223" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac" dependencies = [ + "serde_core", "serde_derive", ] @@ -3315,10 +3315,19 @@ dependencies = [ ] [[package]] -name = "serde_derive" -version = "1.0.219" +name = "serde_core" +version = "1.0.223" 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 = [ "proc-macro2", "quote", @@ -3327,14 +3336,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a7b8868..61a597d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gman" -version = "0.0.1" +version = "0.1.0" edition = "2024" authors = ["Alex Clarke "] description = "Universal secret management and injection tool" @@ -58,10 +58,14 @@ gcloud-sdk = { version = "0.28.1", features = [ crc32c = "0.6.8" azure_identity = "0.27.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"] } +[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] pretty_assertions = "1.4.1" proptest = "1.5.0" diff --git a/deployment/chocolatey/chocolateyinstall.ps1.template b/deployment/chocolatey/chocolateyinstall.ps1.template index 3bd79c3..57457bf 100644 --- a/deployment/chocolatey/chocolateyinstall.ps1.template +++ b/deployment/chocolatey/chocolateyinstall.ps1.template @@ -2,7 +2,7 @@ $ErrorActionPreference = 'Stop'; $PackageName = 'gman' $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' $packageArgs = @{ @@ -16,5 +16,5 @@ $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\ diff --git a/deployment/chocolatey/gman.nuspec.template b/deployment/chocolatey/gman.nuspec.template index 755a8c7..a64e13f 100644 --- a/deployment/chocolatey/gman.nuspec.template +++ b/deployment/chocolatey/gman.nuspec.template @@ -33,9 +33,9 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe https://github.com/Dark-Alex-17/gman/blob/main/README.md https://github.com/Dark-Alex-17/gman/issues cli cross-platform terminal credential-management secret-management rust - Universal command line credential management and injection tool + Universal CLI credential management and injection tool - Universal command line credential management and injection tool. + Universal CLI credential management and injection tool. **Usage** To get started, run `gman --help` in a terminal. diff --git a/deployment/homebrew/gman.rb.template b/deployment/homebrew/gman.rb.template index 65d36cb..4ed310b 100644 --- a/deployment/homebrew/gman.rb.template +++ b/deployment/homebrew/gman.rb.template @@ -1,16 +1,16 @@ # Documentation: https://docs.brew.sh/Formula-Cookbook # https://rubydoc.brew.sh/Formula -class GMan < Formula +class Gman < Formula desc "Universal command line credential management and injection tool" homepage "https://github.com/Dark-Alex-17/gman" 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" 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" 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" end version "$version" diff --git a/src/config.rs b/src/config.rs index 1d3c7ce..2b086ea 100644 --- a/src/config.rs +++ b/src/config.rs @@ -144,6 +144,7 @@ impl ProviderConfig { match &mut self.provider_type { SupportedProvider::Local { provider_def } => { debug!("Using local secret provider"); + provider_def.runtime_provider_name = self.name.clone(); provider_def } SupportedProvider::AwsSecretsManager { provider_def } => { diff --git a/src/providers/local.rs b/src/providers/local.rs index 298e799..eea7c38 100644 --- a/src/providers/local.rs +++ b/src/providers/local.rs @@ -5,9 +5,9 @@ use std::path::{Path, PathBuf}; use std::{env, fs}; use zeroize::Zeroize; -use crate::config::Config; -use crate::providers::SecretProvider; +use crate::config::{Config, get_config_file_path, load_config}; use crate::providers::git_sync::{SyncOpts, repo_name_from_url, sync_and_push}; +use crate::providers::{SecretProvider, SupportedProvider}; use crate::{ 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)] pub git_user_email: Option, pub git_executable: Option, + #[serde(skip)] + pub runtime_provider_name: Option, } impl Default for LocalProvider { @@ -65,6 +67,7 @@ impl Default for LocalProvider { git_user_name: None, git_user_email: None, git_executable: None, + runtime_provider_name: None, } } } @@ -169,7 +172,9 @@ impl SecretProvider for LocalProvider { config_changed = true; debug!("Prompting user to set git_remote in config for sync"); 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| { LocalProvider { git_remote_url: Some(s.clone()), @@ -185,9 +190,7 @@ impl SecretProvider for LocalProvider { } if config_changed { - debug!("Saving updated config"); - confy::store("gman", "config", &self) - .with_context(|| "failed to save updated config")?; + self.persist_git_settings_to_config()?; } let sync_opts = SyncOpts { @@ -203,6 +206,52 @@ impl SecretProvider for 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> { if let Some(remote) = &self.git_remote_url { let name = repo_name_from_url(remote); @@ -424,6 +473,7 @@ mod tests { use super::*; use pretty_assertions::assert_eq; use secrecy::{ExposeSecret, SecretString}; + use std::env as std_env; use tempfile::tempdir; #[test] @@ -458,9 +508,96 @@ mod tests { fs::write(&file, "secretpw\n").unwrap(); let provider = LocalProvider { password_file: Some(file), + runtime_provider_name: None, ..LocalProvider::default() }; let pw = provider.get_password().unwrap(); 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"); + } + } } diff --git a/tests/providers/local_tests.rs b/tests/providers/local_tests.rs index 96685c3..8ca0d7e 100644 --- a/tests/providers/local_tests.rs +++ b/tests/providers/local_tests.rs @@ -34,6 +34,7 @@ fn test_local_provider_valid() { git_user_name: None, git_user_email: Some("test@example.com".to_string()), git_executable: None, + runtime_provider_name: None, }; assert!(provider.validate().is_ok()); @@ -48,6 +49,7 @@ fn test_local_provider_invalid_email() { git_user_name: None, git_user_email: Some("test".to_string()), git_executable: None, + runtime_provider_name: None, }; assert!(config.validate().is_err());