From c07994c41748a69d0cfe1502e26ed98d6d256537 Mon Sep 17 00:00:00 2001 From: npc0-hue Date: Thu, 23 Jul 2026 15:25:27 +0800 Subject: [PATCH] Fix run artifact package download names --- platform/service/artifact_download.go | 69 ++++++++++++++++++++++++-- platform/service/distributions_test.go | 31 ++++++++++++ 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/platform/service/artifact_download.go b/platform/service/artifact_download.go index 848e25d..4e23276 100644 --- a/platform/service/artifact_download.go +++ b/platform/service/artifact_download.go @@ -39,12 +39,13 @@ func (svc *CoreService) OpenArtifactDownloadForSession(sessionID string, request return domain.ArtifactDownloadReference{}, validationError("artifact must be available before download") } + filename, contentType := svc.artifactDownloadPresentation(artifact) reference := domain.ArtifactDownloadReference{ ArtifactID: artifact.ID, OwnerKind: artifact.OwnerKind, OwnerID: artifact.OwnerID, - Filename: artifactDownloadFilename(artifact.ID), - ContentType: "application/octet-stream", + Filename: filename, + ContentType: contentType, SizeBytes: artifact.SizeBytes, Checksum: artifact.Checksum, State: artifact.State, @@ -98,10 +99,11 @@ func (svc *CoreService) ReadArtifactContentForSession(sessionID string, request } end := int(request.Offset) + limit part := domain.CopyBytes(payload[int(request.Offset):end]) + filename, contentType := svc.artifactDownloadPresentation(artifact) content := domain.ArtifactContent{ ArtifactID: artifact.ID, - Filename: artifactDownloadFilename(artifact.ID), - ContentType: "application/octet-stream", + Filename: filename, + ContentType: contentType, Offset: request.Offset, SizeBytes: int64(len(part)), TotalSizeBytes: artifact.SizeBytes, @@ -205,3 +207,62 @@ func artifactDownloadFilename(artifactID string) string { } return fmt.Sprintf("%s.bin", name) } + +func (svc *CoreService) artifactDownloadPresentation(artifact domain.Artifact) (string, string) { + if filename, contentType, ok := svc.distributionArtifactDownloadPresentation(artifact.ID); ok { + return filename, contentType + } + return artifactDownloadFilename(artifact.ID), "application/octet-stream" +} + +func (svc *CoreService) distributionArtifactDownloadPresentation(artifactID string) (string, string, bool) { + runs, err := svc.store.RunDistributions().List(domain.RunDistributionFilter{}) + if err == nil { + for _, distribution := range runs { + if distribution.ArtifactID == artifactID { + return distributionPackageFilename("run", distribution.TargetOS, distribution.TargetArch, distribution.PackageFormat), packageContentType(distribution.PackageFormat), true + } + } + } + clients, err := svc.store.ClientManagerDistributions().List(domain.ClientManagerDistributionFilter{}) + if err == nil { + for _, distribution := range clients { + if distribution.ArtifactID == artifactID { + format := packageFormatForTarget(distribution.TargetOS) + return distributionPackageFilename(distribution.ProfileKey, distribution.TargetOS, distribution.TargetArch, format), packageContentType(format), true + } + } + } + return "", "", false +} + +func distributionPackageFilename(base string, targetOS string, targetArch string, packageFormat string) string { + name := sanitizeIDPart(base) + os := sanitizeIDPart(targetOS) + arch := sanitizeIDPart(targetArch) + format := strings.TrimPrefix(strings.TrimSpace(packageFormat), ".") + if name == "" { + name = "artifact" + } + if os != "" { + name += "-" + os + } + if arch != "" { + name += "-" + arch + } + if format == "" { + format = "bin" + } + return name + "." + format +} + +func packageContentType(packageFormat string) string { + switch strings.TrimSpace(packageFormat) { + case "zip": + return "application/zip" + case "tar.gz", "tgz": + return "application/gzip" + default: + return "application/octet-stream" + } +} diff --git a/platform/service/distributions_test.go b/platform/service/distributions_test.go index 4c3c7ce..550e5a5 100644 --- a/platform/service/distributions_test.go +++ b/platform/service/distributions_test.go @@ -190,6 +190,37 @@ func TestCoreServiceDistributionBuildRejectsPrematureSuccessAndCanRetryAfterUplo } } +func TestCoreServiceRunDistributionDownloadUsesTargetPackageName(t *testing.T) { + svc, session, instance := newDistributionTestFixture(t) + distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ + ServerInstanceID: instance.ID, + TargetOS: "windows", + TargetArch: "amd64", + IdempotencyKey: "idem-run-download-name", + }) + if err != nil { + t.Fatalf("generate run distribution: %v", err) + } + payload := []byte("windows zipped run package") + completeDistributionBuild(t, svc, distribution, payload) + + reference, err := svc.OpenArtifactDownloadForSession(session, domain.ArtifactDownloadReferenceRequest{ArtifactID: distribution.ArtifactID}) + if err != nil { + t.Fatalf("open run artifact download: %v", err) + } + if reference.Filename != "run-windows-amd64.zip" || reference.ContentType != "application/zip" { + t.Fatalf("expected windows run package metadata, got %+v", reference) + } + + content, err := svc.ReadArtifactContentForSession(session, domain.ArtifactContentRequest{ArtifactID: distribution.ArtifactID, Limit: len(payload)}) + if err != nil { + t.Fatalf("read run artifact content: %v", err) + } + if content.Filename != reference.Filename || content.ContentType != reference.ContentType { + t.Fatalf("expected content metadata to match reference, reference=%+v content=%+v", reference, content) + } +} + func TestCoreServiceRunDistributionRetryReusesPartialArtifact(t *testing.T) { svc, session, instance := newDistributionTestFixture(t) key, plainKey, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentRun, "")