@platforma-sdk/package-builder-lib 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/assets/conda-dockerfile.template +45 -0
  2. package/assets/python-dockerfile.template +22 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +33 -0
  4. package/dist/_virtual/_rolldown/runtime.js +13 -0
  5. package/dist/core/archive.cjs +52 -0
  6. package/dist/core/archive.cjs.map +1 -0
  7. package/dist/core/archive.js +47 -0
  8. package/dist/core/archive.js.map +1 -0
  9. package/dist/core/conda/builder.cjs +226 -0
  10. package/dist/core/conda/builder.cjs.map +1 -0
  11. package/dist/core/conda/builder.js +220 -0
  12. package/dist/core/conda/builder.js.map +1 -0
  13. package/dist/core/conda/validate-spec.cjs +76 -0
  14. package/dist/core/conda/validate-spec.cjs.map +1 -0
  15. package/dist/core/conda/validate-spec.js +73 -0
  16. package/dist/core/conda/validate-spec.js.map +1 -0
  17. package/dist/core/core.cjs +403 -0
  18. package/dist/core/core.cjs.map +1 -0
  19. package/dist/core/core.d.ts +82 -0
  20. package/dist/core/core.d.ts.map +1 -0
  21. package/dist/core/core.js +397 -0
  22. package/dist/core/core.js.map +1 -0
  23. package/dist/core/docker-conda.cjs +75 -0
  24. package/dist/core/docker-conda.cjs.map +1 -0
  25. package/dist/core/docker-conda.js +71 -0
  26. package/dist/core/docker-conda.js.map +1 -0
  27. package/dist/core/docker-python.cjs +147 -0
  28. package/dist/core/docker-python.cjs.map +1 -0
  29. package/dist/core/docker-python.js +144 -0
  30. package/dist/core/docker-python.js.map +1 -0
  31. package/dist/core/docker.cjs +223 -0
  32. package/dist/core/docker.cjs.map +1 -0
  33. package/dist/core/docker.js +207 -0
  34. package/dist/core/docker.js.map +1 -0
  35. package/dist/core/envs.cjs +58 -0
  36. package/dist/core/envs.cjs.map +1 -0
  37. package/dist/core/envs.d.ts +26 -0
  38. package/dist/core/envs.d.ts.map +1 -0
  39. package/dist/core/envs.js +51 -0
  40. package/dist/core/envs.js.map +1 -0
  41. package/dist/core/package-info.cjs +450 -0
  42. package/dist/core/package-info.cjs.map +1 -0
  43. package/dist/core/package-info.d.ts +57 -0
  44. package/dist/core/package-info.d.ts.map +1 -0
  45. package/dist/core/package-info.js +447 -0
  46. package/dist/core/package-info.js.map +1 -0
  47. package/dist/core/paths.cjs +15 -0
  48. package/dist/core/paths.cjs.map +1 -0
  49. package/dist/core/paths.js +14 -0
  50. package/dist/core/paths.js.map +1 -0
  51. package/dist/core/resolver.cjs +99 -0
  52. package/dist/core/resolver.cjs.map +1 -0
  53. package/dist/core/resolver.js +95 -0
  54. package/dist/core/resolver.js.map +1 -0
  55. package/dist/core/schemas/artifacts.cjs +136 -0
  56. package/dist/core/schemas/artifacts.cjs.map +1 -0
  57. package/dist/core/schemas/artifacts.d.ts +280 -0
  58. package/dist/core/schemas/artifacts.d.ts.map +1 -0
  59. package/dist/core/schemas/artifacts.js +122 -0
  60. package/dist/core/schemas/artifacts.js.map +1 -0
  61. package/dist/core/schemas/entrypoints.cjs +43 -0
  62. package/dist/core/schemas/entrypoints.cjs.map +1 -0
  63. package/dist/core/schemas/entrypoints.d.ts +32 -0
  64. package/dist/core/schemas/entrypoints.d.ts.map +1 -0
  65. package/dist/core/schemas/entrypoints.js +41 -0
  66. package/dist/core/schemas/entrypoints.js.map +1 -0
  67. package/dist/core/schemas/sw-json.cjs +111 -0
  68. package/dist/core/schemas/sw-json.cjs.map +1 -0
  69. package/dist/core/schemas/sw-json.js +110 -0
  70. package/dist/core/schemas/sw-json.js.map +1 -0
  71. package/dist/core/storage.cjs +86 -0
  72. package/dist/core/storage.cjs.map +1 -0
  73. package/dist/core/storage.js +83 -0
  74. package/dist/core/storage.js.map +1 -0
  75. package/dist/core/sw-json-render.cjs +327 -0
  76. package/dist/core/sw-json-render.cjs.map +1 -0
  77. package/dist/core/sw-json-render.js +322 -0
  78. package/dist/core/sw-json-render.js.map +1 -0
  79. package/dist/core/util.cjs +313 -0
  80. package/dist/core/util.cjs.map +1 -0
  81. package/dist/core/util.d.ts +66 -0
  82. package/dist/core/util.d.ts.map +1 -0
  83. package/dist/core/util.js +281 -0
  84. package/dist/core/util.js.map +1 -0
  85. package/dist/defaults.cjs +54 -0
  86. package/dist/defaults.cjs.map +1 -0
  87. package/dist/defaults.d.ts +21 -0
  88. package/dist/defaults.d.ts.map +1 -0
  89. package/dist/defaults.js +37 -0
  90. package/dist/defaults.js.map +1 -0
  91. package/dist/index.cjs +31 -0
  92. package/dist/index.cjs.map +1 -0
  93. package/dist/index.d.ts +20 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +12 -0
  96. package/dist/index.js.map +1 -0
  97. package/package.json +56 -0
  98. package/src/__test__/Dockerfile +1 -0
  99. package/src/__test__/docker.test.ts +67 -0
  100. package/src/__test__/package.json +39 -0
  101. package/src/__test__/python-example/main.py +1 -0
  102. package/src/__test__/python-example/requirements.txt +0 -0
  103. package/src/__test__/vite.config.mts +7 -0
  104. package/src/core/__tests__/docker-conda.test.ts +145 -0
  105. package/src/core/__tests__/docker-entrypoint-generation.test.ts +119 -0
  106. package/src/core/__tests__/docker-python.test.ts +374 -0
  107. package/src/core/__tests__/publish-docker-images.test.ts +309 -0
  108. package/src/core/archive.ts +102 -0
  109. package/src/core/conda/builder.test.ts +71 -0
  110. package/src/core/conda/builder.ts +384 -0
  111. package/src/core/conda/validate-spec.test.ts +213 -0
  112. package/src/core/conda/validate-spec.ts +107 -0
  113. package/src/core/core.ts +835 -0
  114. package/src/core/docker-conda.ts +149 -0
  115. package/src/core/docker-python.ts +283 -0
  116. package/src/core/docker.ts +300 -0
  117. package/src/core/envs.ts +31 -0
  118. package/src/core/package-info.test.ts +82 -0
  119. package/src/core/package-info.ts +843 -0
  120. package/src/core/paths.ts +16 -0
  121. package/src/core/resolver.ts +195 -0
  122. package/src/core/schemas/artifacts.test.ts +72 -0
  123. package/src/core/schemas/artifacts.ts +279 -0
  124. package/src/core/schemas/entrypoints.test.ts +171 -0
  125. package/src/core/schemas/entrypoints.ts +150 -0
  126. package/src/core/schemas/sw-json.ts +216 -0
  127. package/src/core/schemas/test-artifacts.ts +362 -0
  128. package/src/core/storage.ts +125 -0
  129. package/src/core/sw-json-render.test.ts +215 -0
  130. package/src/core/sw-json-render.ts +682 -0
  131. package/src/core/util.ts +390 -0
  132. package/src/defaults.ts +38 -0
  133. package/src/index.ts +31 -0
  134. package/src/requirements.txt +1 -0
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { dockerSchema } from "../core/schemas/artifacts";
3
+
4
+ describe("Docker Package Schema Validation", () => {
5
+ it('should reject context with "./"', () => {
6
+ const invalidPackage = {
7
+ type: "docker" as const,
8
+ context: "./",
9
+ dockerfile: "Dockerfile",
10
+ };
11
+
12
+ const result = dockerSchema.safeParse(invalidPackage);
13
+ expect(result.success).toBe(false);
14
+ if (!result.success) {
15
+ expect(result.error.issues[0].message).toContain('Context cannot be "./" or "."');
16
+ }
17
+ });
18
+
19
+ it('should reject context with "."', () => {
20
+ const invalidPackage = {
21
+ type: "docker" as const,
22
+ context: ".",
23
+ dockerfile: "Dockerfile",
24
+ };
25
+
26
+ const result = dockerSchema.safeParse(invalidPackage);
27
+ expect(result.success).toBe(false);
28
+ if (!result.success) {
29
+ expect(result.error.issues[0].message).toContain('Context cannot be "./" or "."');
30
+ }
31
+ });
32
+
33
+ it("should accept valid context paths", () => {
34
+ const validPackages = [
35
+ {
36
+ type: "docker" as const,
37
+ context: "docker-context",
38
+ dockerfile: "Dockerfile",
39
+ },
40
+ {
41
+ type: "docker" as const,
42
+ context: "/absolute/path",
43
+ dockerfile: "Dockerfile",
44
+ },
45
+ {
46
+ type: "docker" as const,
47
+ context: "../relative/path",
48
+ dockerfile: "Dockerfile",
49
+ },
50
+ ];
51
+
52
+ for (const validPackage of validPackages) {
53
+ const result = dockerSchema.safeParse(validPackage);
54
+ expect(result.success).toBe(true);
55
+ }
56
+ });
57
+
58
+ it("should accept docker package without optional fields", () => {
59
+ const minimalPackage = {
60
+ type: "docker" as const,
61
+ context: "valid-context",
62
+ };
63
+
64
+ const result = dockerSchema.safeParse(minimalPackage);
65
+ expect(result.success).toBe(true);
66
+ });
67
+ });
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@platforma-open/docker-build-test",
3
+ "version": "0.0.1",
4
+ "dependencies": {
5
+ "@platforma-open/milaboratories.runenv-python-3": "catalog:"
6
+ },
7
+ "block-software": {
8
+ "entrypoints": {
9
+ "test-docker": {
10
+ "binary": {
11
+ "artifact": {
12
+ "type": "python",
13
+ "registry": "platforma-open",
14
+ "environment": "@platforma-open/milaboratories.runenv-python-3:3.12.10",
15
+ "dependencies": {
16
+ "toolset": "pip",
17
+ "requirements": "requirements.txt"
18
+ },
19
+ "root": "./python-example"
20
+ },
21
+ "cmd": [
22
+ "aaaa"
23
+ ]
24
+ },
25
+ "docker": {
26
+ "artifact": {
27
+ "type": "docker",
28
+ "dockerfile": "Dockerfile",
29
+ "context": "docker-context"
30
+ },
31
+ "cmd": [
32
+ "echo",
33
+ "hello"
34
+ ]
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
@@ -0,0 +1 @@
1
+ print("Hello, World!")
File without changes
@@ -0,0 +1,7 @@
1
+ export default {
2
+ build: {
3
+ rolldownOptions: {
4
+ external: ["package.json"],
5
+ },
6
+ },
7
+ };
@@ -0,0 +1,145 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import * as os from "node:os";
5
+ import { prepareDockerOptions } from "../docker-conda";
6
+ import type * as artifacts from "../schemas/artifacts";
7
+ import type winston from "winston";
8
+
9
+ import * as defaults from "../../defaults";
10
+ import * as util from "../util";
11
+
12
+ // Mock the os module
13
+ vi.mock("node:os", () => ({
14
+ platform: vi.fn(),
15
+ tmpdir: vi.fn(),
16
+ arch: vi.fn().mockReturnValue("x64"),
17
+ }));
18
+
19
+ // Mock winston logger
20
+ const mockLogger = {
21
+ info: vi.fn(),
22
+ warn: vi.fn(),
23
+ debug: vi.fn(),
24
+ error: vi.fn(),
25
+ } as unknown as winston.Logger;
26
+
27
+ // Mock Python package for testing - will be set in beforeEach
28
+ let mockCondaPackage: artifacts.condaType;
29
+
30
+ describe("Docker Conda Functions", () => {
31
+ let tempDir: string;
32
+ let testPackageRoot: string;
33
+
34
+ beforeEach(() => {
35
+ // Set up OS mocks
36
+ vi.mocked(os.platform).mockReturnValue("linux");
37
+ vi.mocked(os.tmpdir).mockReturnValue("/tmp");
38
+
39
+ // Create temporary directories for testing
40
+ tempDir = fs.mkdtempSync(path.join("/tmp", "python-docker-test"));
41
+ testPackageRoot = path.join(tempDir, "package");
42
+
43
+ fs.mkdirSync(testPackageRoot, { recursive: true });
44
+
45
+ // Create src directory for the root property
46
+ fs.mkdirSync(path.join(testPackageRoot, "src"), { recursive: true });
47
+
48
+ // Create a mock spec file
49
+ fs.writeFileSync(
50
+ path.join(testPackageRoot, "some-spec.yaml"),
51
+ `
52
+ name: my-env
53
+ channels: []
54
+ dependencies: []
55
+ `,
56
+ );
57
+
58
+ // Initialize mockPythonPackage with absolute path
59
+ mockCondaPackage = {
60
+ type: "conda",
61
+ name: "test-conda-package",
62
+ version: "1.0.0",
63
+ registry: { name: "test" },
64
+ roots: {
65
+ "linux-x64": path.join(testPackageRoot, "src"), // Use absolute path
66
+ },
67
+ "docker-registry": "test-docker-registry",
68
+
69
+ "micromamba-version": "some-micromamba-version",
70
+ "conda-root-dir": "some-conda-root-dir",
71
+ spec: "some-spec.yaml",
72
+ pkg: "/app",
73
+ };
74
+
75
+ // Clear all mocks
76
+ vi.clearAllMocks();
77
+ });
78
+
79
+ afterEach(() => {
80
+ // Clean up temporary directories
81
+ try {
82
+ fs.rmSync(tempDir, { recursive: true, force: true });
83
+ } catch {
84
+ // Ignore cleanup errors
85
+ }
86
+ });
87
+
88
+ describe("prepareDockerOptions", () => {
89
+ it("should create dist/docker directories recursively", () => {
90
+ // Verify directories don't exist initially
91
+ expect(fs.existsSync(path.join(testPackageRoot, "dist"))).toBe(false);
92
+
93
+ const result = prepareDockerOptions(
94
+ mockLogger,
95
+ testPackageRoot,
96
+ "artifact-id",
97
+ mockCondaPackage,
98
+ util.currentArch(),
99
+ );
100
+
101
+ // Verify directories were created
102
+ expect(fs.existsSync(path.join(testPackageRoot, "dist", "docker"))).toBe(true);
103
+
104
+ // Verify result structure
105
+ expect(result).toMatchObject({
106
+ context: expect.stringContaining(testPackageRoot) as string,
107
+ dockerfile: expect.stringContaining("Dockerfile") as string,
108
+ entrypoint: [],
109
+ });
110
+
111
+ // Verify Dockerfile was created
112
+ expect(fs.existsSync(result.dockerfile)).toBe(true);
113
+ expect(fs.existsSync(result.context)).toBe(true);
114
+ });
115
+
116
+ it("should generate Dockerfile with correct content", () => {
117
+ const result = prepareDockerOptions(
118
+ mockLogger,
119
+ testPackageRoot,
120
+ "artifact-id",
121
+ mockCondaPackage,
122
+ util.currentArch(),
123
+ );
124
+
125
+ expect(result).toMatchObject({
126
+ context: expect.stringContaining(testPackageRoot) as string,
127
+ dockerfile: expect.stringContaining("Dockerfile") as string,
128
+ entrypoint: [],
129
+ });
130
+
131
+ // Verify Dockerfile was created
132
+ expect(fs.existsSync(result.dockerfile)).toBe(true);
133
+ expect(fs.existsSync(result.context)).toBe(true);
134
+
135
+ const dockerfileContent = fs.readFileSync(result.dockerfile, "utf-8");
136
+
137
+ // Check essential Dockerfile components
138
+ expect(dockerfileContent).toContain(`FROM ${defaults.CONDA_DOCKER_BASE_IMAGE}`);
139
+ expect(dockerfileContent).toContain("COPY . /app");
140
+ expect(dockerfileContent).toContain("RUN micromamba");
141
+ expect(dockerfileContent).toContain(defaults.CONDA_FROZEN_ENV_SPEC_FILE);
142
+ expect(dockerfileContent).toContain(defaults.CONDA_DATA_LOCATION);
143
+ });
144
+ });
145
+ });
@@ -0,0 +1,119 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { PackageInfo } from "../package-info";
5
+ import type winston from "winston";
6
+
7
+ // Mock winston logger
8
+ const mockLogger = {
9
+ info: vi.fn(),
10
+ warn: vi.fn(),
11
+ debug: vi.fn(),
12
+ error: vi.fn(),
13
+ } as unknown as winston.Logger;
14
+
15
+ // Mock the docker-python module
16
+ vi.mock("../docker-python", () => ({
17
+ prepareDockerOptions: vi.fn().mockReturnValue({
18
+ context: "/tmp/test-context",
19
+ dockerfile: "/tmp/test-dockerfile",
20
+ entrypoint: [],
21
+ }),
22
+ }));
23
+
24
+ describe("Docker Entrypoint Generation", () => {
25
+ let tempDir: string;
26
+ let testPackageRoot: string;
27
+
28
+ beforeEach(() => {
29
+ // Create temporary directories for testing
30
+ tempDir = fs.mkdtempSync(path.join("/tmp", "docker-entrypoint-test-"));
31
+ testPackageRoot = path.join(tempDir, "package");
32
+
33
+ fs.mkdirSync(testPackageRoot, { recursive: true });
34
+ vi.clearAllMocks();
35
+ });
36
+
37
+ afterEach(() => {
38
+ // Clean up temporary directories
39
+ try {
40
+ fs.rmSync(tempDir, { recursive: true, force: true });
41
+ } catch {
42
+ // Ignore cleanup errors
43
+ }
44
+ });
45
+
46
+ it("should generate Docker entrypoint for Python binary entrypoint", () => {
47
+ const packageJson = {
48
+ name: "test-python-package",
49
+ version: "1.0.0",
50
+ "block-software": {
51
+ entrypoints: {
52
+ script1: {
53
+ binary: {
54
+ artifact: {
55
+ type: "python",
56
+ environment: "@platforma-open/milaboratories.runenv-python-3:3.12.10",
57
+ root: "./src",
58
+ registry: "test-registry",
59
+ },
60
+ cmd: ["{pkg}/script1"],
61
+ },
62
+ },
63
+ },
64
+ },
65
+ };
66
+
67
+ fs.writeFileSync(
68
+ path.join(testPackageRoot, "package.json"),
69
+ JSON.stringify(packageJson, null, 2),
70
+ );
71
+
72
+ const packageInfo = new PackageInfo(mockLogger, { packageRoot: testPackageRoot });
73
+ const entrypoints = packageInfo.entrypoints;
74
+
75
+ expect(entrypoints.has("script1")).toBe(true);
76
+ expect(entrypoints.has("script1:docker")).toBe(true);
77
+
78
+ const dockerEntrypoint = entrypoints.get("script1:docker")!;
79
+ expect(dockerEntrypoint.type).toBe("software");
80
+ if (dockerEntrypoint.type === "software") {
81
+ expect(dockerEntrypoint.artifact.type).toBe("docker");
82
+ expect(dockerEntrypoint.cmd).toEqual(["{pkg}/script1"]);
83
+ }
84
+ });
85
+
86
+ it("should not generate Docker entrypoint for non-Python packages", () => {
87
+ const packageJson = {
88
+ name: "test-binary-package",
89
+ version: "1.0.0",
90
+ "block-software": {
91
+ entrypoints: {
92
+ script1: {
93
+ binary: {
94
+ artifact: {
95
+ type: "binary",
96
+ roots: {
97
+ "linux-x64": "./src",
98
+ },
99
+ registry: "test-registry",
100
+ },
101
+ cmd: ["{pkg}/script1"],
102
+ },
103
+ },
104
+ },
105
+ },
106
+ };
107
+
108
+ fs.writeFileSync(
109
+ path.join(testPackageRoot, "package.json"),
110
+ JSON.stringify(packageJson, null, 2),
111
+ );
112
+
113
+ const packageInfo = new PackageInfo(mockLogger, { packageRoot: testPackageRoot });
114
+ const entrypoints = packageInfo.entrypoints;
115
+
116
+ expect(entrypoints.has("script1")).toBe(true);
117
+ expect(entrypoints.has("script1:docker")).toBe(false);
118
+ });
119
+ });