From d3a2d06e2a14d4c4924f02447bc91eb65c376d85 Mon Sep 17 00:00:00 2001 From: Matthew Binning Date: Fri, 16 Jan 2026 16:22:12 -0800 Subject: [PATCH] Feature/ci-encdec --- .gitlab-ci.yml | 103 ++++++++++++++++++++++++++++++++++++++++++++++--- Dockerfile | 20 ++-------- nix.dock | 19 +++++++++ 3 files changed, 120 insertions(+), 22 deletions(-) create mode 100644 nix.dock diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2c2b262..7d12f58 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,20 @@ +# Define pipeline inputs for runtime configuration +spec: + inputs: + gpg_passphrase: + description: "Passphrase for GPG signing key" + type: string + default: "" + +--- + stages: - configure - build - test - package + - encrypt + - publish # Set variables used for builds in CI, should probably set this in the build container if possible variables: @@ -21,8 +33,6 @@ variables: configure: stage: configure image: alpine:latest - before_script: - - apk add --no-cache jq script: - BUILD_IMAGE=$(grep '"image":' .devcontainer/devcontainer.json | cut -d '"' -f 4) - printf 'BUILD_IMAGE=%s' "$BUILD_IMAGE" > build_image.env @@ -76,7 +86,7 @@ test: - test-ci-cd # Build Docker image using Nix and load it into Docker (DooD pattern) -build-docker-image: +package-docker-image-with-nix: stage: package image: nixos/nix:latest before_script: @@ -105,7 +115,7 @@ build-docker-image: # The runner should have /var/run/docker.sock mounted # Alternative: Build using Docker directly (DooD) -build-docker-traditional: +package-docker-image: stage: package image: docker:latest services: [] # No dind service @@ -115,10 +125,9 @@ build-docker-traditional: # Verify Docker access - docker info script: - # Build the Docker image - docker build -t hello-world:traditional-${CI_COMMIT_SHORT_SHA} . - # Test run - docker run --rm hello-world:traditional-${CI_COMMIT_SHORT_SHA} + - docker save -o hello-world.tar.gz hello-world:traditional-${CI_COMMIT_SHORT_SHA} dependencies: - build tags: @@ -126,3 +135,85 @@ build-docker-traditional: only: - branches when: manual + artifacts: + expire_in: 1 week + name: wf-image-dood-$BUILD_VARIANT-$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA + paths: + - ./hello-world.tar.gz + +encrypt: + stage: encrypt + image: alpine:latest + needs: + - job: package-docker-image + artifacts: true + before_script: + # Install GnuPG + - apk add --no-cache gnupg + script: + # Import GPG keys (assuming they're configured as CI/CD variables) + # You may need to set GPG_PRIVATE_KEY as a CI/CD variable containing your private key + - | + if [ -n "$GPG_PRIVATE_KEY" ]; then + gpg --batch --import "$GPG_PRIVATE_KEY" + else + echo "Warning: GPG_PRIVATE_KEY not set. Using existing keyring." + fi + + # Encrypt and sign using the passphrase from pipeline input or CI/CD variable + # The passphrase can come from either the pipeline input or a CI/CD variable + #- | + # PASSPHRASE="${INPUT_GPG_PASSPHRASE:-$GPG_PASSPHRASE}" + # + # if [ -z "$PASSPHRASE" ]; then + # echo "Error: No passphrase provided. Set gpg_passphrase input or GPG_PASSPHRASE variable." + # exit 1 + # fi + + - echo "Encrypting hello-world.tar.gz..." + - | + echo "$[[ inputs.gpg_passphrase ]]" | gpg --batch --yes \ + --pinentry-mode loopback \ + --passphrase-fd 0 \ + --encrypt \ + --sign \ + -r matthew.binning@whitefoxdefense.com \ + -o hello-world.tar.gz.gpg \ + hello-world.tar.gz + # Verify the encrypted file was created + - | + if [ -f "hello-world.tar.gz.gpg" ]; then + echo "Encryption successful!" + ls -lh hello-world.tar.gz.gpg + else + echo "Error: Encrypted file not created" + exit 1 + fi + artifacts: + expire_in: 1 week + name: encrypted-image-$CI_COMMIT_SHORT_SHA + paths: + - hello-world.tar.gz.gpg + tags: + - test-ci-cd + +publish_gpg: + stage: publish + image: curlimages/curl:8.5.0 + # If any build variant fails, this job will not publish any artifacts. + needs: + - job: encrypt + artifacts: true + script: + # Version scheme: CI-only versions are easy to identify + clean up later + - PKG_NAME="hello-world.tar.gz.gpg" + - FILE="./hello-world.tar.gz.gpg" + + - PKG_VERSION="${CI_COMMIT_SHORT_SHA}-${CI_PIPELINE_IID}" + - echo "Uploading ${FILE} as ${PKG_NAME} ${PKG_VERSION}" + + - | + curl --fail \ + --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \ + --upload-file "${FILE}" \ + "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PKG_NAME}/${PKG_VERSION}/$(basename "${FILE}")" diff --git a/Dockerfile b/Dockerfile index 7372f2a..6a081fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,7 @@ -FROM nixos/nix:latest +FROM rust:latest WORKDIR /app COPY . . - -RUN mkdir -p ~/.config/nix && \ - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf && \ - nix build .#app - -# Extract the built binary -RUN cp -rL result/* /tmp/app/ || cp result /tmp/app/hello-world - -# Use a minimal runtime image - no system dependencies needed! -FROM debian:bookworm-slim - -# All functionality is in Rust crates, no need for system binaries -COPY --from=0 /tmp/app/ /usr/local/bin/ - -CMD ["/usr/local/bin/hello-world"] \ No newline at end of file +RUN cargo build --release +# RFE: Statically link for a scratch runtime stage. +CMD ["/app/target/release/hello-world"] \ No newline at end of file diff --git a/nix.dock b/nix.dock new file mode 100644 index 0000000..7372f2a --- /dev/null +++ b/nix.dock @@ -0,0 +1,19 @@ +FROM nixos/nix:latest + +WORKDIR /app +COPY . . + +RUN mkdir -p ~/.config/nix && \ + echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf && \ + nix build .#app + +# Extract the built binary +RUN cp -rL result/* /tmp/app/ || cp result /tmp/app/hello-world + +# Use a minimal runtime image - no system dependencies needed! +FROM debian:bookworm-slim + +# All functionality is in Rust crates, no need for system binaries +COPY --from=0 /tmp/app/ /usr/local/bin/ + +CMD ["/usr/local/bin/hello-world"] \ No newline at end of file