Skip to content

Commit

Permalink
new: Rework terminal color handling. (#135)
Browse files Browse the repository at this point in the history
* Remove env vars.

* Remove color from snapshots.

* Copy stuff.

* Update handler.

* Add option and update docs.

* Add config setting.

* Update snapshots.

* Update json schema.

* Fix defaults.

* Update snapshots.

* Polish.

* Update windows tests.

* Update console.

* Testing.

* Ok lets try again.

* More fixes.

* Remove test.

* Add back logging.

* More debugging.

* And again.

* Remove logs.
  • Loading branch information
milesj committed Jun 14, 2022
1 parent 72c3ad3 commit 334da1f
Show file tree
Hide file tree
Showing 48 changed files with 545 additions and 207 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,4 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: run
args: -- --log trace ci
env:
CLICOLOR_FORCE: true
args: -- --color --log trace ci
2 changes: 1 addition & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ script = "rm -rf *.profraw crates/**/*.profraw tests/fixtures/**/*.profraw"

## OTHER

[tasks.gen-json-schemas]
[tasks.json-schemas]
command = "cargo"
args = ["run", "-p", "moon_config"]

Expand Down
3 changes: 3 additions & 0 deletions crates/cli/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ pub struct App {
)]
pub cache: CacheMode,

#[clap(long, env = "MOON_COLOR", help = "Force colored output for moon")]
pub color: bool,

#[clap(
arg_enum,
long,
Expand Down
4 changes: 2 additions & 2 deletions crates/cli/src/commands/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use console::Term;
use itertools::Itertools;
use moon_logger::color;
use moon_terminal::{ExtendedTerm, Label};
use moon_utils::is_test_env;
use moon_workspace::Workspace;
use std::env;

pub async fn project(id: &str, json: bool) -> Result<(), Box<dyn std::error::Error>> {
let workspace = Workspace::load().await?;
Expand All @@ -23,7 +23,7 @@ pub async fn project(id: &str, json: bool) -> Result<(), Box<dyn std::error::Err
term.render_entry("Source", &color::file(&project.source))?;

// Dont show in test snapshots
if env::var("MOON_TEST").is_err() {
if !is_test_env() {
term.render_entry("Root", &color::path(&project.root))?;
}

Expand Down
178 changes: 178 additions & 0 deletions crates/cli/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use console::{set_colors_enabled, set_colors_enabled_stderr};
use moon_logger::color::{no_color, supports_color};
use std::env;

fn setup_no_colors() {
env::set_var("NO_COLOR", "1");
// https://github.com/mitsuhiko/clicolors-control/issues/19
env::set_var("CLICOLOR", "0");

set_colors_enabled(false);
set_colors_enabled_stderr(false);
}

pub fn setup_colors(force: bool) {
let supported_level = supports_color().to_string();

// If being forced by --color or other env vars
if force
|| env::var("MOON_COLOR").is_ok()
|| env::var("FORCE_COLOR").is_ok()
|| env::var("CLICOLOR_FORCE").is_ok()
{
let mut color_level = env::var("MOON_COLOR")
.or_else(|_| env::var("FORCE_COLOR"))
.unwrap_or(supported_level);

// https://nodejs.org/api/cli.html#force_color1-2-3
if color_level.is_empty() || color_level == "true" {
color_level = "1".to_owned();
} else if color_level == "false" {
color_level = "0".to_owned();
}

if color_level == "0" {
setup_no_colors();
} else {
set_colors_enabled(true);
set_colors_enabled_stderr(true);

// https://bixense.com/clicolors/
env::set_var("CLICOLOR_FORCE", &color_level);
env::set_var("FORCE_COLOR", &color_level);
}

return;
}

if no_color() {
setup_no_colors();
} else {
env::set_var("CLICOLOR", supported_level);
}
}

#[cfg(test)]
mod test {
use super::*;
use serial_test::serial;

fn reset_vars() {
env::remove_var("NO_COLOR");
env::remove_var("CLICOLOR");
env::remove_var("CLICOLOR_FORCE");
env::remove_var("FORCE_COLOR");
env::remove_var("MOON_COLOR");
}

mod setup_color {
use super::*;

mod no_color {
use super::*;

#[test]
#[serial]
fn sets_vars() {
env::set_var("NO_COLOR", "1");

setup_colors(false);

assert_eq!(env::var("CLICOLOR").unwrap(), "0");
assert_eq!(env::var("NO_COLOR").unwrap(), "1");

reset_vars();
}
}

mod forced_color {
use super::*;

#[test]
#[serial]
fn forces_via_arg() {
setup_colors(true);

assert_eq!(env::var("CLICOLOR_FORCE").unwrap(), "2");
assert_eq!(env::var("FORCE_COLOR").unwrap(), "2");
assert!(env::var("NO_COLOR").is_err());

reset_vars();
}

#[test]
#[serial]
fn forces_over_no_color() {
env::set_var("NO_COLOR", "1");

setup_colors(true);

assert_eq!(env::var("CLICOLOR_FORCE").unwrap(), "2");
assert_eq!(env::var("FORCE_COLOR").unwrap(), "2");
assert_eq!(env::var("NO_COLOR").unwrap(), "1");

reset_vars();
}

#[test]
#[serial]
fn disables_if_zero() {
for var in ["MOON_COLOR", "FORCE_COLOR"] {
env::set_var(var, "0");

setup_colors(false);

assert_eq!(env::var("CLICOLOR").unwrap(), "0");
assert_eq!(env::var("NO_COLOR").unwrap(), "1");

reset_vars();
}
}

#[test]
#[serial]
fn disables_if_false_string() {
for var in ["MOON_COLOR", "FORCE_COLOR"] {
env::set_var(var, "false");

setup_colors(false);

assert_eq!(env::var("CLICOLOR").unwrap(), "0");
assert_eq!(env::var("NO_COLOR").unwrap(), "1");

reset_vars();
}
}

#[test]
#[serial]
fn enables_if_empty_string() {
for var in ["MOON_COLOR", "FORCE_COLOR"] {
env::set_var(var, "");

setup_colors(false);

assert_eq!(env::var("CLICOLOR_FORCE").unwrap(), "1");
assert_eq!(env::var("FORCE_COLOR").unwrap(), "1");

reset_vars();
}
}

#[test]
#[serial]
fn enables_if_true_string() {
for var in ["MOON_COLOR", "FORCE_COLOR"] {
env::set_var(var, "true");

setup_colors(false);

assert_eq!(env::var("CLICOLOR_FORCE").unwrap(), "1");
assert_eq!(env::var("FORCE_COLOR").unwrap(), "1");

reset_vars();
}
}
}
}
}
4 changes: 4 additions & 0 deletions crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod app;
mod commands;
mod enums;
mod helpers;

use crate::commands::bin::bin;
use crate::commands::ci::{ci, CiOptions};
Expand All @@ -10,6 +11,7 @@ use crate::commands::project_graph::project_graph;
use crate::commands::run::{run, RunOptions};
use crate::commands::setup::setup;
use crate::commands::teardown::teardown;
use crate::helpers::setup_colors;
use app::{App, Commands};
use clap::Parser;
use console::Term;
Expand Down Expand Up @@ -37,6 +39,8 @@ pub async fn run_cli() {
// Create app and parse arguments
let args = App::parse();

setup_colors(args.color);

// Setup logging
if env::var("MOON_LOG").is_err() {
env::set_var("MOON_LOG", args.log.to_string().to_lowercase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,70 @@ expression: "fs::read_to_string(workspace_config).unwrap()"
---
$schema: 'https://moonrepo.dev/schemas/workspace.json'

# A map of all projects found within the workspace. Each entry requires a unique project ID
# as the map key, and a file system path to the project folder as the map value. File paths
# are relative from the workspace root, and cannot reference projects located outside the
# workspace boundary.
# REQUIRED: A map of all projects found within the workspace, or a list or file system globs.
# When using a map, each entry requires a unique project ID as the map key, and a file system
# path to the project folder as the map value. File paths are relative from the workspace root,
# and cannot reference projects located outside the workspace boundary.
projects:
- 'app'
- 'packages/*'

# OPTIONAL: Configures Node.js within the toolchain. moon manages its own version of Node.js
# Configures Node.js within the toolchain. moon manages its own version of Node.js
# instead of relying on a version found on the host machine. This ensures deterministic
# and reproducible builds across any machine.
node:
# The version to use. Must be a semantic version that includes major, minor, and patch.
# We suggest using the latest active LTS version: https://nodejs.org/en/about/releases
version: '16.15.0'

# OPTIONAL: The package manager to use when managing dependencies.
# The package manager to use when managing dependencies.
# Accepts "npm", "pnpm", or "yarn". Defaults to "npm".
packageManager: 'npm'



# OPTIONAL: Add `node.version` as a constaint in the root `package.json` `engines`.
# Add `node.version` as a constaint in the root `package.json` `engines`.
addEnginesConstraint: true

# OPTIONAL: Dedupe dependencies after the lockfile has changed.
# Dedupe dependencies after the lockfile has changed.
dedupeOnLockfileChange: true

# OPTIONAL: Sync a project's `dependsOn` as normal dependencies within the project's
# Sync a project's `dependsOn` as normal dependencies within the project's
# `package.json`. Will use "workspace:*" ranges when available in the package manager.
syncProjectWorkspaceDependencies: true

# OPTIONAL: Sync `node.version` to a 3rd-party version manager's config file.
# Sync `node.version` to a 3rd-party version manager's config file.
# Accepts "nodenv" (.node-version), "nvm" (.nvmrc), or none.
# syncVersionManagerConfig: 'nvm'

# OPTIONAL: Configures how moon integrates with TypeScript.
# Configures how moon integrates with TypeScript.
typescript:
# OPTIONAL: Name of `tsconfig.json` file in project root.
# Name of `tsconfig.json` file in project root.
projectConfigFileName: 'tsconfig.json'

# OPTIONAL: Name of `tsconfig.json` file in workspace root.
# Name of `tsconfig.json` file in workspace root.
rootConfigFileName: 'tsconfig.json'

# OPTIONAL: Sync a project's `dependsOn` as TypeScript project references within the
# Sync a project's `dependsOn` as TypeScript project references within the
# project's `tsconfig.json` and the workspace root `tsconfig.json`.
syncProjectReferences: true

# OPTIONAL: Configures the version control system to utilize within the workspace. A VCS
# Configures the version control system to utilize within the workspace. A VCS
# is required for determining touched (added, modified, etc) files, calculating file hashes,
# computing affected files, and much more.
vcs:
# OPTIONAL: The manager/binary to use when managing the repository.
# The manager/binary to use when managing the repository.
# Accepts "git", or "svn". Defaults to "git".
manager: 'git'

# OPTIONAL: The default branch (master/main/trunk) in the repository for comparing the
# The default branch (master/main/trunk) in the repository for comparing the
# local branch against. For git, this is is typically "master" or "main",
# and must include the remote prefix (before /). For svn, this should always be "trunk".
defaultBranch: 'master'

# Configures aspects of the action runner.
actionRunner:
# Force colors to be inherited for all tasks that are ran as a child process
# and their output is piped to the action runner.
inheritColorsForPipedTasks: true

Loading

0 comments on commit 334da1f

Please sign in to comment.