Skip to content

Commit

Permalink
Add rye init option to set mercurial VCS for new project.
Browse files Browse the repository at this point in the history
Usage:
```
rye init --vcs [git | mercurial]
```
Default can be configured in ~/.rye/config.toml
```
[default]
vcs = "mercurial"
```
When not specified, or when git specified, current behavior is preserved.
When mercurial is specified, a mercurial VCS is initialized unless the WD is already in a mercurial VCS, and a hgignore file is created.
  • Loading branch information
adenyes committed Jul 30, 2024
1 parent 0da8f3f commit 665e2a5
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 100 deletions.
63 changes: 26 additions & 37 deletions rye/src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ use tempfile::tempdir;
use crate::bootstrap::ensure_self_venv;
use crate::config::Config;
use crate::platform::{
get_default_author_with_fallback, get_latest_cpython_version, get_pinnable_version,
get_python_version_request_from_pyenv_pin,
get_latest_cpython_version, get_pinnable_version, get_python_version_request_from_pyenv_pin,
};
use crate::pyproject::BuildSystem;
use crate::sources::py::PythonVersionRequest;
use crate::utils::{
copy_dir, escape_string, format_requirement, get_venv_python_bin, is_inside_git_work_tree,
CommandOutput, CopyDirOptions, IoPathContext,
copy_dir, escape_string, format_requirement, get_venv_python_bin, CommandOutput,
CopyDirOptions, IoPathContext,
};
use crate::vcs::ProjectVCS;

/// Initialize a new or existing Python project with Rye.
#[derive(Parser, Debug)]
Expand Down Expand Up @@ -83,6 +83,10 @@ pub struct Args {
/// Turns off all output.
#[arg(short, long, conflicts_with = "verbose")]
quiet: bool,

/// Which VCS should be used? (defaults to git)
#[arg(long)]
vcs: Option<ProjectVCS>,
}

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -128,9 +132,6 @@ const RUST_INIT_PY_TEMPLATE: &str = include_str!("../templates/lib/maturin/__ini
/// Template for the Cargo.toml.
const CARGO_TOML_TEMPLATE: &str = include_str!("../templates/lib/maturin/Cargo.toml.j2");

/// Template for fresh gitignore files.
const GITIGNORE_TEMPLATE: &str = include_str!("../templates/gitignore.j2");

/// Script used for setup.py setup proxy.
const SETUP_PY_PROXY_SCRIPT: &str = r#"
import json, sys
Expand Down Expand Up @@ -199,8 +200,13 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
.unwrap_or_else(|| "unknown".into())
}));

let project_vcs = match cmd.vcs {
Some(project_vcs) => project_vcs,
None => cfg.default_vcs().unwrap_or(ProjectVCS::Git),
};

let version = "0.1.0";
let author = get_default_author_with_fallback(&dir);
let author = flat_author(project_vcs.get_author(&dir, cfg.default_author()));
let license = match cmd.license {
Some(license) => Some(license),
None => cfg.default_license(),
Expand Down Expand Up @@ -326,36 +332,10 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
name_safe.insert(0, '_');
}

// if git init is successful prepare the local git repository
if !is_inside_git_work_tree(&dir)
&& Command::new("git")
.arg("init")
.current_dir(&dir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|status| status.success())
.unwrap_or(false)
&& is_metadata_author_none
if !project_vcs.inside_work_tree(&dir) && project_vcs.init_dir(&dir) && is_metadata_author_none
{
let new_author = get_default_author_with_fallback(&dir);
if author != new_author {
metadata.author = new_author;
}
}

let gitignore = dir.join(".gitignore");

// create a .gitignore if one is missing
if !gitignore.is_file() {
let rv = env.render_named_str(
"gitignore.txt",
GITIGNORE_TEMPLATE,
context! {
is_rust => matches!(build_system, BuildSystem::Maturin)
},
)?;
fs::write(&gitignore, rv).path_context(&gitignore, "failed to write .gitignore")?;
metadata.author = flat_author(project_vcs.get_author(&dir, cfg.default_author()));
project_vcs.render_templates(&dir, &env, build_system)?;
}

let rv = env.render_named_str(
Expand Down Expand Up @@ -457,6 +437,15 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
Ok(())
}

fn flat_author(author: (Option<String>, Option<String>)) -> Option<(String, String)> {
match author {
(Some(name), Some(email)) => Some((name, email)),
(Some(name), None) => Some((name, "[email protected]".to_string())),
(None, Some(email)) => Some(("Unknown".to_string(), email)),
_ => None,
}
}

#[derive(Default)]
struct Metadata {
name: Option<String>,
Expand Down
13 changes: 13 additions & 0 deletions rye/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::platform::{get_app_dir, get_latest_cpython_version};
use crate::pyproject::{BuildSystem, SourceRef, SourceRefType};
use crate::sources::py::PythonVersionRequest;
use crate::utils::{toml, IoPathContext};
use crate::vcs::ProjectVCS;

static CONFIG: Mutex<Option<Arc<Config>>> = Mutex::new(None);
static AUTHOR_REGEX: Lazy<Regex> =
Expand Down Expand Up @@ -126,6 +127,18 @@ impl Config {
}
}

pub fn default_vcs(&self) -> Option<ProjectVCS> {
match self
.doc
.get("default")
.and_then(|x| x.get("vcs"))
.and_then(|x| x.as_str())
{
Some(vcs) => vcs.parse::<ProjectVCS>().ok(),
None => None,
}
}

/// Returns the default license
pub fn default_license(&self) -> Option<String> {
self.doc
Expand Down
1 change: 1 addition & 0 deletions rye/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod sources;
mod sync;
mod utils;
mod uv;
mod vcs;

static SHOW_CONTINUE_PROMPT: AtomicBool = AtomicBool::new(false);
static DISABLE_CTRLC_HANDLER: AtomicBool = AtomicBool::new(false);
Expand Down
35 changes: 0 additions & 35 deletions rye/src/platform.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::Mutex;
use std::{env, fs};

use anyhow::{anyhow, Context, Error};

use crate::config::Config;
use crate::pyproject::latest_available_python_version;
use crate::sources::py::{PythonVersion, PythonVersionRequest};
use crate::utils::IoPathContext;
Expand Down Expand Up @@ -177,39 +175,6 @@ pub fn list_known_toolchains() -> Result<Vec<(PythonVersion, PathBuf)>, Error> {
Ok(rv)
}

/// Returns the default author from git or the config.
pub fn get_default_author_with_fallback(dir: &PathBuf) -> Option<(String, String)> {
let (mut name, mut email) = Config::current().default_author();
let is_name_none = name.is_none();
let is_email_none = email.is_none();

if let Ok(rv) = Command::new("git")
.arg("config")
.arg("--get-regexp")
.current_dir(dir)
.arg("^user.(name|email)$")
.stdout(Stdio::piped())
.output()
{
for line in std::str::from_utf8(&rv.stdout).ok()?.lines() {
match line.split_once(' ') {
Some((key, value)) if key == "user.email" && is_email_none => {
email = Some(value.to_string());
}
Some((key, value)) if key == "user.name" && is_name_none => {
name = Some(value.to_string());
}
_ => {}
}
}
}

Some((
name?,
email.unwrap_or_else(|| "[email protected]".into()),
))
}

/// Reads the current `.python-version` file.
pub fn get_python_version_request_from_pyenv_pin(root: &Path) -> Option<PythonVersionRequest> {
let mut here = root.to_owned();
Expand Down
15 changes: 15 additions & 0 deletions rye/src/templates/hgignore.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# python generated files
__pycache__/
.*\.py[oc]
build/
dist/
wheels/
.*\.egg-info

{%- if is_rust %}
# Rust
target/
{%- endif %}

# venv
\.venv
28 changes: 0 additions & 28 deletions rye/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,18 +392,6 @@ pub fn get_venv_python_bin(venv_path: &Path) -> PathBuf {
py
}

pub fn is_inside_git_work_tree(dir: &PathBuf) -> bool {
Command::new("git")
.arg("rev-parse")
.arg("--is-inside-work-tree")
.current_dir(dir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|status| status.success())
.unwrap_or(false)
}

/// Returns a success exit status.
pub fn success_status() -> ExitStatus {
#[cfg(windows)]
Expand Down Expand Up @@ -576,19 +564,3 @@ mod test_expand_env_vars {
assert_eq!("This string has an env var: Example value", output);
}
}

#[cfg(test)]
mod test_is_inside_git_work_tree {
use std::path::PathBuf;

use super::is_inside_git_work_tree;
#[test]
fn test_is_inside_git_work_tree_true() {
assert!(is_inside_git_work_tree(&PathBuf::from(".")));
}

#[test]
fn test_is_inside_git_work_tree_false() {
assert!(!is_inside_git_work_tree(&PathBuf::from("/")));
}
}
Loading

0 comments on commit 665e2a5

Please sign in to comment.