@stream44.studio/t44-docker.com 0.1.0-rc.3
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.
- package/.dco-signatures +9 -0
- package/.github/workflows/dco.yaml +12 -0
- package/.github/workflows/gordian-open-integrity.yaml +13 -0
- package/.github/workflows/test.yaml +29 -0
- package/.o/GordianOpenIntegrity-CurrentLifehash.svg +1026 -0
- package/.o/GordianOpenIntegrity-InceptionLifehash.svg +1026 -0
- package/.o/GordianOpenIntegrity.yaml +21 -0
- package/.o/stream44.studio/assets/Icon-v1.svg +1170 -0
- package/.repo-identifier +1 -0
- package/DCO.md +34 -0
- package/LICENSE.txt +23 -0
- package/README.md +73 -0
- package/caps/Cli.test.ts +116 -0
- package/caps/Cli.ts +134 -0
- package/caps/Container.test.ts +168 -0
- package/caps/Container.ts +484 -0
- package/caps/ContainerContext.test.ts +78 -0
- package/caps/ContainerContext.ts +111 -0
- package/caps/Containers.test.ts +59 -0
- package/caps/Containers.ts +47 -0
- package/caps/Hub.test.ts +107 -0
- package/caps/Hub.ts +367 -0
- package/caps/Image/tpl/Dockerfile.alpine +40 -0
- package/caps/Image/tpl/Dockerfile.distroless +45 -0
- package/caps/Image/tpl/package.json +8 -0
- package/caps/Image.test.ts +269 -0
- package/caps/Image.ts +623 -0
- package/caps/ImageContext.test.ts +130 -0
- package/caps/ImageContext.ts +126 -0
- package/caps/Project.test.ts +267 -0
- package/caps/Project.ts +304 -0
- package/lib/waitForFetch.ts +95 -0
- package/package.json +19 -0
- package/structs/Hub/WorkspaceConnectionConfig.ts +53 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env bun test
|
|
2
|
+
|
|
3
|
+
import * as bunTest from 'bun:test'
|
|
4
|
+
import { run } from 't44/standalone-rt'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
test: { describe, it, expect },
|
|
9
|
+
imageContext,
|
|
10
|
+
} = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
|
|
11
|
+
const spine = await encapsulate({
|
|
12
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
13
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
14
|
+
'#': {
|
|
15
|
+
test: {
|
|
16
|
+
type: CapsulePropertyTypes.Mapping,
|
|
17
|
+
value: 't44/caps/ProjectTest',
|
|
18
|
+
options: {
|
|
19
|
+
'#': {
|
|
20
|
+
bunTest,
|
|
21
|
+
env: {}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
imageContext: {
|
|
26
|
+
type: CapsulePropertyTypes.Mapping,
|
|
27
|
+
value: './ImageContext',
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}, {
|
|
32
|
+
importMeta: import.meta,
|
|
33
|
+
importStack: makeImportStack(),
|
|
34
|
+
capsuleName: '@stream44.studio/t44-docker.com/caps/ImageContext.test'
|
|
35
|
+
})
|
|
36
|
+
return { spine }
|
|
37
|
+
}, async ({ spine, apis }: any) => {
|
|
38
|
+
return apis[spine.capsuleSourceLineRef]
|
|
39
|
+
}, {
|
|
40
|
+
importMeta: import.meta
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('ImageContext Capsule', () => {
|
|
44
|
+
|
|
45
|
+
describe('DOCKERFILE_VARIANTS', () => {
|
|
46
|
+
it('should have correct structure', () => {
|
|
47
|
+
expect(imageContext.DOCKERFILE_VARIANTS).toBeDefined();
|
|
48
|
+
expect(imageContext.DOCKERFILE_VARIANTS.alpine).toBeDefined();
|
|
49
|
+
expect(imageContext.DOCKERFILE_VARIANTS.distroless).toBeDefined();
|
|
50
|
+
expect(imageContext.DOCKERFILE_VARIANTS.alpine.dockerfile).toBe('Dockerfile.alpine');
|
|
51
|
+
expect(imageContext.DOCKERFILE_VARIANTS.alpine.tagSuffix).toBe('alpine');
|
|
52
|
+
expect(imageContext.DOCKERFILE_VARIANTS.distroless.dockerfile).toBe('Dockerfile.distroless');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('getImageTag', () => {
|
|
57
|
+
it('should compute correct image tag', () => {
|
|
58
|
+
imageContext.organization = 'test-org';
|
|
59
|
+
imageContext.repository = 'test-repo';
|
|
60
|
+
const tag = imageContext.getImageTag({ variant: 'alpine', arch: 'linux-arm64' });
|
|
61
|
+
expect(tag).toBe('test-org/test-repo:alpine-arm64');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should compute tag for x64', () => {
|
|
65
|
+
imageContext.organization = 'test-org';
|
|
66
|
+
imageContext.repository = 'test-repo';
|
|
67
|
+
const tag = imageContext.getImageTag({ variant: 'alpine', arch: 'linux-x64' });
|
|
68
|
+
expect(tag).toBe('test-org/test-repo:alpine-amd64');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should throw without variant/arch', () => {
|
|
72
|
+
imageContext.variant = undefined;
|
|
73
|
+
imageContext.arch = undefined;
|
|
74
|
+
expect(() => imageContext.getImageTag()).toThrow('variant and arch must be set');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should use instance variant/arch when not passed as opts', () => {
|
|
78
|
+
imageContext.organization = 'test-org';
|
|
79
|
+
imageContext.repository = 'test-repo';
|
|
80
|
+
imageContext.variant = 'alpine';
|
|
81
|
+
imageContext.arch = 'linux-arm64';
|
|
82
|
+
const tag = imageContext.getImageTag();
|
|
83
|
+
expect(tag).toBe('test-org/test-repo:alpine-arm64');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('getLatestImageTag', () => {
|
|
88
|
+
it('should append -latest suffix', () => {
|
|
89
|
+
imageContext.organization = 'test-org';
|
|
90
|
+
imageContext.repository = 'test-repo';
|
|
91
|
+
const tag = imageContext.getLatestImageTag({ variant: 'alpine', arch: 'linux-arm64' });
|
|
92
|
+
expect(tag).toBe('test-org/test-repo:alpine-arm64-latest');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('getBuildContextDir', () => {
|
|
97
|
+
it('should compute correct build context dir from buildContextBaseDir', () => {
|
|
98
|
+
imageContext.buildContextBaseDir = '/tmp/build';
|
|
99
|
+
const dir = imageContext.getBuildContextDir({ variant: 'alpine' });
|
|
100
|
+
expect(dir).toBe('/tmp/build/alpine');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should derive buildContextBaseDir from appBaseDir when not set', () => {
|
|
104
|
+
imageContext.buildContextBaseDir = '';
|
|
105
|
+
imageContext.appBaseDir = '/tmp/myapp';
|
|
106
|
+
const dir = imageContext.getBuildContextDir({ variant: 'alpine' });
|
|
107
|
+
expect(dir).toBe(join('/tmp/myapp', '.~o/t44-docker.com', 'alpine'));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should throw without variant', () => {
|
|
111
|
+
imageContext.variant = undefined;
|
|
112
|
+
expect(() => imageContext.getBuildContextDir()).toThrow('variant must be set');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should use instance variant when not passed as opts', () => {
|
|
116
|
+
imageContext.buildContextBaseDir = '/tmp/build';
|
|
117
|
+
imageContext.variant = 'distroless';
|
|
118
|
+
const dir = imageContext.getBuildContextDir();
|
|
119
|
+
expect(dir).toBe('/tmp/build/distroless');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('templateDir', () => {
|
|
124
|
+
it('should default to the bundled tpl directory', () => {
|
|
125
|
+
expect(imageContext.templateDir).toBeTruthy();
|
|
126
|
+
expect(imageContext.templateDir).toContain('Image');
|
|
127
|
+
expect(imageContext.templateDir).toContain('tpl');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { join } from 'path'
|
|
2
|
+
|
|
3
|
+
const DEFAULT_TEMPLATE_DIR = join(__dirname, 'Image', 'tpl');
|
|
4
|
+
|
|
5
|
+
const DOCKERFILE_VARIANTS = {
|
|
6
|
+
alpine: { dockerfile: 'Dockerfile.alpine', tagSuffix: 'alpine', variantDir: 'alpine', variant: 'alpine' },
|
|
7
|
+
distroless: { dockerfile: 'Dockerfile.distroless', tagSuffix: 'distroless', variantDir: 'distroless', variant: 'distroless' },
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export async function capsule({
|
|
11
|
+
encapsulate,
|
|
12
|
+
CapsulePropertyTypes,
|
|
13
|
+
makeImportStack
|
|
14
|
+
}: {
|
|
15
|
+
encapsulate: any
|
|
16
|
+
CapsulePropertyTypes: any
|
|
17
|
+
makeImportStack: any
|
|
18
|
+
}) {
|
|
19
|
+
|
|
20
|
+
return encapsulate({
|
|
21
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
22
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
23
|
+
'#': {
|
|
24
|
+
cli: {
|
|
25
|
+
type: CapsulePropertyTypes.Mapping,
|
|
26
|
+
value: './Cli',
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
verbose: {
|
|
30
|
+
type: CapsulePropertyTypes.Literal,
|
|
31
|
+
value: false,
|
|
32
|
+
},
|
|
33
|
+
organization: {
|
|
34
|
+
type: CapsulePropertyTypes.Literal,
|
|
35
|
+
value: '' as string,
|
|
36
|
+
},
|
|
37
|
+
repository: {
|
|
38
|
+
type: CapsulePropertyTypes.Literal,
|
|
39
|
+
value: '' as string,
|
|
40
|
+
},
|
|
41
|
+
appBaseDir: {
|
|
42
|
+
type: CapsulePropertyTypes.Literal,
|
|
43
|
+
value: '' as string,
|
|
44
|
+
},
|
|
45
|
+
buildContextBaseDir: {
|
|
46
|
+
type: CapsulePropertyTypes.Literal,
|
|
47
|
+
value: '' as string,
|
|
48
|
+
},
|
|
49
|
+
templateDir: {
|
|
50
|
+
type: CapsulePropertyTypes.Literal,
|
|
51
|
+
value: DEFAULT_TEMPLATE_DIR as string,
|
|
52
|
+
},
|
|
53
|
+
variant: {
|
|
54
|
+
type: CapsulePropertyTypes.Literal,
|
|
55
|
+
value: undefined as string | undefined,
|
|
56
|
+
},
|
|
57
|
+
arch: {
|
|
58
|
+
type: CapsulePropertyTypes.Literal,
|
|
59
|
+
value: undefined as string | undefined,
|
|
60
|
+
},
|
|
61
|
+
buildScriptName: {
|
|
62
|
+
type: CapsulePropertyTypes.Literal,
|
|
63
|
+
value: undefined as string | undefined,
|
|
64
|
+
},
|
|
65
|
+
files: {
|
|
66
|
+
type: CapsulePropertyTypes.Literal,
|
|
67
|
+
value: undefined as Record<string, any> | undefined,
|
|
68
|
+
},
|
|
69
|
+
attestations: {
|
|
70
|
+
type: CapsulePropertyTypes.Literal,
|
|
71
|
+
value: undefined as { sbom?: boolean; provenance?: boolean } | undefined,
|
|
72
|
+
},
|
|
73
|
+
tagLatest: {
|
|
74
|
+
type: CapsulePropertyTypes.Literal,
|
|
75
|
+
value: false,
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
DOCKERFILE_VARIANTS: {
|
|
79
|
+
type: CapsulePropertyTypes.Constant,
|
|
80
|
+
value: DOCKERFILE_VARIANTS,
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Compute image tag from org/repo/variant/arch
|
|
85
|
+
*/
|
|
86
|
+
getImageTag: {
|
|
87
|
+
type: CapsulePropertyTypes.Function,
|
|
88
|
+
value: function (this: any, opts?: { variant?: string; arch?: string }): string {
|
|
89
|
+
const variant = opts?.variant ?? this.variant;
|
|
90
|
+
const arch = opts?.arch ?? this.arch;
|
|
91
|
+
if (!variant || !arch) {
|
|
92
|
+
throw new Error('variant and arch must be set to get image tag');
|
|
93
|
+
}
|
|
94
|
+
const variantInfo = DOCKERFILE_VARIANTS[variant as keyof typeof DOCKERFILE_VARIANTS];
|
|
95
|
+
const archInfo = this.cli.DOCKER_ARCHS[arch as keyof typeof this.cli.DOCKER_ARCHS];
|
|
96
|
+
return `${this.organization}/${this.repository}:${variantInfo.tagSuffix}-${archInfo.arch}`;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
getLatestImageTag: {
|
|
101
|
+
type: CapsulePropertyTypes.Function,
|
|
102
|
+
value: function (this: any, opts?: { variant?: string; arch?: string }): string {
|
|
103
|
+
return `${this.getImageTag(opts)}-latest`;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
getBuildContextDir: {
|
|
108
|
+
type: CapsulePropertyTypes.Function,
|
|
109
|
+
value: function (this: any, opts?: { variant?: string }): string {
|
|
110
|
+
const variant = opts?.variant ?? this.variant;
|
|
111
|
+
if (!variant) {
|
|
112
|
+
throw new Error('variant must be set to get build context directory');
|
|
113
|
+
}
|
|
114
|
+
const effectiveBuildContextBaseDir = this.buildContextBaseDir
|
|
115
|
+
|| (this.appBaseDir ? join(this.appBaseDir, '.~o/t44-docker.com') : '');
|
|
116
|
+
return join(effectiveBuildContextBaseDir, DOCKERFILE_VARIANTS[variant as keyof typeof DOCKERFILE_VARIANTS].variantDir);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}, {
|
|
122
|
+
importMeta: import.meta,
|
|
123
|
+
importStack: makeImportStack(),
|
|
124
|
+
capsuleName: '@stream44.studio/t44-docker.com/caps/ImageContext',
|
|
125
|
+
})
|
|
126
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env bun test --timeout 180000
|
|
2
|
+
|
|
3
|
+
import * as bunTest from 'bun:test'
|
|
4
|
+
import { describe, it, expect } from 'bun:test'
|
|
5
|
+
import { run } from 't44/standalone-rt'
|
|
6
|
+
import { join, basename } from 'path'
|
|
7
|
+
import { mkdir, writeFile } from 'fs/promises'
|
|
8
|
+
|
|
9
|
+
const { test: { workbenchDir } } = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
|
|
10
|
+
const spine = await encapsulate({
|
|
11
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
12
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
13
|
+
'#': {
|
|
14
|
+
test: {
|
|
15
|
+
type: CapsulePropertyTypes.Mapping,
|
|
16
|
+
value: 't44/caps/ProjectTest',
|
|
17
|
+
options: { '#': { bunTest, env: {} } }
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}, {
|
|
22
|
+
importMeta: import.meta,
|
|
23
|
+
importStack: makeImportStack(),
|
|
24
|
+
capsuleName: '@stream44.studio/t44-docker.com/caps/Project.test'
|
|
25
|
+
})
|
|
26
|
+
return { spine }
|
|
27
|
+
}, async ({ spine, apis }: any) => {
|
|
28
|
+
return apis[spine.capsuleSourceLineRef]
|
|
29
|
+
}, { importMeta: import.meta })
|
|
30
|
+
|
|
31
|
+
async function createSampleApp(baseDir: string): Promise<void> {
|
|
32
|
+
await mkdir(baseDir, { recursive: true });
|
|
33
|
+
|
|
34
|
+
await writeFile(join(baseDir, 'index.ts'), `
|
|
35
|
+
const server = Bun.serve({
|
|
36
|
+
port: 3000,
|
|
37
|
+
fetch(req) {
|
|
38
|
+
return new Response("Hello from project test!");
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
console.log("READY");
|
|
42
|
+
console.log(\`Server running on port \${server.port}\`);
|
|
43
|
+
`);
|
|
44
|
+
|
|
45
|
+
await writeFile(join(baseDir, 'package.json'), JSON.stringify({
|
|
46
|
+
name: 'project-test-app',
|
|
47
|
+
version: '0.1.0',
|
|
48
|
+
scripts: { start: 'bun run index.ts' }
|
|
49
|
+
}, null, 2));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
describe('Project Capsule', () => {
|
|
53
|
+
|
|
54
|
+
describe('getDevelopmentContainerConfig', () => {
|
|
55
|
+
it('should return correct container config', async () => {
|
|
56
|
+
const config = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
|
|
57
|
+
const spine = await encapsulate({
|
|
58
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
59
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
60
|
+
'#': {
|
|
61
|
+
project: {
|
|
62
|
+
type: CapsulePropertyTypes.Mapping,
|
|
63
|
+
value: './Project',
|
|
64
|
+
options: {
|
|
65
|
+
'@stream44.studio/t44-docker.com/caps/ImageContext': {
|
|
66
|
+
'#': {
|
|
67
|
+
organization: 'test-docker-com',
|
|
68
|
+
repository: 'project-config-test',
|
|
69
|
+
appBaseDir: '/tmp/test',
|
|
70
|
+
verbose: false
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
'@stream44.studio/t44-docker.com/caps/ContainerContext': {
|
|
74
|
+
'#': {
|
|
75
|
+
ports: [{ internal: 3000, external: 4000 }],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}, { importMeta: import.meta, importStack: makeImportStack(), capsuleName: '@stream44.studio/t44-docker.com/caps/Project.test.config' })
|
|
83
|
+
return { spine }
|
|
84
|
+
}, async ({ spine, apis }: any) => {
|
|
85
|
+
return apis[spine.capsuleSourceLineRef].project.getDevelopmentContainerConfig()
|
|
86
|
+
}, { importMeta: import.meta, runFromSnapshot: false })
|
|
87
|
+
|
|
88
|
+
expect(config.image).toContain('test-docker-com/project-config-test');
|
|
89
|
+
expect(config.ports).toContainEqual({ internal: 3000, external: 4000 });
|
|
90
|
+
expect(config.name).toContain('dev');
|
|
91
|
+
expect(config.waitFor).toBe('READY');
|
|
92
|
+
expect(config.detach).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should merge runConfig env', async () => {
|
|
96
|
+
const config = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
|
|
97
|
+
const spine = await encapsulate({
|
|
98
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
99
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
100
|
+
'#': {
|
|
101
|
+
project: {
|
|
102
|
+
type: CapsulePropertyTypes.Mapping,
|
|
103
|
+
value: './Project',
|
|
104
|
+
options: {
|
|
105
|
+
'@stream44.studio/t44-docker.com/caps/ImageContext': {
|
|
106
|
+
'#': {
|
|
107
|
+
organization: 'test-docker-com',
|
|
108
|
+
repository: 'project-env-test',
|
|
109
|
+
appBaseDir: '/tmp/test',
|
|
110
|
+
verbose: false
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
'@stream44.studio/t44-docker.com/caps/ContainerContext': {
|
|
114
|
+
'#': {
|
|
115
|
+
ports: [{ internal: 3000, external: 4000 }],
|
|
116
|
+
env: {
|
|
117
|
+
MY_VAR: 'my-value',
|
|
118
|
+
DEBUG: 'true'
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}, { importMeta: import.meta, importStack: makeImportStack(), capsuleName: '@stream44.studio/t44-docker.com/caps/Project.test.env' })
|
|
127
|
+
return { spine }
|
|
128
|
+
}, async ({ spine, apis }: any) => {
|
|
129
|
+
return apis[spine.capsuleSourceLineRef].project.getDevelopmentContainerConfig()
|
|
130
|
+
}, { importMeta: import.meta, runFromSnapshot: false })
|
|
131
|
+
|
|
132
|
+
expect(config.env.MY_VAR).toBe('my-value');
|
|
133
|
+
expect(config.env.DEBUG).toBe('true');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('buildDev + runDev + stopDev', () => {
|
|
138
|
+
it('should build, run, verify, and stop a dev container', async () => {
|
|
139
|
+
const appDir = join(workbenchDir, 'project-full-test');
|
|
140
|
+
await createSampleApp(appDir);
|
|
141
|
+
|
|
142
|
+
await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
|
|
143
|
+
const spine = await encapsulate({
|
|
144
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
145
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
146
|
+
'#': {
|
|
147
|
+
project: {
|
|
148
|
+
type: CapsulePropertyTypes.Mapping,
|
|
149
|
+
value: './Project',
|
|
150
|
+
options: {
|
|
151
|
+
'@stream44.studio/t44-docker.com/caps/ContainerContext': {
|
|
152
|
+
'#': {
|
|
153
|
+
ports: [{ internal: 3000, external: 13581 }],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
'@stream44.studio/t44-docker.com/caps/ImageContext': {
|
|
157
|
+
'#': {
|
|
158
|
+
organization: 'test-docker-com',
|
|
159
|
+
repository: 'project-full-test',
|
|
160
|
+
verbose: false
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}, { importMeta: import.meta, importStack: makeImportStack(), capsuleName: '@stream44.studio/t44-docker.com/caps/Project.test.full' })
|
|
168
|
+
return { spine }
|
|
169
|
+
}, async ({ spine, apis }: any) => {
|
|
170
|
+
const project = apis[spine.capsuleSourceLineRef].project
|
|
171
|
+
|
|
172
|
+
project.image.context.appBaseDir = appDir;
|
|
173
|
+
project.image.context.buildContextBaseDir = join(appDir, '.~o/t44-docker.com');
|
|
174
|
+
|
|
175
|
+
const buildResult = await project.buildDev();
|
|
176
|
+
expect(buildResult.imageTag).toBeTruthy();
|
|
177
|
+
expect(buildResult.imageTag).toContain('test-docker-com/project-full-test');
|
|
178
|
+
|
|
179
|
+
const { containerId, stop, ensureRunning } = await project.runDev();
|
|
180
|
+
expect(containerId).toBeTruthy();
|
|
181
|
+
|
|
182
|
+
const isRunning = await ensureRunning();
|
|
183
|
+
expect(isRunning).toBe(true);
|
|
184
|
+
|
|
185
|
+
await stop();
|
|
186
|
+
await project.image.removeImage({ image: buildResult.imageTag, force: true }).catch(() => { });
|
|
187
|
+
}, { importMeta: import.meta, runFromSnapshot: false })
|
|
188
|
+
}, 180000);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('retagImages', () => {
|
|
192
|
+
it('should retag images from source to target org/repo', async () => {
|
|
193
|
+
const appDir = join(workbenchDir, 'project-retag-test');
|
|
194
|
+
await createSampleApp(appDir);
|
|
195
|
+
|
|
196
|
+
await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
|
|
197
|
+
const sourceSpine = await encapsulate({
|
|
198
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
199
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
200
|
+
'#': {
|
|
201
|
+
project: {
|
|
202
|
+
type: CapsulePropertyTypes.Mapping,
|
|
203
|
+
value: './Project',
|
|
204
|
+
options: {
|
|
205
|
+
'@stream44.studio/t44-docker.com/caps/ImageContext': {
|
|
206
|
+
'#': {
|
|
207
|
+
organization: 'test-docker-com',
|
|
208
|
+
repository: 'project-retag-source',
|
|
209
|
+
verbose: false
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}, { importMeta: import.meta, importStack: makeImportStack(), capsuleName: '@stream44.studio/t44-docker.com/caps/Project.test.retag-source' })
|
|
217
|
+
|
|
218
|
+
const targetSpine = await encapsulate({
|
|
219
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
220
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
221
|
+
'#': {
|
|
222
|
+
project: {
|
|
223
|
+
type: CapsulePropertyTypes.Mapping,
|
|
224
|
+
value: './Project',
|
|
225
|
+
options: {
|
|
226
|
+
'@stream44.studio/t44-docker.com/caps/ImageContext': {
|
|
227
|
+
'#': {
|
|
228
|
+
organization: 'test-docker-com',
|
|
229
|
+
repository: 'project-retag-target',
|
|
230
|
+
verbose: false
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}, { importMeta: import.meta, importStack: makeImportStack(), capsuleName: '@stream44.studio/t44-docker.com/caps/Project.test.retag-target' })
|
|
238
|
+
|
|
239
|
+
return { sourceSpine, targetSpine }
|
|
240
|
+
}, async ({ sourceSpine, targetSpine, apis }: any) => {
|
|
241
|
+
const sourceProject = apis[sourceSpine.capsuleSourceLineRef].project
|
|
242
|
+
const targetProject = apis[targetSpine.capsuleSourceLineRef].project
|
|
243
|
+
|
|
244
|
+
sourceProject.image.context.appBaseDir = appDir;
|
|
245
|
+
sourceProject.image.context.buildContextBaseDir = join(appDir, '.~o/t44-docker.com');
|
|
246
|
+
targetProject.image.context.appBaseDir = appDir;
|
|
247
|
+
targetProject.image.context.buildContextBaseDir = join(appDir, '.~o/t44-docker.com');
|
|
248
|
+
|
|
249
|
+
const currentArch = sourceProject.cli.getCurrentPlatformArch();
|
|
250
|
+
await sourceProject.image.buildVariant({ variant: 'alpine', arch: currentArch });
|
|
251
|
+
|
|
252
|
+
await targetProject.retagImages({ organization: 'test-docker-com', repository: 'project-retag-source' });
|
|
253
|
+
|
|
254
|
+
const targetTags = await targetProject.image.getTags({ organization: 'test-docker-com', repository: 'project-retag-target' });
|
|
255
|
+
expect(targetTags.length).toBeGreaterThan(0);
|
|
256
|
+
|
|
257
|
+
for (const tag of targetTags) {
|
|
258
|
+
await targetProject.image.removeImage({ image: tag.tag, force: true }).catch(() => { });
|
|
259
|
+
}
|
|
260
|
+
const sourceTags = await sourceProject.image.getTags({ organization: 'test-docker-com', repository: 'project-retag-source' });
|
|
261
|
+
for (const tag of sourceTags) {
|
|
262
|
+
await sourceProject.image.removeImage({ image: tag.tag, force: true }).catch(() => { });
|
|
263
|
+
}
|
|
264
|
+
}, { importMeta: import.meta, runFromSnapshot: false })
|
|
265
|
+
}, 120000);
|
|
266
|
+
});
|
|
267
|
+
});
|