Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.1.0] - 2026-03-09

### Added

- Five built-in color schemes for table output, selectable and persisted per user:
- `default` - GitHub-style SemVer severity (`#D73A49` / `#0366D6` / `#28A745`)
- `okabe-ito` - Color-blind safe Okabe–Ito palette (`#E69F00` / `#0072B2` / `#009E73`)
- `traffic-light` - Classic red/yellow/green (`#E74C3C` / `#F1C40F` / `#2ECC71`)
- `severity` - Monitoring/observability style (`#8E44AD` / `#3498DB` / `#95A5A6`)
- `high-contrast` - Maximum distinction, color-blind safe (`#CC79A7` / `#0072B2` / `#F0E442`)
- All colors rendered with true-color (24-bit) escape codes for exact hex fidelity
- `--set-color-scheme` flag: run without a value to preview all schemes visually, or pass a scheme name to save it permanently
- Color scheme preference persisted to `~/.config/pycu/config.toml` (Linux/macOS) or `%APPDATA%\pycu\config.toml` (Windows)
- First-run interactive prompt to choose a color scheme on initial install
- `--uninstall` now also removes the `pycu/` config directory

## [1.0.0] - 2026-03-08

### Added
Expand All @@ -26,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Progress bar during PyPI lookups
- Install scripts for Linux/macOS (`install.sh`) and Windows (`install.ps1`)

[Unreleased]: https://github.com/Logic-py/python-check-updates/compare/1.0.0...HEAD
[Unreleased]: https://github.com/Logic-py/python-check-updates/compare/1.1.0...HEAD

[1.1.0]: https://github.com/Logic-py/python-check-updates/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/Logic-py/python-check-updates/releases/tag/1.0.0
61 changes: 60 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pycu"
version = "1.0.1"
version = "1.1.0"
edition = "2024"
rust-version = "1.85"
description = "Check your Python dependencies for newer versions on PyPI"
Expand All @@ -18,6 +18,7 @@ path = "src/main.rs"

[dependencies]
anyhow = "1"
dirs = "6"
clap = { version = "4", features = ["derive"] }
flate2 = "1"
futures = "0.3"
Expand All @@ -34,4 +35,5 @@ toml = "1"
zip = { version = "8.2.0", default-features = false, features = ["deflate"] }

[dev-dependencies]
tempfile = "3"
wiremock = "0.6"
57 changes: 55 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use clap::{Parser, ValueEnum};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Parser)]
Expand Down Expand Up @@ -28,6 +29,12 @@ pub struct Cli {
#[arg(short = 't', long, value_name = "LEVEL", default_value = "latest")]
pub target: TargetLevel,

/// Preview all color schemes, or set one persistently.
/// Without a value: show all schemes visually.
/// With a value: save that scheme and exit.
#[arg(long, value_name = "SCHEME", num_args = 0..=1, default_missing_value = "")]
pub set_color_scheme: Option<String>,

/// Update pycu itself to the latest release
#[arg(long)]
pub self_update: bool,
Expand All @@ -49,9 +56,49 @@ pub enum TargetLevel {
Patch,
}

#[derive(ValueEnum, Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(rename_all = "kebab-case")]
pub enum ColorScheme {
/// #D73A49 / #0366D6 / #28A745 - GitHub-style SemVer severity (default)
Default,
/// #E69F00 / #0072B2 / #009E73 - Okabe–Ito, color-blind safe
OkabeIto,
/// #E74C3C / #F1C40F / #2ECC71 - traffic-light (red/yellow/green)
TrafficLight,
/// #8E44AD / #3498DB / #95A5A6 - monitoring style (purple/blue/gray)
Severity,
/// #CC79A7 / #0072B2 / #F0E442 - maximum distinction, color-blind safe
HighContrast,
}

pub async fn run() -> anyhow::Result<()> {
let cli = Cli::parse();

if let Some(raw) = cli.set_color_scheme {
if raw.is_empty() {
// --set-color-scheme used without a value: show the preview
crate::output::table::print_color_scheme_preview();
} else {
// --set-color-scheme <SCHEME>: parse, save, confirm
use clap::ValueEnum;
let scheme = ColorScheme::from_str(&raw, true).map_err(|e| anyhow::anyhow!(
"Unknown color scheme '{}'. {}\nRun `pycu --set-color-scheme` to see all options.",
raw, e
))?;
let config = crate::config::Config {
color_scheme: scheme.clone(),
};
crate::config::save(&config)?;
let path = crate::config::config_path().unwrap_or_else(|| PathBuf::from("config.toml"));
println!(
"Color scheme set to '{}' and saved to {}.",
raw,
path.display()
);
}
return Ok(());
}

// Self-update is independent of any project file
if cli.self_update {
let client = crate::pypi::client::PypiClient::new()?.into_inner();
Expand All @@ -62,6 +109,12 @@ pub async fn run() -> anyhow::Result<()> {
return crate::uninstall::run();
}

// Load persisted config, running first-run setup if no config exists or is unreadable
let config = match crate::config::load() {
Ok(Some(cfg)) => cfg,
Ok(None) | Err(_) => crate::config::first_run_setup()?,
};

let file_path = match cli.file {
Some(p) => p,
None => resolve_default_file()?,
Expand Down Expand Up @@ -97,7 +150,7 @@ pub async fn run() -> anyhow::Result<()> {
.collect();

if cli.upgrade {
crate::output::table::print_table(&updates, false);
crate::output::table::print_table(&updates, false, &config.color_scheme);
let count = crate::upgrade::apply_upgrades(&file_path, &updates)?;
if count > 0 {
println!(
Expand All @@ -113,7 +166,7 @@ pub async fn run() -> anyhow::Result<()> {
if cli.json {
crate::output::json::print_json(&updates)?;
} else {
crate::output::table::print_table(&updates, true);
crate::output::table::print_table(&updates, true, &config.color_scheme);
}

Ok(())
Expand Down
Loading