Skip to content
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Parsing for target additions, which doesn't allow for empty additions or additions with invalid characters.
- License identifiers are now checked for invalid characters and being empty.
- The `PACKIT_DISABLE_PROMPTS` environment variable, which triggers an error when Packit tries to prompt.
- The `LocalMetadataExistence` verifier check.

### Changes
- The build tests are now turned off by default and can be turned on with `--execute-build-test` (`--skip-build-test` is removed).
Expand All @@ -36,6 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Fix the `include_license` field resolving symlinks, now they are not resolved anymore.
- Fix errors in command not resulting in non-zero exit code.
- Fix update functionality checking if dependency is still satisfied in a wrong way.
- Fix the re-install fix in the repairer uninstalling a package when it cannot be installed.


## [v0.0.4](https://github.com/pack-it/packit/compare/0.0.3...0.0.4) - 2026-08-16
Expand Down
1 change: 1 addition & 0 deletions docs/verifier.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This is a list of checks which are currently implemented in the verifier. With a
| InvalidFiles | General | Checks directories for invalid files (files which shouldn't be there).
| StorageConsistency | Package | Checks if packages in the register also exist in the package storage in the prefix directory. |
| RegisterConsistency | Package | Checks if packages in storage also exist in the register. Note that this is package related, but cannot only check the specified packages, because packages are based on what's found in storage. |
| LocalMetadataExistence | Package | Checks if packages have local metadata. |
| DependencyTree | Package | Checks if the dependency tree is broken based on the dependencies specified in the register. |
| Alterations | Package | Checks for alterations in packages using a checksum which is compared to the checksum from the pre-build. |
| MissingDependents | Package | Checks for missing dependents of packages in the register. |
Expand Down
13 changes: 10 additions & 3 deletions src/integrity/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum Check {
// Package related checks
StorageConsistency,
RegisterConsistency,
LocalMetadataExistence,
DependencyTree,
Alterations,
MissingDependents,
Expand Down Expand Up @@ -56,22 +57,26 @@ impl Check {
Self::MissingLink => &[],
Self::StorageConsistency => &[Self::StrayDirectory],
Self::RegisterConsistency => &[Self::StrayDirectory],
Self::LocalMetadataExistence => &[Self::StorageConsistency, Self::RegisterConsistency],
Self::InvalidActive => &[Self::StorageConsistency, Self::RegisterConsistency],
Self::MissingDependencies => &[Self::StorageConsistency, Self::RegisterConsistency],
Self::InvalidDependencies => &[Self::StorageConsistency, Self::RegisterConsistency],
Self::MissingDependencies => &[Self::LocalMetadataExistence, Self::StorageConsistency, Self::RegisterConsistency],
Self::InvalidDependencies => &[Self::LocalMetadataExistence, Self::StorageConsistency, Self::RegisterConsistency],
Self::MissingDependents => &[
Self::LocalMetadataExistence,
Self::StorageConsistency,
Self::RegisterConsistency,
Self::MissingDependencies,
Self::InvalidDependencies,
],
Self::InvalidDependents => &[
Self::LocalMetadataExistence,
Self::StorageConsistency,
Self::RegisterConsistency,
Self::MissingDependencies,
Self::InvalidDependencies,
],
Self::DependencyTree => &[
Self::LocalMetadataExistence,
Self::StorageConsistency,
Self::RegisterConsistency,
Self::MissingDependencies,
Expand All @@ -81,8 +86,9 @@ impl Check {
],
Self::MissingDependencySymlinks => &[Self::DependencyTree],
Self::InvalidFiles => &[Self::DependencyTree],
Self::Alterations => &[Self::StorageConsistency, Self::RegisterConsistency],
Self::Alterations => &[Self::LocalMetadataExistence, Self::StorageConsistency, Self::RegisterConsistency],
Self::Test => &[
Self::LocalMetadataExistence,
Self::ForbiddenLink,
Self::MissingLink,
Self::DependencyTree,
Expand All @@ -109,6 +115,7 @@ impl Check {
&[
Self::StorageConsistency,
Self::RegisterConsistency,
Self::LocalMetadataExistence,
Self::DependencyTree,
Self::Alterations,
Self::PackitGroup,
Expand Down
2 changes: 1 addition & 1 deletion src/integrity/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub enum VerifierError {
#[error("Could not check permissions")]
PermissionError(#[from] PermissionError),

#[error("Could not use repository manager for check or fix")]
#[error("Could not use repositories for check or fix")]
RepositoryError(#[from] RepositoryError),

#[error("Cannot perform check, because of an error when executing a script")]
Expand Down
15 changes: 15 additions & 0 deletions src/integrity/issue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ pub enum Issue {
/// A list of packages which are present in the package directory, but not in the `Register.toml`.
InconsistentRegister(HashSet<PackageId>),

/// A list of packages which do not have local metadata.
MissingLocalMetadata(HashSet<PackageId>),

/// A list of packages which are changed (when they shouldn't be).
AlteredPackage(Vec<PackageId>),

Expand Down Expand Up @@ -198,6 +201,15 @@ impl Display for Issue {
writeln!(f, " - {}", package.style())?;
}
},
Issue::MissingLocalMetadata(package_ids) => {
writeln!(f, "Missing local metadata")?;
let issue_explanation = "The following packages do not have local metadata:";
writeln!(f, "{issue_explanation}")?;

for package in package_ids {
writeln!(f, " - {}", package.style())?;
}
},
Issue::AlteredPackage(altered) => {
writeln!(f, "Altered packages")?;
let issue_explanation = "The following packages were found to be changed when they shouldn't be:";
Expand Down Expand Up @@ -263,6 +275,9 @@ impl Issue {
Issue::InconsistentRegister(_) => {
"To fix this issue we try to reconstruct the Register.toml with data still in the Packit directory"
},
Issue::MissingLocalMetadata(_) => {
"To fix this issue we try to download the metadata from the source metadata repository again, otherwise re-install the package"
},
Issue::FailedTest(_) | Issue::AlteredPackage(_) => "To fix this issue we try to re-install the package",
Issue::MissingPackitGroup => "There is no automatic fix for this issue available yet",
}
Expand Down
1 change: 1 addition & 0 deletions src/integrity/repairer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ impl Repairer {
Issue::BrokenTree(missing) => package::fix_broken_tree(missing, register, config, manager)?,
Issue::InconsistentStorage(missing) => package::fix_inconsistent_storage(missing, register, config, manager)?,
Issue::InconsistentRegister(missing) => package::fix_inconsistent_register(missing, register, config, manager)?,
Issue::MissingLocalMetadata(missing) => package::fix_missing_local_metadata(missing, register, config, manager)?,
Issue::MissingDependencies(missing) => package::fix_missing_dependencies(missing, register, manager)?,
Issue::InvalidDependencies(invalid) => package::fix_invalid_dependencies(invalid, register)?,
Issue::MissingDependents(missing) => package::fix_missing_dependents(missing, register),
Expand Down
60 changes: 55 additions & 5 deletions src/integrity/repairer/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use std::{collections::HashSet, fs, path::Path, str::FromStr};

use crate::{
cli::display::{logging::warning, styled::Styled},
config::Config,
config::{Config, Repository},
installer::{
Installer, InstallerOptions, Symlinker,
types::{Dependency, OptionalPackageId, PackageId, PackageName, Version},
types::{Dependency, PackageId, PackageName, Version},
},
integrity::{
error::{Result, VerifierError},
Expand All @@ -17,10 +17,14 @@ use crate::{
Target,
symlink::{self, SymlinkError},
},
register::package_register::PackageRegister,
register::{
metadata::{LocalMetaHandler, error::LocalMetadataError},
package_register::PackageRegister,
},
repositories::{
error::RepositoryError,
manager::RepositoryManager,
metadata::MetadataProvider,
types::{Checksum, PackageVersionMeta},
},
utils::ioerror::IOResultExt,
Expand Down Expand Up @@ -212,6 +216,45 @@ fn get_latest_satisfying_packages(package_version_meta: &PackageVersionMeta, sto
dependencies
}

/// Fixes missing local metadata of a package by trying to download it again.
/// If downloading is not possible, tries to re-install the package.
pub fn fix_missing_local_metadata(
missing: HashSet<PackageId>,
register: &mut PackageRegister,
config: &Config,
manager: &RepositoryManager,
) -> Result<()> {
let local_meta_handler = LocalMetaHandler::new(&config.prefix_directory);
for package_id in missing {
let Some(package_version) = register.get_package_version_mut(&package_id) else {
warning!("Could not fix missing metadata {}", package_id.style());
continue;
};

// Create repository provider for package
let repository = Repository::new(
&package_version.metadata_repository_url,
&package_version.metadata_repository_provider,
);
let Some(provider) = MetadataProvider::create_from_repository(&repository) else {
warning!("Could not fix missing metadata {}, unable to create provider", package_id.style());
continue;
};

let package_handler = local_meta_handler.get_package(&package_id);
match package_handler.refresh(&provider) {
Ok(_) => continue,
Err(LocalMetadataError::RepositoryError(_)) => {
println!("Trying to re-install {}", package_id.style());
reinstall_package(&package_id, register, manager, config)?;
},
Err(e) => return Err(e.into()),
};
}

Ok(())
}

/// Fixes missing dependencies by adding them to the register.
/// If an installed satisfying package can be found it's used, otherwise the latest version is used instead.
pub fn fix_missing_dependencies(
Expand Down Expand Up @@ -390,13 +433,20 @@ fn reinstall_package(package_id: &PackageId, register: &mut PackageRegister, man
};
let dependents = package_version.dependents.clone();

// Check if the package is available in any repository
let optional_id = package_id.clone().into();
if let Err(e) = manager.read_package_and_version(&optional_id, &Target::current()) {
warning!("Skipping re-install of {}", package_id.style());
return Err(e.into());
}

// Temporarily remove the package
let installer_options = InstallerOptions::default().skip_symlinking(!package.symlinked).skip_active(true);
let mut installer = Installer::new(config, register, manager, installer_options);
installer.uninstall(&OptionalPackageId::from(package_id.clone()))?;
installer.uninstall(&optional_id)?;

// Re-install the package
installer.install(&OptionalPackageId::from(package_id.clone()))?;
installer.install(&optional_id)?;

// Re-add package as dependent
for dependent in &dependents {
Expand Down
1 change: 1 addition & 0 deletions src/integrity/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ impl Verifier {
Check::InvalidFiles => general::check_invalid_files(packages, register, config)?,
Check::RegisterConsistency => package::check_register_consistency(register, config)?,
Check::StorageConsistency => package::check_storage_consistency(packages, config)?,
Check::LocalMetadataExistence => package::check_local_metadata_existence(packages, config)?,
Check::DependencyTree => package::check_dependency_tree(packages, register),
Check::Alterations => package::check_alterations(packages, register, config)?,
Check::MissingDependencies => package::check_missing_dependencies(packages, register, config)?,
Expand Down
23 changes: 23 additions & 0 deletions src/integrity/verifier/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,29 @@ pub fn check_register_consistency(register: &PackageRegister, config: &Config) -
Ok(Some(Issue::InconsistentRegister(missing)))
}

/// Checks if the local metadata of the given packages exists.
/// Returns a missing local metadata issue or `None` if local metadata exists for all given packages.
pub fn check_local_metadata_existence(packages: &Vec<PackageId>, config: &Config) -> Result<Option<Issue>> {
let mut missing = HashSet::new();

let local_meta_handler = LocalMetaHandler::new(&config.prefix_directory);
for package_id in packages {
let package_handler = local_meta_handler.get_package(package_id);

match package_handler.read_metadata() {
Ok(_) => continue,
Err(LocalMetadataError::LocalMetadataFileNotFound { .. }) => missing.insert(package_id.clone()),
Err(e) => return Err(e.into()),
};
}

if missing.is_empty() {
return Ok(None);
}

Ok(Some(Issue::MissingLocalMetadata(missing)))
}

/// Checks for the given packages if the package active is valid.
pub fn check_invalid_active(packages: &HashSet<PackageName>, register: &PackageRegister, config: &Config) -> Result<Option<Issue>> {
let mut invalid_active = Vec::new();
Expand Down