diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f49ea9..bc6ef24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,50 +8,158 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Init wizard with daemon install step (`unarr init`, replaces `unarr setup`) -- Interactive config menu with 7 categories (`unarr config [category]`) -- Migration wizard from Sonarr/Radarr/Prowlarr (`unarr migrate`) [pre-beta] - - Auto-detect instances via Docker, config files, port scan, Prowlarr - - Import download history and blocklist to avoid re-downloading - - Detect Plex/Jellyfin/Emby media servers and library paths - - Extract debrid tokens from *arr download clients - - JSON export with `--dry-run --json` -- Media server detection in `unarr init` (suggests library paths as download directory) -- `preferred_quality` setting in config (2160p/1080p/720p) -- Clean command to remove temp files, logs, and cached data (`unarr clean`) -- Daemon mode with background download management (`unarr start`) -- One-shot download command (`unarr download`) -- Stream to media player (`unarr stream`) -- Doctor command for diagnostics (`unarr doctor`) -- Status command for daemon monitoring (`unarr status`) -- Download engine with torrent support (debrid and usenet coming soon) -- File organization (Movies/TV Shows directory structure) -- Post-download verification -- Desktop notifications (Linux, macOS) -- Docker support with multi-stage build -- Cross-platform install scripts (shell, PowerShell) -- Dependabot for automated dependency updates -- golangci-lint configuration with gosec -### Changed -- Renamed `internal/commands/` to `internal/cmd/` +- **organize**: use server metadata for file organization and subtitle handling +- **stream**: add NAT-PMP port mapping for remote downloads -## [0.1.0] - 2025-02-14 +## [0.4.1] - 2026-04-01 ### Added -- Initial release -- Search across 30+ torrent sources with advanced filters -- TrueSpec torrent inspection (quality, codec, seeds, score) -- Watch command (streaming providers + torrent alternatives) -- Popular and recent content browsing -- System statistics -- Interactive configuration -- JSON output mode (`--json`) for scripting -- Colored terminal output with `--no-color` support -- Homebrew tap distribution -- GoReleaser with UPX compression -- CI pipeline (test, build, lint, vet) -- Lefthook git hooks (gofmt, go vet, conventional commits) -[Unreleased]: https://github.com/torrentclaw/unarr/compare/v0.1.0...HEAD -[0.1.0]: https://github.com/torrentclaw/unarr/releases/tag/v0.1.0 +- **cli**: add login command and refactor shared helpers +- **stream**: report watch progress to API via HTTP Range tracking + +### Fixed + +- **ci**: fix lint errors and pin CI to Go 1.25 +- **lint**: remove unused newStubCmd function + +### Other + +- **cli**: remove moreseed stub command +- **cli**: remove redundant stub commands (monitor, open, add, compare) + +## [0.4.0] - 2026-03-31 + +### Added + +- **cli**: upgrade command, rich status, and version cache + +### Fixed + +- **progress**: always report status transitions and poll for control signals + +## [0.3.7] - 2026-03-31 + +### CI/CD + +- **docker**: remove dockerhub-description sync step + +## [0.3.6] - 2026-03-31 + +### CI/CD + +- **deps**: bump docker/metadata-action from 5 to 6 +- **deps**: bump docker/setup-qemu-action from 3 to 4 +- **deps**: bump docker/login-action from 3 to 4 +- **deps**: bump docker/build-push-action from 6 to 7 +- **deps**: bump codecov/codecov-action from 5 to 6 +- **docker**: add Docker Hub description sync and DOCKERHUB.md + +### Fixed + +- **ci**: upgrade golangci-lint to v2.11.3 for Go 1.25 support +- **docker**: upgrade alpine packages to patch CVE-2025-60876 and CVE-2026-27171 +- **lint**: use default:none to disable errcheck, fix all gofmt and exhaustive +- **lint**: disable errcheck, tune gosec/exclusions for codebase state +- **lint**: configure linters for codebase maturity, fix gofmt and ineffassign +- **lint**: exclude common fire-and-forget patterns from errcheck +- **lint**: resolve errcheck and bodyclose warnings for golangci-lint v2 + +## [0.3.5] - 2026-03-30 + +### Changed + +- migrate lint config to v2, remove daemon auto-upgrade, add trust badges + +## [0.3.3] - 2026-03-30 + +### Fixed + +- **ci**: remove go-client checkout steps + +## [0.3.2] - 2026-03-30 + +### Added + +- **init**: add 60s countdown, skip key, and cancel detection to browser auth + +### CI/CD + +- **release**: add Docker Hub publish and VirusTotal scan jobs + +### Documentation + +- add beta notice, fix install URLs to get.torrentclaw.com + +### Fixed + +- **ci**: fix virustotal job condition syntax +- **docker**: simplify Dockerfile for CI builds (no local go-client) +- **release**: disable homebrew tap (needs PAT, not GITHUB_TOKEN) + +### Other + +- re-enable homebrew tap in goreleaser + +## [0.3.1] - 2026-03-30 + +### Fixed + +- **build**: unused variable in Windows process check +- **release**: disable homebrew tap until repo is created + +### Other + +- rename module from torrentclaw-cli to unarr + +### Build + +- remove UPX compression (antivirus false positives, startup penalty) + +## [0.3.0] - 2026-03-29 + +### Added + +- **agent**: add WebSocket transport with HTTP fallback +- **auth**: browser-based CLI authentication (like Claude Code) +- **daemon**: add auto-scan, force start, and stall timeout default +- **debrid**: add HTTPS downloader for debrid direct URLs +- **stream**: UPnP port forwarding for remote video playback +- **usenet**: implement full NNTP download pipeline +- add migrate command, media server detection, and debrid auto-config +- replace setup with init wizard + interactive config menu +- add clean command to remove temp files, logs, and cached data +- add Sentry error reporting +- improve daemon resilience, streaming, and usenet downloads +- initial commit — unarr CLI + +### Changed + +- extract BuildSyncItems to library package, remove duplication + +### Documentation + +- improve CLI help, shell completion, and README + +### Fixed + +- **torrent**: expand tracker list, add DHT persistence and configurable timeouts +- force-start tasks bypass HasCapacity check in dispatch loop +- add panic recovery to auto-scan, cap DHT nodes at 200 +- harden usenet/debrid downloaders from critico review + +### Build + +- add -s -w -trimpath to Makefile, add build-small target with UPX +[Unreleased]: https://github.com/torrentclaw/unarr/compare/v0.4.1...HEAD +[0.4.1]: https://github.com/torrentclaw/unarr/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/torrentclaw/unarr/compare/v0.3.7...v0.4.0 +[0.3.7]: https://github.com/torrentclaw/unarr/compare/v0.3.6...v0.3.7 +[0.3.6]: https://github.com/torrentclaw/unarr/compare/v0.3.5...v0.3.6 +[0.3.5]: https://github.com/torrentclaw/unarr/compare/v0.3.3...v0.3.5 +[0.3.3]: https://github.com/torrentclaw/unarr/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/torrentclaw/unarr/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/torrentclaw/unarr/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/torrentclaw/unarr/releases/tag/v0.3.0 + diff --git a/Makefile b/Makefile index 6207d50..08462b6 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all build test lint coverage clean fmt vet check install-hooks +.PHONY: all build test lint coverage clean fmt vet check install-hooks changelog release release-patch release-minor release-major release-dry BINARY = unarr SENTRY_DSN ?= @@ -48,6 +48,29 @@ install-hooks: install: go install ./cmd/unarr/ +## Preview changelog for next release +changelog: + @git-cliff --unreleased --strip header + +## Create a release: make release-patch, release-minor, release-major, or release V=0.5.0 +release: + @test -n "$(V)" || { echo "Usage: make release V=0.5.0"; exit 1; } + @./scripts/release.sh $(V) + +release-patch: + @./scripts/release.sh patch + +release-minor: + @./scripts/release.sh minor + +release-major: + @./scripts/release.sh major + +## Preview release without making changes +release-dry: + @test -n "$(V)" || { echo "Usage: make release-dry V=patch|minor|major|0.5.0"; exit 1; } + @./scripts/release.sh --dry-run $(V) + ## Remove generated files clean: rm -f $(BINARY) coverage.out coverage.html diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..c5efe7f --- /dev/null +++ b/cliff.toml @@ -0,0 +1,79 @@ +# git-cliff configuration +# https://git-cliff.org/docs/configuration + +[changelog] +header = """# 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.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n +""" +body = """ +{%- macro remote_url() -%} + https://github.com/torrentclaw/unarr +{%- endmacro -%} + +{% if version -%} + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{%- else -%} + ## [Unreleased] +{%- endif %} + +{% for group, commits in commits | group_by(attribute="group") %} +### {{ group | striptags | trim | upper_first }} +{% for commit in commits +| filter(attribute="scope") +| sort(attribute="scope") %} + - **{{ commit.scope }}**: {{ commit.message }} + {%- if commit.breaking %} (**BREAKING**){% endif %} +{%- endfor -%} +{% for commit in commits %} + {%- if not commit.scope %} + - {{ commit.message }} + {%- if commit.breaking %} (**BREAKING**){% endif %} + {%- endif %} +{%- endfor %} +{% endfor %} +""" +footer = """ +{%- macro remote_url() -%} + https://github.com/torrentclaw/unarr +{%- endmacro -%} + +{% for release in releases -%} + {% if release.version -%} + {% if release.previous.version -%} + [{{ release.version | trim_start_matches(pat="v") }}]: {{ self::remote_url() }}/compare/{{ release.previous.version }}...{{ release.version }} + {% else -%} + [{{ release.version | trim_start_matches(pat="v") }}]: {{ self::remote_url() }}/releases/tag/{{ release.version }} + {% endif -%} + {% else -%} + {% if release.previous.version -%} + [Unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}...HEAD + {% endif -%} + {% endif -%} +{% endfor %} +""" +trim = true + +[git] +conventional_commits = true +filter_unconventional = true +split_commits = false +commit_parsers = [ + { message = "^feat", group = "Added" }, + { message = "^fix", group = "Fixed" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Changed" }, + { message = "^style", group = "Changed" }, + { message = "^doc", group = "Documentation" }, + { message = "^ci", group = "CI/CD" }, + { message = "^chore\\(deps\\)", skip = true }, + { message = "^chore", group = "Other" }, + { message = "^test", skip = true }, +] +protect_breaking_commits = false +filter_commits = false +tag_pattern = "v[0-9].*" +sort_commits = "newest" diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..da9b911 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# +# release.sh — Automate version bump, changelog generation, and tag creation. +# +# Usage: +# ./scripts/release.sh patch|minor|major Auto-bump from latest tag +# ./scripts/release.sh 0.5.0 Explicit version +# ./scripts/release.sh --dry-run patch Preview without changes +# +set -euo pipefail + +VERSION_FILE="internal/cmd/version.go" +CHANGELOG_FILE="CHANGELOG.md" + +# ── Colors ────────────────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +info() { echo -e "${CYAN}▸${NC} $*"; } +ok() { echo -e "${GREEN}✓${NC} $*"; } +warn() { echo -e "${YELLOW}⚠${NC} $*"; } +error() { echo -e "${RED}✗${NC} $*" >&2; } +die() { error "$@"; exit 1; } + +# ── Args ──────────────────────────────────────────────────────────── +DRY_RUN=false +BUMP="" + +for arg in "$@"; do + case "$arg" in + --dry-run) DRY_RUN=true ;; + patch|minor|major) BUMP="$arg" ;; + [0-9]*) BUMP="$arg" ;; + -h|--help) + echo "Usage: $0 [--dry-run] " + exit 0 + ;; + *) die "Unknown argument: $arg" ;; + esac +done + +[ -z "$BUMP" ] && die "Usage: $0 [--dry-run] " + +# ── Prerequisites ─────────────────────────────────────────────────── +command -v git-cliff >/dev/null 2>&1 || die "git-cliff not found. Install: https://git-cliff.org/docs/installation" + +if [ "$DRY_RUN" = false ]; then + [ -n "$(git status --porcelain)" ] && die "Working tree is dirty. Commit or stash changes first." +fi + +CURRENT_BRANCH=$(git branch --show-current) +[ "$CURRENT_BRANCH" = "main" ] || warn "Not on main branch (current: $CURRENT_BRANCH)" + +# ── Resolve version ──────────────────────────────────────────────── +LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") +LATEST_VERSION="${LATEST_TAG#v}" + +bump_version() { + local version="$1" part="$2" + IFS='.' read -r major minor patch <<< "$version" + case "$part" in + major) echo "$((major + 1)).0.0" ;; + minor) echo "$major.$((minor + 1)).0" ;; + patch) echo "$major.$minor.$((patch + 1))" ;; + esac +} + +case "$BUMP" in + patch|minor|major) NEXT_VERSION=$(bump_version "$LATEST_VERSION" "$BUMP") ;; + *) NEXT_VERSION="$BUMP" ;; +esac + +NEXT_TAG="v${NEXT_VERSION}" + +echo "" +echo -e "${BOLD} Release Plan${NC}" +echo -e " ─────────────────────────────" +echo -e " Current tag: ${YELLOW}${LATEST_TAG}${NC}" +echo -e " Next version: ${GREEN}${NEXT_TAG}${NC}" +echo -e " Dry run: ${DRY_RUN}" +echo "" + +# ── Preview changelog ─────────────────────────────────────────────── +info "Generating changelog for ${NEXT_TAG}..." +CHANGELOG_PREVIEW=$(git-cliff --tag "$NEXT_TAG" --unreleased --strip header) + +if [ -z "$CHANGELOG_PREVIEW" ]; then + die "No conventional commits found since ${LATEST_TAG}. Nothing to release." +fi + +echo -e "${BOLD} Changes in ${NEXT_TAG}:${NC}" +echo "$CHANGELOG_PREVIEW" | sed 's/^/ /' +echo "" + +# ── Dry run stops here ───────────────────────────────────────────── +if [ "$DRY_RUN" = true ]; then + ok "Dry run complete. No changes made." + exit 0 +fi + +# ── Confirm ───────────────────────────────────────────────────────── +echo -ne "${YELLOW}Proceed with release ${NEXT_TAG}? [y/N]${NC} " +read -r CONFIRM +[[ "$CONFIRM" =~ ^[Yy]$ ]] || { info "Aborted."; exit 0; } + +# ── Update version.go ────────────────────────────────────────────── +info "Updating ${VERSION_FILE}..." +sed -i "s/var Version = \".*\"/var Version = \"${NEXT_VERSION}\"/" "$VERSION_FILE" +ok "Version set to ${NEXT_VERSION}" + +# ── Update CHANGELOG.md ──────────────────────────────────────────── +info "Updating ${CHANGELOG_FILE}..." +git-cliff --tag "$NEXT_TAG" --output "$CHANGELOG_FILE" +ok "Changelog updated" + +# ── Commit and tag ────────────────────────────────────────────────── +info "Creating release commit..." +git add "$VERSION_FILE" "$CHANGELOG_FILE" +git commit -m "chore(release): ${NEXT_VERSION} + +- Bump version to ${NEXT_VERSION} +- Update CHANGELOG.md" + +info "Creating annotated tag ${NEXT_TAG}..." +git tag -a "$NEXT_TAG" -m "Release ${NEXT_TAG}" + +echo "" +ok "Release ${NEXT_TAG} created successfully!" +echo "" +echo -e " ${BOLD}Next steps:${NC}" +echo -e " Push to trigger CI release pipeline:" +echo "" +echo -e " ${CYAN}git push origin main --follow-tags${NC}" +echo ""