mirror of
https://github.com/lukaszraczylo/kportal.git
synced 2026-06-05 23:03:40 +00:00
First release.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '**.go'
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
version:
|
||||
name: Calculate Version
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version_formatted.outputs.version }}
|
||||
version_tag: ${{ steps.version_formatted.outputs.version_tag }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Calculate semantic version
|
||||
id: semver
|
||||
uses: lukaszraczylo/semver-generator@v1
|
||||
with:
|
||||
config_file: semver.yaml
|
||||
repository_local: true
|
||||
|
||||
- name: Format version
|
||||
id: version_formatted
|
||||
run: |
|
||||
VERSION="${{ steps.semver.outputs.semantic_version }}"
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "version_tag=v${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Display version
|
||||
run: |
|
||||
echo "Calculated version: ${{ steps.version_formatted.outputs.version }}"
|
||||
echo "Version tag: ${{ steps.version_formatted.outputs.version_tag }}"
|
||||
|
||||
release:
|
||||
name: Release with GoReleaser
|
||||
needs: version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
||||
GORELEASER_CURRENT_TAG: ${{ needs.version.outputs.version_tag }}
|
||||
@@ -1,2 +1,5 @@
|
||||
CLAUDE.md
|
||||
kportal
|
||||
DEPLOYMENT_SUMMARY.md
|
||||
HOMEBREW_COMPLIANCE.md
|
||||
RELEASE_SETUP.md
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- id: kportal
|
||||
main: ./cmd/kportal
|
||||
binary: kportal
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.version={{.Version}}
|
||||
|
||||
archives:
|
||||
- id: kportal
|
||||
format: tar.gz
|
||||
name_template: "kportal-{{ .Version }}-{{ .Os }}-{{ .Arch }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE
|
||||
- README.md
|
||||
|
||||
checksum:
|
||||
name_template: "kportal-{{ .Version }}-checksums.txt"
|
||||
algorithm: sha256
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^Merge'
|
||||
- '^WIP'
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: lukaszraczylo
|
||||
name: kportal
|
||||
name_template: "Release {{.Version}}"
|
||||
draft: false
|
||||
prerelease: auto
|
||||
|
||||
brews:
|
||||
- repository:
|
||||
owner: lukaszraczylo
|
||||
name: brew-taps
|
||||
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
|
||||
folder: Formula
|
||||
homepage: https://lukaszraczylo.github.io/kportal
|
||||
description: "Modern Kubernetes port-forward manager with interactive TUI"
|
||||
license: MIT
|
||||
test: |
|
||||
system "#{bin}/kportal", "--version"
|
||||
install: |
|
||||
bin.install "kportal"
|
||||
@@ -0,0 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.1.5] - 2025-11-23
|
||||
|
||||
### Added
|
||||
- Interactive TUI built with Bubble Tea
|
||||
- Real-time health check monitoring with grace period
|
||||
- Toggle forwards on/off with Space key
|
||||
- Error display below table showing detailed error messages
|
||||
- Version display in UI title
|
||||
- Complete log suppression for clean UI (klog included)
|
||||
- Automatic error clearing when connection recovers
|
||||
|
||||
### Changed
|
||||
- Replaced tview with Bubble Tea for better architecture
|
||||
- Removed artificial 10-second delay before health checks
|
||||
- Improved thread safety with message-passing architecture
|
||||
- Enhanced status indicators (Active ●, Starting ○, Reconnecting ◐, Error ✗)
|
||||
|
||||
### Fixed
|
||||
- Deadlock issues with tview UI
|
||||
- Logs covering the legend in interactive mode
|
||||
- Re-enable hang bug when toggling forwards
|
||||
- Race conditions in status updates
|
||||
|
||||
## [0.1.0] - 2025-11-22
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
- Multi-context and multi-namespace support
|
||||
- Automatic pod restart handling with prefix matching
|
||||
- Label selector support for dynamic pod selection
|
||||
- Hot-reload configuration watching
|
||||
- Exponential backoff retry logic (max 10s)
|
||||
- Port conflict detection with PID information
|
||||
- kftray JSON to kportal YAML converter
|
||||
- Alias support for cleaner display names
|
||||
- Health check system
|
||||
- Verbose and interactive modes
|
||||
- Configuration validation
|
||||
- Comprehensive test suite
|
||||
|
||||
[Unreleased]: https://github.com/lukaszraczylo/kportal/compare/v0.1.5...HEAD
|
||||
[0.1.5]: https://github.com/lukaszraczylo/kportal/compare/v0.1.0...v0.1.5
|
||||
[0.1.0]: https://github.com/lukaszraczylo/kportal/releases/tag/v0.1.0
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
# Contributing to kportal
|
||||
|
||||
Thank you for your interest in contributing to kportal! This document provides guidelines and instructions for contributing.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Be respectful and constructive. We're all here to build something awesome together.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
Before creating a bug report, please check if the issue already exists. When creating a bug report, include:
|
||||
|
||||
- **Clear title and description**
|
||||
- **Steps to reproduce**
|
||||
- **Expected behavior**
|
||||
- **Actual behavior**
|
||||
- **Screenshots** (if applicable)
|
||||
- **Environment details** (OS, kportal version, Go version, Kubernetes version)
|
||||
- **Configuration file** (sanitized)
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, include:
|
||||
|
||||
- **Clear title and description**
|
||||
- **Use case** - why this enhancement would be useful
|
||||
- **Possible implementation** (optional)
|
||||
- **Alternative solutions** (if any)
|
||||
|
||||
### Pull Requests
|
||||
|
||||
1. **Fork the repository**
|
||||
```bash
|
||||
git clone https://github.com/yourusername/kportal.git
|
||||
cd kportal
|
||||
```
|
||||
|
||||
2. **Create a feature branch**
|
||||
```bash
|
||||
git checkout -b feature/amazing-feature
|
||||
```
|
||||
|
||||
3. **Make your changes**
|
||||
- Write clear, readable code
|
||||
- Follow Go conventions and best practices
|
||||
- Add tests for new functionality
|
||||
- Update documentation as needed
|
||||
|
||||
4. **Run quality checks**
|
||||
```bash
|
||||
make all # Runs fmt, vet, staticcheck, test, build
|
||||
```
|
||||
|
||||
5. **Commit your changes**
|
||||
|
||||
Follow [semantic commit messages](#commit-message-format):
|
||||
```bash
|
||||
git commit -m "feat: add amazing feature"
|
||||
```
|
||||
|
||||
6. **Push to your fork**
|
||||
```bash
|
||||
git push origin feature/amazing-feature
|
||||
```
|
||||
|
||||
7. **Open a Pull Request**
|
||||
- Provide a clear description of the changes
|
||||
- Reference any related issues
|
||||
- Ensure all CI checks pass
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.23 or higher
|
||||
- Access to a Kubernetes cluster
|
||||
- kubectl configured
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Install development tools
|
||||
make install-tools
|
||||
|
||||
# Build the binary
|
||||
make build
|
||||
|
||||
# Run tests
|
||||
make test
|
||||
|
||||
# Run with race detection
|
||||
go test -race ./...
|
||||
|
||||
# Check code quality
|
||||
make vet
|
||||
make staticcheck
|
||||
make fmt
|
||||
```
|
||||
|
||||
### Running Locally
|
||||
|
||||
```bash
|
||||
# Build and install
|
||||
make build
|
||||
make install
|
||||
|
||||
# Run with your config
|
||||
kportal -c .kportal.yaml
|
||||
|
||||
# Run in verbose mode
|
||||
kportal -v
|
||||
```
|
||||
|
||||
## Commit Message Format
|
||||
|
||||
We use semantic commit messages for automatic version generation:
|
||||
|
||||
### Commit Types
|
||||
|
||||
- **feat** - New feature (bumps minor version)
|
||||
- **fix** - Bug fix (bumps patch version)
|
||||
- **docs** - Documentation only changes (bumps patch version)
|
||||
- **style** - Code style changes (formatting, missing semi colons, etc.)
|
||||
- **refactor** - Code refactoring
|
||||
- **test** - Adding or updating tests
|
||||
- **chore** - Maintenance tasks
|
||||
- **breaking** - Breaking changes (bumps major version)
|
||||
|
||||
### Format
|
||||
|
||||
```
|
||||
<type>: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Feature
|
||||
git commit -m "feat: add health check grace period"
|
||||
|
||||
# Bug fix
|
||||
git commit -m "fix: resolve port conflict detection bug"
|
||||
|
||||
# Breaking change
|
||||
git commit -m "breaking: change config file format
|
||||
|
||||
BREAKING CHANGE: Config file format has changed from JSON to YAML.
|
||||
Migration tool available with --convert flag."
|
||||
|
||||
# Documentation
|
||||
git commit -m "docs: update installation instructions"
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
kportal/
|
||||
├── cmd/kportal/ # Main application entry point
|
||||
├── internal/
|
||||
│ ├── config/ # Configuration parsing and validation
|
||||
│ ├── forward/ # Port-forward manager and workers
|
||||
│ ├── healthcheck/ # Health monitoring system
|
||||
│ ├── k8s/ # Kubernetes client wrapper
|
||||
│ ├── retry/ # Retry logic with backoff
|
||||
│ ├── ui/ # Terminal UI implementations
|
||||
│ └── converter/ # kftray JSON converter
|
||||
├── Formula/ # Homebrew formula
|
||||
├── .github/workflows/ # CI/CD pipelines
|
||||
└── docs/ # Documentation and GitHub Pages
|
||||
```
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
### Go Style
|
||||
|
||||
- Follow [Effective Go](https://golang.org/doc/effective_go)
|
||||
- Use `gofmt` for formatting
|
||||
- Keep functions small and focused
|
||||
- Write descriptive variable names
|
||||
- Add comments for exported functions
|
||||
- Handle errors explicitly
|
||||
|
||||
### Testing
|
||||
|
||||
- Write tests for new functionality
|
||||
- Aim for meaningful test coverage
|
||||
- Use table-driven tests where appropriate
|
||||
- Mock external dependencies
|
||||
- Test edge cases and error conditions
|
||||
|
||||
Example test:
|
||||
```go
|
||||
func TestForwardWorker_Start(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
forward config.Forward
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid pod forward",
|
||||
forward: config.Forward{
|
||||
Resource: "pod/test",
|
||||
Port: 8080,
|
||||
LocalPort: 8080,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
// Add more test cases
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Test implementation
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
- Update README.md for user-facing changes
|
||||
- Add code comments for complex logic
|
||||
- Update CHANGELOG.md following [Keep a Changelog](https://keepachangelog.com/)
|
||||
- Document new configuration options
|
||||
- Add examples for new features
|
||||
|
||||
## Release Process
|
||||
|
||||
Releases are automated via GitHub Actions:
|
||||
|
||||
1. **Version is determined automatically** by semver-gen based on commit messages
|
||||
2. **Create a tag** following semantic versioning:
|
||||
```bash
|
||||
git tag -a v0.2.0 -m "Release version 0.2.0"
|
||||
git push origin v0.2.0
|
||||
```
|
||||
3. **GitHub Actions will**:
|
||||
- Build binaries for all platforms
|
||||
- Create GitHub release
|
||||
- Update Homebrew formula
|
||||
- Generate release notes
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Questions?** Open a [GitHub Discussion](https://github.com/lukaszraczylo/kportal/discussions)
|
||||
- **Bug or feature request?** Open a [GitHub Issue](https://github.com/lukaszraczylo/kportal/issues)
|
||||
- **Want to chat?** Reach out on GitHub
|
||||
|
||||
## Recognition
|
||||
|
||||
Contributors will be recognized in:
|
||||
- GitHub's contributor graph
|
||||
- Release notes
|
||||
- README.md (for significant contributions)
|
||||
|
||||
Thank you for contributing to kportal! 🎉
|
||||
@@ -0,0 +1,84 @@
|
||||
class Kportal < Formula
|
||||
desc "Modern Kubernetes port-forward manager with interactive TUI"
|
||||
homepage "https://lukaszraczylo.github.io/kportal"
|
||||
license "MIT"
|
||||
|
||||
# Version will be dynamically set by bump-homebrew-formula-action
|
||||
# This is a template - actual releases will have specific version and URLs
|
||||
version "0.1.5"
|
||||
|
||||
on_macos do
|
||||
on_arm do
|
||||
url "https://github.com/lukaszraczylo/kportal/releases/download/v#{version}/kportal-#{version}-darwin-arm64.tar.gz"
|
||||
sha256 "PLACEHOLDER_SHA256_DARWIN_ARM64"
|
||||
end
|
||||
|
||||
on_intel do
|
||||
url "https://github.com/lukaszraczylo/kportal/releases/download/v#{version}/kportal-#{version}-darwin-amd64.tar.gz"
|
||||
sha256 "PLACEHOLDER_SHA256_DARWIN_AMD64"
|
||||
end
|
||||
end
|
||||
|
||||
on_linux do
|
||||
on_arm do
|
||||
url "https://github.com/lukaszraczylo/kportal/releases/download/v#{version}/kportal-#{version}-linux-arm64.tar.gz"
|
||||
sha256 "PLACEHOLDER_SHA256_LINUX_ARM64"
|
||||
end
|
||||
|
||||
on_intel do
|
||||
url "https://github.com/lukaszraczylo/kportal/releases/download/v#{version}/kportal-#{version}-linux-amd64.tar.gz"
|
||||
sha256 "PLACEHOLDER_SHA256_LINUX_AMD64"
|
||||
end
|
||||
end
|
||||
|
||||
# Optional dependency - kubectl is commonly already installed
|
||||
# but kportal requires it to function
|
||||
depends_on "kubernetes-cli" => :optional
|
||||
|
||||
def install
|
||||
bin.install "kportal"
|
||||
|
||||
# Generate shell completions if the binary supports it
|
||||
# This will be implemented in future versions
|
||||
# generate_completions_from_executable(bin/"kportal", "completion")
|
||||
end
|
||||
|
||||
def caveats
|
||||
<<~EOS
|
||||
kportal requires:
|
||||
• kubectl installed and configured
|
||||
• Access to a Kubernetes cluster
|
||||
• A valid kubeconfig file (~/.kube/config)
|
||||
|
||||
Quick start:
|
||||
1. Create a configuration file: .kportal.yaml
|
||||
2. Add your port-forward definitions
|
||||
3. Run: kportal
|
||||
|
||||
For configuration examples and full documentation:
|
||||
https://lukaszraczylo.github.io/kportal
|
||||
|
||||
To validate your configuration:
|
||||
kportal --check
|
||||
EOS
|
||||
end
|
||||
|
||||
test do
|
||||
# Test that binary runs and reports correct version
|
||||
assert_match version.to_s, shell_output("#{bin}/kportal --version")
|
||||
|
||||
# Test that binary can validate an empty config (should fail gracefully)
|
||||
(testpath/".kportal.yaml").write <<~YAML
|
||||
contexts:
|
||||
test:
|
||||
namespaces:
|
||||
default:
|
||||
- resource: test-pod
|
||||
port: 8080
|
||||
local_port: 8080
|
||||
YAML
|
||||
|
||||
# Should be able to validate config even without kube access
|
||||
system bin/"kportal", "--check", "-c", testpath/".kportal.yaml"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Lukasz Raczylo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,172 +1,249 @@
|
||||
# kportal
|
||||
|
||||
A robust Kubernetes port-forwarding tool that manages multiple concurrent port-forwards across different contexts, namespaces, and resources with automatic reconnection and failure recovery.
|
||||
[](https://github.com/lukaszraczylo/kportal/releases)
|
||||
[](LICENSE)
|
||||
[](https://goreportcard.com/report/github.com/lukaszraczylo/kportal)
|
||||
|
||||
## Features
|
||||
**Modern Kubernetes port-forward manager with interactive terminal UI**
|
||||
|
||||
- **Multi-Context Support**: Forward ports from multiple Kubernetes contexts simultaneously
|
||||
- **Automatic Pod Restart Handling**: Detects and reconnects to pods when they restart
|
||||
- **Label Selector Support**: Dynamically target pods using label selectors
|
||||
- **Prefix Matching**: Automatically find and reconnect to pods with name prefixes
|
||||
- **Hot-Reload**: Configuration file changes are automatically detected and applied
|
||||
- **Resilient Connections**: Infinite retry with exponential backoff (max 10s)
|
||||
- **Port Conflict Detection**: Validates port availability before starting
|
||||
- **Multiple Ports Per Resource**: Forward multiple ports from the same pod/service
|
||||
kportal simplifies managing multiple Kubernetes port-forwards with an elegant, interactive terminal interface. Built with [Bubble Tea](https://github.com/charmbracelet/bubbletea), it provides real-time status updates, automatic reconnection, and hot-reload configuration support.
|
||||
|
||||
## Installation
|
||||

|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🎯 **Interactive TUI** - Beautiful terminal interface with keyboard navigation (↑↓/jk, Space to toggle, q to quit)
|
||||
- 🔄 **Auto-Reconnect** - Automatic retry with exponential backoff on connection failures (max 10s)
|
||||
- ⚡ **Hot-Reload** - Update configuration without restarting - changes applied automatically
|
||||
- 🏥 **Health Checks** - Real-time port forward status monitoring with 5-second intervals
|
||||
- 🎨 **Multi-Context** - Support for multiple Kubernetes contexts and namespaces
|
||||
- 📦 **Batch Management** - Manage all port-forwards from a single configuration file
|
||||
- 🔌 **Toggle Forwards** - Enable/disable individual port-forwards on the fly with Space key
|
||||
- 🚀 **Grace Period** - Smart 10-second grace period to avoid false "Error" status on startup
|
||||
- 📊 **Status Display** - Clear visual indicators: Active (●), Starting (○), Reconnecting (◐), Error (✗)
|
||||
- 🔍 **Error Reporting** - Detailed error messages displayed below the table
|
||||
- 🔄 **Pod Restart Handling** - Detects and reconnects to pods when they restart
|
||||
- 🏷️ **Label Selector Support** - Dynamically target pods using label selectors
|
||||
- 📋 **Prefix Matching** - Automatically find and reconnect to pods with name prefixes
|
||||
- 🚫 **Port Conflict Detection** - Validates port availability before starting with detailed PID info
|
||||
- 🎭 **Alias Support** - Cleaner, more readable display names for your forwards
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
### Homebrew (macOS/Linux)
|
||||
|
||||
```bash
|
||||
# Install development tools (including semver-gen for version management)
|
||||
make install-tools
|
||||
brew install lukaszraczylo/tap/kportal
|
||||
```
|
||||
|
||||
# Build from source (version automatically generated from git history)
|
||||
### Quick Install Script
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
|
||||
```
|
||||
|
||||
### Manual Download
|
||||
|
||||
Download the latest binary for your platform from the [releases page](https://github.com/lukaszraczylo/kportal/releases):
|
||||
|
||||
- **macOS**: `kportal-{version}-darwin-{amd64|arm64}.tar.gz`
|
||||
- **Linux**: `kportal-{version}-linux-{amd64|arm64}.tar.gz`
|
||||
- **Windows**: `kportal-{version}-windows-{amd64|arm64}.zip`
|
||||
|
||||
### Build from Source
|
||||
|
||||
```bash
|
||||
git clone https://github.com/lukaszraczylo/kportal.git
|
||||
cd kportal
|
||||
make build
|
||||
|
||||
# Install to user bin directory
|
||||
make install
|
||||
|
||||
# Install system-wide (requires sudo)
|
||||
sudo make install-system
|
||||
|
||||
# Or build manually
|
||||
go build -o kportal ./cmd/kportal
|
||||
```
|
||||
|
||||
## Usage
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```bash
|
||||
# Use default config file (.kportal.yaml)
|
||||
./kportal
|
||||
|
||||
# Use custom config file
|
||||
./kportal -c myconfig.yaml
|
||||
|
||||
# Enable verbose logging
|
||||
./kportal -v
|
||||
|
||||
# Validate configuration without starting
|
||||
./kportal --check
|
||||
|
||||
# Convert kftray JSON config to kportal YAML
|
||||
./kportal --convert configs.json --convert-output .kportal.yaml
|
||||
```
|
||||
|
||||
### Configuration File
|
||||
|
||||
Create a `.kportal.yaml` file in your current directory:
|
||||
1. **Create a configuration file** (`.kportal.yaml`):
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
- name: production
|
||||
namespaces:
|
||||
- name: default
|
||||
- name: backend
|
||||
forwards:
|
||||
# Pod with prefix matching (auto-handles restarts)
|
||||
- resource: pod/my-app
|
||||
protocol: tcp
|
||||
port: 8080
|
||||
localPort: 8080
|
||||
alias: my-api # Optional: cleaner log output
|
||||
|
||||
# Service forwarding with alias
|
||||
- resource: service/postgres
|
||||
protocol: tcp
|
||||
port: 5432
|
||||
localPort: 5432
|
||||
alias: prod-db
|
||||
|
||||
- name: monitoring
|
||||
- name: frontend
|
||||
forwards:
|
||||
- resource: service/redis
|
||||
protocol: tcp
|
||||
port: 6379
|
||||
localPort: 6380
|
||||
alias: prod-redis
|
||||
```
|
||||
|
||||
2. **Run kportal**:
|
||||
|
||||
```bash
|
||||
kportal
|
||||
```
|
||||
|
||||
3. **Navigate the interface**:
|
||||
- `↑↓` or `j/k` - Navigate through forwards
|
||||
- `Space` or `Enter` - Toggle forward on/off
|
||||
- `q` - Quit application
|
||||
|
||||
## 📖 Configuration
|
||||
|
||||
### Simple Configuration
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
- name: <context-name>
|
||||
namespaces:
|
||||
- name: <namespace-name>
|
||||
forwards:
|
||||
- resource: <resource-type>/<resource-name>
|
||||
protocol: tcp
|
||||
port: <remote-port>
|
||||
localPort: <local-port>
|
||||
alias: <friendly-name> # Optional
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
# Production cluster
|
||||
- name: prod-us-west
|
||||
namespaces:
|
||||
- name: databases
|
||||
forwards:
|
||||
# Direct pod connection with prefix matching
|
||||
- resource: pod/postgres-primary
|
||||
protocol: tcp
|
||||
port: 5432
|
||||
localPort: 5432
|
||||
alias: prod-postgres
|
||||
|
||||
# Service connection
|
||||
- resource: service/redis-master
|
||||
protocol: tcp
|
||||
port: 6379
|
||||
localPort: 6379
|
||||
alias: prod-redis
|
||||
|
||||
# Pod with label selector
|
||||
- resource: pod
|
||||
selector: app=prometheus
|
||||
selector: app=mongodb
|
||||
protocol: tcp
|
||||
port: 9090
|
||||
localPort: 9090
|
||||
alias: prometheus
|
||||
port: 27017
|
||||
localPort: 27017
|
||||
alias: mongo
|
||||
|
||||
- name: staging
|
||||
- name: applications
|
||||
forwards:
|
||||
- resource: deployment/api-server
|
||||
protocol: tcp
|
||||
port: 8080
|
||||
localPort: 8080
|
||||
alias: api
|
||||
|
||||
# Development cluster
|
||||
- name: dev-local
|
||||
namespaces:
|
||||
- name: default
|
||||
forwards:
|
||||
# Multiple ports from same pod
|
||||
- resource: pod/test-app
|
||||
port: 8080
|
||||
localPort: 8081
|
||||
alias: test-http
|
||||
|
||||
- resource: pod/test-app
|
||||
port: 9090
|
||||
localPort: 9091
|
||||
alias: test-metrics
|
||||
- resource: service/grafana
|
||||
protocol: tcp
|
||||
port: 3000
|
||||
localPort: 3000
|
||||
alias: grafana-dashboard
|
||||
```
|
||||
|
||||
### Resource Types
|
||||
### Configuration Options
|
||||
|
||||
#### Pod with Prefix Matching
|
||||
```yaml
|
||||
- resource: pod/my-app # Matches my-app-xyz789, my-app-abc123, etc.
|
||||
port: 8080
|
||||
localPort: 8080
|
||||
```
|
||||
Automatically reconnects to new pods when they restart.
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `resource` | string | Yes | Kubernetes resource with type prefix (e.g., `service/name`, `pod/name`) |
|
||||
| `protocol` | string | Yes | Connection protocol (typically `tcp`) |
|
||||
| `port` | int | Yes | Remote port on the Kubernetes resource |
|
||||
| `localPort` | int | Yes | Local port to forward to |
|
||||
| `alias` | string | No | Friendly name for display (defaults to resource name) |
|
||||
| `selector` | string | No | Label selector for dynamic pod selection (e.g., `app=nginx,env=prod`) |
|
||||
|
||||
#### Pod with Label Selector
|
||||
```yaml
|
||||
- resource: pod
|
||||
selector: app=nginx,env=prod
|
||||
port: 80
|
||||
localPort: 8080
|
||||
```
|
||||
Dynamically selects pods matching the label selector.
|
||||
### Resource Formats
|
||||
|
||||
#### Service
|
||||
```yaml
|
||||
- resource: service/postgres
|
||||
port: 5432
|
||||
localPort: 5432
|
||||
```
|
||||
Most stable option - forwards to service endpoints.
|
||||
- **Pod by name**: `pod/pod-name` or just `pod-name`
|
||||
- **Pod by prefix**: `pod/my-app` (matches `my-app-xyz789`, `my-app-abc123`, etc.)
|
||||
- **Pod by selector**: Set `resource: pod` and use `selector: app=nginx`
|
||||
- **Service**: `service/service-name` or `svc/service-name`
|
||||
- **Deployment**: `deployment/deployment-name` or `deploy/deployment-name`
|
||||
|
||||
#### Using Aliases
|
||||
## 🎮 Usage
|
||||
|
||||
Aliases provide cleaner, more readable log output:
|
||||
|
||||
```yaml
|
||||
- resource: service/victoria-metrics-cluster-vmselect
|
||||
port: 8481
|
||||
localPort: 8481
|
||||
alias: vmetrics # Shows "vmetrics:8481→8481" instead of full path
|
||||
```
|
||||
|
||||
**Without alias:**
|
||||
```
|
||||
[home/monitoring/service/victoria-metrics-cluster-vmselect:8481] Forwarding...
|
||||
```
|
||||
|
||||
**With alias:**
|
||||
```
|
||||
[vmetrics:8481] Forwarding vmetrics:8481→8481 → localhost:8481
|
||||
```
|
||||
|
||||
### Converting from kftray
|
||||
|
||||
kportal can automatically convert kftray JSON configurations to kportal YAML format:
|
||||
### Interactive Mode (Default)
|
||||
|
||||
```bash
|
||||
# Convert kftray config
|
||||
kportal --convert kftray-config.json --convert-output .kportal.yaml
|
||||
|
||||
# The converter will:
|
||||
# 1. Read the kftray JSON format
|
||||
# 2. Group forwards by context and namespace
|
||||
# 3. Generate kportal YAML with all aliases preserved
|
||||
# 4. Display a summary of the conversion
|
||||
kportal
|
||||
```
|
||||
|
||||
**Example kftray JSON:**
|
||||
Starts the interactive TUI where you can:
|
||||
- View all configured port-forwards in a table
|
||||
- See real-time status updates (Active, Starting, Reconnecting, Error)
|
||||
- Toggle forwards on/off with Space key
|
||||
- View detailed error messages at the bottom of the screen
|
||||
|
||||
### Verbose Mode
|
||||
|
||||
```bash
|
||||
kportal -v
|
||||
```
|
||||
|
||||
Runs in verbose mode with:
|
||||
- Detailed logging to stdout
|
||||
- Periodic status table updates every 2 seconds
|
||||
- Full error traces
|
||||
- No interactive controls (for automation/debugging)
|
||||
|
||||
### Validate Configuration
|
||||
|
||||
```bash
|
||||
kportal --check
|
||||
```
|
||||
|
||||
Validates your configuration file without starting any forwards:
|
||||
- Checks YAML syntax
|
||||
- Validates all required fields
|
||||
- Detects duplicate local ports
|
||||
- Shows validation errors with line numbers
|
||||
|
||||
### Custom Configuration File
|
||||
|
||||
```bash
|
||||
kportal -c /path/to/config.yaml
|
||||
```
|
||||
|
||||
### Version Information
|
||||
|
||||
```bash
|
||||
kportal --version
|
||||
# Output: kportal version 0.1.5
|
||||
```
|
||||
|
||||
## 🔄 kftray Migration
|
||||
|
||||
Migrate from kftray JSON configuration:
|
||||
|
||||
```bash
|
||||
kportal --convert configs.json --convert-output .kportal.yaml
|
||||
```
|
||||
|
||||
**Example conversion:**
|
||||
|
||||
kftray JSON:
|
||||
```json
|
||||
[
|
||||
{
|
||||
@@ -182,7 +259,7 @@ kportal --convert kftray-config.json --convert-output .kportal.yaml
|
||||
]
|
||||
```
|
||||
|
||||
**Converts to kportal YAML:**
|
||||
Converts to kportal YAML:
|
||||
```yaml
|
||||
contexts:
|
||||
- name: production
|
||||
@@ -196,66 +273,343 @@ contexts:
|
||||
alias: prod-db
|
||||
```
|
||||
|
||||
## How It Works
|
||||
## 🎨 Status Indicators
|
||||
|
||||
| Indicator | Status | Description |
|
||||
|-----------|--------|-------------|
|
||||
| `● Active` | 🟢 Green | Port-forward is active and healthy |
|
||||
| `○ Starting` | 🟡 Yellow | Initial connection in progress (10s grace period) |
|
||||
| `◐ Reconnecting` | 🟡 Yellow | Attempting to reconnect after failure |
|
||||
| `✗ Error` | 🔴 Red | Connection failed - see error details below table |
|
||||
| `○ Disabled` | ⚪ Gray | Port-forward manually disabled via Space key |
|
||||
|
||||
## 🛠️ Advanced Features
|
||||
|
||||
### Hot-Reload
|
||||
|
||||
kportal automatically watches for configuration file changes and reloads:
|
||||
|
||||
```bash
|
||||
# Edit your config while kportal is running
|
||||
vim .kportal.yaml
|
||||
|
||||
# Changes are applied automatically within seconds:
|
||||
# - New forwards are started
|
||||
# - Removed forwards are stopped
|
||||
# - Existing forwards continue running unchanged
|
||||
```
|
||||
|
||||
Supports manual reload via `SIGHUP`:
|
||||
```bash
|
||||
kill -HUP $(pgrep kportal)
|
||||
```
|
||||
|
||||
### Health Checks
|
||||
|
||||
Built-in health monitoring system:
|
||||
- **Check interval**: Every 5 seconds
|
||||
- **Timeout**: 2 seconds per check
|
||||
- **Grace period**: 10 seconds for new connections
|
||||
- **Automatic updates**: Real-time status changes in UI
|
||||
- **Error tracking**: Detailed error messages for failed connections
|
||||
|
||||
### Error Display
|
||||
|
||||
When connections fail, errors are displayed below the table:
|
||||
|
||||
```
|
||||
Errors:
|
||||
• prod-postgres: dial tcp 127.0.0.1:5432: connect: connection refused
|
||||
• prod-redis: i/o timeout after 2.0s
|
||||
```
|
||||
|
||||
Errors automatically clear when:
|
||||
- Connection becomes healthy
|
||||
- Forward is disabled
|
||||
- Forward is removed
|
||||
|
||||
### Port Conflict Detection
|
||||
|
||||
kportal checks for port conflicts at multiple stages:
|
||||
|
||||
**At startup:**
|
||||
```
|
||||
Port conflicts detected:
|
||||
Port 8080:
|
||||
• Requested by: api-server (context: prod, namespace: default)
|
||||
• Currently used by: PID 1234 (chrome)
|
||||
```
|
||||
|
||||
**During hot-reload:**
|
||||
- Only validates new ports being added
|
||||
- Skips currently managed ports
|
||||
- Rejects configuration if conflicts found
|
||||
|
||||
### Pod Restart Handling
|
||||
|
||||
When a pod restarts:
|
||||
1. The port-forward connection breaks
|
||||
2. kportal immediately attempts to re-resolve the resource
|
||||
3. For prefix matches: finds the newest pod with that prefix
|
||||
4. For selectors: re-queries pods with matching labels
|
||||
5. Reconnects to the new pod
|
||||
6. Logs the switch: `Switched to new pod: old-pod → new-pod`
|
||||
1. Port-forward connection breaks
|
||||
2. kportal immediately re-resolves the resource:
|
||||
- For prefix matches: Finds newest pod with matching prefix
|
||||
- For selectors: Re-queries pods with matching labels
|
||||
3. Reconnects to new pod
|
||||
4. Logs the switch: `Switched to new pod: old-pod-abc → new-pod-xyz`
|
||||
|
||||
### Retry Strategy
|
||||
|
||||
Backoff intervals: **1s → 2s → 4s → 8s → 10s (max)**
|
||||
Exponential backoff with maximum interval:
|
||||
- **Intervals**: 1s → 2s → 4s → 8s → 10s (max)
|
||||
- **Infinite retries**: Continues until connection succeeds
|
||||
- **Independent**: Each forward has its own retry logic
|
||||
- **Grace period**: First 10 seconds show "Starting" instead of "Error"
|
||||
|
||||
- Connection failures trigger immediate resource re-resolution
|
||||
- Retries continue indefinitely until successful
|
||||
- Each forward has independent retry logic
|
||||
## 🔧 Development
|
||||
|
||||
### Hot-Reload
|
||||
### Prerequisites
|
||||
|
||||
The config file is watched for changes:
|
||||
1. File change detected
|
||||
2. New config loaded and validated
|
||||
3. Changes diff'd against current state
|
||||
4. New forwards started, removed forwards stopped
|
||||
5. Unchanged forwards continue running
|
||||
- Go 1.23 or higher
|
||||
- Access to a Kubernetes cluster
|
||||
- kubectl configured with contexts
|
||||
|
||||
If validation fails, the previous configuration remains active.
|
||||
|
||||
## Development
|
||||
|
||||
### Build Commands
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Build binary
|
||||
make build
|
||||
|
||||
# Check current version (from semver-gen)
|
||||
make version
|
||||
|
||||
# Run all checks (fmt, vet, staticcheck, test, build)
|
||||
make all
|
||||
|
||||
# Run tests with race detection
|
||||
# Run tests
|
||||
make test
|
||||
|
||||
# Run code quality checks
|
||||
make vet
|
||||
make staticcheck
|
||||
make fmt
|
||||
# Run all checks (fmt, vet, staticcheck, test)
|
||||
make all
|
||||
|
||||
# Install development tools (staticcheck, mockery, semver-gen)
|
||||
make install-tools
|
||||
# Check current version
|
||||
make version
|
||||
|
||||
# Generate test coverage report
|
||||
make coverage
|
||||
# Install locally
|
||||
make install
|
||||
|
||||
# Install system-wide
|
||||
sudo make install-system
|
||||
|
||||
# Clean build artifacts
|
||||
make clean
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
kportal/
|
||||
├── cmd/kportal/ # Main application entry point
|
||||
├── internal/
|
||||
│ ├── config/ # Configuration parsing and validation
|
||||
│ ├── forward/ # Port-forward manager and workers
|
||||
│ │ ├── manager.go # Orchestrates all forwards
|
||||
│ │ ├── worker.go # Individual forward worker
|
||||
│ │ └── port_checker.go # Port conflict detection
|
||||
│ ├── healthcheck/ # Health monitoring system
|
||||
│ │ └── checker.go # Port health checking
|
||||
│ ├── k8s/ # Kubernetes client wrapper
|
||||
│ │ ├── client.go # K8s client management
|
||||
│ │ ├── port_forward.go # Port-forward implementation
|
||||
│ │ └── resolver.go # Resource resolution
|
||||
│ ├── retry/ # Retry logic with backoff
|
||||
│ │ └── backoff.go # Exponential backoff
|
||||
│ ├── ui/ # Terminal UI implementations
|
||||
│ │ ├── bubbletea_ui.go # Interactive TUI (Bubble Tea)
|
||||
│ │ └── table_ui.go # Simple table for verbose mode
|
||||
│ └── converter/ # kftray JSON converter
|
||||
├── Formula/ # Homebrew formula
|
||||
├── .github/workflows/ # CI/CD pipelines
|
||||
│ └── release.yml # Release automation
|
||||
├── install.sh # Installation script
|
||||
├── semver.yaml # Semantic version config
|
||||
├── Makefile # Build automation
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## 📝 Examples
|
||||
|
||||
### Database Access
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
production:
|
||||
namespaces:
|
||||
databases:
|
||||
- resource: postgres-primary
|
||||
port: 5432
|
||||
local_port: 5432
|
||||
alias: prod-db
|
||||
```
|
||||
|
||||
Connect with:
|
||||
```bash
|
||||
kportal # Start in another terminal
|
||||
psql -h localhost -p 5432 -U postgres
|
||||
```
|
||||
|
||||
### Multiple Services
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
dev:
|
||||
namespaces:
|
||||
default:
|
||||
- resource: api
|
||||
port: 8080
|
||||
local_port: 8080
|
||||
- resource: frontend
|
||||
port: 3000
|
||||
local_port: 3000
|
||||
- resource: redis
|
||||
port: 6379
|
||||
local_port: 6379
|
||||
```
|
||||
|
||||
Access:
|
||||
- API: `http://localhost:8080`
|
||||
- Frontend: `http://localhost:3000`
|
||||
- Redis: `redis-cli -p 6379`
|
||||
|
||||
### Cross-Context Setup
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
prod-us:
|
||||
namespaces:
|
||||
backend:
|
||||
- resource: api
|
||||
port: 8080
|
||||
local_port: 8080
|
||||
alias: prod-us-api
|
||||
|
||||
prod-eu:
|
||||
namespaces:
|
||||
backend:
|
||||
- resource: api
|
||||
port: 8080
|
||||
local_port: 8081 # Different local port
|
||||
alias: prod-eu-api
|
||||
```
|
||||
|
||||
Compare APIs across regions simultaneously.
|
||||
|
||||
### Debug Multiple Pod Versions
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
production:
|
||||
namespaces:
|
||||
default:
|
||||
# Version 1
|
||||
- resource: pod
|
||||
selector: app=myapp,version=v1
|
||||
port: 8080
|
||||
local_port: 8080
|
||||
alias: app-v1
|
||||
|
||||
# Version 2
|
||||
- resource: pod
|
||||
selector: app=myapp,version=v2
|
||||
port: 8080
|
||||
local_port: 8081
|
||||
alias: app-v2
|
||||
|
||||
# Debug port for v2
|
||||
- resource: pod
|
||||
selector: app=myapp,version=v2
|
||||
port: 6060
|
||||
local_port: 6060
|
||||
alias: app-v2-pprof
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
**Problem**: `Port 8080: already in use by PID 1234 (chrome)`
|
||||
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Find the process
|
||||
lsof -i :8080
|
||||
|
||||
# Kill the process
|
||||
kill 1234
|
||||
|
||||
# Or use a different local port in config
|
||||
local_port: 8081
|
||||
```
|
||||
|
||||
### Connection Refused
|
||||
|
||||
**Problem**: `dial tcp 127.0.0.1:8080: connect: connection refused`
|
||||
|
||||
**Common causes**:
|
||||
1. **Pod not ready yet** - Wait for status to change from "Starting" → "Active" (10s grace period)
|
||||
2. **Wrong port number** - Verify the pod/service actually exposes that port
|
||||
3. **Service not exposed** - Check with `kubectl get svc` and `kubectl describe svc <name>`
|
||||
|
||||
**Debug**:
|
||||
```bash
|
||||
# Check pod status
|
||||
kubectl get pods -n <namespace>
|
||||
|
||||
# Check if port is exposed
|
||||
kubectl describe pod <pod-name> -n <namespace>
|
||||
|
||||
# Check service endpoints
|
||||
kubectl get endpoints <service-name> -n <namespace>
|
||||
```
|
||||
|
||||
### Context Not Found
|
||||
|
||||
**Problem**: `context "prod" not found in kubeconfig`
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# List available contexts
|
||||
kubectl config get-contexts
|
||||
|
||||
# Verify context name matches
|
||||
kubectl config current-context
|
||||
|
||||
# Update your config to use the correct context name
|
||||
```
|
||||
|
||||
### Health Check Errors During Startup
|
||||
|
||||
**Problem**: Seeing "Error" status immediately after starting
|
||||
|
||||
**This is normal!** kportal has a 10-second grace period. If the connection is still failing after 10 seconds, check:
|
||||
- Pod is running: `kubectl get pods`
|
||||
- Port is correct in config
|
||||
- Network connectivity to cluster
|
||||
|
||||
### Logs Covering UI
|
||||
|
||||
**Problem**: Kubernetes client logs appearing over the interactive UI
|
||||
|
||||
**This is fixed in v0.1.5+**. The interactive mode now completely suppresses all logs including:
|
||||
- Standard Go `log` package
|
||||
- Kubernetes `klog` output
|
||||
- Any stderr/stdout leakage
|
||||
|
||||
If you still see logs, please file an issue!
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Make your changes and add tests
|
||||
4. Run checks: `make all`
|
||||
5. Commit your changes (follow [semantic commit messages](#semantic-versioning))
|
||||
6. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
7. Open a Pull Request
|
||||
|
||||
### Semantic Versioning
|
||||
|
||||
This project uses [semver-gen](https://github.com/lukaszraczylo/semver-generator) for automatic semantic version generation based on git commit messages.
|
||||
@@ -265,130 +619,37 @@ This project uses [semver-gen](https://github.com/lukaszraczylo/semver-generator
|
||||
- **Minor** (0.X.0): `feat`, `feature`, `add`, `enhance`, `update`, `improve`
|
||||
- **Major** (X.0.0): `breaking`, `major`, `BREAKING CHANGE`
|
||||
|
||||
The version is automatically calculated from your git history and embedded in the binary at build time.
|
||||
|
||||
Example commits:
|
||||
```bash
|
||||
# Check current version
|
||||
make version
|
||||
|
||||
# Build with auto-generated version
|
||||
make build
|
||||
|
||||
# Verify version in binary
|
||||
./kportal --version
|
||||
git commit -m "feat: add health check grace period" # Bumps minor version
|
||||
git commit -m "fix: resolve port conflict detection" # Bumps patch version
|
||||
git commit -m "breaking: change config file format" # Bumps major version
|
||||
```
|
||||
|
||||
Configuration is managed in `semver.yaml`. To manually install semver-gen:
|
||||
## 📄 License
|
||||
|
||||
```bash
|
||||
# Automatically installed via make install-tools
|
||||
# Or install manually from https://github.com/lukaszraczylo/semver-generator
|
||||
```
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
### Project Structure
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
```
|
||||
kportal/
|
||||
├── cmd/kportal/ # CLI entry point
|
||||
├── internal/
|
||||
│ ├── config/ # Configuration parsing and validation
|
||||
│ ├── forward/ # Port-forward workers and manager
|
||||
│ ├── k8s/ # Kubernetes client, resolver, port-forward wrapper
|
||||
│ └── retry/ # Exponential backoff logic
|
||||
├── test/
|
||||
│ ├── integration/ # Integration tests
|
||||
│ ├── fixtures/ # Test configurations
|
||||
│ └── helpers/ # Test utilities
|
||||
├── .kportal.yaml # Example configuration
|
||||
├── semver.yaml # Semantic version configuration
|
||||
├── Makefile # Build automation
|
||||
└── CLAUDE.md # Development guide
|
||||
```
|
||||
- Built with [Bubble Tea](https://github.com/charmbracelet/bubbletea) by Charm - An awesome framework for building terminal UIs
|
||||
- Styled with [Lipgloss](https://github.com/charmbracelet/lipgloss) - Terminal styling library
|
||||
- Inspired by [kftray](https://github.com/hcavarsan/kftray) - Original GUI port-forward manager
|
||||
- Uses [client-go](https://github.com/kubernetes/client-go) for Kubernetes integration
|
||||
- Version management by [semver-gen](https://github.com/lukaszraczylo/semver-generator)
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
- [Website](https://lukaszraczylo.github.io/kportal)
|
||||
- [Issue Tracker](https://github.com/lukaszraczylo/kportal/issues)
|
||||
- [Releases](https://github.com/lukaszraczylo/kportal/releases)
|
||||
- [Changelog](CHANGELOG.md)
|
||||
|
||||
## Signal Handling
|
||||
|
||||
- `CTRL+C` / `SIGTERM`: Graceful shutdown (closes all forwards)
|
||||
- `Ctrl+C` / `SIGTERM`: Graceful shutdown (closes all forwards)
|
||||
- `SIGHUP`: Reload configuration file
|
||||
|
||||
## Port Conflict Detection
|
||||
---
|
||||
|
||||
kportal validates ports at multiple stages:
|
||||
|
||||
1. **Config Parse Time**: Detects duplicate local ports in configuration
|
||||
2. **Startup Time**: Checks if ports are available on the system
|
||||
3. **Hot-Reload Time**: Validates new ports before applying changes
|
||||
|
||||
Errors show which process is using conflicting ports (with PID).
|
||||
|
||||
## Examples
|
||||
|
||||
### Forward Multiple Services from Production
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
- name: production
|
||||
namespaces:
|
||||
- name: default
|
||||
forwards:
|
||||
- resource: service/api
|
||||
port: 8080
|
||||
localPort: 8080
|
||||
- resource: service/postgres
|
||||
port: 5432
|
||||
localPort: 5432
|
||||
- resource: service/redis
|
||||
port: 6379
|
||||
localPort: 6379
|
||||
```
|
||||
|
||||
### Monitor Multiple Environments
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
- name: production
|
||||
namespaces:
|
||||
- name: monitoring
|
||||
forwards:
|
||||
- resource: service/prometheus
|
||||
port: 9090
|
||||
localPort: 9090
|
||||
|
||||
- name: staging
|
||||
namespaces:
|
||||
- name: monitoring
|
||||
forwards:
|
||||
- resource: service/prometheus
|
||||
port: 9090
|
||||
localPort: 9091 # Different local port
|
||||
```
|
||||
|
||||
### Debug Specific Pods
|
||||
|
||||
```yaml
|
||||
contexts:
|
||||
- name: production
|
||||
namespaces:
|
||||
- name: default
|
||||
forwards:
|
||||
# Forward app HTTP and debug ports
|
||||
- resource: pod
|
||||
selector: app=myapp,version=v2
|
||||
port: 8080
|
||||
localPort: 8080
|
||||
|
||||
- resource: pod
|
||||
selector: app=myapp,version=v2
|
||||
port: 6060 # pprof
|
||||
localPort: 6060
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions welcome! Please ensure:
|
||||
- Code passes `make check` (fmt, vet, staticcheck)
|
||||
- Tests pass with `make test`
|
||||
- New features include tests
|
||||
Made with ❤️ by [Lukasz Raczylo](https://github.com/lukaszraczylo)
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
# Release Infrastructure Setup Summary
|
||||
|
||||
This document summarizes all the release infrastructure that has been set up for kportal.
|
||||
|
||||
## ✅ Completed Setup
|
||||
|
||||
### 1. GitHub Actions CI/CD Pipeline
|
||||
|
||||
**File**: `.github/workflows/release.yml`
|
||||
|
||||
**Features**:
|
||||
- Multi-platform binary builds (Linux, macOS, Windows - amd64 & arm64)
|
||||
- Automatic release creation on version tags
|
||||
- Binary archiving (tar.gz for Unix, zip for Windows)
|
||||
- SHA256 checksum generation
|
||||
- Automated Homebrew formula updates
|
||||
- Release notes generation
|
||||
|
||||
**How to trigger**:
|
||||
```bash
|
||||
# Commit with semantic versioning keywords
|
||||
git commit -m "feat: add new feature"
|
||||
|
||||
# Tag the release
|
||||
git tag -a v0.2.0 -m "Release v0.2.0"
|
||||
|
||||
# Push tags
|
||||
git push origin v0.2.0
|
||||
```
|
||||
|
||||
The pipeline will automatically:
|
||||
1. Build binaries for all platforms
|
||||
2. Create GitHub release with binaries
|
||||
3. Update Homebrew tap formula
|
||||
4. Generate release notes
|
||||
|
||||
### 2. Installation Methods
|
||||
|
||||
#### A. Homebrew Formula
|
||||
|
||||
**File**: `Formula/kportal.rb`
|
||||
|
||||
**Installation command**:
|
||||
```bash
|
||||
brew install lukaszraczylo/tap/kportal
|
||||
```
|
||||
|
||||
**Note**: Formula is automatically updated by CI/CD pipeline. You'll need to create a separate tap repository:
|
||||
1. Create repo: `https://github.com/lukaszraczylo/brew-taps`
|
||||
2. Add Formula/kportal.rb to that repo
|
||||
3. Set `HOMEBREW_TAP_TOKEN` secret in GitHub repository settings
|
||||
|
||||
#### B. Quick Install Script
|
||||
|
||||
**File**: `install.sh`
|
||||
|
||||
**Features**:
|
||||
- Auto-detects OS and architecture
|
||||
- Downloads appropriate binary
|
||||
- Extracts and installs to /usr/local/bin
|
||||
- Verifies installation
|
||||
- Colorful output with emoji indicators
|
||||
|
||||
**Installation command**:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
|
||||
```
|
||||
|
||||
#### C. Manual Download
|
||||
|
||||
Users can download binaries directly from GitHub releases:
|
||||
```
|
||||
https://github.com/lukaszraczylo/kportal/releases
|
||||
```
|
||||
|
||||
### 3. Documentation
|
||||
|
||||
#### A. Comprehensive README.md
|
||||
|
||||
**File**: `README.md`
|
||||
|
||||
**Contents**:
|
||||
- Feature showcase with emojis
|
||||
- Multiple installation methods
|
||||
- Quick start guide
|
||||
- Configuration examples
|
||||
- Usage instructions
|
||||
- Advanced features documentation
|
||||
- Troubleshooting guide
|
||||
- Contributing guidelines
|
||||
|
||||
#### B. GitHub Pages Website
|
||||
|
||||
**File**: `docs/index.html`
|
||||
|
||||
**Features**:
|
||||
- Modern, responsive design with TailwindCSS
|
||||
- Hero section with clear CTA
|
||||
- Feature showcase cards
|
||||
- Installation guide
|
||||
- Configuration examples with syntax highlighting
|
||||
- Documentation links
|
||||
- Mobile-friendly
|
||||
|
||||
**URL** (once enabled): `https://lukaszraczylo.github.io/kportal`
|
||||
|
||||
**To enable**:
|
||||
1. Go to GitHub repository settings
|
||||
2. Pages section
|
||||
3. Source: Deploy from a branch
|
||||
4. Branch: main
|
||||
5. Folder: /docs
|
||||
|
||||
### 4. Supporting Files
|
||||
|
||||
#### CHANGELOG.md
|
||||
**File**: `CHANGELOG.md`
|
||||
|
||||
Tracks all changes following Keep a Changelog format. Update this file with each release.
|
||||
|
||||
#### CONTRIBUTING.md
|
||||
**File**: `CONTRIBUTING.md`
|
||||
|
||||
Guidelines for:
|
||||
- Bug reporting
|
||||
- Feature requests
|
||||
- Pull request process
|
||||
- Commit message format
|
||||
- Development setup
|
||||
- Testing guidelines
|
||||
|
||||
## 🚀 Release Workflow
|
||||
|
||||
### Standard Release Process
|
||||
|
||||
1. **Develop features**
|
||||
```bash
|
||||
git checkout -b feature/my-feature
|
||||
# Make changes
|
||||
make test
|
||||
make all
|
||||
```
|
||||
|
||||
2. **Commit with semantic messages**
|
||||
```bash
|
||||
git commit -m "feat: add amazing feature"
|
||||
git commit -m "fix: resolve bug in health check"
|
||||
```
|
||||
|
||||
3. **Update CHANGELOG.md**
|
||||
```markdown
|
||||
## [0.2.0] - 2025-11-24
|
||||
|
||||
### Added
|
||||
- Amazing new feature
|
||||
|
||||
### Fixed
|
||||
- Bug in health check
|
||||
```
|
||||
|
||||
4. **Tag the release**
|
||||
```bash
|
||||
git tag -a v0.2.0 -m "Release v0.2.0"
|
||||
git push origin main
|
||||
git push origin v0.2.0
|
||||
```
|
||||
|
||||
5. **CI/CD automatically**:
|
||||
- Builds all binaries
|
||||
- Creates GitHub release
|
||||
- Updates Homebrew formula
|
||||
- Attaches binaries and checksums
|
||||
|
||||
### Version Bumping (Semantic Versioning)
|
||||
|
||||
Version is automatically determined by semver-gen from commit messages:
|
||||
|
||||
- **Patch** (0.0.X): `fix`, `bugfix`, `hotfix`, `patch`, `docs`, `test`, `refactor`
|
||||
- **Minor** (0.X.0): `feat`, `feature`, `add`, `enhance`, `update`, `improve`
|
||||
- **Major** (X.0.0): `breaking`, `major`, `BREAKING CHANGE`
|
||||
|
||||
## 📦 Platform Support
|
||||
|
||||
### Supported Platforms
|
||||
|
||||
| OS | Architecture | Archive Format |
|
||||
|---------|-------------|----------------|
|
||||
| Linux | amd64 | tar.gz |
|
||||
| Linux | arm64 | tar.gz |
|
||||
| macOS | amd64 | tar.gz |
|
||||
| macOS | arm64 | tar.gz |
|
||||
| Windows | amd64 | zip |
|
||||
| Windows | arm64 | zip |
|
||||
|
||||
## 🔒 Required GitHub Secrets
|
||||
|
||||
For full automation, set these secrets in your GitHub repository:
|
||||
|
||||
1. **GITHUB_TOKEN** - Automatically provided by GitHub Actions
|
||||
2. **HOMEBREW_TAP_TOKEN** - Personal access token for updating Homebrew tap
|
||||
- Create at: https://github.com/settings/tokens
|
||||
- Permissions needed: `repo` scope
|
||||
- Add to repository secrets
|
||||
|
||||
## 📝 Next Steps
|
||||
|
||||
### 1. Enable GitHub Pages
|
||||
- Repository Settings → Pages → Source: main branch, /docs folder
|
||||
|
||||
### 2. Create Homebrew Tap Repository
|
||||
```bash
|
||||
# Create new repository
|
||||
gh repo create lukaszraczylo/brew-taps --public
|
||||
|
||||
# Clone and set up
|
||||
git clone https://github.com/lukaszraczylo/brew-taps
|
||||
cd brew-taps
|
||||
cp ../kportal/Formula/kportal.rb ./Formula/
|
||||
git add Formula/kportal.rb
|
||||
git commit -m "Initial formula for kportal"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### 3. Add GitHub Token to Secrets
|
||||
- Repository Settings → Secrets and variables → Actions
|
||||
- New repository secret
|
||||
- Name: `HOMEBREW_TAP_TOKEN`
|
||||
- Value: Your personal access token
|
||||
|
||||
### 4. Create First Release
|
||||
```bash
|
||||
cd kportal
|
||||
git add .
|
||||
git commit -m "feat: initial release setup"
|
||||
git push origin main
|
||||
git tag -a v0.1.5 -m "Release v0.1.5"
|
||||
git push origin v0.1.5
|
||||
```
|
||||
|
||||
### 5. Test Installation Methods
|
||||
|
||||
After first release, test:
|
||||
```bash
|
||||
# Homebrew (once tap is set up)
|
||||
brew install lukaszraczylo/tap/kportal
|
||||
|
||||
# Quick install script
|
||||
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
|
||||
|
||||
# Manual download
|
||||
# Visit releases page and download binary
|
||||
```
|
||||
|
||||
## 🎨 Customization
|
||||
|
||||
### Update Website Colors
|
||||
|
||||
Edit `docs/index.html`:
|
||||
```javascript
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#3b82f6', // Blue
|
||||
secondary: '#8b5cf6', // Purple
|
||||
dark: '#0f172a', // Dark slate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Update Release Notes Template
|
||||
|
||||
Edit `.github/workflows/release.yml` in the "Generate release notes" step.
|
||||
|
||||
## 📊 Monitoring
|
||||
|
||||
After releases, monitor:
|
||||
- GitHub Actions workflow runs
|
||||
- GitHub Releases page
|
||||
- Homebrew tap repository commits
|
||||
- Download statistics on releases page
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Release workflow fails
|
||||
- Check GitHub Actions logs
|
||||
- Verify all required secrets are set
|
||||
- Ensure tag follows v\d+.\d+.\d+ format
|
||||
|
||||
### Homebrew formula not updating
|
||||
- Verify HOMEBREW_TAP_TOKEN is valid
|
||||
- Check tap repository permissions
|
||||
- Review release workflow logs
|
||||
|
||||
### Install script fails
|
||||
- Test locally with different OS/arch combinations
|
||||
- Check release binary naming matches script expectations
|
||||
- Verify binaries are attached to release
|
||||
|
||||
## ✅ Checklist for First Release
|
||||
|
||||
- [ ] All code committed and pushed
|
||||
- [ ] GitHub Pages enabled
|
||||
- [ ] Homebrew tap repository created
|
||||
- [ ] HOMEBREW_TAP_TOKEN secret set
|
||||
- [ ] CHANGELOG.md updated
|
||||
- [ ] Version tag created and pushed
|
||||
- [ ] Release workflow completed successfully
|
||||
- [ ] Binaries attached to release
|
||||
- [ ] Homebrew formula updated
|
||||
- [ ] Install script tested
|
||||
- [ ] Documentation website live
|
||||
- [ ] README.md installation links work
|
||||
|
||||
---
|
||||
|
||||
**Documentation last updated**: 2025-11-23
|
||||
**Setup completed for**: kportal v0.1.5
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="scroll-smooth">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>kportal - Kubernetes Port-Forward Manager</title>
|
||||
<meta name="description" content="Professional Kubernetes port-forward manager with interactive terminal UI. Manage multiple port-forwards with auto-reconnect, hot-reload, and real-time health checks.">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Inter', sans-serif; }
|
||||
code, pre { font-family: 'JetBrains Mono', monospace; }
|
||||
.theme-transition { transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; }
|
||||
</style>
|
||||
<script>
|
||||
// Theme initialization
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 theme-transition">
|
||||
<!-- Navigation -->
|
||||
<nav class="fixed w-full bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 z-50 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="flex justify-between h-16 items-center">
|
||||
<div class="flex items-center space-x-3">
|
||||
<i class="fas fa-network-wired text-blue-600 dark:text-blue-400 text-xl"></i>
|
||||
<span class="text-xl font-semibold text-gray-900 dark:text-gray-100">kportal</span>
|
||||
</div>
|
||||
<div class="hidden md:flex space-x-8">
|
||||
<a href="#features" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Features</a>
|
||||
<a href="#installation" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Installation</a>
|
||||
<a href="#configuration" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Configuration</a>
|
||||
<a href="#docs" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Documentation</a>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<button id="theme-toggle" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100">
|
||||
<i class="fas fa-moon dark:hidden text-xl"></i>
|
||||
<i class="fas fa-sun hidden dark:inline text-xl"></i>
|
||||
</button>
|
||||
<a href="https://github.com/lukaszraczylo/kportal" target="_blank"
|
||||
class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100">
|
||||
<i class="fab fa-github text-xl"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="pt-32 pb-20 bg-gradient-to-b from-gray-50 to-white dark:from-gray-800 dark:to-gray-900 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="text-center">
|
||||
<h1 class="text-5xl md:text-6xl font-bold text-gray-900 dark:text-gray-100 mb-6">
|
||||
Kubernetes Port-Forward<br/>
|
||||
<span class="text-blue-600 dark:text-blue-400">Manager</span>
|
||||
</h1>
|
||||
<p class="text-xl text-gray-600 dark:text-gray-300 mb-10 max-w-2xl mx-auto leading-relaxed">
|
||||
Professional terminal interface for managing multiple Kubernetes port-forwards with auto-reconnect, hot-reload, and real-time health monitoring.
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center mb-12">
|
||||
<a href="#installation"
|
||||
class="bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white px-8 py-3 rounded font-medium transition">
|
||||
Get Started
|
||||
</a>
|
||||
<a href="https://github.com/lukaszraczylo/kportal"
|
||||
class="bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-900 dark:text-gray-100 px-8 py-3 rounded font-medium border border-gray-300 dark:border-gray-600 transition">
|
||||
<i class="fab fa-github mr-2"></i>View on GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex justify-center gap-4 text-sm">
|
||||
<img src="https://img.shields.io/github/v/release/lukaszraczylo/kportal" alt="Version"/>
|
||||
<img src="https://img.shields.io/github/license/lukaszraczylo/kportal" alt="License"/>
|
||||
<img src="https://goreportcard.com/badge/github.com/lukaszraczylo/kportal" alt="Go Report"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section id="features" class="py-20 bg-white dark:bg-gray-900 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4">Features</h2>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300">Everything you need for production-grade port-forwarding</p>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-3 gap-8">
|
||||
<div class="p-8 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-lg hover:shadow-lg transition theme-transition">
|
||||
<div class="w-12 h-12 bg-blue-50 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-desktop text-blue-600 dark:text-blue-400 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-3">Interactive TUI</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400">Beautiful terminal interface powered by Bubble Tea. Toggle forwards on/off, view real-time status updates.</p>
|
||||
</div>
|
||||
|
||||
<div class="p-8 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-lg hover:shadow-lg transition theme-transition">
|
||||
<div class="w-12 h-12 bg-green-50 dark:bg-green-900/30 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-sync-alt text-green-600 dark:text-green-400 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-3">Auto-Reconnect</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400">Automatic reconnection on failure with exponential backoff. Never lose connectivity to your services.</p>
|
||||
</div>
|
||||
|
||||
<div class="p-8 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-lg hover:shadow-lg transition theme-transition">
|
||||
<div class="w-12 h-12 bg-purple-50 dark:bg-purple-900/30 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-fire text-purple-600 dark:text-purple-400 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-3">Hot-Reload</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400">Configuration changes applied automatically. Add, remove, or modify forwards without restarting.</p>
|
||||
</div>
|
||||
|
||||
<div class="p-8 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-lg hover:shadow-lg transition theme-transition">
|
||||
<div class="w-12 h-12 bg-yellow-50 dark:bg-yellow-900/30 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-heartbeat text-yellow-600 dark:text-yellow-400 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-3">Health Checks</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400">Real-time health monitoring with 5-second intervals. Grace period prevents false error alerts.</p>
|
||||
</div>
|
||||
|
||||
<div class="p-8 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-lg hover:shadow-lg transition theme-transition">
|
||||
<div class="w-12 h-12 bg-red-50 dark:bg-red-900/30 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-exclamation-triangle text-red-600 dark:text-red-400 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-3">Error Reporting</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400">Detailed error messages displayed in the interface. Know exactly what went wrong.</p>
|
||||
</div>
|
||||
|
||||
<div class="p-8 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-lg hover:shadow-lg transition theme-transition">
|
||||
<div class="w-12 h-12 bg-indigo-50 dark:bg-indigo-900/30 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-layer-group text-indigo-600 dark:text-indigo-400 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-3">Multi-Context</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400">Support for multiple Kubernetes contexts and namespaces. Manage all your clusters from one place.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Installation Section -->
|
||||
<section id="installation" class="py-20 bg-gray-50 dark:bg-gray-800 theme-transition">
|
||||
<div class="max-w-4xl mx-auto px-6">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4">Installation</h2>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300">Get started in seconds</p>
|
||||
</div>
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-700 p-8 border border-gray-200 dark:border-gray-600 rounded-lg theme-transition">
|
||||
<div class="flex items-center mb-4">
|
||||
<i class="fas fa-beer text-orange-500 dark:text-orange-400 text-2xl mr-3"></i>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100">Homebrew</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">macOS & Linux</p>
|
||||
</div>
|
||||
</div>
|
||||
<div onclick="copyToClipboard('brew install lukaszraczylo/tap/kportal', this)"
|
||||
class="bg-gray-900 dark:bg-gray-950 text-gray-100 p-4 rounded text-sm cursor-pointer hover:bg-gray-800 dark:hover:bg-black transition group relative">
|
||||
<code class="block">brew install lukaszraczylo/tap/kportal</code>
|
||||
<i class="fas fa-copy absolute top-3 right-3 text-gray-500 group-hover:text-gray-300 text-xs"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-700 p-8 border border-gray-200 dark:border-gray-600 rounded-lg theme-transition">
|
||||
<div class="flex items-center mb-4">
|
||||
<i class="fas fa-terminal text-green-500 dark:text-green-400 text-2xl mr-3"></i>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100">Quick Install</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">All platforms</p>
|
||||
</div>
|
||||
</div>
|
||||
<div onclick="copyToClipboard('curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash', this)"
|
||||
class="bg-gray-900 dark:bg-gray-950 text-gray-100 p-4 rounded text-sm cursor-pointer hover:bg-gray-800 dark:hover:bg-black transition group relative">
|
||||
<code class="block">curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash</code>
|
||||
<i class="fas fa-copy absolute top-3 right-3 text-gray-500 group-hover:text-gray-300 text-xs"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-700 p-8 border border-gray-200 dark:border-gray-600 rounded-lg theme-transition">
|
||||
<div class="flex items-center mb-4">
|
||||
<i class="fas fa-download text-blue-500 dark:text-blue-400 text-2xl mr-3"></i>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100">Manual Download</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">Direct download from GitHub releases</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://github.com/lukaszraczylo/kportal/releases"
|
||||
class="block text-center bg-gray-900 dark:bg-gray-950 text-gray-100 px-4 py-3 rounded text-sm font-medium hover:bg-gray-800 dark:hover:bg-black transition">
|
||||
<i class="fas fa-download mr-2"></i>Download Binary
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Configuration Section -->
|
||||
<section id="configuration" class="py-20 bg-white dark:bg-gray-900 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4">Configuration</h2>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300">Simple YAML configuration</p>
|
||||
</div>
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="bg-gray-900 dark:bg-black rounded-lg p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center">
|
||||
<i class="fas fa-file-code text-gray-400 dark:text-gray-500 mr-2"></i>
|
||||
<span class="text-gray-400 dark:text-gray-500 text-sm font-mono">.kportal.yaml</span>
|
||||
</div>
|
||||
<button onclick="copyToClipboard(document.getElementById('config-code').textContent, this)" class="text-gray-400 hover:text-gray-200">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<pre class="text-sm text-gray-100 overflow-x-auto"><code id="config-code">contexts:
|
||||
- name: production
|
||||
namespaces:
|
||||
- name: backend
|
||||
forwards:
|
||||
- resource: service/postgres
|
||||
protocol: tcp
|
||||
port: 5432
|
||||
localPort: 5432
|
||||
alias: prod-db
|
||||
|
||||
- name: frontend
|
||||
forwards:
|
||||
- resource: service/redis
|
||||
protocol: tcp
|
||||
port: 6379
|
||||
localPort: 6379
|
||||
alias: prod-redis</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 grid md:grid-cols-2 gap-6">
|
||||
<div class="p-6 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg theme-transition">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
||||
<i class="fas fa-cube text-blue-600 dark:text-blue-400 mr-2"></i>Resource Types
|
||||
</h3>
|
||||
<ul class="space-y-2 text-gray-700 dark:text-gray-300 text-sm">
|
||||
<li><code class="bg-white dark:bg-gray-900 px-2 py-1 rounded text-xs">pod/name</code> - Direct pod</li>
|
||||
<li><code class="bg-white dark:bg-gray-900 px-2 py-1 rounded text-xs">service/name</code> - Service</li>
|
||||
<li><code class="bg-white dark:bg-gray-900 px-2 py-1 rounded text-xs">deployment/name</code> - Deployment</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg theme-transition">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
|
||||
<i class="fas fa-cog text-purple-600 dark:text-purple-400 mr-2"></i>Features
|
||||
</h3>
|
||||
<ul class="space-y-2 text-gray-700 dark:text-gray-300 text-sm">
|
||||
<li>Pod prefix matching</li>
|
||||
<li>Label selectors</li>
|
||||
<li>Alias support</li>
|
||||
<li>Auto-reconnect</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Documentation Section -->
|
||||
<section id="docs" class="py-20 bg-gray-50 dark:bg-gray-800 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4">Documentation</h2>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300">Everything you need to know</p>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-4 gap-6">
|
||||
<a href="https://github.com/lukaszraczylo/kportal#quick-start"
|
||||
class="p-6 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg hover:border-blue-600 dark:hover:border-blue-400 hover:shadow-lg transition group theme-transition">
|
||||
<i class="fas fa-rocket text-blue-600 dark:text-blue-400 text-2xl mb-3 block"></i>
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2 group-hover:text-blue-600 dark:group-hover:text-blue-400">Quick Start</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">Get up and running in minutes</p>
|
||||
</a>
|
||||
<a href="https://github.com/lukaszraczylo/kportal#configuration"
|
||||
class="p-6 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg hover:border-blue-600 dark:hover:border-blue-400 hover:shadow-lg transition group theme-transition">
|
||||
<i class="fas fa-file-alt text-green-600 dark:text-green-400 text-2xl mb-3 block"></i>
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2 group-hover:text-green-600 dark:group-hover:text-green-400">Configuration</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">Detailed configuration guide</p>
|
||||
</a>
|
||||
<a href="https://github.com/lukaszraczylo/kportal#advanced-features"
|
||||
class="p-6 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg hover:border-blue-600 dark:hover:border-blue-400 hover:shadow-lg transition group theme-transition">
|
||||
<i class="fas fa-sliders-h text-purple-600 dark:text-purple-400 text-2xl mb-3 block"></i>
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2 group-hover:text-purple-600 dark:group-hover:text-purple-400">Advanced</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">Hot-reload, health checks, more</p>
|
||||
</a>
|
||||
<a href="https://github.com/lukaszraczylo/kportal#troubleshooting"
|
||||
class="p-6 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg hover:border-blue-600 dark:hover:border-blue-400 hover:shadow-lg transition group theme-transition">
|
||||
<i class="fas fa-life-ring text-yellow-600 dark:text-yellow-400 text-2xl mb-3 block"></i>
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2 group-hover:text-yellow-600 dark:group-hover:text-yellow-400">Troubleshooting</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-sm">Common issues and solutions</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-gray-900 dark:bg-black text-gray-400 dark:text-gray-500 py-12 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="grid md:grid-cols-3 gap-8">
|
||||
<div>
|
||||
<div class="flex items-center space-x-3 mb-4">
|
||||
<i class="fas fa-network-wired text-blue-500 dark:text-blue-400 text-xl"></i>
|
||||
<span class="text-xl font-semibold text-white">kportal</span>
|
||||
</div>
|
||||
<p class="text-sm">Professional Kubernetes port-forward manager</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-white font-semibold mb-4">Links</h3>
|
||||
<ul class="space-y-2 text-sm">
|
||||
<li><a href="https://github.com/lukaszraczylo/kportal" class="hover:text-white transition">
|
||||
<i class="fab fa-github mr-2"></i>GitHub
|
||||
</a></li>
|
||||
<li><a href="https://github.com/lukaszraczylo/kportal/issues" class="hover:text-white transition">
|
||||
<i class="fas fa-bug mr-2"></i>Issues
|
||||
</a></li>
|
||||
<li><a href="https://github.com/lukaszraczylo/kportal/releases" class="hover:text-white transition">
|
||||
<i class="fas fa-tag mr-2"></i>Releases
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-white font-semibold mb-4">Built With</h3>
|
||||
<ul class="space-y-2 text-sm">
|
||||
<li><i class="fas fa-code mr-2"></i>Bubble Tea</li>
|
||||
<li><i class="fas fa-palette mr-2"></i>Lipgloss</li>
|
||||
<li><i class="fas fa-dharmachakra mr-2"></i>client-go</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-8 pt-8 border-t border-gray-800 dark:border-gray-900 text-center text-sm">
|
||||
<p>Made by <a href="https://github.com/lukaszraczylo" class="text-blue-500 dark:text-blue-400 hover:text-blue-400 dark:hover:text-blue-300 transition">Lukasz Raczylo</a></p>
|
||||
<p class="mt-2">MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Theme toggle
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
themeToggle.addEventListener('click', () => {
|
||||
if (document.documentElement.classList.contains('dark')) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.theme = 'light';
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.theme = 'dark';
|
||||
}
|
||||
});
|
||||
|
||||
// Copy to clipboard function
|
||||
function copyToClipboard(text, button) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
const originalHTML = button.innerHTML;
|
||||
button.innerHTML = '<i class="fas fa-check text-green-500"></i>';
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalHTML;
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// Smooth scrolling
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+171
@@ -0,0 +1,171 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# kportal installation script
|
||||
# Usage: curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
|
||||
|
||||
REPO="lukaszraczylo/kportal"
|
||||
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Print functions
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
# Detect OS
|
||||
detect_os() {
|
||||
case "$(uname -s)" in
|
||||
Linux*) echo "linux";;
|
||||
Darwin*) echo "darwin";;
|
||||
MINGW*|MSYS*|CYGWIN*) echo "windows";;
|
||||
*) echo "unknown";;
|
||||
esac
|
||||
}
|
||||
|
||||
# Detect architecture
|
||||
detect_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) echo "amd64";;
|
||||
aarch64|arm64) echo "arm64";;
|
||||
armv7l) echo "arm";;
|
||||
*) echo "unknown";;
|
||||
esac
|
||||
}
|
||||
|
||||
# Get latest version from GitHub
|
||||
get_latest_version() {
|
||||
curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" |
|
||||
grep '"tag_name":' |
|
||||
sed -E 's/.*"v([^"]+)".*/\1/'
|
||||
}
|
||||
|
||||
# Main installation
|
||||
main() {
|
||||
echo ""
|
||||
echo "╔════════════════════════════════════════╗"
|
||||
echo "║ kportal Installation Script ║"
|
||||
echo "║ Kubernetes Port Forwarding Made Easy ║"
|
||||
echo "╚════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Detect system
|
||||
OS=$(detect_os)
|
||||
ARCH=$(detect_arch)
|
||||
|
||||
if [ "$OS" = "unknown" ] || [ "$ARCH" = "unknown" ]; then
|
||||
print_error "Unsupported operating system or architecture"
|
||||
print_info "OS: $(uname -s), Arch: $(uname -m)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_info "Detected: ${OS}/${ARCH}"
|
||||
|
||||
# Get latest version
|
||||
print_info "Fetching latest version..."
|
||||
VERSION=$(get_latest_version)
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
print_error "Failed to fetch latest version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "Latest version: v${VERSION}"
|
||||
|
||||
# Construct download URL
|
||||
if [ "$OS" = "windows" ]; then
|
||||
ARCHIVE="kportal-${VERSION}-${OS}-${ARCH}.zip"
|
||||
else
|
||||
ARCHIVE="kportal-${VERSION}-${OS}-${ARCH}.tar.gz"
|
||||
fi
|
||||
|
||||
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/v${VERSION}/${ARCHIVE}"
|
||||
|
||||
# Create temporary directory
|
||||
TMP_DIR=$(mktemp -d)
|
||||
trap "rm -rf ${TMP_DIR}" EXIT
|
||||
|
||||
# Download binary
|
||||
print_info "Downloading kportal..."
|
||||
if ! curl -fsSL -o "${TMP_DIR}/${ARCHIVE}" "${DOWNLOAD_URL}"; then
|
||||
print_error "Failed to download kportal"
|
||||
print_info "URL: ${DOWNLOAD_URL}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract archive
|
||||
print_info "Extracting archive..."
|
||||
cd "${TMP_DIR}"
|
||||
if [ "$OS" = "windows" ]; then
|
||||
unzip -q "${ARCHIVE}"
|
||||
BINARY="kportal.exe"
|
||||
else
|
||||
tar -xzf "${ARCHIVE}"
|
||||
BINARY="kportal"
|
||||
fi
|
||||
|
||||
# Check if binary exists
|
||||
if [ ! -f "${BINARY}" ]; then
|
||||
print_error "Binary not found after extraction"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make binary executable
|
||||
chmod +x "${BINARY}"
|
||||
|
||||
# Install binary
|
||||
print_info "Installing kportal to ${INSTALL_DIR}..."
|
||||
|
||||
# Check if we need sudo
|
||||
if [ ! -w "${INSTALL_DIR}" ]; then
|
||||
print_warning "Installation directory requires sudo access"
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
sudo mv "${BINARY}" "${INSTALL_DIR}/${BINARY}"
|
||||
else
|
||||
print_error "sudo not found. Please run with appropriate permissions"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
mv "${BINARY}" "${INSTALL_DIR}/${BINARY}"
|
||||
fi
|
||||
|
||||
# Verify installation
|
||||
if command -v kportal >/dev/null 2>&1; then
|
||||
INSTALLED_VERSION=$(kportal --version | grep -oP 'kportal version \K[0-9.]+' || echo "unknown")
|
||||
print_success "kportal v${INSTALLED_VERSION} installed successfully!"
|
||||
else
|
||||
print_warning "kportal installed but not found in PATH"
|
||||
print_info "You may need to add ${INSTALL_DIR} to your PATH"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_success "Installation complete!"
|
||||
echo ""
|
||||
echo "Get started:"
|
||||
echo " 1. Create a config file: touch .kportal.yaml"
|
||||
echo " 2. Run: kportal"
|
||||
echo ""
|
||||
echo "Documentation: https://lukaszraczylo.github.io/kportal"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user