Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rye init --vcs option to set version control system. Defaults to git, adds mercurial and none options. #1289

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
uses: taiki-e/install-action@v2
with:
tool: cargo-insta
- name: Setup Mercurial (macos)
run: brew install mercurial
- name: Check
run: make check
- name: Test
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ success: Initialized project in /Users/john/Development/my-project.

[possible values: `hatchling`, `setuptools`, `flit`, `pdm`, `maturin`]

* `--vcs <PROJECT_VCS>`: Which version control system should be used (defaults to git)?

[possible values: `git`. `mercurial`, `none`]

* `--license <LICENSE>`: Which license should be used? [SPDX identifier](https://spdx.org/licenses/)

* `--name <NAME>`: The name of the package
Expand Down
3 changes: 3 additions & 0 deletions docs/guide/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ toolchain = "[email protected]"
# This is the default build system that is used
build-system = "hatchling"

# This is the default version control system that is used
vcs = "git"

# This is the default license that is used
license = "MIT"

Expand Down
62 changes: 25 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,9 @@ 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 +131,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 +199,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 +331,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 +436,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 @@ -18,6 +18,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
32 changes: 3 additions & 29 deletions rye/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use std::convert::Infallible;
use std::ffi::OsString;
use std::io::{Cursor, Read};
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus, Stdio};
#[cfg(windows)]
use std::process::Stdio;
use std::process::{Command, ExitStatus};
use std::{fmt, fs};

use anyhow::{anyhow, bail, Context, Error};
Expand Down Expand Up @@ -392,18 +394,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 +566,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