Skip to content

Commit

Permalink
Add cache management command (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
ofek committed May 12, 2024
1 parent e14bb1a commit b6ffddd
Show file tree
Hide file tree
Showing 14 changed files with 208 additions and 19 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ passthrough = [
"PYAPP_EXEC_NOTEBOOK",
"PYAPP_EXEC_SCRIPT",
"PYAPP_EXEC_SPEC",
"PYAPP_EXPOSE_CACHE",
"PYAPP_EXPOSE_METADATA",
"PYAPP_EXPOSE_PIP",
"PYAPP_EXPOSE_PYTHON",
Expand Down
48 changes: 32 additions & 16 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Display;
use std::fs::{self, File};
use std::hash::{Hash, Hasher};
use std::io::Read;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
use highway::PortableHash;
Expand Down Expand Up @@ -926,6 +926,23 @@ fn set_self_command() {
}
}

fn set_exposed_command(path: &Path, command_name: &str, indicator: &Regex) {
if !path.is_file() {
return;
}

let command_path = path.to_str().unwrap();
let command_source = fs::read_to_string(command_path).unwrap();
if indicator.is_match(&command_source) {
let variable = format!("PYAPP_EXPOSE_{}", command_name.to_uppercase());
if is_enabled(&variable) {
set_runtime_variable(&variable, "1");
} else {
set_runtime_variable(&variable, "0");
}
}
}

fn set_exposed_commands() {
let indicator = Regex::new(r"(?m)^#\[command\(hide = env!").unwrap();
let commands_dir: PathBuf = [
Expand All @@ -937,24 +954,23 @@ fn set_exposed_commands() {
.iter()
.collect();

for entry in fs::read_dir(commands_dir).unwrap() {
for entry in fs::read_dir(&commands_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if !path.is_file() {
continue;
}
set_exposed_command(
&path,
path.file_stem().unwrap().to_str().unwrap(),
&indicator,
);
}

let command_name = path.file_stem().unwrap().to_str().unwrap();
let command_path = path.to_str().unwrap();
let command_source = fs::read_to_string(command_path).unwrap();
if indicator.is_match(&command_source) {
let variable = format!("PYAPP_EXPOSE_{}", command_name.to_uppercase());
if is_enabled(&variable) {
set_runtime_variable(&variable, "1");
} else {
set_runtime_variable(&variable, "0");
}
}
let command_groups = ["cache"];
for command_group in command_groups {
set_exposed_command(
&commands_dir.join(command_group).join("cli.rs"),
command_group,
&indicator,
);
}
}

Expand Down
9 changes: 9 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## Unreleased

***Added:***

- Add `cache` management command

***Fixed:***

- The `pip` management command is now resilient to cache removal
- Management commands now properly support the `-h`/`--help` flag

## 0.19.0 - 2024-04-24

***Added:***
Expand Down
8 changes: 8 additions & 0 deletions docs/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ This will update the project to the latest available version in the currently us

These commands are hidden by default and each can be individually exposed by setting its corresponding `PYAPP_EXPOSE_<COMMAND>` option (e.g. `PYAPP_EXPOSE_METADATA`) to `true` or `1`.

#### Cache

```
<EXE> self cache [dist|pip|uv]
```

This is the command group for managing the cache. Each subcommand has a `-r`/`--remove` flag to remove the cached asset. Not passing that flag will display the location instead.

#### Metadata

```
Expand Down
9 changes: 8 additions & 1 deletion scripts/validate_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ def main():
available_options = set(re.findall(r'"(PYAPP_[^_].+?)"', Path('build.rs').read_text('utf-8')))
available_options -= IGNORED

root_commands = Path('src/commands/self_cmd')
expose_option = re.compile(r'^#\[command\(hide = env!\("(PYAPP_EXPOSE_.+?)"\)', re.MULTILINE)
for entry in Path('src/commands/self_cmd').iterdir():
for entry in root_commands.iterdir():
if entry.is_file() and (match := expose_option.search(entry.read_text('utf-8'))):
available_options.add(match.group(1))

command_groups = ['cache']
for command_group in command_groups:
path = root_commands / command_group / 'cli.rs'
if match := expose_option.search(path.read_text('utf-8')):
available_options.add(match.group(1))

expected_options = sorted(available_options)
if defined_options != expected_options:
left_padding = max(len(option) for option in expected_options)
Expand Down
27 changes: 27 additions & 0 deletions src/commands/self_cmd/cache/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use anyhow::Result;
use clap::{Args, Subcommand};

/// Manage the cache
#[derive(Args, Debug)]
#[command(hide = env!("PYAPP_EXPOSE_CACHE") == "0")]
pub struct Cli {
#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
Dist(super::dist::Cli),
Pip(super::pip::Cli),
Uv(super::uv::Cli),
}

impl Cli {
pub fn exec(self) -> Result<()> {
match self.command {
Commands::Dist(cli) => cli.exec(),
Commands::Pip(cli) => cli.exec(),
Commands::Uv(cli) => cli.exec(),
}
}
}
35 changes: 35 additions & 0 deletions src/commands/self_cmd/cache/dist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::fs;

use anyhow::Result;
use clap::Args;

use crate::app;

/// Manage the distribution cache
#[derive(Args, Debug)]
#[command()]
pub struct Cli {
#[arg(short, long)]
remove: bool,
}

impl Cli {
pub fn exec(self) -> Result<()> {
let distributions_dir = app::distributions_cache();
let distribution_file = distributions_dir.join(app::distribution_id());
if !distribution_file.exists() {
if self.remove {
println!("Does not exist");
}
return Ok(());
}

if self.remove {
fs::remove_file(distribution_file)?;
} else {
println!("{}", distribution_file.display());
}

Ok(())
}
}
4 changes: 4 additions & 0 deletions src/commands/self_cmd/cache/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod cli;
pub mod dist;
pub mod pip;
pub mod uv;
39 changes: 39 additions & 0 deletions src/commands/self_cmd/cache/pip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::fs;

use anyhow::Result;
use clap::Args;

use crate::app;

/// Manage the external pip cache
#[derive(Args, Debug)]
#[command()]
pub struct Cli {
#[arg(short, long)]
remove: bool,
}

impl Cli {
pub fn exec(self) -> Result<()> {
if !app::pip_external() {
println!("External pip not enabled");
return Ok(());
}

let external_pip = app::external_pip_zipapp();
if !external_pip.exists() {
if self.remove {
println!("Does not exist");
}
return Ok(());
}

if self.remove {
fs::remove_file(external_pip)?;
} else {
println!("{}", external_pip.display());
}

Ok(())
}
}
39 changes: 39 additions & 0 deletions src/commands/self_cmd/cache/uv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::fs;

use anyhow::Result;
use clap::Args;

use crate::app;

/// Manage the UV cache
#[derive(Args, Debug)]
#[command()]
pub struct Cli {
#[arg(short, long)]
remove: bool,
}

impl Cli {
pub fn exec(self) -> Result<()> {
if !app::uv_enabled() {
println!("UV is not enabled");
return Ok(());
}

let managed_uv = app::managed_uv();
if !managed_uv.exists() {
if self.remove {
println!("Does not exist");
}
return Ok(());
}

if self.remove {
fs::remove_file(managed_uv)?;
} else {
println!("{}", managed_uv.display());
}

Ok(())
}
}
4 changes: 3 additions & 1 deletion src/commands/self_cmd/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use clap::{Args, Subcommand};

/// Manage this application
#[derive(Args, Debug)]
#[command(disable_help_flag = true)]
#[command()]
pub struct Cli {
#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
Cache(super::cache::cli::Cli),
Metadata(super::metadata::Cli),
Pip(super::pip::Cli),
Python(super::python::Cli),
Expand All @@ -23,6 +24,7 @@ enum Commands {
impl Cli {
pub fn exec(self) -> Result<()> {
match self.command {
Commands::Cache(cli) => cli.exec(),
Commands::Metadata(cli) => cli.exec(),
Commands::Pip(cli) => cli.exec(),
Commands::Python(cli) => cli.exec(),
Expand Down
1 change: 1 addition & 0 deletions src/commands/self_cmd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod cache;
pub mod cli;
pub mod metadata;
pub mod pip;
Expand Down
1 change: 1 addition & 0 deletions src/commands/self_cmd/pip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct Cli {
impl Cli {
pub fn exec(self) -> Result<()> {
distribution::ensure_ready()?;
distribution::ensure_installer_available()?;

let mut command = distribution::pip_base_command();
command.args(self.args);
Expand Down
2 changes: 1 addition & 1 deletion src/distribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ fn ensure_base_pip(distribution_directory: &Path) -> Result<()> {
Ok(())
}

fn ensure_installer_available() -> Result<()> {
pub fn ensure_installer_available() -> Result<()> {
if app::uv_as_installer() {
ensure_uv_available()?;
} else if app::pip_external() {
Expand Down

0 comments on commit b6ffddd

Please sign in to comment.