From fed3677f569a38ba49a084b0c5d3ad9f2a54a4fb Mon Sep 17 00:00:00 2001 From: Jayson Reis Date: Tue, 24 Nov 2020 13:17:14 +0100 Subject: [PATCH] feat: Add initial windows support --- .editorconfig | 1 + .github/workflows/release.yml | 43 +++++++++++++++++----- .github/workflows/tests.yml | 15 ++++++++ src/installer.rs | 67 +++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 64 +++++---------------------------- tests/integration.rs | 13 +++++++ 7 files changed, 140 insertions(+), 64 deletions(-) create mode 100644 src/installer.rs diff --git a/.editorconfig b/.editorconfig index c935c45..00d3847 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,7 @@ root = true [*] insert_final_newline = true trim_trailing_whitespace = true +indent_size = 4 [*.{yml,yaml}] indent_size = 2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e5ba6c..f22b35f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,9 @@ jobs: - os: macos image: macos-latest target: x86_64-apple-darwin + - os: windows + image: windows-latest + target: x86_64-pc-windows-msvc steps: - uses: actions/checkout@v2 @@ -42,17 +45,28 @@ jobs: if: ${{ matrix.os == 'linux' }} run: cross build --release --target=${{ matrix.target }} --verbose - - name: Build macos - if: ${{ matrix.os == 'macos' }} + - name: Build != linux + if: ${{ matrix.os != 'linux' }} run: cargo build --release --target=${{ matrix.target }} --verbose - name: Prepare + shell: bash run: | - final_binary=${{ env.BINARY_NAME }}-${{ matrix.target }} - mv target/${{ matrix.target }}/release/${{ env.BINARY_NAME }} $final_binary + compress() { + if [[ "${{ matrix.os }}" = "windows" ]]; then + 7z a $(echo $1 | sed 's/\.exe$//').zip $1 + else + gzip -f9 $1 + fi + } + if [[ "${{ matrix.os }}" = "windows" ]]; then + suffix=.exe + fi + final_binary=${{ env.BINARY_NAME }}-${{ matrix.target }}${suffix} + mv target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}${suffix} $final_binary ls file $final_binary - gzip -f9 $final_binary + compress $final_binary - name: Archive binary artifacts uses: actions/upload-artifact@v2 @@ -61,6 +75,7 @@ jobs: if-no-files-found: warn path: | *.gz + *.zip create_release: name: Create Release @@ -90,8 +105,20 @@ jobs: matrix: include: - target: x86_64-unknown-linux-musl + content_type: application/gzip + extension: gz - target: aarch64-unknown-linux-musl + content_type: application/gzip + extension: gz - target: x86_64-apple-darwin + content_type: application/gzip + extension: gz + - target: x86_64-apple-darwin + content_type: application/gzip + extension: gz + - target: x86_64-pc-windows-msvc + content_type: application/zip + extension: zip steps: - name: Download all workflow run artifacts uses: actions/download-artifact@v2 @@ -103,6 +130,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps - asset_path: ./binaries/${{ env.BINARY_NAME }}-${{ matrix.target }}.gz - asset_name: ${{ env.BINARY_NAME }}-${{ matrix.target }}.gz - asset_content_type: application/gzip + asset_path: ./binaries/${{ env.BINARY_NAME }}-${{ matrix.target }}.${{ matrix.extension }} + asset_name: ${{ env.BINARY_NAME }}-${{ matrix.target }}.${{ matrix.extension }} + asset_content_type: ${{ matrix.content_type }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3a0f9d5..7cea7f9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,6 +20,8 @@ jobs: image: ubuntu-latest - os: macos image: macos-latest + - os: windows + image: windows-latest steps: - uses: actions/checkout@v2 @@ -39,6 +41,19 @@ jobs: set -ex brew install gpg sops kustomize + # Chocolatey times out, so scoop it is + - name: Install windows + if: ${{ matrix.os == 'windows' }} + run: | + Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') + scoop install gnupg sops kustomize + echo "C:\Users\runneradmin\scoop\shims" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Check for kustomize + if: ${{ matrix.os == 'windows' }} + run: | + echo $env:PATH + get-command kustomize + - name: Build run: cargo build --verbose - name: Run tests diff --git a/src/installer.rs b/src/installer.rs new file mode 100644 index 0000000..7df3a44 --- /dev/null +++ b/src/installer.rs @@ -0,0 +1,67 @@ +#[cfg(target_family = "unix")] +use std::os::unix::fs::symlink; +#[cfg(target_family = "windows")] +use std::os::windows::fs::symlink_file as symlink; + +use std::path::MAIN_SEPARATOR; +use std::{ + env::{args, var}, + fs::{canonicalize, create_dir_all}, + path::PathBuf, +}; + +use color_eyre::eyre::{eyre, WrapErr}; +use dirs::home_dir; + +use crate::types::Kind; +use crate::{API_VERSION, XDG_CONFIG_HOME}; + +pub fn install() -> color_eyre::Result<()> { + let home = home_dir().ok_or_else(|| eyre!("Failed to determine home director"))?; + let install_directory = get_install_directory(home); + + let source = PathBuf::from(args().next().unwrap()); + let source = + canonicalize(&source).wrap_err("failed to find the absolute path of the current binary")?; + + let kinds = [ + Kind::ConfigMapGenerator, + Kind::SecretGenerator, + Kind::SimpleDecrypt, + ]; + + let binary_suffix = if cfg!(windows) { ".exe" } else { "" }; + + for kind in &kinds { + let kind = format!("{:?}", kind); + let destination_folder = install_directory.join(&kind.to_lowercase()); + create_dir_all(&destination_folder).wrap_err_with(|| { + format!( + "failed to create directory {}", + &destination_folder.to_string_lossy() + ) + })?; + + let destination = destination_folder.join(format!("{}{}", &kind, &binary_suffix)); + println!( + "Linking kustomize-sops-rs to {}", + &destination.to_string_lossy() + ); + if destination.exists() { + std::fs::remove_file(&destination).wrap_err("failed to delete old file")?; + } + symlink(&source, destination).wrap_err("failed to create link")?; + } + Ok(()) +} + +fn get_install_directory(home: PathBuf) -> PathBuf { + let api_directory = API_VERSION.replace("/", &MAIN_SEPARATOR.to_string()); + var(XDG_CONFIG_HOME) + .wrap_err("failed to get the install directory") + .map(|config| PathBuf::from(config)) + .unwrap_or_else(|_| home.join(".config")) + .join("kustomize") + .join("plugin") + .join(api_directory) +} diff --git a/src/lib.rs b/src/lib.rs index 0b5b03d..66d2482 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod cli; pub mod decryption; +pub mod installer; pub mod maps; pub mod types; diff --git a/src/main.rs b/src/main.rs index 24943ba..1877b5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,18 @@ use std::{ - env::{args, var}, - fs::{canonicalize, create_dir_all, File}, + fs::File, io::{stdout, Write}, - os::unix::fs::symlink, - path::PathBuf, process::exit, }; use clap::Clap; -use color_eyre::{ - eyre::{eyre, Context}, - Result, -}; -use dirs::home_dir; +use color_eyre::Result; +use serde_yaml::{from_reader, to_string}; + use kustomize_sops::{ - cli::Arguments, cli::SubCommand, decryption::decrypt_file, maps::generate_data_field, - maps::generate_output_map, types::Input, types::Kind, API_VERSION, CONFIG_MAP_OUTPUT, - SECRET_OUTPUT, XDG_CONFIG_HOME, + cli::Arguments, cli::SubCommand, decryption::decrypt_file, installer, + maps::generate_data_field, maps::generate_output_map, types::Input, types::Kind, + CONFIG_MAP_OUTPUT, SECRET_OUTPUT, }; -use serde_yaml::{from_reader, to_string}; fn main() -> Result<()> { color_eyre::install()?; @@ -33,7 +27,7 @@ fn main() -> Result<()> { }; } match arguments.subcommand { - Some(SubCommand::Install) => return install(), + Some(SubCommand::Install) => return installer::install(), None => { eprintln!("The yaml file is required if no command is set"); exit(1); @@ -61,45 +55,3 @@ fn process_simple_decrypt(input: &Input) -> Result<()> { Ok(()) } - -fn install() -> Result<()> { - let home = home_dir().ok_or_else(|| eyre!("Failed to determine home director"))?; - let install_directory = var(XDG_CONFIG_HOME) - .wrap_err("failed to get the install directory") - .map(|config| PathBuf::from(config)) - .unwrap_or_else(|_| home.join(".config")) - .join("kustomize") - .join("plugin") - .join(API_VERSION); - - let source = PathBuf::from(args().next().unwrap()); - let source = - canonicalize(&source).wrap_err("failed to find the absolute path of the current binary")?; - - let kinds = [ - Kind::ConfigMapGenerator, - Kind::SecretGenerator, - Kind::SimpleDecrypt, - ]; - - for kind in &kinds { - let kind = format!("{:?}", kind); - let destination_folder = install_directory.join(&kind.to_lowercase()); - create_dir_all(&destination_folder).wrap_err_with(|| { - format!( - "failed to create directory {}", - &destination_folder.to_string_lossy() - ) - })?; - let destination = destination_folder.join(&kind); - println!( - "Linking kustomize-sops-rs to {}", - &destination.to_string_lossy() - ); - if !destination.exists() { - // XXX: To implement on windows this needs to change - symlink(&source, destination).wrap_err("failed to create link")?; - } - } - Ok(()) -} diff --git a/tests/integration.rs b/tests/integration.rs index f93235b..21b3422 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -16,6 +16,18 @@ fn setup_tests() { } fn configure_gpg() -> Result<()> { + let output = Command::new("gpg") + .arg("--list-keys") + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .output() + .wrap_err("failed to run gpg")?; + + check_output_status(&output)?; + if String::from_utf8_lossy(&output.stdout).contains("EBC846D0169D43A96ABA1C31AD471BDF8E8A0484") + { + return Ok(()); + } let output = Command::new("gpg") .args(&["--import", "tests/kustomization/private.key"]) .stderr(Stdio::piped()) @@ -59,6 +71,7 @@ fn run_with_kustomize() { .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() + .wrap_err("failed to execute kustomize") .unwrap(); let stdout = String::from_utf8_lossy(&output.stdout);