Skip to content

Commit 927f539

Browse files
kstekoviclaude
andcommitted
Add structured logging and auto-detect Podman runtime
Add a structured logger with BERG_VERBOSE support to replace all console.log calls. Debug messages are hidden by default, shown only when BERG_VERBOSE=true. Auto-detect Podman socket and configure DOCKER_HOST and TESTCONTAINERS_RYUK_DISABLED so users no longer need to set them manually. Compatible with Docker (no-op when Docker is detected). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a86d783 commit 927f539

13 files changed

Lines changed: 141 additions & 18 deletions

File tree

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,9 @@ Following tools are required to run the test suite
2828
- recommended version is 22.
2929
- [Node Version Manager (nvm)](https://github.com/nvm-sh/nvm) is recommended optional tool to install & manage multiple Node environments
3030
- [npx](https://github.com/npm/npx) CLI tool used to exeute binaries from project's `node_modules` directly (instead of providing absolute/relative path to the commannds). It is used in multiple build steps.
31-
- [Podman](https://podman.io) | [Docker](https://www.docker.com) as a container runtime used by TestContainers. Note that when using Podman as container runtime you may need to export following environment variables and start podman socket:
31+
- [Podman](https://podman.io) | [Docker](https://www.docker.com) as a container runtime used by TestContainers. Podman is auto-detected: `DOCKER_HOST`, `TESTCONTAINERS_RYUK_DISABLED`, and `podman.socket` are configured automatically. To override, set the environment variables manually before running the test suite:
3232
- `export TESTCONTAINERS_RYUK_DISABLED=true`
3333
- `export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock`
34-
- the path can be found by command `podman info --debug` and look for `path` in `remoteSocket` section.
35-
- `systemctl --user start podman.socket`
3634
- Java. Yes we'll need Java to write deployments/applications that will be deployed onto the running WildFly container.
3735
- [Maven](https://maven.apache.org). Yes, we'll need Maven to ease up the development of the deployed applications & downloading needed JDBC drivers for datasource & drivers UI tests. Maven is mostly used embedded by [node-maven](https://github.com/headcr4sh/node-maven) JS wrapper to execute Maven & Java related tasks into the build automation.
3836

@@ -96,6 +94,7 @@ Following is a table of supported environment properties that can be used when r
9694
| `MYSQL_IMAGE` | `docker.io/library/mysql:latest` | MySQL image to be used for datasource tests |
9795
| `MARIADB_IMAGE` | `docker.io/library/mariadb:latest` | MariaDB image to be used for datasource tests |
9896
| `MSSQL_IMAGE` | `mcr.microsoft.com/mssql/server:2022-latest` | Microsoft SQL Server image to be used for datasource tests |
97+
| `BERG_VERBOSE` | `false` | Enable verbose Berg logging (debug messages for container setup, CLI commands, etc.) |
9998

10099
## Custom method documentation
101100

config/containers/database.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PullPolicy, GenericContainer, StartedTestContainer, Wait } from "testcontainers";
22
import { DatabaseConfig } from "../interfaces";
3-
import { handleContainerError } from "../helpers";
3+
import { handleContainerError, logger } from "../helpers";
44

55
export function startDatabaseContainer(
66
config: DatabaseConfig,
@@ -18,7 +18,7 @@ export function startDatabaseContainer(
1818
return containerBuilder
1919
.start()
2020
.then((container) => {
21-
console.log(config.successMessage);
21+
logger.debug(config.successMessage);
2222
startedContainersMap.set(config.containerMapKey, container);
2323
return container;
2424
})

config/containers/wildfly.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
MANAGEMENT_INTERFACE_ADDRESS,
1212
} from "../../cypress.config";
1313
import { WildflyManagementResponse } from "../interfaces";
14-
import { buildLocalhostUrl } from "../helpers";
14+
import { buildLocalhostUrl, logger } from "../helpers";
1515

1616
export function pollWildflyState(managementApi: string, container: StartedTestContainer): Promise<string> {
1717
const startTime = new Date().getTime();
@@ -34,7 +34,7 @@ export function pollWildflyState(managementApi: string, container: StartedTestCo
3434
}
3535
})
3636
.catch(() => {
37-
console.log("WildFly server is not ready yet");
37+
logger.debug("WildFly server is not ready yet");
3838
});
3939
}, WILDFLY_POLL_INTERVAL_MS);
4040
});
@@ -61,7 +61,7 @@ export function configureWildflyNetworkMode(
6161
networkName?: string,
6262
): Promise<{ portOffset: number }> {
6363
if (useHostMode) {
64-
console.log("host mode");
64+
logger.debug("host mode");
6565
return findAPortNotInUse(WILDFLY_PORT_RANGE.min, WILDFLY_PORT_RANGE.max).then((freePort) => {
6666
const portOffset = freePort - WILDFLY_PORT_RANGE.min;
6767
wildfly
@@ -75,7 +75,7 @@ export function configureWildflyNetworkMode(
7575
return { portOffset };
7676
});
7777
} else {
78-
console.log(`default network mode, network name: ${networkName}`);
78+
logger.debug(`default network mode, network name: ${networkName}`);
7979
wildfly
8080
.withNetworkMode(networkName!)
8181
.withNetworkAliases("wildfly")
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { existsSync } from "fs";
2+
import { execSync } from "child_process";
3+
import os from "os";
4+
import { logger } from "./logger";
5+
6+
function isPodmanSocket(dockerHost: string): boolean {
7+
return dockerHost.includes("podman");
8+
}
9+
10+
function configurePodman(): void {
11+
if (!process.env.TESTCONTAINERS_RYUK_DISABLED) {
12+
process.env.TESTCONTAINERS_RYUK_DISABLED = "true";
13+
}
14+
}
15+
16+
function detectContainerRuntime(): void {
17+
if (process.env.DOCKER_HOST) {
18+
if (isPodmanSocket(process.env.DOCKER_HOST)) {
19+
configurePodman();
20+
logger.info(`DOCKER_HOST points to Podman (${process.env.DOCKER_HOST}), configured TESTCONTAINERS_RYUK_DISABLED`);
21+
} else {
22+
logger.debug(`DOCKER_HOST already set to "${process.env.DOCKER_HOST}"`);
23+
}
24+
return;
25+
}
26+
27+
const uid = os.userInfo().uid;
28+
const podmanSocketPaths = [`/run/user/${uid}/podman/podman.sock`, "/var/run/podman/podman.sock"];
29+
30+
for (const socketPath of podmanSocketPaths) {
31+
if (!existsSync(socketPath)) {
32+
try {
33+
execSync("systemctl --user start podman.socket", { stdio: "ignore" });
34+
} catch {
35+
continue;
36+
}
37+
}
38+
39+
if (existsSync(socketPath)) {
40+
process.env.DOCKER_HOST = `unix://${socketPath}`;
41+
configurePodman();
42+
logger.info(`Detected Podman runtime at ${socketPath}, configured DOCKER_HOST and TESTCONTAINERS_RYUK_DISABLED`);
43+
return;
44+
}
45+
}
46+
47+
if (existsSync("/var/run/docker.sock")) {
48+
logger.debug("Detected Docker runtime");
49+
return;
50+
}
51+
52+
logger.debug("No container runtime detected automatically");
53+
}
54+
55+
detectContainerRuntime();

config/helpers/error-handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { logger } from "./logger";
2+
13
export function handleContainerError(err: unknown): Error {
2-
console.log(err);
4+
logger.error(err);
35
return err instanceof Error ? err : new Error(JSON.stringify(err));
46
}

config/helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./error-handler";
22
export * from "./url-builder";
33
export * from "./container-helpers";
4+
export * from "./logger";

config/helpers/logger.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const isVerbose = process.env.BERG_VERBOSE === "true";
2+
3+
export const logger = {
4+
error: (...args: unknown[]) => console.error("[berg]", ...args),
5+
info: (...args: unknown[]) => console.log("[berg]", ...args),
6+
debug: (...args: unknown[]) => {
7+
if (isVerbose) console.log("[berg:debug]", ...args);
8+
},
9+
};

config/tasks/cli-tasks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import axios from "axios";
22
import { ExecuteCliParams, AxiosErrorResponse } from "../interfaces";
3+
import { logger } from "../helpers";
34

45
export function createExecuteCli() {
56
return ({ managementApi, operation, address, ...args }: ExecuteCliParams) => {
@@ -11,7 +12,7 @@ export function createExecuteCli() {
1112
})
1213
.then((response) => response.data as unknown)
1314
.catch((err: AxiosErrorResponse) => {
14-
console.log(err);
15+
logger.error(err);
1516
throw new Error(err.response.data);
1617
});
1718
};

config/tasks/keycloak-tasks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
FIXTURES_PATH,
1010
} from "../../cypress.config";
1111
import { StartKeycloakContainerParams } from "../interfaces";
12-
import { buildLocalhostUrl, buildKeycloakStartCommand, handleContainerError } from "../helpers";
12+
import { buildLocalhostUrl, buildKeycloakStartCommand, handleContainerError, logger } from "../helpers";
1313

1414
export function createKeycloakContainer(startedContainers: Map<string, StartedTestContainer>) {
1515
return ({ name }: StartKeycloakContainerParams) => {
@@ -35,7 +35,7 @@ export function createKeycloakContainer(startedContainers: Map<string, StartedTe
3535
.then((keycloakContainer) => {
3636
startedContainers.set(name, keycloakContainer);
3737
const keycloakServer = buildLocalhostUrl(freePort);
38-
console.log(`Keycloak is ready: ${keycloakServer}`);
38+
logger.info(`Keycloak is ready: ${keycloakServer}`);
3939
return keycloakServer;
4040
})
4141
.catch((err: unknown) => {

config/tasks/wildfly-tasks.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
JBOSS_CLI_PATH,
1010
} from "../../cypress.config";
1111
import { StartWildflyContainerParams, ExecuteInContainerParams, AxiosErrorResponse } from "../interfaces";
12-
import { getHostnameMapping, handleContainerError, calculateManagementPort } from "../helpers";
12+
import { getHostnameMapping, handleContainerError, calculateManagementPort, logger } from "../helpers";
1313
import { configureWildflyNetworkMode, configureWildflyPostStart } from "../containers";
1414

1515
export function createWildflyContainer(
@@ -53,7 +53,7 @@ export function createWildflyContainer(
5353
);
5454
})
5555
.then((wildflyServer) => {
56-
console.log(`WildFly server is ready: ${wildflyServer}`);
56+
logger.info(`WildFly server is ready: ${wildflyServer}`);
5757
return wildflyServer;
5858
})
5959
.catch((err: unknown) => {
@@ -67,7 +67,7 @@ export function createExecuteInContainer(
6767
startedContainersManagementPorts: Map<string, number>,
6868
) {
6969
return ({ containerName, command }: ExecuteInContainerParams) => {
70-
console.log(`CLI commands: ${command}`);
70+
logger.debug(`CLI commands: ${command}`);
7171
const containerToExec = startedContainers.get(containerName);
7272
let managementPort = startedContainersManagementPorts.get(containerName);
7373
managementPort = managementPort ?? WILDFLY_MANAGEMENT_PORT;
@@ -86,7 +86,7 @@ export function createExecuteInContainer(
8686
if (value.exitCode === 0) {
8787
return value;
8888
} else {
89-
console.log(value);
89+
logger.debug(value);
9090
throw new Error(`Command failed with exit code ${value.exitCode}: ${value.output || ""}`);
9191
}
9292
})

0 commit comments

Comments
 (0)