Skip to content

Commit

Permalink
fix: compose should escape all $ except env (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
fuxingloh authored Jun 11, 2024
1 parent 8963ed5 commit c0531d5
Show file tree
Hide file tree
Showing 20 changed files with 630 additions and 229 deletions.
29 changes: 0 additions & 29 deletions .github/renovate.json

This file was deleted.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@swc/core": "^1.5.24",
"@swc/core": "^1.5.28",
"@swc/jest": "^0.2.36",
"@types/node": "^20.12.8",
"@types/node": "^20.14.2",
"@workspace/eslint-config": "workspace:*",
"@workspace/jest-preset": "workspace:*",
"@workspace/prettier-config": "workspace:*",
Expand All @@ -25,7 +25,7 @@
"husky": "^9.0.11",
"jest": "29.7.0",
"lint-staged": "^15.2.5",
"prettier": "^3.3.0",
"prettier": "^3.3.2",
"turbo": "^2.0.3",
"typescript": "5.4.5",
"wait-for-expect": "^3.0.2"
Expand Down
108 changes: 92 additions & 16 deletions packages/chainfile-docker/src/compose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { describe, expect, it } from '@jest/globals';

import { Compose } from './compose';

describe('ganache.json', () => {
describe('synth', () => {
const chainfile: Chainfile = {
$schema: 'https://chainfile.org/schema.json',
caip2: 'eip155:1337',
name: 'Ganache',
caip2: 'eip155:0',
name: 'Example',
values: {
url: 'http://${rpc_user}:${rpc_password}@ganache:8554',
url: 'http://${rpc_user}:${rpc_password}@dns:1234',
version: {
required: true,
},
rpc_user: {
random: {
type: 'bytes',
Expand All @@ -26,10 +29,38 @@ describe('ganache.json', () => {
},
},
containers: {
ganache: {
dns: {
image: 'docker.io/trufflesuite/ganache',
tag: 'v7.9.2',
tag: {
$value: 'version',
},
source: 'https://github.com/trufflesuite/ganache',
endpoints: {
rpc: {
port: 8545,
protocol: 'HTTP JSON-RPC 2.0',
authorization: {
type: 'HttpBasic',
username: {
$value: 'rpc_user',
},
password: {
$value: 'rpc_password',
},
},
probes: {
readiness: {
params: [],
method: 'eth_blockNumber',
match: {
result: {
type: 'string',
},
},
},
},
},
},
environment: {
RPCUSER: {
$value: 'rpc_user',
Expand All @@ -43,14 +74,37 @@ describe('ganache.json', () => {
memory: 256,
},
},
another: {
image: 'docker.io/trufflesuite/ganache',
tag: 'v7.9.2',
source: 'https://github.com/trufflesuite/ganache',
command: ['sh', '-c', 'echo ${ENV_1} ${ENV_2}'],
environment: {
ENV_1: 'value_1',
ENV_2: {
$value: 'url',
},
},
resources: {
cpu: 0.25,
memory: 256,
},
},
},
};

const compose = new Compose(chainfile, {}, 'suffix');
const compose = new Compose(
chainfile,
{
version: 'v1',
},
'suffix',
);

it('should synth .env', async () => {
expect(compose.synthDotEnv().split('\n')).toStrictEqual([
expect.stringMatching(/^url=http:\/\/[0-9a-f]{32}:[0-9a-f]{32}@ganache:8554$/),
expect.stringMatching(/^url=http:\/\/[0-9a-f]{32}:[0-9a-f]{32}@dns:1234$/),
'version=v1',
expect.stringMatching(/^rpc_user=[0-9a-f]{32}$/),
expect.stringMatching(/^rpc_password=[0-9a-f]{32}$/),
expect.stringMatching(/^CHAINFILE_VALUES=\{.+}$/),
Expand All @@ -61,10 +115,10 @@ describe('ganache.json', () => {
expect(compose.synthCompose().split('\n')).toStrictEqual([
'# Generated by @chainfile/docker:0.0.0, do not edit manually.',
'# Version: 0.0.0',
'# Chainfile Name: Ganache',
'# Chainfile CAIP-2: eip155:1337',
'# Chainfile Name: Example',
'# Chainfile CAIP-2: eip155:0',
'',
'name: ganache',
'name: example',
'services:',
' agent:',
' container_name: agent-suffix',
Expand All @@ -73,21 +127,43 @@ describe('ganache.json', () => {
" - '0:1569'",
' environment:',
' CHAINFILE_JSON: >-',
` ${JSON.stringify(chainfile).replaceAll('$', '$$$')}`,
' {"$$schema":"https://chainfile.org/schema.json","caip2":"eip155:0","name":"Example","values":{"url":"http://$${rpc_user}:$${rpc_password}@dns:1234","version":{"required":true},"rpc_user":{"random":{"type":"bytes","length":16,"encoding":"hex"}},"rpc_password":{"random":{"type":"bytes","length":16,"encoding":"hex"}}},"containers":{"dns":{"image":"docker.io/trufflesuite/ganache","tag":{"$$value":"version"},"source":"https://github.com/trufflesuite/ganache","endpoints":{"rpc":{"port":8545,"protocol":"HTTP',
' JSON-RPC',
' 2.0","authorization":{"type":"HttpBasic","username":{"$$value":"rpc_user"},"password":{"$$value":"rpc_password"}},"probes":{"readiness":{"params":[],"method":"eth_blockNumber","match":{"result":{"type":"string"}}}}}},"environment":{"RPCUSER":{"$$value":"rpc_user"},"RPCPASSWORD":{"$$value":"rpc_password"}},"resources":{"cpu":0.25,"memory":256}},"another":{"image":"docker.io/trufflesuite/ganache","tag":"v7.9.2","source":"https://github.com/trufflesuite/ganache","command":["sh","-c","echo',
' $${ENV_1}',
' $${ENV_2}"],"environment":{"ENV_1":"value_1","ENV_2":{"$$value":"url"}},"resources":{"cpu":0.25,"memory":256}}}}',
' CHAINFILE_VALUES: ${CHAINFILE_VALUES}',
expect.stringMatching(' DEBUG: '),
expect.stringMatching(/ {6}DEBUG: .+/),
' volumes:',
' - type: volume',
' source: chainfile',
' target: /var/chainfile',
' networks:',
' chainfile: {}',
' ganache:',
' container_name: ganache-suffix',
' image: docker.io/trufflesuite/ganache:v7.9.2',
' dns:',
' container_name: dns-suffix',
' image: docker.io/trufflesuite/ganache:${version}',
' environment:',
' RPCUSER: ${rpc_user}',
' RPCPASSWORD: ${rpc_password}',
' ports:',
" - '0:8545'",
' volumes:',
' - type: volume',
' source: chainfile',
' target: /var/chainfile',
' networks:',
' chainfile: {}',
' another:',
' container_name: another-suffix',
' image: docker.io/trufflesuite/ganache:v7.9.2',
' command:',
' - sh',
" - '-c'",
' - echo $${ENV_1} $${ENV_2}',
' environment:',
' ENV_1: value_1',
' ENV_2: ${url}',
' ports: []',
' volumes:',
' - type: volume',
Expand Down
62 changes: 36 additions & 26 deletions packages/chainfile-docker/src/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,41 @@ export class Compose {
}

public synthCompose(): string {
return [
`# Generated by @chainfile/docker:${version}, do not edit manually.`,
`# Version: ${version}`,
`# Chainfile Name: ${this.chainfile.name}`,
`# Chainfile CAIP-2: ${this.chainfile.caip2}`,
'',
yaml.dump(
{
name: this.chainfile.name.toLowerCase().replaceAll(/[^a-z0-9_-]/g, '_'),
services: {
...this.createAgent(),
...this.createServices(),
},
networks: {
chainfile: {},
return (
[
`# Generated by @chainfile/docker:${version}, do not edit manually.`,
`# Version: ${version}`,
`# Chainfile Name: ${this.chainfile.name}`,
`# Chainfile CAIP-2: ${this.chainfile.caip2}`,
'',
yaml.dump(
{
name: this.chainfile.name.toLowerCase().replaceAll(/[^a-z0-9_-]/g, '_'),
services: {
...this.createAgent(),
...this.createServices(),
},
networks: {
chainfile: {},
},
volumes: {
chainfile: {},
},
},
volumes: {
chainfile: {},
{
lineWidth: 120,
},
},
{
lineWidth: 120,
},
),
].join('\n');
),
]
.join('\n')
// Replace all $ with $$$ to escape them in the compose file
.replaceAll('$', '$$$')
.replaceAll('$$$${CHAINFILE_VALUES}$$$$', '${CHAINFILE_VALUES}')
// Unescape $$$${value}$$$$ to ${value}
.replaceAll(/\$\$\$\$\{([a-z]+(_[a-z0-9]+)*)}\$\$\$\$/g, (_, key) => {
return `$\{${key}}`;
})
);
}

private createAgent(): Record<'agent', object> {
Expand All @@ -78,8 +88,8 @@ export class Compose {
ports: ['0:1569'],
environment: {
// Docker compose automatically evaluate environment literals here
CHAINFILE_JSON: JSON.stringify(this.chainfile).replaceAll('$', '$$$'),
CHAINFILE_VALUES: '${CHAINFILE_VALUES}',
CHAINFILE_JSON: JSON.stringify(this.chainfile),
CHAINFILE_VALUES: '$${CHAINFILE_VALUES}$$',
DEBUG: process.env.DEBUG ?? 'false',
},
volumes: [
Expand Down Expand Up @@ -165,7 +175,7 @@ export class Compose {
if (typeof value === 'string') {
return value;
}
return `$\{${value.$value}}`;
return `$$\{${value.$value}}$$`;
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/chainfile-schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@
"type": "object",
"properties": {
"$value": {
"type": "string"
"type": "string",
"description": "Name of the value to reference in the values section.",
"pattern": "^[a-z]+(_[a-z0-9]+)*$"
}
},
"required": ["$value"],
Expand Down
2 changes: 1 addition & 1 deletion packages/chainfile-testcontainers/src/testcontainers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class ChainfileTestcontainers {

public constructor(
protected readonly chainfile: Chainfile | any,
protected readonly values: Record<string, string> = {},
values: Record<string, string> = {},
) {
this.compose = new Compose(chainfile, values);
this.filename = `compose.${this.compose.suffix}.yml`;
Expand Down
Loading

0 comments on commit c0531d5

Please sign in to comment.