First release.

This commit is contained in:
2025-11-23 17:19:02 +00:00
parent 21ea41781d
commit ca84221f6e
11 changed files with 1974 additions and 279 deletions
+72
View File
@@ -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 }}
+3
View File
@@ -1,2 +1,5 @@
CLAUDE.md
kportal
DEPLOYMENT_SUMMARY.md
HOMEBREW_COMPLIANCE.md
RELEASE_SETUP.md
+68
View File
@@ -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"
+52
View File
@@ -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
View File
@@ -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! 🎉
+84
View File
@@ -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
+21
View File
@@ -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.
+540 -279
View File
@@ -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.
[![Release](https://img.shields.io/github/v/release/lukaszraczylo/kportal)](https://github.com/lukaszraczylo/kportal/releases)
[![License](https://img.shields.io/github/license/lukaszraczylo/kportal)](LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/lukaszraczylo/kportal)](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
![kportal Demo](docs/images/demo.png)
## ✨ 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)
+320
View File
@@ -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
View File
@@ -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
View File
@@ -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 "$@"