implements PR-31 pack wizard validation snapshot gate

This commit is contained in:
bQUARKz 2026-03-20 05:56:38 +00:00
parent 4fd956265f
commit 96643282af
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 94 additions and 1 deletions

View File

@ -9,8 +9,10 @@ import p.packer.PackerWorkspaceService;
import p.packer.events.PackerEventKind; import p.packer.events.PackerEventKind;
import p.packer.events.PackerEventSink; import p.packer.events.PackerEventSink;
import p.packer.events.PackerProgress; import p.packer.events.PackerProgress;
import p.packer.dtos.PackerDiagnosticDTO;
import p.packer.dtos.PackerPackSummaryAssetDTO; import p.packer.dtos.PackerPackSummaryAssetDTO;
import p.packer.dtos.PackerPackSummaryDTO; import p.packer.dtos.PackerPackSummaryDTO;
import p.packer.dtos.PackerPackValidationAssetDTO;
import p.packer.messages.*; import p.packer.messages.*;
import p.packer.messages.assets.*; import p.packer.messages.assets.*;
import p.packer.messages.diagnostics.PackerDiagnosticCategory; import p.packer.messages.diagnostics.PackerDiagnosticCategory;
@ -158,7 +160,20 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
@Override @Override
public ValidatePackWorkspaceResult validatePackWorkspace(ValidatePackWorkspaceRequest request) { public ValidatePackWorkspaceResult validatePackWorkspace(ValidatePackWorkspaceRequest request) {
throw new UnsupportedOperationException("pack workspace validation is not implemented yet"); final PackerProjectContext project = Objects.requireNonNull(request, "request").project();
final PackerRuntimeSnapshot snapshot = runtimeRegistry.getOrLoad(project).snapshot();
final List<PackerPackValidationAssetDTO> blockedAssets = snapshot.assets().stream()
.filter(runtimeAsset -> runtimeAsset.registryEntry().isPresent() && runtimeAsset.registryEntry().get().includedInBuild())
.map(this::toPackValidationAssetDTO)
.filter(PackerPackValidationAssetDTO::blocked)
.toList();
final PackerOperationStatus status = blockedAssets.isEmpty()
? PackerOperationStatus.SUCCESS
: PackerOperationStatus.PARTIAL;
final String summary = blockedAssets.isEmpty()
? "Pack validation completed without blocking diagnostics."
: "Pack validation found blocking diagnostics in " + blockedAssets.size() + " included assets.";
return new ValidatePackWorkspaceResult(status, summary, blockedAssets);
} }
@Override @Override
@ -677,6 +692,29 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
0L); 0L);
} }
private PackerPackValidationAssetDTO toPackValidationAssetDTO(PackerRuntimeAsset runtimeAsset) {
final PackerRegistryEntry registryEntry = runtimeAsset.registryEntry()
.orElseThrow(() -> new IllegalArgumentException("pack validation asset must be registered"));
final PackerAssetDeclarationParseResult parsed = runtimeAsset.parsedDeclaration();
final PackerAssetDeclaration declaration = parsed.declaration();
final String assetName = declaration != null
? declaration.name()
: runtimeAsset.assetRoot().getFileName().toString();
final List<PackerDiagnostic> blockingDiagnostics = Stream.of(
parsed.diagnostics(),
runtimeAsset.walkDiagnostics(),
identityMismatchDiagnostics(registryEntry, parsed, runtimeAsset.manifestPath()))
.flatMap(Collection::stream)
.filter(PackerDiagnostic::blocking)
.toList();
return new PackerPackValidationAssetDTO(
registryEntry.assetId(),
assetName,
runtimeAsset.assetRoot(),
0L,
PackerReadMessageMapper.toDiagnosticDTOs(blockingDiagnostics));
}
private void saveRuntimeCache(PackerProjectContext project, PackerRuntimeSnapshot snapshot) { private void saveRuntimeCache(PackerProjectContext project, PackerRuntimeSnapshot snapshot) {
cacheRepository.save(project, snapshot.cacheState()); cacheRepository.save(project, snapshot.cacheState());
} }

View File

@ -140,6 +140,61 @@ final class FileSystemPackerWorkspaceServiceTest {
assertEquals(0L, result.packSummary().assets().getFirst().lastModified()); assertEquals(0L, result.packSummary().assets().getFirst().lastModified());
} }
@Test
void packValidationReturnsGreenWhenIncludedRegisteredAssetsHaveNoBlockers() throws Exception {
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-validation-green"));
final FileSystemPackerWorkspaceService service = service();
final var result = service.validatePackWorkspace(new ValidatePackWorkspaceRequest(project(projectRoot)));
assertEquals(PackerOperationStatus.SUCCESS, result.status());
assertTrue(result.canPack());
assertTrue(result.assets().isEmpty());
}
@Test
void packValidationReturnsBlockingDiagnosticsPerIncludedAsset() throws Exception {
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-validation-blocked"));
final Path manifestPath = projectRoot.resolve("assets/ui/atlas/asset.json");
final ObjectNode manifest = (ObjectNode) MAPPER.readTree(manifestPath.toFile());
manifest.remove("name");
MAPPER.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
final FileSystemPackerWorkspaceService service = service();
final var result = service.validatePackWorkspace(new ValidatePackWorkspaceRequest(project(projectRoot)));
assertEquals(PackerOperationStatus.PARTIAL, result.status());
assertFalse(result.canPack());
assertEquals(1, result.assets().size());
assertEquals(1, result.assets().getFirst().assetId());
assertEquals("atlas", result.assets().getFirst().assetName());
assertEquals(projectRoot.resolve("assets/ui/atlas").toAbsolutePath().normalize(), result.assets().getFirst().assetPath());
assertEquals(0L, result.assets().getFirst().lastModified());
assertTrue(result.assets().getFirst().diagnostics().stream().allMatch(diagnostic -> diagnostic.blocking()));
assertTrue(result.assets().getFirst().diagnostics().stream()
.anyMatch(diagnostic -> diagnostic.message().contains("Field 'name' must be a non-blank string")));
}
@Test
void packValidationIgnoresRegisteredAssetsExcludedFromBuild() throws Exception {
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-validation-excluded"));
final Path registryPath = projectRoot.resolve("assets/.prometeu/index.json");
final ObjectNode registry = (ObjectNode) MAPPER.readTree(registryPath.toFile());
((ObjectNode) registry.path("assets").get(0)).put("included_in_build", false);
MAPPER.writerWithDefaultPrettyPrinter().writeValue(registryPath.toFile(), registry);
final Path manifestPath = projectRoot.resolve("assets/ui/atlas/asset.json");
final ObjectNode manifest = (ObjectNode) MAPPER.readTree(manifestPath.toFile());
manifest.remove("name");
MAPPER.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
final FileSystemPackerWorkspaceService service = service();
final var result = service.validatePackWorkspace(new ValidatePackWorkspaceRequest(project(projectRoot)));
assertEquals(PackerOperationStatus.SUCCESS, result.status());
assertTrue(result.canPack());
assertTrue(result.assets().isEmpty());
}
@Test @Test
void createsRegisteredAssetAndWritesManifest() throws Exception { void createsRegisteredAssetAndWritesManifest() throws Exception {
final Path projectRoot = tempDir.resolve("created"); final Path projectRoot = tempDir.resolve("created");