add registry control

This commit is contained in:
bQUARKz 2026-02-25 06:00:03 +00:00
parent 3b477113e1
commit 7b15aebef2
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
19 changed files with 161 additions and 30 deletions

View File

@ -4,7 +4,7 @@ import lombok.extern.slf4j.Slf4j;
import p.studio.compiler.exceptions.BuildException;
import p.studio.compiler.messages.BuilderPipelineConfig;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.workspaces.stages.DependencyPipelineStage;
import p.studio.compiler.workspaces.stages.ResolvePipelineStage;
import p.studio.utilities.logs.LogAggregator;
import p.studio.utilities.structures.ReadOnlyCollection;
@ -16,7 +16,7 @@ public class BuilderPipelineService {
static {
final var stages = List.<PipelineStage>of(
new DependencyPipelineStage()
new ResolvePipelineStage()
);
INSTANCE = new BuilderPipelineService(stages);
}

View File

@ -13,7 +13,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
@Slf4j
public class DependencyPipelineStage implements PipelineStage {
public class ResolvePipelineStage implements PipelineStage {
@Override
public BuildingIssues run(final BuilderPipelineContext ctx, LogAggregator logs) {
final Path rootCanonPath;

View File

@ -14,7 +14,7 @@ public record PrometeuManifestDTO(
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
@JsonSubTypes.Type(value = DependencyDeclaration.Local.class),
@JsonSubTypes.Type(value = DependencyDeclaration.Git.class)
@JsonSubTypes.Type(value = DependencyDeclaration.Registry.class)
})
public interface DependencyDeclaration {
record Local(String path) implements DependencyDeclaration {
@ -24,13 +24,12 @@ public record PrometeuManifestDTO(
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
record Git(String url, String rev) implements DependencyDeclaration {
@JsonCreator
public Git(@JsonProperty("url") final String url, @JsonProperty("rev") final String rev) {
this.url = url;
this.rev = rev;
}
}
record Registry(String name, String version) implements DependencyDeclaration {
@JsonCreator
public Registry(@JsonProperty("name") final String name, @JsonProperty("version") final String version) {
this.name = name;
this.version = version;
}
}
}
}

View File

@ -1,7 +1,6 @@
package p.studio.compiler.messages;
import java.nio.file.Path;
import java.util.List;
public record DependencyConfig(
boolean explain,

View File

@ -32,7 +32,7 @@ public final class DependencyContext {
this.config = config;
}
public static DependencyContext basedOn(DependencyConfig config) {
public static DependencyContext basedOn(final DependencyConfig config) {
return new DependencyContext(config);
}

View File

@ -7,7 +7,6 @@ import p.studio.compiler.models.DependencyContext;
import p.studio.compiler.models.ResolvedWorkspace;
import p.studio.compiler.workspaces.phases.*;
import p.studio.utilities.logs.LogAggregator;
import p.studio.utilities.structures.ReadOnlyCollection;
import java.util.List;

View File

@ -12,6 +12,8 @@ import p.studio.compiler.models.PrometeuManifest;
import p.studio.compiler.utilities.PrometeuManifestUtils;
import p.studio.compiler.workspaces.DependencyPhase;
import p.studio.compiler.workspaces.DependencyReference;
import p.studio.registry.models.ProjectRef;
import p.studio.registry.utilities.RegistryStore;
import p.studio.utilities.structures.ReadOnlyCollection;
import p.studio.utilities.structures.ReadOnlyList;
@ -26,6 +28,7 @@ public class DiscoverPhase implements DependencyPhase {
public BuildingIssues run(final DependencyContext ctx) {
final Map<Path, ProjectInfoId> projectIndexByDirectory = new HashMap<>();
final BuildingIssues issues = BuildingIssues.empty();
// Discovers projects; registers them; adds dependencies to queue
while (!ctx.pending.isEmpty()) {
final var rootPathCanon = ctx.pending.pollFirst();
@ -51,7 +54,7 @@ public class DiscoverPhase implements DependencyPhase {
}
final var prometeuManifestDTO = PrometeuManifestUtils.INSTANCE.read(manifestPathCanon);
final var manifestMaybe = map(rootPathCanon, prometeuManifestDTO, issues);
final var manifestMaybe = map(ctx.mainProjectRootPathCanon, prometeuManifestDTO, issues);
if (manifestMaybe.isEmpty()) {
continue;
@ -123,29 +126,46 @@ public class DiscoverPhase implements DependencyPhase {
return List.of();
}
final var registryMaybe = RegistryStore.INSTANCE.load(rootProjectCanonPath);
if (registryMaybe.isEmpty()) {
issues.add(builder -> builder
.error(true)
.message("[DEPS]: failed to load registry from " + rootProjectCanonPath));
return List.of();
}
final var registry = registryMaybe.get();
final var deps = new ArrayList<DependencyReference>(dependencies.size());
for (var dependency : dependencies) {
// Resolves each dependency based on its declaration type
switch (dependency) {
case PrometeuManifestDTO.DependencyDeclaration.Local local -> {
case PrometeuManifestDTO.DependencyDeclaration.Local loc -> {
// Resolves local dependency path; reports canonicalization errors
final var pathResolve = rootProjectCanonPath.resolve(loc.path());
try {
final Path dependencyPathCanon = rootProjectCanonPath.resolve(local.path()).toRealPath();
deps.add(new DependencyReference(dependencyPathCanon));
final Path pathCanon = pathResolve.toRealPath();
deps.add(new DependencyReference(pathCanon));
} catch (IOException e) {
issues.add(builder -> builder
.error(true)
.message("[DEPS]: failed to canonicalize dependency path: " + local.path() + " from (" + rootProjectCanonPath + ")")
.message("[DEPS]: failed to canonicalize dependency path: " + pathResolve)
.exception(e));
}
}
case PrometeuManifestDTO.DependencyDeclaration.Git git -> issues.add(builder -> builder
.error(true)
.message("[DEPS]: git dependencies are not supported yet: " + git.url() + " from (" + rootProjectCanonPath + ")"));
case PrometeuManifestDTO.DependencyDeclaration.Registry reg -> {
// Looks up the registry for a dependency path; reports missing path errors
final var ref = new ProjectRef(reg.name(), reg.version());
registry.optional(ref).ifPresent(pathCanon -> deps.add(new DependencyReference(pathCanon)));
}
default -> {
}
}
}
return deps;
}
}

View File

@ -0,0 +1,13 @@
package p.studio.registry.models;
import java.util.Optional;
public record ProjectRef(String name, String version) {
public static Optional<ProjectRef> parse(final String value) {
final var ps = value.split("@");
if (ps.length != 2) return Optional.empty();
final var name = ps[0];
final var version = ps[1];
return Optional.of(new ProjectRef(name, version));
}
}

View File

@ -0,0 +1,17 @@
package p.studio.registry.models;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
public class Registry {
private final Map<ProjectRef, Path> pathByDependency;
public Registry(final Map<ProjectRef, Path> pathByDependency) {
this.pathByDependency = pathByDependency;
}
public Optional<Path> optional(final ProjectRef projectRef) {
return Optional.ofNullable(pathByDependency.get(projectRef));
}
}

View File

@ -0,0 +1,62 @@
package p.studio.registry.utilities;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import p.studio.registry.models.ProjectRef;
import p.studio.registry.models.Registry;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public final class RegistryStore {
public static final RegistryStore INSTANCE = new RegistryStore();
private final static ObjectMapper mapper = new ObjectMapper();
public Optional<Registry> load(final Path projectRootPathCanon) {
final var registryPath = projectRootPathCanon
.resolve(".workspace/repos/registry.json");
final Map<String, String> map;
{
final var file = registryPath.toFile();
if (!file.exists() || !file.isFile()) {
return Optional.empty();
}
try {
map = mapper.readValue(file, new TypeReference<>() {});
} catch (Exception e) {
return Optional.empty();
}
}
final Map<ProjectRef, Path> repos = new HashMap<>();
for (final var entry : map.entrySet()) {
final var pathCanon = canonicalize(projectRootPathCanon, entry.getValue());
if (pathCanon.isEmpty()) {
continue;
}
ProjectRef.parse(entry.getKey()).ifPresent(ref -> repos.put(ref, pathCanon.get()));
}
if (map.size() != repos.size()) {
return Optional.empty(); // mal formatted registry file
}
return Optional.of(new Registry(repos));
}
private static Optional<Path> canonicalize(
final Path projectRootPathCanon,
final String value) {
try {
final var pathResolve = projectRootPathCanon.resolve(value);
return Optional.of(pathResolve.toRealPath());
} catch (IOException e) {
return Optional.empty();
}
}
}

View File

@ -23,11 +23,13 @@ public enum I18n {
WORKSPACE_BUILDER("workspace.builder"),
WORKSPACE_BUILDER_LOGS("workspace.builder.logs"),
WORKSPACE_BUILDER_BUTTON_RUN("workspace.builder.button.run"),
WORKSPACE_BUILDER_BUTTON_CLEAR("workspace.builder.button.clear"),
WORKSPACE_ASSETS("workspace.assets"),
WORKSPACE_DEBUG("workspace.debug");
WORKSPACE_DEBUG("workspace.debug"),
;
@Getter
private final String key;

View File

@ -17,6 +17,7 @@ public class BuilderWorkspace implements Workspace {
private final BorderPane root = new BorderPane();
private final TextArea logs = new TextArea();
private final Button buildButton = new Button();
private final Button clearButton = new Button();
@Override
public WorkspaceId id() {
@ -50,11 +51,16 @@ public class BuilderWorkspace implements Workspace {
private ToolBar buildToolBar() {
buildButton.textProperty().bind(Container.i18n().bind(I18n.WORKSPACE_BUILDER_BUTTON_RUN));
buildButton.setOnAction(e -> {
logs.clear();
final var logAggregator = LogAggregator.with(logs::appendText);
final var config = new BuilderPipelineConfig(false, "../test-projects/main");
BuilderPipelineService.INSTANCE.run(config, logAggregator);
});
return new ToolBar(buildButton);
clearButton.textProperty().bind(Container.i18n().bind(I18n.WORKSPACE_BUILDER_BUTTON_CLEAR));
clearButton.setOnAction(e -> logs.clear());
return new ToolBar(buildButton, clearButton);
}
private StackPane buildProjectArea() {

View File

@ -15,6 +15,7 @@ workspace.code=Code
workspace.builder=Builder
workspace.builder.logs=Logs
workspace.builder.button.run=Build
workspace.builder.button.clear=Clear
workspace.assets=Assets
workspace.debug=Debug

View File

@ -11,9 +11,12 @@ menu.help=Ajuda
toolbar.play=Executar
toolbar.stop=Parar
toolbar.export=Exportar
workspace.code=Código
workspace.builder=Construtor
workspace.builder.logs=Logs
workspace.builder.button.run=Construir
workspace.builder.button.clear=Limpar
workspace.assets=Assets
workspace.debug=Depurar

View File

@ -4,10 +4,12 @@
"language": "pbs",
"dependencies": [
{
"path": "../dep2"
"name": "dep2",
"version": "1.0.0"
},
{
"path": "../sdk"
"name": "sdk",
"version": "1.0.0"
}
]
}

View File

@ -4,7 +4,8 @@
"language": "pbs",
"dependencies": [
{
"path": "../sdk"
"name": "sdk",
"version": "1.0.0"
}
]
}

View File

@ -0,0 +1,5 @@
{
"dep1@1.0.0": ".workspace/repos/dep1/1.0.0",
"dep2@1.0.0": ".workspace/repos/dep2/1.0.0",
"sdk@1.0.0": ".workspace/repos/sdk/1.0.0"
}

View File

@ -4,10 +4,12 @@
"language": "pbs",
"dependencies": [
{
"path": "../dep1"
"name": "dep1",
"version": "1.0.0"
},
{
"path": "../sdk"
"name": "sdk",
"version": "1.0.0"
}
]
}