From 85cc54139cd24d34379611f1800633c33ca1bdb9 Mon Sep 17 00:00:00 2001 From: utkarsh patrikar Date: Mon, 1 Jun 2026 19:19:02 +0530 Subject: [PATCH 1/2] feat: add automated release CI pipeline, package scripts, and update project repository link --- .github/workflows/release.yml | 271 ++++++++++++++++++++++++++++++++++ scripts/package.sh | 121 +++++++++++++++ src/ui/dashboard.rs | 2 +- 3 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml create mode 100755 scripts/package.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f7f7396 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,271 @@ +name: Create Release and Packages + +on: + workflow_dispatch: + inputs: + tag_name: + description: 'Tag name for the release (e.g. v0.1.0)' + required: true + type: string + +permissions: + contents: write + +jobs: + build-binaries: + name: Build Binaries + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + use_cross: false + bin_suffix: "" + bin_name: kdc + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + use_cross: true + bin_suffix: "" + bin_name: kdc + - target: x86_64-apple-darwin + os: macos-latest + use_cross: false + bin_suffix: "" + bin_name: kdc + - target: aarch64-apple-darwin + os: macos-latest + use_cross: false + bin_suffix: "" + bin_name: kdc + - target: x86_64-pc-windows-msvc + os: windows-latest + use_cross: false + bin_suffix: ".exe" + bin_name: kdc.exe + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache Cargo registry and target + uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + + - name: Install cross + if: matrix.use_cross + uses: taiki-e/install-action@cross + + - name: Build release binary + run: | + if [ "${{ matrix.use_cross }}" = "true" ]; then + cross build --release --target ${{ matrix.target }} + else + cargo build --release --target ${{ matrix.target }} + fi + shell: bash + + - name: Upload Binary Artifact + uses: actions/upload-artifact@v4 + with: + name: kdc-${{ matrix.target }} + path: target/${{ matrix.target }}/release/${{ matrix.bin_name }} + retention-days: 1 + + create-release: + name: Create Release + needs: build-binaries + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get_version.outputs.version }} + tag_name: ${{ steps.get_version.outputs.tag_name }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get version from input + id: get_version + run: | + TAG_NAME="${{ inputs.tag_name }}" + VERSION="${TAG_NAME#v}" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "tag_name=${TAG_NAME}" >> "$GITHUB_OUTPUT" + shell: bash + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: bin-artifacts + + - name: Package archives and generate checksums + id: package + run: | + mkdir dist + cd bin-artifacts + + for target_dir in ./kdc-*; do + target="${target_dir#./kdc-}" + echo "Packaging archive for $target..." + + if [ -f "$target_dir/kdc.exe" ]; then + # Windows + zip -j "../dist/kdc-v${{ steps.get_version.outputs.version }}-${target}.zip" "$target_dir/kdc.exe" + else + # Unix + chmod +x "$target_dir/kdc" + tar -czf "../dist/kdc-v${{ steps.get_version.outputs.version }}-${target}.tar.gz" -C "$target_dir" kdc + fi + done + + cd ../dist + sha256sum -- kdc-* > sha256sums.txt + cat sha256sums.txt + shell: bash + + - name: Upload archives to release + uses: softprops/action-gh-release@v2 + with: + files: | + dist/* + tag_name: ${{ steps.get_version.outputs.tag_name }} + draft: false + prerelease: false + generate_release_notes: true + + - name: Upload packaging artifacts + uses: actions/upload-artifact@v4 + with: + name: release-dist + path: dist/ + retention-days: 1 + + generate-packages: + name: Generate Packages (APT, RPM, Homebrew) + needs: create-release + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download release-dist artifact + uses: actions/download-artifact@v4 + with: + name: release-dist + path: dist + + - name: Download compiled binaries + uses: actions/download-artifact@v4 + with: + path: bin-artifacts + + - name: Generate APT and RPM packages + run: | + chmod +x scripts/package.sh + ./scripts/package.sh \ + "${{ needs.create-release.outputs.version }}" \ + "bin-artifacts/kdc-x86_64-unknown-linux-gnu/kdc" \ + "bin-artifacts/kdc-aarch64-unknown-linux-gnu/kdc" \ + "dist" + shell: bash + + - name: Generate Homebrew Formula + id: generate_formula + run: | + VERSION="${{ needs.create-release.outputs.version }}" + SHA_MAC_AMD64=$(grep "kdc-v${VERSION}-x86_64-apple-darwin.tar.gz" dist/sha256sums.txt | awk '{print $1}') + SHA_MAC_ARM64=$(grep "kdc-v${VERSION}-aarch64-apple-darwin.tar.gz" dist/sha256sums.txt | awk '{print $1}') + SHA_LINUX_AMD64=$(grep "kdc-v${VERSION}-x86_64-unknown-linux-gnu.tar.gz" dist/sha256sums.txt | awk '{print $1}') + SHA_LINUX_ARM64=$(grep "kdc-v${VERSION}-aarch64-unknown-linux-gnu.tar.gz" dist/sha256sums.txt | awk '{print $1}') + + cat < dist/kdc.rb + class Kdc < Formula + desc "Kubernetes Docker Commander, a project-centric DevOps TUI" + homepage "https://github.com/${{ github.repository }}" + version "${VERSION}" + + if OS.mac? + if Hardware::CPU.arm? + url "https://github.com/${{ github.repository }}/releases/download/v\${version}/kdc-v\${version}-aarch64-apple-darwin.tar.gz" + sha256 "${SHA_MAC_ARM64}" + else + url "https://github.com/${{ github.repository }}/releases/download/v\--version/kdc-v\${version}-x86_64-apple-darwin.tar.gz" + sha256 "${SHA_MAC_AMD64}" + end + elsif OS.linux? + if Hardware::CPU.arm? + url "https://github.com/${{ github.repository }}/releases/download/v\${version}/kdc-v\${version}-aarch64-unknown-linux-gnu.tar.gz" + sha256 "${SHA_LINUX_ARM64}" + else + url "https://github.com/${{ github.repository }}/releases/download/v\${version}/kdc-v\${version}-x86_64-unknown-linux-gnu.tar.gz" + sha256 "${SHA_LINUX_AMD64}" + end + end + + def install + bin.install "kdc" + end + + test do + system "\#{bin}/kdc", "--version" + end + end + EOF + + # Fix target download URL for macOS Intel + sed -i "s/v\\\\--version/v\\\\\${version}/g" dist/kdc.rb + + echo "Formula generated:" + cat dist/kdc.rb + shell: bash + + - name: Update existing release with packages + uses: softprops/action-gh-release@v2 + with: + files: | + dist/*.deb + dist/*.rpm + dist/kdc.rb + tag_name: ${{ needs.create-release.outputs.tag_name }} + append_body: true + + - name: Update Homebrew Tap Repository + env: + TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + if [ -z "$TAP_TOKEN" ]; then + echo "Warning: HOMEBREW_TAP_TOKEN is not configured. Skipping automated Homebrew formula update." + echo "The generated Homebrew formula was uploaded as a release asset (kdc.rb)." + exit 0 + fi + + echo "Configuring git..." + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + GITHUB_REPO="${{ github.repository }}" + GITHUB_OWNER="${GITHUB_REPO%/*}" + + echo "Cloning homebrew tap owned by ${GITHUB_OWNER}..." + git clone "https://${TAP_TOKEN}@github.com/${GITHUB_OWNER}/homebrew-tap.git" homebrew-tap + + mkdir -p homebrew-tap/Formula + cp dist/kdc.rb homebrew-tap/Formula/kdc.rb + + cd homebrew-tap + git add Formula/kdc.rb + + if git diff-index --quiet HEAD; then + echo "No changes in Homebrew formula." + else + git commit -m "Bump kdc to v${{ needs.create-release.outputs.version }}" + git push origin main + echo "Successfully updated Homebrew Formula in tap." + fi + shell: bash diff --git a/scripts/package.sh b/scripts/package.sh new file mode 100755 index 0000000..7a2bd43 --- /dev/null +++ b/scripts/package.sh @@ -0,0 +1,121 @@ +#!/bin/bash +set -euo pipefail + +# Inputs: +# 1. VERSION (e.g. 0.1.0) +# 2. BIN_AMD64 (path to x86_64-unknown-linux-gnu binary) +# 3. BIN_ARM64 (path to aarch64-unknown-linux-gnu binary) +# 4. OUT_DIR (path to write generated packages) + +if [ "$#" -ne 4 ]; then + echo "Error: Missing arguments." + echo "Usage: $0 " + exit 1 +fi + +VERSION="$1" +BIN_AMD64="$2" +BIN_ARM64="$3" +OUT_DIR="$4" + +mkdir -p "$OUT_DIR" + +echo "=== Generating Debian (.deb) packages ===" + +# Build AMD64 DEB +echo "Building deb for amd64..." +DEB_AMD64_DIR="deb_amd64_build" +mkdir -p "$DEB_AMD64_DIR/usr/bin" "$DEB_AMD64_DIR/DEBIAN" +cp "$BIN_AMD64" "$DEB_AMD64_DIR/usr/bin/kdc" +chmod 755 "$DEB_AMD64_DIR/usr/bin/kdc" + +cat < "$DEB_AMD64_DIR/DEBIAN/control" +Package: kdc +Version: ${VERSION} +Section: utils +Priority: optional +Architecture: amd64 +Maintainer: Utkarsh Patrikar +Description: Kubernetes Docker Commander, a project-centric DevOps TUI +EOF + +dpkg-deb --build "$DEB_AMD64_DIR" "$OUT_DIR/kdc_${VERSION}_amd64.deb" +rm -rf "$DEB_AMD64_DIR" + +# Build ARM64 DEB +echo "Building deb for arm64..." +DEB_ARM64_DIR="deb_arm64_build" +mkdir -p "$DEB_ARM64_DIR/usr/bin" "$DEB_ARM64_DIR/DEBIAN" +cp "$BIN_ARM64" "$DEB_ARM64_DIR/usr/bin/kdc" +chmod 755 "$DEB_ARM64_DIR/usr/bin/kdc" + +cat < "$DEB_ARM64_DIR/DEBIAN/control" +Package: kdc +Version: ${VERSION} +Section: utils +Priority: optional +Architecture: arm64 +Maintainer: Utkarsh Patrikar +Description: Kubernetes Docker Commander, a project-centric DevOps TUI +EOF + +dpkg-deb --build "$DEB_ARM64_DIR" "$OUT_DIR/kdc_${VERSION}_arm64.deb" +rm -rf "$DEB_ARM64_DIR" + +echo "=== Generating RPM packages ===" + +# Install rpm tools if not present +if ! command -v rpmbuild &> /dev/null; then + echo "Installing rpm tool..." + sudo apt-get update && sudo apt-get install -y rpm +fi + +# Create rpmbuild directories +RPM_BUILD_DIR="$(pwd)/rpmbuild" +mkdir -p "$RPM_BUILD_DIR"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} + +# Write spec template +cat <<'EOF' > kdc.spec +Name: kdc +Version: %{version} +Release: 1 +Summary: Kubernetes Docker Commander, a project-centric DevOps TUI +License: MIT +URL: https://github.com/KDM-cli/kdc-cli + +%description +Kubernetes Docker Commander, a project-centric DevOps TUI + +%prep +%build + +%install +mkdir -p %{buildroot}/usr/bin +cp %{binary_source} %{buildroot}/usr/bin/kdc +chmod 755 %{buildroot}/usr/bin/kdc + +%files +/usr/bin/kdc +EOF + +# Build AMD64 RPM +echo "Building RPM for x86_64..." +rpmbuild -bb --target x86_64-linux \ + --define "version ${VERSION}" \ + --define "binary_source $(pwd)/$BIN_AMD64" \ + --define "_topdir $RPM_BUILD_DIR" \ + kdc.spec + +# Build ARM64 RPM +echo "Building RPM for aarch64..." +rpmbuild -bb --target aarch64-linux \ + --define "version ${VERSION}" \ + --define "binary_source $(pwd)/$BIN_ARM64" \ + --define "_topdir $RPM_BUILD_DIR" \ + kdc.spec + +# Copy generated RPMs to output directory +cp "$RPM_BUILD_DIR"/RPMS/**/*.rpm "$OUT_DIR/" +rm -rf "$RPM_BUILD_DIR" kdc.spec + +echo "Package generation completed successfully." diff --git a/src/ui/dashboard.rs b/src/ui/dashboard.rs index 14e1d8d..9f91136 100644 --- a/src/ui/dashboard.rs +++ b/src/ui/dashboard.rs @@ -561,7 +561,7 @@ fn render_subtitle(frame: &mut Frame, chunk: Rect, palette: theme::Palette) { Style::default().fg(palette.text), )), Line::from(Span::styled( - "https://github.com/utkarsh232005/kdc-cli", + "https://github.com/KDM-cli/kdc-cli", Style::default().fg(palette.muted), )), Line::from(vec![ From 757f6dbf6900a9a68564916b07e40f811f4cdbe1 Mon Sep 17 00:00:00 2001 From: utkarsh patrikar Date: Mon, 1 Jun 2026 19:34:16 +0530 Subject: [PATCH 2/2] ci: harden GitHub Actions security, add tag validation, and fix RPM copying in package script --- .github/workflows/release.yml | 53 +++++++++++++++++++++++++---------- scripts/package.sh | 2 +- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f7f7396..dfd83f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,8 +8,12 @@ on: required: true type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.inputs.tag_name }} + cancel-in-progress: false + permissions: - contents: write + contents: read jobs: build-binaries: @@ -47,21 +51,25 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@a54c08fd6ec450103756d6dc20d2d48332156d7f # master with: targets: ${{ matrix.target }} - name: Cache Cargo registry and target - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@23bde28c093e9ae8b3b402174d994c9d924d0eb8 # v2.7.5 with: key: ${{ matrix.target }} - name: Install cross if: matrix.use_cross - uses: taiki-e/install-action@cross + uses: taiki-e/install-action@93a027fc80e60808cf702e5e4fa0eb13e01fccf2 # v2.1.20 + with: + tool: cross - name: Build release binary run: | @@ -73,7 +81,7 @@ jobs: shell: bash - name: Upload Binary Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@60119845307540294e774020a87424eeac47d7e6 # v4.4.1 with: name: kdc-${{ matrix.target }} path: target/${{ matrix.target }}/release/${{ matrix.bin_name }} @@ -83,24 +91,35 @@ jobs: name: Create Release needs: build-binaries runs-on: ubuntu-latest + permissions: + contents: write outputs: version: ${{ steps.get_version.outputs.version }} tag_name: ${{ steps.get_version.outputs.tag_name }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Get version from input id: get_version + env: + TAG_NAME: ${{ inputs.tag_name }} run: | - TAG_NAME="${{ inputs.tag_name }}" + # Strict regex check for semver tag starting with 'v' + if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(?:[-+].*)?$ ]]; then + echo "Error: Tag name '$TAG_NAME' is invalid. It must match semver format with a leading 'v' (e.g., v1.0.0, v0.1.0-alpha.1)." >&2 + exit 1 + fi + VERSION="${TAG_NAME#v}" echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "tag_name=${TAG_NAME}" >> "$GITHUB_OUTPUT" shell: bash - name: Download all artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372ec806f1c # v4.1.7 with: path: bin-artifacts @@ -130,7 +149,7 @@ jobs: shell: bash - name: Upload archives to release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@c862437e9029c5496c3ae47d90ec96cac9521fd3 # v2.0.8 with: files: | dist/* @@ -140,7 +159,7 @@ jobs: generate_release_notes: true - name: Upload packaging artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@60119845307540294e774020a87424eeac47d7e6 # v4.4.1 with: name: release-dist path: dist/ @@ -150,18 +169,22 @@ jobs: name: Generate Packages (APT, RPM, Homebrew) needs: create-release runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Download release-dist artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372ec806f1c # v4.1.7 with: name: release-dist path: dist - name: Download compiled binaries - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372ec806f1c # v4.1.7 with: path: bin-artifacts @@ -226,7 +249,7 @@ jobs: shell: bash - name: Update existing release with packages - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@c862437e9029c5496c3ae47d90ec96cac9521fd3 # v2.0.8 with: files: | dist/*.deb diff --git a/scripts/package.sh b/scripts/package.sh index 7a2bd43..606c2a2 100755 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -115,7 +115,7 @@ rpmbuild -bb --target aarch64-linux \ kdc.spec # Copy generated RPMs to output directory -cp "$RPM_BUILD_DIR"/RPMS/**/*.rpm "$OUT_DIR/" +find "$RPM_BUILD_DIR"/RPMS -name '*.rpm' -exec cp {} "$OUT_DIR/" \; rm -rf "$RPM_BUILD_DIR" kdc.spec echo "Package generation completed successfully."