mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-12 11:00:36 +00:00
feat: implement patching
This commit is contained in:
parent
d4371519c2
commit
5661194721
14 changed files with 479 additions and 37 deletions
80
Cargo.lock
generated
80
Cargo.lock
generated
|
@ -1081,6 +1081,21 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gix"
|
||||
version = "0.63.0"
|
||||
|
@ -2271,6 +2286,20 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.17.0+1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libssh2-sys",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
|
@ -2281,6 +2310,32 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-keyutils"
|
||||
version = "0.2.4"
|
||||
|
@ -2553,6 +2608,24 @@ dependencies = [
|
|||
"pathdiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
|
@ -2638,6 +2711,7 @@ dependencies = [
|
|||
"directories",
|
||||
"flate2",
|
||||
"full_moon",
|
||||
"git2",
|
||||
"gix",
|
||||
"indicatif",
|
||||
"indicatif-log-bridge",
|
||||
|
@ -3766,6 +3840,12 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
|
|
@ -30,6 +30,8 @@ toml = "0.8.15"
|
|||
serde_json = "1.0.120"
|
||||
serde_with = "3.9.0"
|
||||
gix = { version = "0.63.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials"] }
|
||||
# TODO: remove this when gitoxide adds support for: committing, pushing, adding
|
||||
git2 = "0.19.0"
|
||||
semver = { version = "1.0.23", features = ["serde"] }
|
||||
reqwest = { version = "0.12.5", default-features = false, features = ["rustls-tls", "blocking"] }
|
||||
tar = "0.4.41"
|
||||
|
|
|
@ -68,19 +68,11 @@ impl InitCommand {
|
|||
.prompt()
|
||||
.unwrap();
|
||||
|
||||
let authors = authors
|
||||
authors
|
||||
.split(',')
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.into())
|
||||
.collect::<Vec<toml_edit::Value>>();
|
||||
|
||||
if !authors.is_empty() {
|
||||
let mut authors_arr = toml_edit::Array::new();
|
||||
authors_arr.extend(authors);
|
||||
|
||||
manifest["authors"] = toml_edit::value(authors_arr);
|
||||
}
|
||||
.for_each(|author| manifest["authors"].as_array_mut().unwrap().push(author));
|
||||
|
||||
let repo = inquire::Text::new(
|
||||
"What is the repository URL of this project? (leave empty for none)",
|
||||
|
@ -124,8 +116,7 @@ impl InitCommand {
|
|||
.prompt()
|
||||
.unwrap();
|
||||
|
||||
let mut target = toml_edit::Table::new();
|
||||
target["environment"] = toml_edit::value(target_env);
|
||||
manifest["target"]["environment"] = toml_edit::value(target_env);
|
||||
|
||||
if target_env == "roblox"
|
||||
|| inquire::Confirm::new(&format!(
|
||||
|
@ -147,21 +138,14 @@ impl InitCommand {
|
|||
)
|
||||
.context("failed to write script file")?;
|
||||
|
||||
let scripts = manifest
|
||||
.entry("scripts")
|
||||
.or_insert(toml_edit::Item::Table(toml_edit::Table::new()))
|
||||
.as_table_mut()
|
||||
.unwrap();
|
||||
|
||||
scripts[&ScriptName::RobloxSyncConfigGenerator.to_string()] =
|
||||
manifest["scripts"][&ScriptName::RobloxSyncConfigGenerator.to_string()] =
|
||||
toml_edit::value(format!(
|
||||
concat!(".", env!("CARGO_PKG_NAME"), "/{}.luau"),
|
||||
ScriptName::RobloxSyncConfigGenerator
|
||||
));
|
||||
}
|
||||
|
||||
let mut indices = toml_edit::Table::new();
|
||||
indices[DEFAULT_INDEX_NAME] =
|
||||
manifest["indices"][DEFAULT_INDEX_NAME] =
|
||||
toml_edit::value(read_config(project.data_dir())?.default_index.as_str());
|
||||
|
||||
project.write_manifest(manifest.to_string())?;
|
||||
|
|
|
@ -97,6 +97,10 @@ impl InstallCommand {
|
|||
.link_dependencies(&downloaded_graph)
|
||||
.context("failed to link dependencies")?;
|
||||
|
||||
project
|
||||
.apply_patches(&downloaded_graph)
|
||||
.context("failed to apply patches")?;
|
||||
|
||||
project
|
||||
.write_lockfile(Lockfile {
|
||||
name: manifest.name,
|
||||
|
|
|
@ -3,14 +3,16 @@ use anyhow::Context;
|
|||
use gix::remote::Direction;
|
||||
use indicatif::MultiProgress;
|
||||
use keyring::Entry;
|
||||
use pesde::Project;
|
||||
use pesde::{lockfile::DownloadedGraph, names::PackageNames, source::VersionId, Project};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashSet, path::Path};
|
||||
use std::{collections::HashSet, path::Path, str::FromStr};
|
||||
|
||||
mod auth;
|
||||
mod config;
|
||||
mod init;
|
||||
mod install;
|
||||
mod patch;
|
||||
mod patch_commit;
|
||||
mod publish;
|
||||
mod run;
|
||||
mod self_install;
|
||||
|
@ -280,6 +282,48 @@ impl IsUpToDate for Project {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct VersionedPackageName(PackageNames, Option<VersionId>);
|
||||
|
||||
impl FromStr for VersionedPackageName {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut parts = s.splitn(2, '@');
|
||||
let name = parts.next().unwrap();
|
||||
let version = parts.next().map(VersionId::from_str).transpose()?;
|
||||
|
||||
Ok(VersionedPackageName(name.parse()?, version))
|
||||
}
|
||||
}
|
||||
|
||||
impl VersionedPackageName {
|
||||
fn get(self, graph: &DownloadedGraph) -> anyhow::Result<(PackageNames, VersionId)> {
|
||||
let version_id = match self.1 {
|
||||
Some(version) => version,
|
||||
None => {
|
||||
let versions = graph.get(&self.0).context("package not found in graph")?;
|
||||
if versions.len() == 1 {
|
||||
let version = versions.keys().next().unwrap().clone();
|
||||
log::debug!("only one version found, using {version}");
|
||||
version
|
||||
} else {
|
||||
anyhow::bail!(
|
||||
"multiple versions found, please specify one of: {}",
|
||||
versions
|
||||
.keys()
|
||||
.map(|v| v.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok((self.0, version_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Subcommand {
|
||||
/// Authentication-related commands
|
||||
|
@ -304,6 +348,12 @@ pub enum Subcommand {
|
|||
|
||||
/// Installs the pesde binary and scripts
|
||||
SelfInstall(self_install::SelfInstallCommand),
|
||||
|
||||
/// Sets up a patching environment for a package
|
||||
Patch(patch::PatchCommand),
|
||||
|
||||
/// Finalizes a patching environment for a package
|
||||
PatchCommit(patch_commit::PatchCommitCommand),
|
||||
}
|
||||
|
||||
impl Subcommand {
|
||||
|
@ -316,6 +366,8 @@ impl Subcommand {
|
|||
Subcommand::Install(install) => install.run(project, multi),
|
||||
Subcommand::Publish(publish) => publish.run(project),
|
||||
Subcommand::SelfInstall(self_install) => self_install.run(project),
|
||||
Subcommand::Patch(patch) => patch.run(project),
|
||||
Subcommand::PatchCommit(patch_commit) => patch_commit.run(project),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
71
src/cli/patch.rs
Normal file
71
src/cli/patch.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use crate::cli::{reqwest_client, IsUpToDate, VersionedPackageName};
|
||||
use anyhow::Context;
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
use pesde::{
|
||||
patches::setup_patches_repo,
|
||||
source::{PackageRef, PackageSource},
|
||||
Project, MANIFEST_FILE_NAME,
|
||||
};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct PatchCommand {
|
||||
/// The package name to patch
|
||||
#[arg(index = 1)]
|
||||
package: VersionedPackageName,
|
||||
}
|
||||
|
||||
impl PatchCommand {
|
||||
pub fn run(self, project: Project) -> anyhow::Result<()> {
|
||||
let graph = if project.is_up_to_date(true)? {
|
||||
project.deser_lockfile()?.graph
|
||||
} else {
|
||||
anyhow::bail!("outdated lockfile, please run the install command first")
|
||||
};
|
||||
|
||||
let (name, version_id) = self.package.get(&graph)?;
|
||||
|
||||
let node = graph
|
||||
.get(&name)
|
||||
.and_then(|versions| versions.get(&version_id))
|
||||
.context("package not found in graph")?;
|
||||
let source = node.node.pkg_ref.source();
|
||||
|
||||
let directory = project
|
||||
.data_dir()
|
||||
.join("patches")
|
||||
.join(name.escaped())
|
||||
.join(version_id.escaped())
|
||||
.join(chrono::Utc::now().timestamp().to_string());
|
||||
std::fs::create_dir_all(&directory)?;
|
||||
|
||||
source.download(
|
||||
&node.node.pkg_ref,
|
||||
&directory,
|
||||
&project,
|
||||
&reqwest_client(project.data_dir())?,
|
||||
)?;
|
||||
|
||||
// TODO: if MANIFEST_FILE_NAME does not exist, try to convert it
|
||||
|
||||
setup_patches_repo(&directory)?;
|
||||
|
||||
println!(
|
||||
concat!(
|
||||
"done! modify the files in the directory, then run `",
|
||||
env!("CARGO_BIN_NAME"),
|
||||
r#" patch-commit {}` to apply.
|
||||
{}: do not commit these changes
|
||||
{}: the {} file will be ignored when patching"#
|
||||
),
|
||||
directory.display().to_string().bold().cyan(),
|
||||
"warning".yellow(),
|
||||
"note".blue(),
|
||||
MANIFEST_FILE_NAME
|
||||
);
|
||||
|
||||
open::that(directory)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
77
src/cli/patch_commit.rs
Normal file
77
src/cli/patch_commit.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use crate::cli::IsUpToDate;
|
||||
use anyhow::Context;
|
||||
use clap::Args;
|
||||
use pesde::{
|
||||
manifest::Manifest, names::PackageNames, patches::create_patch, source::VersionId, Project,
|
||||
MANIFEST_FILE_NAME,
|
||||
};
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct PatchCommitCommand {
|
||||
/// The directory containing the patch to commit
|
||||
#[arg(index = 1)]
|
||||
directory: PathBuf,
|
||||
}
|
||||
|
||||
impl PatchCommitCommand {
|
||||
pub fn run(self, project: Project) -> anyhow::Result<()> {
|
||||
let graph = if project.is_up_to_date(true)? {
|
||||
project.deser_lockfile()?.graph
|
||||
} else {
|
||||
anyhow::bail!("outdated lockfile, please run the install command first")
|
||||
};
|
||||
|
||||
let (name, version_id) = {
|
||||
let patched_manifest = std::fs::read_to_string(self.directory.join(MANIFEST_FILE_NAME))
|
||||
.context("failed to read patched manifest")?;
|
||||
let patched_manifest: Manifest =
|
||||
toml::from_str(&patched_manifest).context("failed to parse patched manifest")?;
|
||||
|
||||
(
|
||||
PackageNames::Pesde(patched_manifest.name),
|
||||
VersionId::new(patched_manifest.version, patched_manifest.target.kind()),
|
||||
)
|
||||
};
|
||||
|
||||
graph
|
||||
.get(&name)
|
||||
.and_then(|versions| versions.get(&version_id))
|
||||
.context("package not found in graph")?;
|
||||
|
||||
let mut manifest = toml_edit::DocumentMut::from_str(
|
||||
&project.read_manifest().context("failed to read manifest")?,
|
||||
)
|
||||
.context("failed to parse manifest")?;
|
||||
|
||||
let patch = create_patch(&self.directory).context("failed to create patch")?;
|
||||
std::fs::remove_dir_all(self.directory).context("failed to remove patch directory")?;
|
||||
|
||||
let patches_dir = project.path().join("patches");
|
||||
std::fs::create_dir_all(&patches_dir).context("failed to create patches directory")?;
|
||||
|
||||
let patch_file_name = format!("{}-{}.patch", name.escaped(), version_id.escaped(),);
|
||||
|
||||
let patch_file = patches_dir.join(&patch_file_name);
|
||||
if patch_file.exists() {
|
||||
anyhow::bail!("patch file already exists: {}", patch_file.display());
|
||||
}
|
||||
|
||||
std::fs::write(&patch_file, patch).context("failed to write patch file")?;
|
||||
|
||||
manifest["patches"][&name.to_string()][&version_id.to_string()] =
|
||||
toml_edit::value(format!("patches/{patch_file_name}"));
|
||||
|
||||
project
|
||||
.write_manifest(manifest.to_string())
|
||||
.context("failed to write manifest")?;
|
||||
|
||||
println!(concat!(
|
||||
"done! run `",
|
||||
env!("CARGO_BIN_NAME"),
|
||||
" install` to apply the patch"
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
lockfile::{DependencyGraph, DownloadedDependencyGraphNode, DownloadedGraph},
|
||||
source::{pesde::PesdePackageSource, PackageRefs, PackageSource, PackageSources},
|
||||
source::{PackageRef, PackageSource, PackageSources},
|
||||
Project, PACKAGES_CONTAINER_NAME,
|
||||
};
|
||||
|
||||
|
@ -33,11 +33,7 @@ impl Project {
|
|||
|
||||
for (name, versions) in graph {
|
||||
for (version_id, node) in versions {
|
||||
let source = match &node.pkg_ref {
|
||||
PackageRefs::Pesde(pkg_ref) => {
|
||||
PackageSources::Pesde(PesdePackageSource::new(pkg_ref.index_url.clone()))
|
||||
}
|
||||
};
|
||||
let source = node.pkg_ref.source();
|
||||
|
||||
if refreshed_sources.insert(source.clone()) {
|
||||
source.refresh(self).map_err(Box::new)?;
|
||||
|
|
|
@ -11,6 +11,7 @@ pub mod linking;
|
|||
pub mod lockfile;
|
||||
pub mod manifest;
|
||||
pub mod names;
|
||||
pub mod patches;
|
||||
pub mod resolver;
|
||||
pub mod scripts;
|
||||
pub mod source;
|
||||
|
@ -87,9 +88,9 @@ impl Project {
|
|||
&self.auth_config
|
||||
}
|
||||
|
||||
pub fn read_manifest(&self) -> Result<Vec<u8>, errors::ManifestReadError> {
|
||||
let bytes = std::fs::read(self.path.join(MANIFEST_FILE_NAME))?;
|
||||
Ok(bytes)
|
||||
pub fn read_manifest(&self) -> Result<String, errors::ManifestReadError> {
|
||||
let string = std::fs::read_to_string(self.path.join(MANIFEST_FILE_NAME))?;
|
||||
Ok(string)
|
||||
}
|
||||
|
||||
pub fn deser_manifest(&self) -> Result<manifest::Manifest, errors::ManifestReadError> {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use crate::{names::PackageName, source::DependencySpecifiers};
|
||||
use crate::{
|
||||
names::{PackageName, PackageNames},
|
||||
source::{DependencySpecifiers, VersionId},
|
||||
};
|
||||
use relative_path::RelativePathBuf;
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -251,6 +254,8 @@ pub struct Manifest {
|
|||
pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>,
|
||||
#[serde(default)]
|
||||
pub includes: BTreeSet<String>,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub patches: BTreeMap<PackageNames, BTreeMap<VersionId, RelativePathBuf>>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub dependencies: BTreeMap<String, DependencySpecifiers>,
|
||||
|
|
25
src/names.rs
25
src/names.rs
|
@ -1,6 +1,5 @@
|
|||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -69,8 +68,9 @@ impl PackageName {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[serde(untagged)]
|
||||
#[derive(
|
||||
Debug, DeserializeFromStr, SerializeDisplay, Clone, Hash, PartialEq, Eq, PartialOrd, Ord,
|
||||
)]
|
||||
pub enum PackageNames {
|
||||
Pesde(PackageName),
|
||||
}
|
||||
|
@ -97,6 +97,18 @@ impl Display for PackageNames {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for PackageNames {
|
||||
type Err = errors::PackageNamesError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(name) = PackageName::from_str(s) {
|
||||
Ok(PackageNames::Pesde(name))
|
||||
} else {
|
||||
Err(errors::PackageNamesError::InvalidPackageName(s.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -119,4 +131,11 @@ pub mod errors {
|
|||
#[error("package {0} `{1}` is not within 3-32 characters long")]
|
||||
InvalidLength(ErrorReason, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum PackageNamesError {
|
||||
#[error("invalid package name {0}")]
|
||||
InvalidPackageName(String),
|
||||
}
|
||||
}
|
||||
|
|
132
src/patches.rs
Normal file
132
src/patches.rs
Normal file
|
@ -0,0 +1,132 @@
|
|||
use crate::{lockfile::DownloadedGraph, Project, MANIFEST_FILE_NAME, PACKAGES_CONTAINER_NAME};
|
||||
use git2::{ApplyLocation, Diff, DiffFormat, DiffLineType, Repository, Signature};
|
||||
use std::{fs::read, path::Path};
|
||||
|
||||
pub fn setup_patches_repo<P: AsRef<Path>>(dir: P) -> Result<Repository, git2::Error> {
|
||||
let repo = Repository::init(&dir)?;
|
||||
|
||||
{
|
||||
let signature = Signature::now(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
concat!(env!("CARGO_PKG_NAME"), "@localhost"),
|
||||
)?;
|
||||
let mut index = repo.index()?;
|
||||
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
|
||||
index.write()?;
|
||||
|
||||
let oid = index.write_tree()?;
|
||||
let tree = repo.find_tree(oid)?;
|
||||
|
||||
repo.commit(
|
||||
Some("HEAD"),
|
||||
&signature,
|
||||
&signature,
|
||||
"begin patch",
|
||||
&tree,
|
||||
&[],
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(repo)
|
||||
}
|
||||
|
||||
pub fn create_patch<P: AsRef<Path>>(dir: P) -> Result<Vec<u8>, git2::Error> {
|
||||
let mut patches = vec![];
|
||||
let repo = Repository::open(dir.as_ref())?;
|
||||
|
||||
let original = repo.head()?.peel_to_tree()?;
|
||||
|
||||
// reset the manifest file to the original state
|
||||
let mut checkout_builder = git2::build::CheckoutBuilder::new();
|
||||
checkout_builder.force();
|
||||
checkout_builder.path(MANIFEST_FILE_NAME);
|
||||
repo.checkout_tree(original.as_object(), Some(&mut checkout_builder))?;
|
||||
|
||||
let diff = repo.diff_tree_to_workdir(Some(&original), None)?;
|
||||
|
||||
diff.print(DiffFormat::Patch, |_delta, _hunk, line| {
|
||||
if matches!(
|
||||
line.origin_value(),
|
||||
DiffLineType::Context | DiffLineType::Addition | DiffLineType::Deletion
|
||||
) {
|
||||
let origin = line.origin();
|
||||
let mut buffer = vec![0; origin.len_utf8()];
|
||||
origin.encode_utf8(&mut buffer);
|
||||
patches.extend(buffer);
|
||||
}
|
||||
|
||||
patches.extend(line.content());
|
||||
|
||||
true
|
||||
})?;
|
||||
|
||||
Ok(patches)
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn apply_patches(&self, graph: &DownloadedGraph) -> Result<(), errors::ApplyPatchesError> {
|
||||
let manifest = self.deser_manifest()?;
|
||||
|
||||
for (name, versions) in manifest.patches {
|
||||
for (version_id, patch_path) in versions {
|
||||
let patch_path = patch_path.to_path(self.path());
|
||||
let patch = Diff::from_buffer(&read(&patch_path).map_err(|e| {
|
||||
errors::ApplyPatchesError::PatchReadError(patch_path.clone(), e)
|
||||
})?)?;
|
||||
|
||||
let Some(node) = graph
|
||||
.get(&name)
|
||||
.and_then(|versions| versions.get(&version_id))
|
||||
else {
|
||||
return Err(errors::ApplyPatchesError::PackageNotFound(name, version_id));
|
||||
};
|
||||
|
||||
let container_folder = node.node.container_folder(
|
||||
&self
|
||||
.path()
|
||||
.join(node.node.base_folder(manifest.target.kind(), true))
|
||||
.join(PACKAGES_CONTAINER_NAME),
|
||||
&name,
|
||||
version_id.version(),
|
||||
);
|
||||
|
||||
{
|
||||
let repo = setup_patches_repo(&container_folder)?;
|
||||
repo.apply(&patch, ApplyLocation::Both, None)?;
|
||||
}
|
||||
|
||||
std::fs::remove_dir_all(container_folder.join(".git")).map_err(|e| {
|
||||
errors::ApplyPatchesError::GitDirectoryRemovalError(container_folder, e)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod errors {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{names::PackageNames, source::VersionId};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ApplyPatchesError {
|
||||
#[error("error deserializing project manifest")]
|
||||
ManifestDeserializationFailed(#[from] crate::errors::ManifestReadError),
|
||||
|
||||
#[error("error interacting with git")]
|
||||
GitError(#[from] git2::Error),
|
||||
|
||||
#[error("error reading patch file at {0}")]
|
||||
PatchReadError(PathBuf, #[source] std::io::Error),
|
||||
|
||||
#[error("error removing .git directory")]
|
||||
GitDirectoryRemovalError(PathBuf, #[source] std::io::Error),
|
||||
|
||||
#[error("package {0}@{1} not found in graph")]
|
||||
PackageNotFound(PackageNames, VersionId),
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ pub trait PackageRef: Debug {
|
|||
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)>;
|
||||
fn use_new_structure(&self) -> bool;
|
||||
fn target_kind(&self) -> TargetKind;
|
||||
fn source(&self) -> PackageSources;
|
||||
}
|
||||
impl PackageRef for PackageRefs {
|
||||
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)> {
|
||||
|
@ -67,6 +68,12 @@ impl PackageRef for PackageRefs {
|
|||
PackageRefs::Pesde(pkg_ref) => pkg_ref.target_kind(),
|
||||
}
|
||||
}
|
||||
|
||||
fn source(&self) -> PackageSources {
|
||||
match self {
|
||||
PackageRefs::Pesde(pkg_ref) => pkg_ref.source(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
@ -75,6 +82,10 @@ impl PackageRef for PackageRefs {
|
|||
pub struct VersionId(Version, TargetKind);
|
||||
|
||||
impl VersionId {
|
||||
pub fn new(version: Version, target: TargetKind) -> Self {
|
||||
VersionId(version, target)
|
||||
}
|
||||
|
||||
pub fn version(&self) -> &Version {
|
||||
&self.0
|
||||
}
|
||||
|
@ -82,6 +93,10 @@ impl VersionId {
|
|||
pub fn target(&self) -> &TargetKind {
|
||||
&self.1
|
||||
}
|
||||
|
||||
pub fn escaped(&self) -> String {
|
||||
format!("{}+{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VersionId {
|
||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::{
|
||||
manifest::{DependencyType, Target, TargetKind},
|
||||
names::PackageName,
|
||||
source::{DependencySpecifiers, PackageRef},
|
||||
source::{pesde::PesdePackageSource, DependencySpecifiers, PackageRef, PackageSources},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||
|
@ -34,6 +34,10 @@ impl PackageRef for PesdePackageRef {
|
|||
fn target_kind(&self) -> TargetKind {
|
||||
self.target.kind()
|
||||
}
|
||||
|
||||
fn source(&self) -> PackageSources {
|
||||
PackageSources::Pesde(PesdePackageSource::new(self.index_url.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for PesdePackageRef {
|
||||
|
|
Loading…
Reference in a new issue