feat: add licenses and refactor
Rust / test (push) Failing after 50s
Details
Rust / test (push) Failing after 50s
Details
This commit is contained in:
parent
f41157cab9
commit
e8ee999ece
41
.drone.yml
41
.drone.yml
|
@ -1,41 +0,0 @@
|
||||||
name: default
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
image: rust:1
|
|
||||||
commands:
|
|
||||||
- cargo run -- --help
|
|
||||||
|
|
||||||
- name: deploy:crate
|
|
||||||
image: rust:1
|
|
||||||
environment:
|
|
||||||
CARGO_REGISTRY_TOKEN:
|
|
||||||
from_secret: CARGO_REGISTRY_TOKEN
|
|
||||||
commands:
|
|
||||||
- cargo publish
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- tag
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
|
|
||||||
- name: deploy:wheel
|
|
||||||
image: rust:1
|
|
||||||
environment:
|
|
||||||
MATURIN_URL:
|
|
||||||
from_secret: MATURIN_URL
|
|
||||||
MATURIN_USERNAME:
|
|
||||||
from_secret: MATURIN_USERNAME
|
|
||||||
MATURIN_PASSWORD:
|
|
||||||
from_secret: MATURIN_PASSWORD
|
|
||||||
commands:
|
|
||||||
- maturin publish
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- tag
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-crate:
|
||||||
|
runs-on: debian-rust
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Publish crate
|
||||||
|
run: cargo publish
|
||||||
|
|
||||||
|
deploy-wheel:
|
||||||
|
runs-on: debian-rust
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Publish wheel
|
||||||
|
env:
|
||||||
|
MATURIN_REPOSITORY_URL: ${{ secrets.PIP_REPOSITORY }}
|
||||||
|
MATURIN_USERNAME: datahearth
|
||||||
|
MATURIN_PYPI_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
pip install maturin
|
||||||
|
maturin publish
|
|
@ -0,0 +1,22 @@
|
||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "main"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "main"
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: debian-rust
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --verbose
|
|
@ -1,2 +1,2 @@
|
||||||
/target
|
target
|
||||||
/my_project
|
demo_pynit
|
File diff suppressed because it is too large
Load Diff
|
@ -14,5 +14,6 @@ anyhow = "1.0"
|
||||||
clap = { version = "4.0", features = ["derive"] }
|
clap = { version = "4.0", features = ["derive"] }
|
||||||
dialoguer = "0.10"
|
dialoguer = "0.10"
|
||||||
regex = "1.7"
|
regex = "1.7"
|
||||||
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# pynit
|
# pynit
|
||||||
|
|
||||||
[![License](https://img.shields.io/crates/l/pynit)](https://gitea.antoine-langlois.net/DataHearth/pynit/src/branch/main/LICENSE)
|
[![License](https://img.shields.io/crates/l/pynit)](https://gitea.antoine-langlois.net/DataHearth/pynit/src/branch/main/LICENSE)
|
||||||
[![Build Status](https://drone.antoine-langlois.net/api/badges/DataHearth/pynit/status.svg?ref=refs/heads/main)](https://drone.antoine-langlois.net/DataHearth/pynit)
|
[![Version](https://img.shields.io/crates/v/pynit)](https://crates.io/crates/pynit)
|
||||||
[![Build Status](https://img.shields.io/crates/v/pynit)](https://crates.io/crates/pynit)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use dialoguer::Select;
|
||||||
|
use dialoguer::{theme::ColorfulTheme, Input};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub fn input<T>(theme: &ColorfulTheme, prompt: &str, empty: bool, default: Option<T>) -> Result<T>
|
||||||
|
where
|
||||||
|
T: Clone + FromStr + ToString,
|
||||||
|
<T as FromStr>::Err: Debug + ToString,
|
||||||
|
{
|
||||||
|
let mut input = Input::<T>::with_theme(theme);
|
||||||
|
|
||||||
|
input.with_prompt(prompt).allow_empty(empty);
|
||||||
|
if let Some(default) = default {
|
||||||
|
input.default(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(input.interact_text()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn input_list<F, T>(
|
||||||
|
theme: &ColorfulTheme,
|
||||||
|
prompt: &str,
|
||||||
|
empty: bool,
|
||||||
|
default: Option<String>,
|
||||||
|
map_fn: F,
|
||||||
|
) -> Result<Vec<T>>
|
||||||
|
where
|
||||||
|
F: FnMut(&str) -> T,
|
||||||
|
{
|
||||||
|
Ok(input(theme, prompt, empty, default)?
|
||||||
|
.split(';')
|
||||||
|
.filter(|v| !v.is_empty())
|
||||||
|
.map(map_fn)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(
|
||||||
|
theme: &ColorfulTheme,
|
||||||
|
prompt: &str,
|
||||||
|
default: usize,
|
||||||
|
items: &[String],
|
||||||
|
) -> Result<usize> {
|
||||||
|
Ok(Select::with_theme(theme)
|
||||||
|
.with_prompt(prompt)
|
||||||
|
.default(default)
|
||||||
|
.items(items)
|
||||||
|
.interact()?)
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use reqwest::{blocking::Client, header::USER_AGENT};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct LicenseDetails {
|
||||||
|
#[serde(flatten)]
|
||||||
|
base_license: License,
|
||||||
|
|
||||||
|
pub body: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct License {
|
||||||
|
pub spdx_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_license_spdx() -> Result<Vec<String>> {
|
||||||
|
let licenses = Client::new()
|
||||||
|
.get(&format!("https://api.github.com/licenses",))
|
||||||
|
.header(USER_AGENT, format!("pynit-{}", env!("CARGO_PKG_VERSION")))
|
||||||
|
.send()?
|
||||||
|
.json::<Vec<License>>()?;
|
||||||
|
|
||||||
|
Ok(licenses
|
||||||
|
.iter()
|
||||||
|
.map(|license| license.spdx_id.clone())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_license(spdx: String) -> Result<LicenseDetails> {
|
||||||
|
Ok(Client::new()
|
||||||
|
.get(&format!("https://api.github.com/licenses/{spdx}"))
|
||||||
|
.header(USER_AGENT, format!("pynit-{}", env!("CARGO_PKG_VERSION")))
|
||||||
|
.send()?
|
||||||
|
.json::<LicenseDetails>()?)
|
||||||
|
}
|
10
src/main.rs
10
src/main.rs
|
@ -7,8 +7,11 @@ use std::{
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use license::get_license;
|
||||||
use pyproject::Pyproject;
|
use pyproject::Pyproject;
|
||||||
|
|
||||||
|
mod components;
|
||||||
|
mod license;
|
||||||
mod pyproject;
|
mod pyproject;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||||
|
@ -81,8 +84,13 @@ fn initialize_folder(
|
||||||
let mut pypro = Pyproject::new(folder.clone(), complete);
|
let mut pypro = Pyproject::new(folder.clone(), complete);
|
||||||
|
|
||||||
pypro.ask_inputs()?;
|
pypro.ask_inputs()?;
|
||||||
let project_name = pypro.get_project_name();
|
|
||||||
|
|
||||||
|
let project_name = pypro.get_project_name();
|
||||||
|
|
||||||
|
fs::write(
|
||||||
|
folder.join("LICENSE"),
|
||||||
|
get_license(pypro.get_license_spdx())?.body,
|
||||||
|
)?;
|
||||||
pypro.create_file()?;
|
pypro.create_file()?;
|
||||||
|
|
||||||
if let Some(layout) = layout {
|
if let Some(layout) = layout {
|
||||||
|
|
171
src/pyproject.rs
171
src/pyproject.rs
|
@ -1,10 +1,15 @@
|
||||||
use std::{fs, path::PathBuf};
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use dialoguer::{theme::ColorfulTheme, Input};
|
use dialoguer::theme::ColorfulTheme;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{input, input_list, select},
|
||||||
|
license::get_license_spdx,
|
||||||
|
};
|
||||||
|
|
||||||
const PYPROJECT: &str = "pyproject.toml";
|
const PYPROJECT: &str = "pyproject.toml";
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -34,102 +39,87 @@ impl Pyproject {
|
||||||
pub fn ask_inputs(&mut self) -> Result<()> {
|
pub fn ask_inputs(&mut self) -> Result<()> {
|
||||||
let theme = ColorfulTheme::default();
|
let theme = ColorfulTheme::default();
|
||||||
|
|
||||||
self.build_system.requires = Input::<String>::with_theme(&theme)
|
self.build_system.requires = input_list(
|
||||||
.with_prompt("build dependencies (comma separated)")
|
&theme,
|
||||||
.default("setuptools,wheel".to_string())
|
"build dependencies (comma separated)",
|
||||||
.interact_text()?
|
false,
|
||||||
.split(',')
|
Some("setuptools,wheel".to_string()),
|
||||||
.filter(|v| !v.is_empty())
|
|v| v.to_string(),
|
||||||
.map(|v| v.to_string())
|
)?;
|
||||||
.collect();
|
self.build_system.build_backend = input::<String>(
|
||||||
self.build_system.build_backend = Input::with_theme(&theme)
|
&theme,
|
||||||
.with_prompt("build back-end")
|
"build back-end",
|
||||||
.default("setuptools.build_meta".to_string())
|
false,
|
||||||
.interact_text()?;
|
Some("setuptools.build_meta".to_string()),
|
||||||
|
)?;
|
||||||
|
|
||||||
// ? might want to switch to OsString instead, if the Serialize macro supports it
|
// ? might want to switch to OsString instead, if the Serialize macro supports it
|
||||||
let folder = match self
|
self.project.name = input::<String>(
|
||||||
.folder
|
&theme,
|
||||||
.file_name()
|
"project name",
|
||||||
.ok_or(anyhow!("project can't terminate by \"..\""))?
|
false,
|
||||||
.to_str()
|
match self
|
||||||
{
|
.folder
|
||||||
Some(v) => Some(v.to_string()),
|
.file_name()
|
||||||
None => None,
|
.ok_or(anyhow!("project can't terminate by \"..\""))?
|
||||||
};
|
.to_str()
|
||||||
let mut input: Input<String> = Input::with_theme(&theme);
|
{
|
||||||
if let Some(folder) = folder {
|
Some(v) => Some(v.to_string()),
|
||||||
self.project.name = input
|
None => None,
|
||||||
.with_prompt("project name")
|
},
|
||||||
.default(folder)
|
)?;
|
||||||
.interact_text()?;
|
self.project.version =
|
||||||
} else {
|
input::<String>(&theme, "version", false, Some("0.1.0".to_string()))?;
|
||||||
self.project.name = input.with_prompt("project name").interact_text()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.project.version = Input::with_theme(&theme)
|
|
||||||
.with_prompt("version")
|
|
||||||
.default("0.1.0".to_string())
|
|
||||||
.interact_text()?;
|
|
||||||
|
|
||||||
if self.complete {
|
if self.complete {
|
||||||
self.project.description = Input::with_theme(&theme)
|
self.ask_complete(&theme)?;
|
||||||
.with_prompt("description")
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?;
|
|
||||||
self.project.readme = Input::with_theme(&theme)
|
|
||||||
.with_prompt("readme")
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?;
|
|
||||||
self.project.requires_python = Input::with_theme(&theme)
|
|
||||||
.with_prompt("minimum python version")
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?;
|
|
||||||
self.project.license = Input::with_theme(&theme)
|
|
||||||
.with_prompt("license")
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?;
|
|
||||||
self.project.authors = Input::<String>::with_theme(&theme)
|
|
||||||
.with_prompt(
|
|
||||||
r#"authors (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
|
||||||
)
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?
|
|
||||||
.split(';')
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.map(|v| self.parse_contributor(v))
|
|
||||||
.collect();
|
|
||||||
self.project.maintainers = Input::<String>::with_theme(&theme)
|
|
||||||
.with_prompt(
|
|
||||||
r#"maintainers (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
|
||||||
)
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?
|
|
||||||
.split(';')
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.map(|v| self.parse_contributor(v))
|
|
||||||
.collect();
|
|
||||||
self.project.keywords = Input::<String>::with_theme(&theme)
|
|
||||||
.with_prompt("keywords (e.g: KEYW1;KEYW2)")
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?
|
|
||||||
.split(';')
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.map(|v| v.to_string())
|
|
||||||
.collect();
|
|
||||||
self.project.classifiers = Input::<String>::with_theme(&theme)
|
|
||||||
.with_prompt("classifiers (e.g: CLASS1;CLASS2)")
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?
|
|
||||||
.split(';')
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.map(|v| v.to_string())
|
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ask_complete(&mut self, theme: &ColorfulTheme) -> Result<()> {
|
||||||
|
self.project.description = input::<String>(theme, "description", true, None)?;
|
||||||
|
self.project.readme = input::<String>(theme, "readme", true, None)?;
|
||||||
|
self.project.requires_python =
|
||||||
|
input::<String>(theme, "minimum python version", true, None)?;
|
||||||
|
let license_spdx = get_license_spdx()?;
|
||||||
|
let license_index = select(
|
||||||
|
theme,
|
||||||
|
"license",
|
||||||
|
license_spdx
|
||||||
|
.binary_search(&"MIT".into())
|
||||||
|
.or(Err(anyhow!("MIT license not found")))?,
|
||||||
|
&license_spdx[..],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.project.license = license_spdx[license_index].clone();
|
||||||
|
self.project.authors = input_list(
|
||||||
|
theme,
|
||||||
|
r#"authors (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
|v| self.parse_contributor(v),
|
||||||
|
)?;
|
||||||
|
self.project.maintainers = input_list(
|
||||||
|
theme,
|
||||||
|
r#"maintainers (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
|v| self.parse_contributor(v),
|
||||||
|
)?;
|
||||||
|
self.project.keywords =
|
||||||
|
input_list(theme, "keywords (e.g: KEYW1;KEYW2)", true, None, |v| {
|
||||||
|
v.to_string()
|
||||||
|
})?;
|
||||||
|
self.project.classifiers =
|
||||||
|
input_list(theme, "classifiers (e.g: CLASS1;CLASS2)", true, None, |v| {
|
||||||
|
v.to_string()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_contributor(&self, contributor: &str) -> Contributor {
|
fn parse_contributor(&self, contributor: &str) -> Contributor {
|
||||||
let name_regex =
|
let name_regex =
|
||||||
Regex::new(r#"name="([\w\s\-\.]*)""#).expect("invalid name regex expression");
|
Regex::new(r#"name="([\w\s\-\.]*)""#).expect("invalid name regex expression");
|
||||||
|
@ -157,6 +147,11 @@ impl Pyproject {
|
||||||
// ? clone or maybe something else ?
|
// ? clone or maybe something else ?
|
||||||
self.project.name.clone()
|
self.project.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_license_spdx(&self) -> String {
|
||||||
|
self.project.license.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Consume self and write everything to a `self.folder/pyproject.toml`
|
/// Consume self and write everything to a `self.folder/pyproject.toml`
|
||||||
pub fn create_file(self) -> Result<()> {
|
pub fn create_file(self) -> Result<()> {
|
||||||
fs::write(self.folder.join(PYPROJECT), toml::to_vec(&self)?)?;
|
fs::write(self.folder.join(PYPROJECT), toml::to_vec(&self)?)?;
|
||||||
|
|
Loading…
Reference in New Issue