diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dfd83f8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,294 @@ +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 + +concurrency: + group: ${{ github.workflow }}-${{ github.event.inputs.tag_name }} + cancel-in-progress: false + +permissions: + contents: read + +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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@a54c08fd6ec450103756d6dc20d2d48332156d7f # master + with: + targets: ${{ matrix.target }} + + - name: Cache Cargo registry and target + uses: Swatinem/rust-cache@23bde28c093e9ae8b3b402174d994c9d924d0eb8 # v2.7.5 + with: + key: ${{ matrix.target }} + + - name: Install cross + if: matrix.use_cross + uses: taiki-e/install-action@93a027fc80e60808cf702e5e4fa0eb13e01fccf2 # v2.1.20 + with: + tool: 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@60119845307540294e774020a87424eeac47d7e6 # v4.4.1 + 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 + 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Get version from input + id: get_version + env: + TAG_NAME: ${{ inputs.tag_name }} + run: | + # 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@fa0a91b85d4f404e444e00e005971372ec806f1c # v4.1.7 + 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@c862437e9029c5496c3ae47d90ec96cac9521fd3 # v2.0.8 + 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@60119845307540294e774020a87424eeac47d7e6 # v4.4.1 + with: + name: release-dist + path: dist/ + retention-days: 1 + + generate-packages: + name: Generate Packages (APT, RPM, Homebrew) + needs: create-release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Download release-dist artifact + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372ec806f1c # v4.1.7 + with: + name: release-dist + path: dist + + - name: Download compiled binaries + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372ec806f1c # v4.1.7 + 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@c862437e9029c5496c3ae47d90ec96cac9521fd3 # v2.0.8 + 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..606c2a2 --- /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 +find "$RPM_BUILD_DIR"/RPMS -name '*.rpm' -exec cp {} "$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![