feat: move distribution builds to platform Docker builder

This commit is contained in:
npc0-hue
2026-07-30 19:25:50 +08:00
parent 1e004dc9ec
commit e614a17fe3
45 changed files with 4492 additions and 294 deletions
+29 -36
View File
@@ -47,19 +47,12 @@ func (svc *CoreService) GenerateRunDistributionForSession(sessionID string, requ
if err := svc.promoteLegacyRunBinding(&instance); err != nil {
return domain.RunDistribution{}, err
}
builderEndpointID := instance.RunEndpointID
if strings.TrimSpace(instance.DeploymentTargetID) != "" {
builderEndpointID = instance.DeploymentTargetID
}
endpoint, err := svc.store.RunEndpoints().Get(builderEndpointID)
if err != nil {
return domain.RunDistribution{}, err
}
if err := svc.validateRunnableEndpoint(endpoint, domain.JobCapabilityDistributionBuild); err != nil {
return domain.RunDistribution{}, err
if ready, reason := svc.distributionBuilderReadiness(); !ready {
_ = svc.recordAuditEvent(user.ID, "run.generate.denied", "server-instance", instance.ID, domain.AuditResultDenied, reason)
return domain.RunDistribution{}, validationError(reason)
}
key, plainKey, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentRun, "")
key, _, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentRun, "")
if err != nil {
return domain.RunDistribution{}, err
}
@@ -70,7 +63,6 @@ func (svc *CoreService) GenerateRunDistributionForSession(sessionID string, requ
return domain.RunDistribution{}, err
}
_ = plainKey
artifactID := artifactIDForDistribution(distributionID + "-binary")
buildJobID := jobIDFromParts("job-distribution-build", instance.ID, distributionID)
stamp := svc.now()
@@ -106,7 +98,7 @@ func (svc *CoreService) GenerateRunDistributionForSession(sessionID string, requ
job, err := svc.CreateJob(domain.Job{
ID: buildJobID,
ServerInstanceID: instance.ID,
RunEndpointID: builderEndpointID,
RunEndpointID: platformDistributionBuilderEndpointID,
Capability: domain.JobCapabilityDistributionBuild,
TargetKey: "distribution/run",
InputRef: "input://distribution-build/" + distribution.ID,
@@ -122,26 +114,29 @@ func (svc *CoreService) GenerateRunDistributionForSession(sessionID string, requ
if job.ID != buildJobID || job.Capability != domain.JobCapabilityDistributionBuild {
return domain.RunDistribution{}, validationError("distribution build idempotency key conflicts with another job")
}
if err := svc.recordAuditEvent(user.ID, "run.generate", "server-instance", instance.ID, domain.AuditResultQueued, "queued run binary build job with redacted runtime key ref"); err != nil {
svc.enqueueDistributionBuild(job)
if err := svc.recordAuditEvent(user.ID, "run.generate", "server-instance", instance.ID, domain.AuditResultQueued, "queued run binary build job in the platform builder with redacted runtime key ref"); err != nil {
return domain.RunDistribution{}, err
}
return domain.CopyRunDistribution(distribution), nil
}
// promoteLegacyRunBinding reserves a server-scoped endpoint for a generated
// Run before a legacy server first requests a distribution. Its existing
// endpoint remains the trusted build target; reusing it in the package would
// allow the generated Run to replace the builder registration.
// promoteLegacyRunBinding reserves the server-scoped endpoint used by a
// generated Run. A legacy shared endpoint remains an optional deployment target
// for non-build workflows; distribution builds are always platform-owned.
func (svc *CoreService) promoteLegacyRunBinding(instance *domain.ServerInstance) error {
if instance == nil || strings.TrimSpace(instance.DeploymentTargetID) != "" || (instance.State != domain.ServerInstanceStateDraft && instance.State != domain.ServerInstanceStateFailed) {
return nil
}
builderEndpointID := strings.TrimSpace(instance.RunEndpointID)
if builderEndpointID == "" {
return validationError("legacy Run generation requires a build target endpoint")
currentEndpointID := strings.TrimSpace(instance.RunEndpointID)
dedicatedEndpointID := dedicatedRunEndpointID(instance.ID)
if currentEndpointID == dedicatedEndpointID {
return nil
}
instance.DeploymentTargetID = builderEndpointID
instance.RunEndpointID = dedicatedRunEndpointID(instance.ID)
if currentEndpointID != "" {
instance.DeploymentTargetID = currentEndpointID
}
instance.RunEndpointID = dedicatedEndpointID
instance.UpdatedAt = svc.now()
if err := validator.ValidateServerInstance(*instance); err != nil {
return err
@@ -191,15 +186,12 @@ func (svc *CoreService) GenerateClientManagerDistributionForSession(sessionID st
if err := svc.requireCompleteRuntimeBindings(user.ID, instance.ID, "client-manager.build.denied"); err != nil {
return domain.ClientManagerDistribution{}, err
}
endpoint, err := svc.store.RunEndpoints().Get(instance.RunEndpointID)
if err != nil {
return domain.ClientManagerDistribution{}, err
}
if err := svc.validateRunnableEndpoint(endpoint, domain.JobCapabilityDistributionBuild); err != nil {
return domain.ClientManagerDistribution{}, err
if ready, reason := svc.distributionBuilderReadiness(); !ready {
_ = svc.recordAuditEvent(user.ID, "client-manager.build.denied", "server-instance", instance.ID, domain.AuditResultDenied, reason)
return domain.ClientManagerDistribution{}, validationError(reason)
}
key, plainKey, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentClientManager, request.ProfileKey)
key, _, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentClientManager, request.ProfileKey)
if err != nil {
return domain.ClientManagerDistribution{}, err
}
@@ -210,7 +202,6 @@ func (svc *CoreService) GenerateClientManagerDistributionForSession(sessionID st
return domain.ClientManagerDistribution{}, err
}
_ = plainKey
artifactID := artifactIDForDistribution(clientDistributionID + "-binary")
stamp := svc.now()
buildJobID := jobIDFromParts("job-distribution-build", instance.ID, clientDistributionID)
@@ -283,7 +274,7 @@ func (svc *CoreService) GenerateClientManagerDistributionForSession(sessionID st
job, err := svc.CreateJob(domain.Job{
ID: buildJobID,
ServerInstanceID: instance.ID,
RunEndpointID: instance.RunEndpointID,
RunEndpointID: platformDistributionBuilderEndpointID,
Capability: domain.JobCapabilityDistributionBuild,
TargetKey: "distribution/client-manager/" + request.ProfileKey,
InputRef: "input://distribution-build/" + distribution.ID,
@@ -303,7 +294,8 @@ func (svc *CoreService) GenerateClientManagerDistributionForSession(sessionID st
if job.ID != buildJobID || job.Capability != domain.JobCapabilityDistributionBuild {
return domain.ClientManagerDistribution{}, validationError("distribution build idempotency key conflicts with another job")
}
if err := svc.recordAuditEvent(user.ID, "client-manager.build", "server-instance", instance.ID, domain.AuditResultQueued, "queued client-manager source build with redacted runtime key ref"); err != nil {
svc.enqueueDistributionBuild(job)
if err := svc.recordAuditEvent(user.ID, "client-manager.build", "server-instance", instance.ID, domain.AuditResultQueued, "queued client-manager source build in the platform builder with redacted runtime key ref"); err != nil {
return domain.ClientManagerDistribution{}, err
}
return domain.CopyClientManagerDistribution(distribution), nil
@@ -467,7 +459,7 @@ func (svc *CoreService) GetServerRuntimeActionsForSession(sessionID string, serv
if endpointErr != nil && strings.TrimSpace(instance.DeploymentTargetID) != "" {
endpoint, endpointErr = svc.store.RunEndpoints().Get(instance.DeploymentTargetID)
}
if endpointErr != nil {
if endpointErr != nil && !errors.Is(endpointErr, repo.ErrNotFound) {
return domain.ServerRuntimeActions{}, endpointErr
}
hasAvailableRunPackage := false
@@ -493,6 +485,7 @@ func (svc *CoreService) GetServerRuntimeActionsForSession(sessionID string, serv
}
}
bindingsComplete, bindingReason := svc.runtimeBindingReadiness(instance.ID)
builderReady, builderReason := svc.distributionBuilderReadiness()
dependencyPermissionDeclared := pluginDeclares(plugin, "server.dependencies.manage")
actions := domain.ServerRuntimeActions{
ServerInstanceID: instance.ID,
@@ -505,11 +498,11 @@ func (svc *CoreService) GetServerRuntimeActionsForSession(sessionID string, serv
return domain.RunEndpointStatusOffline
}(),
Actions: []domain.ServerRuntimeAction{
runtimeAction("generate-run", "Generate run", pluginDeclares(plugin, "server.run.distribution") && svc.endpointSupports(endpoint, domain.JobCapabilityDistributionBuild) && bindingsComplete, fallbackReason(!pluginDeclares(plugin, "server.run.distribution") || !svc.endpointSupports(endpoint, domain.JobCapabilityDistributionBuild), "run endpoint cannot build distributions", bindingReason)),
runtimeAction("generate-run", "Generate run", pluginDeclares(plugin, "server.run.distribution") && builderReady && bindingsComplete, fallbackReason(!pluginDeclares(plugin, "server.run.distribution"), "plugin permission is not declared", fallbackReason(!builderReady, builderReason, bindingReason))),
runtimeAction("download-run", "Download run", hasAvailableRunPackage, "run package has not been generated"),
runtimeAction("push-run-update", "Push run update", runRegistered && pluginDeclares(plugin, "server.run.distribution") && svc.endpointSupports(endpoint, domain.JobCapabilityRunSelfUpdate) && bindingsComplete, fallbackReason(!runRegistered, "dedicated Run has not registered", fallbackReason(!pluginDeclares(plugin, "server.run.distribution") || !svc.endpointSupports(endpoint, domain.JobCapabilityRunSelfUpdate), "run endpoint cannot self-update", bindingReason))),
runtimeAction("reset-run-key", "Reset run key", pluginDeclares(plugin, "server.run.distribution"), "plugin permission is not declared"),
runtimeAction("generate-client-manager", "Generate client manager", pluginDeclares(plugin, "server.client-manager.manage") && svc.endpointSupports(endpoint, domain.JobCapabilityDistributionBuild) && bindingsComplete, fallbackReason(!pluginDeclares(plugin, "server.client-manager.manage") || !svc.endpointSupports(endpoint, domain.JobCapabilityDistributionBuild), "run endpoint cannot build distributions", bindingReason)),
runtimeAction("generate-client-manager", "Generate client manager", pluginDeclares(plugin, "server.client-manager.manage") && builderReady && bindingsComplete, fallbackReason(!pluginDeclares(plugin, "server.client-manager.manage"), "client-manager permission is not declared", fallbackReason(!builderReady, builderReason, bindingReason))),
runtimeAction("download-client-manager", "Download client manager", hasAvailableClientPackage, "client-manager package has not been generated"),
runtimeAction("reset-client-manager-key", "Reset client-manager key", pluginDeclares(plugin, "server.client-manager.manage"), "client-manager permission is not declared"),
runtimeAction("dependencies-check", "Check dependencies", runRegistered && dependencyPermissionDeclared && svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesCheck) && bindingsComplete, fallbackReason(!runRegistered, "dedicated Run has not registered", fallbackReason(!dependencyPermissionDeclared, "plugin permission is not declared", fallbackReason(!svc.endpointSupports(endpoint, domain.JobCapabilityDependenciesCheck), "run endpoint cannot check dependencies", bindingReason)))),