package service import ( "strings" "time" "browser.local/platform/domain" "browser.local/platform/repo" "browser.local/platform/validator" ) func (svc *CoreService) GetDistributionBuildInput(request domain.DistributionBuildInputRequest) (domain.DistributionBuildInput, error) { if err := validator.ValidateDistributionBuildInputRequest(request); err != nil { return domain.DistributionBuildInput{}, err } session, err := svc.validatedRunSession(request.RunEndpointID, request.SessionToken) if err != nil { return domain.DistributionBuildInput{}, err } svc.jobMu.Lock() job, err := svc.fencedJob(session, request.JobID, request.LeaseToken, request.Attempt) svc.jobMu.Unlock() if err != nil { return domain.DistributionBuildInput{}, err } if job.Capability != domain.JobCapabilityDistributionBuild { return domain.DistributionBuildInput{}, validationError("job is not a distribution build") } if job.State != domain.JobStateAccepted && job.State != domain.JobStateRunning { return domain.DistributionBuildInput{}, validationError("distribution build job is not active") } runDistributions, err := svc.store.RunDistributions().List(domain.RunDistributionFilter{ServerInstanceID: job.ServerInstanceID}) if err != nil { return domain.DistributionBuildInput{}, err } for _, distribution := range runDistributions { if distribution.BuildJobID != job.ID { continue } key, err := svc.activeComponentKey(distribution.ServerInstanceID, domain.DistributionComponentRun, "") if err != nil { return domain.DistributionBuildInput{}, err } if key.Generation != distribution.KeyGeneration { return domain.DistributionBuildInput{}, validationError("run build key generation is no longer current") } plainKey, err := svc.decryptRuntimeKey(key.EncryptedKey) if err != nil { return domain.DistributionBuildInput{}, err } packageInput, err := svc.runDistributionPackageContext(distribution) if err != nil { return domain.DistributionBuildInput{}, err } return domain.DistributionBuildInput{ JobID: job.ID, ComponentKind: domain.DistributionComponentRun, ServerInstanceID: distribution.ServerInstanceID, PluginID: distribution.PluginID, RunEndpointID: distribution.RunEndpointID, ProfileKey: packageInput.profileKey, TargetOS: distribution.TargetOS, TargetArch: distribution.TargetArch, TargetRelease: distribution.ID, PlatformURL: runReleasePlatformURL(), PackageFormat: distribution.PackageFormat, ArtifactID: distribution.ArtifactID, OutputFilename: executableFilename("run", distribution.TargetOS), SecretRef: distribution.SecretRef, KeyGeneration: distribution.KeyGeneration, AuthKey: plainKey, WorkspaceSeed: packageInput.workspaceSeed, AutonomousLifecycle: packageInput.autonomousLifecycle, }, nil } return domain.DistributionBuildInput{}, repo.ErrNotFound } func (svc *CoreService) projectDistributionBuildProgress(job domain.Job, stamp time.Time) error { if job.Capability != domain.JobCapabilityDistributionBuild { return nil } return nil } func (svc *CoreService) projectDistributionBuildResult(job domain.Job, stamp time.Time) error { if job.Capability != domain.JobCapabilityDistributionBuild { return nil } if err := svc.validateDistributionBuildResult(job); err != nil { return err } status := domain.DistributionStatusFailed var artifact domain.Artifact if job.State == domain.JobStateSucceeded { artifactID := strings.TrimPrefix(job.ResultRef, "artifact://") if artifactID == "" || artifactID == job.ResultRef { return validationError("distribution build result must reference an artifact") } var err error artifact, err = svc.store.Artifacts().Get(artifactID) if err != nil { return err } if artifact.State != domain.ArtifactStateAvailable || artifact.OwnerKind != domain.ArtifactOwnerKindJob || artifact.OwnerID != job.ID { return validationError("distribution build artifact is unavailable or outside the job scope") } status = domain.DistributionStatusAvailable } runDistributions, err := svc.store.RunDistributions().List(domain.RunDistributionFilter{ServerInstanceID: job.ServerInstanceID}) if err != nil { return err } for _, distribution := range runDistributions { if distribution.BuildJobID != job.ID { continue } if distribution.Status == domain.DistributionStatusRevoked { if artifact.ID != "" { return svc.expireDistributionArtifact(artifact.ID) } return nil } if job.State == domain.JobStateSucceeded && artifact.ID != distribution.ArtifactID { return validationError("distribution build returned an unexpected artifact") } distribution.Status = status if artifact.ID != "" { distribution.Checksum = artifact.Checksum } distribution.UpdatedAt = stamp if err := validator.ValidateRunDistribution(distribution); err != nil { return err } return svc.store.RunDistributions().Update(distribution) } return repo.ErrNotFound } func (svc *CoreService) validateDistributionBuildResult(job domain.Job) error { if job.Capability != domain.JobCapabilityDistributionBuild { return nil } expectedArtifactID := "" runDistributions, err := svc.store.RunDistributions().List(domain.RunDistributionFilter{ServerInstanceID: job.ServerInstanceID}) if err != nil { return err } for _, distribution := range runDistributions { if distribution.BuildJobID == job.ID { expectedArtifactID = distribution.ArtifactID break } } if expectedArtifactID == "" { return repo.ErrNotFound } if job.State != domain.JobStateSucceeded { return nil } artifactID := strings.TrimPrefix(job.ResultRef, "artifact://") if artifactID == "" || artifactID == job.ResultRef || artifactID != expectedArtifactID { return validationError("distribution build result must reference the expected artifact") } artifact, err := svc.store.Artifacts().Get(artifactID) if err != nil { return err } if artifact.State != domain.ArtifactStateAvailable || artifact.OwnerKind != domain.ArtifactOwnerKindJob || artifact.OwnerID != job.ID { return validationError("distribution build artifact is unavailable or outside the job scope") } return nil } func executableFilename(base string, targetOS string) string { if targetOS == "windows" { return base + ".exe" } return base }