diff --git a/docs/docker-to-iac/available-commands.md b/docs/docker-to-iac/available-commands.md index b391b22..0589207 100644 --- a/docs/docker-to-iac/available-commands.md +++ b/docs/docker-to-iac/available-commands.md @@ -7,13 +7,71 @@ menuTitle: Available Commands The following commands are currently supported: +## Build Commands + - `npm run build` - - Will build the module and create output files inside `dist/` direcotry. + - Builds the module using TypeScript compiler and creates output files inside the `dist/` directory. + +## Code Quality Commands + - `npm run lint` - - Will run eslint command. ESLint is also run as part of GitHub action test for new pull request on the default `main` branch. + - Runs ESLint to check code quality. ESLint is also run as part of GitHub action test for new pull requests on the default `main` branch. + +## Testing Commands + - `npm run test` - - Runs the tests defined in the `test/test.ts` file. The tests are also part of the GitHub action pipeline for new pull requests into `main`. -- `npm run semantic-release` - - Runs the semantic-release command which is part of the release process of [docker-to-iac](https://www.npmjs.com/package/@deploystack/docker-to-iac) modules to npm registry. The release is executed through a [GitHub action pipeline](/docs/docker-to-iac/publishing-to-npm.md). + - Runs the complete test suite including both unit tests and end-to-end tests. +- `npm run test:unit` + - Runs only the unit tests to validate individual components. +- `npm run test:e2e` + - Runs only the end-to-end tests which validate the entire translation process from Docker run commands or Docker Compose files to infrastructure as code. +- `npm run test:watch` + - Runs tests in watch mode, which automatically re-runs tests when files change. +- `npm run test:coverage` + - Runs tests with coverage reporting to identify untested code paths. + +## Release Commands + +- `npm run release` + - Runs the release-it command which is part of the release process of [docker-to-iac](https://www.npmjs.com/package/@deploystack/docker-to-iac) modules to npm registry. The release is executed through configurations defined in `.release-it.js`. + +## Other Commands + +- `npm run pretest:e2e` + - Automatically run before e2e tests to clean the output directory. + +You can view all commands and their configurations in the [package.json](https://github.com/deploystackio/docker-to-iac/blob/main/package.json) file. + +## Examples + +### Running Unit Tests Only + +```bash +npm run test:unit +``` + +### Running End-to-End Tests Only + +```bash +npm run test:e2e +``` + +### Running All Tests with Coverage + +```bash +npm run test:coverage +``` + +### Building the Module + +```bash +npm run build +``` + +### Checking Code Quality + +```bash +npm run lint +``` -View all commands config from [package.json](https://github.com/deploystackio/docker-to-iac/blob/main/package.json). +Each command is configured to provide the most relevant feedback for its purpose. For example, unit tests provide detailed output about each individual function, while end-to-end tests show a summary of the complete translation process from Docker configurations to infrastructure as code. diff --git a/docs/docker-to-iac/example-of-a-new-parser.md b/docs/docker-to-iac/example-of-a-new-parser.md index 1bcdfe7..381a6f0 100644 --- a/docs/docker-to-iac/example-of-a-new-parser.md +++ b/docs/docker-to-iac/example-of-a-new-parser.md @@ -1,5 +1,5 @@ --- -description: Example code for adding a new parser to docker-to-iac, supporting both Docker run commands and Docker Compose files, with multi-file output capabilities +description: Example code for adding a new parser to docker-to-iac, supporting both Docker run commands and Docker Compose files, with multi-file output and service connections menuTitle: Adding a New Parser --- @@ -18,10 +18,14 @@ import { ParserInfo, TemplateFormat, ParserConfig, - FileOutput + FileOutput, + DockerImageInfo } from './base-parser'; import { ApplicationConfig } from '../types/container-config'; +import { parsePort } from '../utils/parsePort'; +import { parseCommand } from '../utils/parseCommand'; +// Define default configuration for your parser const defaultParserConfig: ParserConfig = { files: [ { @@ -29,46 +33,191 @@ const defaultParserConfig: ParserConfig = { templateFormat: TemplateFormat.yaml, isMain: true, description: 'Main IaC configuration file' + }, + { + path: 'templates/resources.yaml', + templateFormat: TemplateFormat.yaml, + description: 'Additional resources configuration' } ], cpu: 512, - memory: '1GB' + memory: '1GB', + region: 'default-region', + subscriptionName: 'basic-tier' }; +// Optional: Add helper functions for your specific provider +function getNewProviderServiceType(imageInfo: DockerImageInfo): string { + // Logic to determine service type based on image + // Example: Check if it's a database, web service, etc. + return 'web-service'; // Default type +} + +// Optional: Add function to determine if an image is a managed service +function isNewProviderManagedService(imageInfo: DockerImageInfo): boolean { + // Check if this image should be handled as a managed service + const imageUrl = `${imageInfo.repository}:${imageInfo.tag || 'latest'}`; + return imageUrl.includes('postgres') || imageUrl.includes('redis'); +} + class NewProviderParser extends BaseParser { - // Legacy method implementation (calls parseFiles under the hood) - parse(config: ApplicationConfig, templateFormat: TemplateFormat = TemplateFormat.yaml): any { - return super.parse(config, templateFormat); - } - - // New multi-file implementation + // Multi-file implementation - required by BaseParser parseFiles(config: ApplicationConfig): { [path: string]: FileOutput } { - // Process the application configuration - const services = config.services; + // Initialize result containers + const services: Array = []; + const managedServices: Array = []; + + // Track service mappings for managed services + const managedServiceMap = new Map(); - // Your parser implementation here: - // 1. Process each service - // 2. Map container configurations to your IaC format - // 3. Handle provider-specific requirements + // First pass: identify and register managed services + for (const [serviceName, serviceConfig] of Object.entries(config.services)) { + if (isNewProviderManagedService(serviceConfig.image)) { + // Create a managed service instead of a regular service + const managedName = `${serviceName}-managed`; + + // Track the mapping for service connections later + managedServiceMap.set(serviceName, managedName); + + // Add to managed services collection + managedServices.push({ + name: managedName, + type: getNewProviderServiceType(serviceConfig.image), + // Add provider-specific managed service properties + plan: defaultParserConfig.subscriptionName + }); + + // Skip further processing of this service + continue; + } + + // Regular services will be processed in the second pass + } + + // Second pass: process regular services with their connections + for (const [serviceName, serviceConfig] of Object.entries(config.services)) { + // Skip managed services already processed + if (managedServiceMap.has(serviceName)) { + continue; + } + + // Extract ports from service configuration + const ports = new Set(); + if (serviceConfig.ports) { + serviceConfig.ports.forEach(port => { + if (typeof port === 'object' && port !== null) { + ports.add(port.container); + } else { + const parsedPort = parsePort(port); + if (parsedPort) { + ports.add(parsedPort); + } + } + }); + } + + // Prepare basic service definition + const service: any = { + name: serviceName, + type: getNewProviderServiceType(serviceConfig.image), + image: serviceConfig.image, + command: parseCommand(serviceConfig.command), + environment: [] + }; + + // Add ports if available + if (ports.size > 0) { + service.ports = Array.from(ports); + } + + // Process service connections if available + if (config.serviceConnections) { + // First add regular environment variables + for (const [key, value] of Object.entries(serviceConfig.environment)) { + // Check if this variable is handled by service connections + const isHandledByConnection = config.serviceConnections.some(conn => + conn.fromService === serviceName && + Object.keys(conn.variables).includes(key) + ); + + if (!isHandledByConnection) { + // Regular environment variable + service.environment.push({ + key, + value: value.toString() + }); + } + } + + // Then add service connection variables with provider-specific syntax + for (const connection of config.serviceConnections) { + if (connection.fromService === serviceName) { + for (const [varName, varInfo] of Object.entries(connection.variables)) { + // Check if target is a managed service + if (managedServiceMap.has(connection.toService)) { + const targetName = managedServiceMap.get(connection.toService); + + // Use provider-specific reference syntax + service.environment.push({ + key: varName, + // Example: ${resources.MANAGED_SERVICE_NAME.CONNECTION_STRING} + value: `\${resources.${targetName}.${connection.property || 'connectionString'}}` + }); + } else { + // Regular service connection + service.environment.push({ + key: varName, + // Example: ${services.SERVICE_NAME.HOST_PORT} + value: `\${services.${connection.toService}.${connection.property || 'hostport'}}` + }); + } + } + } + } + } else { + // No service connections, just add all environment variables + service.environment = Object.entries(serviceConfig.environment).map(([key, value]) => ({ + key, + value: value.toString() + })); + } + + // Add service to collection + services.push(service); + } - // Create your primary IaC configuration - const primaryConfig = { - // Your main configuration structure + // Create main configuration + const mainConfig = { + version: '1.0', + provider: 'new-provider', + region: defaultParserConfig.region, + services }; - // Return object with file mappings - at minimum return your main file - return { + // Create resources configuration if we have managed services + const resourcesConfig = managedServices.length > 0 ? { + version: '1.0', + managedResources: managedServices + } : {}; + + // Return file mappings - the main file is required + const result: { [path: string]: FileOutput } = { 'awesome-iac.yaml': { - content: this.formatFileContent(primaryConfig, TemplateFormat.yaml), + content: this.formatFileContent(mainConfig, TemplateFormat.yaml), format: TemplateFormat.yaml, isMain: true } - // Add additional files as needed: - // 'templates/service.yaml': { - // content: this.formatFileContent(serviceConfig, TemplateFormat.yaml), - // format: TemplateFormat.yaml - // } }; + + // Add resources file if we have managed services + if (managedServices.length > 0) { + result['templates/resources.yaml'] = { + content: this.formatFileContent(resourcesConfig, TemplateFormat.yaml), + format: TemplateFormat.yaml + }; + } + + return result; } getInfo(): ParserInfo { @@ -87,119 +236,148 @@ class NewProviderParser extends BaseParser { export default new NewProviderParser(); ``` -## Parser Configuration - -### Multi-File Configuration +## Configuration and Provider-Specific Logic -Define the files your parser will generate, please read more here: [Multi-File Configuration in docker-to-iac](/docs/docker-to-iac/multi-file-configuration.md). +### Service Type Detection -### File Output - -Your parser must implement the `parseFiles` method which returns a mapping of file paths to content: +Create a file for service type configuration in `src/config/newprovider/service-types.ts`: ```typescript -parseFiles(config: ApplicationConfig): { [path: string]: FileOutput } { - return { - 'awesome-iac.yaml': { - content: this.formatFileContent(mainConfig, TemplateFormat.yaml), - format: TemplateFormat.yaml, - isMain: true // Mark one file as the main file - }, - 'templates/deployment.yaml': { - content: this.formatFileContent(deploymentConfig, TemplateFormat.yaml), - format: TemplateFormat.yaml - } - // Add more files as needed - }; +interface NewProviderServiceTypeConfig { + type: string; + description: string; + versions: string; + isManaged?: boolean; } -``` -The `isMain: true` property is required for at least one file - this maintains backward compatibility with existing code. - -### Supported Ouput Formats - -Select appropriate output formats for your provider: - -- `yaml` - YAML format (recommended for human readability) -- `json` - JSON format (recommended for programmatic handling) -- `text` - Plain text (if provider requires specific format) - -## Testing - -### Add Test Files +interface NewProviderServiceTypesConfig { + serviceTypes: { + [key: string]: NewProviderServiceTypeConfig; + }; +} -1. Add Docker Compose test files in `test/docker-compose-files/`: +export const newProviderServiceTypesConfig: NewProviderServiceTypesConfig = { + serviceTypes: { + 'docker.io/library/mariadb': { + type: 'database', + description: 'MariaDB database service', + versions: '*' + }, + 'docker.io/library/postgres': { + type: 'database', + description: 'PostgreSQL database', + versions: '*', + isManaged: true + }, + 'docker.io/library/redis': { + type: 'cache', + description: 'Redis cache', + versions: '*', + isManaged: true + } + } +}; -```yaml -# test/docker-compose-files/basic-web.yml -version: '3' -services: - web: - image: nginx:latest - ports: - - "80:80" -``` +export function getNewProviderServiceType(imageString: string): string { + const baseImage = imageString.split(':')[0]; + return newProviderServiceTypesConfig.serviceTypes[baseImage]?.type || 'web'; +} -2. Add Docker run test files in `test/docker-run-files/`: +export function isNewProviderManagedService(imageString: string): boolean { + const baseImage = imageString.split(':')[0]; + return !!newProviderServiceTypesConfig.serviceTypes[baseImage]?.isManaged; +} -```bash -# test/docker-run-files/basic-nginx.txt -docker run -d -p 80:80 nginx:latest +export type { NewProviderServiceTypeConfig, NewProviderServiceTypesConfig }; ``` -### Test Implementation +### Service Connection Properties -Your parser will be automatically tested through the main test suite. The test system will: +Update the service connection properties in `src/config/connection-properties.ts`: -- Process both Docker run and Docker Compose inputs -- Generate outputs in all formats -- Create organized output directories - -Run tests: +```typescript +export const servicePropertyMappings: Record = { + 'host': { + render: 'host', + digitalOcean: 'PRIVATE_DOMAIN', + newProvider: 'HOST' // Add your provider mapping + }, + 'port': { + render: 'port', + digitalOcean: 'PRIVATE_PORT', + newProvider: 'PORT' // Add your provider mapping + }, + 'hostport': { + render: 'hostport', + digitalOcean: 'PRIVATE_URL', + newProvider: 'ENDPOINT' // Add your provider mapping + } +}; -```bash -npm run test +export const databasePropertyMappings: Record = { + 'connectionString': { + render: 'connectionString', + digitalOcean: 'DATABASE_URL', + newProvider: 'CONNECTION_STRING' // Add your provider mapping + }, + // Add other mappings... +}; ``` -### Verify Outputs +## Adding Your Parser to the System -Check generated files in: +Update `src/index.ts` to include your new parser: -- `test/output/docker-compose/[filename]/np/` -- `test/output/docker-run/[filename]/np/` +```typescript +// Import your new parser +import newProviderParserInstance from './parsers/new-provider'; + +// Add it to the parsers array +const parsers: BaseParser[] = [ + cloudFormationParserInstance, + renderParserInstance, + digitalOceanParserInstance, + newProviderParserInstance // Add your parser here +]; +``` -## Building and Documentation +## Testing -1. Build the module: +Please read our guidelines for testing parsers in the [Testing section](/docs/docker-to-iac/testing.md). -```bash -npm run build -``` +## New parser documentation -2. Update documentation: - - Add parser-specific information - - Document any special considerations - - Include examples for both Docker run and Docker Compose - - Follow [documentation guidelines](https://github.com/deploystackio/documentation/blob/main/README.md) +Please update documentation in the [github.com/deploystackio/documentation](https://github.com/deploystackio/documentation) repository. -## Checlist +## Checklist 1. Support both input types: - Docker run commands - Docker Compose files -2. Handle resource mappings consistently: +2. Handle all service types: + - Regular web/application services + - Managed database services + - Cache services + +3. Handle resource mappings consistently: - Container ports - Environment variables - Volume mounts - Resource limits + - Service connections + +4. Process service connections correctly: + - Service-to-service references + - Service-to-managed-service references + - Use provider-specific connection syntax -3. Provide clear error messages for: +5. Provide clear error messages for: - Unsupported features - Invalid configurations - Missing required fields -4. Test edge cases: - - Multiple services - - Complex configurations - - Various image formats +6. Test edge cases: + - Multiple services with interdependencies + - Complex configurations with service connections + - Various image formats and service types diff --git a/docs/docker-to-iac/project-structure.md b/docs/docker-to-iac/project-structure.md index 46259a8..bc19ea0 100644 --- a/docs/docker-to-iac/project-structure.md +++ b/docs/docker-to-iac/project-structure.md @@ -1,69 +1,89 @@ --- -description: Directory structure and organization of the docker-to-iac module, including guidance for adding new parsers and source handlers. +description: Directory structure and organization of the docker-to-iac module, including guidance for adding new parsers, source handlers, and tests. menuTitle: Project Structure --- # Project Structure of docker-to-iac Module -The project follows standard npm module organization with additional structure to handle both Docker run commands and Docker Compose files. +The project follows standard npm module organization with a well-defined structure to handle both Docker run commands and Docker Compose files, supporting multiple output formats and comprehensive testing. ## Directory Structure ```bash docker-to-iac/ -|-- .github/ -|-- dist/ -|-- src/ -| |-- index.ts -| |-- config/ +|-- src/ # Source code +| |-- index.ts # Main entry point +| |-- config/ # Provider-specific configurations +| | |-- connection-properties.ts +| | |-- digitalocean/ +| | | |-- database-types.ts | | |-- render/ -| | | |-- service-types.ts -| |-- parsers/ +| | |-- service-types.ts +| |-- parsers/ # IaC parsers for different cloud providers | | |-- aws-cloudformation.ts | | |-- base-parser.ts | | |-- digitalocean.ts | | |-- render.ts -| |-- sources/ +| |-- sources/ # Input source handlers | | |-- base.ts | | |-- factory.ts -| | |-- compose/ +| | |-- compose/ # Docker Compose handling | | | |-- index.ts | | | |-- validate.ts -| | |-- run/ -| | | |-- index.ts -| |-- types/ +| | |-- run/ # Docker run command handling +| | |-- index.ts +| |-- types/ # TypeScript type definitions | | |-- container-config.ts -| |-- utils/ +| | |-- environment-config.ts +| | |-- service-connections.ts +| |-- utils/ # Helper utilities | |-- constructImageString.ts +| |-- detectDatabaseEnvVars.ts | |-- digitalOceanParserServiceName.ts +| |-- getDigitalOceanDatabaseType.ts | |-- getImageUrl.ts | |-- parseCommand.ts | |-- parseDockerImage.ts -| |-- parseEnvironmentVariables.ts -| |-- parsePort.ts -|-- test/ -| |-- docker-compose-files/ -| |-- docker-run-files/ -| |-- output/ -| |-- test.ts -|-- .gitignore -|-- eslint.config.mjs -|-- LICENSE -|-- README.md -|-- package-lock.json -|-- package.json -|-- release.config.cjs -|-- tsconfig.json +| |-- parseEnvFile.ts +| |-- processEnvironmentVariablesGeneration.ts +| |-- resolveEnvironmentValue.ts +| |-- (... and many more) +|-- test/ # Test files +| |-- e2e/ # End-to-end tests +| | |-- assertions/ # Test assertions +| | | |-- digitalocean.ts +| | | |-- do-port-assertions.ts +| | | |-- port-assertions.ts +| | | |-- render.ts +| | |-- docker-compose-files/ # Test Docker Compose files +| | |-- docker-run-files/ # Test Docker run commands +| | |-- output/ # Test output directory +| | |-- utils/ # Test utilities +| | |-- index.ts # Main E2E test executor +| | |-- test1.ts # Environment variables and volume mapping tests +| | |-- test2.ts # Port mapping tests +| | |-- test3.ts # Environment variable substitution tests +| | |-- test4.ts # Schema validation tests +| |-- unit/ # Unit tests +| | |-- config/ # Configuration tests +| | |-- parsers/ # Parser tests +| | |-- sources/ # Source handler tests +| | |-- utils/ # Utility function tests +| |-- test.ts # Main test entry point +|-- eslint.config.mjs # ESLint configuration +|-- tsconfig.json # TypeScript configuration +|-- vitest.config.ts # Vitest configuration +|-- package.json # Package configuration +|-- README.md # Project documentation ``` ## Directory Purposes ### Core Directories -- `src/` - Source code -- `test/` - Test files and test cases -- `dist/` - Compiled output -- `.github/` - GitHub workflows and templates +- `src/` - Source code for the module +- `test/` - Test files organized by test type (unit and end-to-end) +- `dist/` - Compiled output (generated during build) ### Source Code Organization @@ -71,59 +91,85 @@ docker-to-iac/ Contains provider-specific configurations: +- `connection-properties.ts` - Cross-provider connection property mappings +- `digitalocean/` - DigitalOcean App Platform specific configurations + - `database-types.ts` - Database type mappings for DigitalOcean - `render/` - Render.com specific configurations - - `service-types.ts` - Service type mappings for Render.com deployments (web, private services, Redis) - -Each cloud provider can have its own subdirectory for configuration files that affect how the parser handles specific cases for that provider. + - `service-types.ts` - Service type mappings for Render deployments #### Parsers (`src/parsers/`) Contains IaC-specific parsers for different cloud providers: -- `base-parser.ts` - Base parser class +- `base-parser.ts` - Base parser class that defines common functionality - `aws-cloudformation.ts` - AWS CloudFormation parser - `digitalocean.ts` - DigitalOcean App Platform parser - `render.ts` - Render Blueprint parser +- ... additional parsers for other providers #### Source Handlers (`src/sources/`) Handles different input types: +- `base.ts` - Base source handler interface +- `factory.ts` - Factory for creating appropriate source handlers - `compose/` - Docker Compose file processing + - `index.ts` - Main Compose parser + - `validate.ts` - Compose file validation - `run/` - Docker run command processing -- `base.ts` - Base source handler -- `factory.ts` - Source handler factory + - `index.ts` - Docker run command parser #### Types (`src/types/`) TypeScript type definitions: -- `container-config.ts` - Container configuration types +- `container-config.ts` - Container and service configuration types +- `environment-config.ts` - Environment variable configuration types +- `service-connections.ts` - Service connection configuration types #### Utilities (`src/utils/`) Helper functions for parsing and processing: -- Docker image handling -- Command parsing -- Environment variable processing -- Port mapping utilities -- and many more... +- `constructImageString.ts` - Docker image string construction +- `detectDatabaseEnvVars.ts` - Database environment variable detection +- `digitalOceanParserServiceName.ts` - Name formatting for DigitalOcean +- `getDigitalOceanDatabaseType.ts` - Database type detection for DigitalOcean +- `parseDockerImage.ts` - Docker image parsing +- `parseEnvFile.ts` - Environment file parsing +- `resolveEnvironmentValue.ts` - Environment variable resolution +- And many more utility functions for specific operations -### Adding New Source +### Test Organization -Add new input source handlers in `src/sources/`: +#### End-to-End Tests (`test/e2e/`) -```bash -src/sources/new-source/ -|-- index.ts -|-- validate.ts # if needed -``` +Integration tests that validate the complete workflow: + +- `assertions/` - Validation functions for test output +- `docker-compose-files/` - Test Docker Compose files +- `docker-run-files/` - Test Docker run commands +- `output/` - Generated test outputs +- `utils/` - Test helper utilities +- `test1.ts` through `test4.ts` - Specific test scenarios: + 1. Environment variables and volume mapping + 2. Port mappings + 3. Environment variable substitution + 4. Schema validation + +#### Unit Tests (`test/unit/`) + +Tests for individual components: + +- `config/` - Tests for configuration modules +- `parsers/` - Tests for IaC parsers +- `sources/` - Tests for source handlers +- `utils/` - Tests for utility functions + +## Adding New Parser -## Testing +Please check our [Adding a New Parser](/docs/docker-to-iac/example-of-a-new-parser.md) documentation for detailed instructions on how to add a new parser to the project. This includes creating a new parser file, implementing the parsing logic, and ensuring compatibility with existing configurations. -Place test files in appropriate directories: +### Adding New Tests -- Docker Compose files: `test/docker-compose-files/` -- Docker run commands: `test/docker-run-files/` -- Test output: `test/output/` +Please refer to the [Testing](/docs/docker-to-iac/testing.md) documentation for guidelines on adding new tests, including unit and end-to-end tests. diff --git a/docs/docker-to-iac/publishing-to-npm.md b/docs/docker-to-iac/publishing-to-npm.md index 19fd727..8352ac2 100644 --- a/docs/docker-to-iac/publishing-to-npm.md +++ b/docs/docker-to-iac/publishing-to-npm.md @@ -1,12 +1,83 @@ --- -description: Explore our automated NPM publishing workflow for docker-to-iac. From pull request to package release, understand how we ensure quality through CI/CD. +description: Explore our automated NPM publishing workflow for docker-to-iac. From preparing a release branch to package deployment, understand our conventional commits-based process. menuTitle: Publishing to NPM --- # Publishing docker-to-iac module to NPM -We have created an organization @deploystack for NPM. Publishing in NPM happens automatically through `semantic-release`. Config: [https://github.com/deploystackio/docker-to-iac/blob/main/.github/workflows/release-pr.yml](https://github.com/deploystackio/docker-to-iac/blob/main/.github/workflows/release-pr.yml) +The docker-to-iac module is published to the @deploystack organization on NPM. The publishing process is automated through GitHub Actions and follows a conventional commit-based workflow. -The prerequisite for a release is a successful pull request to the default branch `main`. This means that all tests must first be successfully completed. `semantic-release` creates a new version and automatically publishes the node package to: [https://www.npmjs.com/package/@deploystack/docker-to-iac](https://www.npmjs.com/package/@deploystack/docker-to-iac) +## Release Process Overview -Semantic-Release docs: [https://semantic-release.gitbook.io/semantic-release](https://semantic-release.gitbook.io/semantic-release) +The release process follows these steps: + +1. Initiate a release preparation using the GitHub workflow +2. Review and merge the release pull request +3. Automatic publishing to NPM when the release PR is merged + +## Starting a Release + +Releases can be initiated through the GitHub Actions UI: + +1. Navigate to the "Actions" tab in the repository +2. Select the "Release Process" workflow +3. Click "Run workflow" +4. Choose the release type: + - `patch` (bug fixes) + - `minor` (new features) + - `major` (breaking changes) +5. Optionally select "Prerelease" for beta versions +6. Click "Run workflow" + +## What Happens During Release Preparation + +The workflow performs the following steps: + +1. Updates the version in package.json based on conventional commits +2. Updates the CHANGELOG.md file with details of changes since the last release +3. Creates a new branch with these changes (named `release-v{version}`) +4. Provides a link to create a pull request + +## Creating the Pull Request + +After the workflow completes: + +1. Follow the link provided in the workflow output to create a pull request +2. **Important**: Add the `release` label to your pull request +3. Request a review of the PR + +## Publishing Process + +When the pull request with the `release` label is merged: + +1. The GitHub Action automatically creates a Git tag for the new version +2. A GitHub release is created with the changelog contents +3. The package is built using `npm run build` +4. The package is published to NPM with public access + +## Npm Package + +The published package is available at: [https://www.npmjs.com/package/@deploystack/docker-to-iac](https://www.npmjs.com/package/@deploystack/docker-to-iac) + +## Conventional Commits + +The project uses conventional commits to determine version bumps and generate changelogs. Commit messages should follow this pattern: + +- `feat: ...` - A new feature (minor version bump) +- `fix: ...` - A bug fix (patch version bump) +- `chore: ...` - Maintenance changes +- `docs: ...` - Documentation changes +- `style: ...` - Code style changes +- `refactor: ...` - Code refactoring +- `perf: ...` - Performance improvements +- `test: ...` - Test updates + +Breaking changes should include `BREAKING CHANGE:` in the commit message body or footer. + +## Configuration Files + +The release process is configured through several files: + +- `.github/workflows/release-pr.yml` - GitHub Actions workflow +- `.release-it.js` - Configuration for release-it +- `package.json` - NPM scripts for the release process diff --git a/docs/docker-to-iac/testing.md b/docs/docker-to-iac/testing.md index 6a1a9b4..fe357ad 100644 --- a/docs/docker-to-iac/testing.md +++ b/docs/docker-to-iac/testing.md @@ -1,11 +1,11 @@ --- -description: Learn how to test the docker-to-iac module including Docker run commands and Docker Compose files, with support for multi-file output generation and directory structure preservation. +description: Learn how to test the docker-to-iac module including Docker run commands and Docker Compose files, with support for integration and end-to-end testing. menuTitle: Testing --- # Testing docker-to-iac Module -Before submitting a pull request, test your code locally. Testing covers both code quality and functional aspects for Docker run commands and Docker Compose files, including the new multi-file output capabilities. +Before submitting a pull request, test your code locally. Testing covers both code quality and functional aspects for Docker run commands and Docker Compose files, including multi-file output capabilities and cross-platform compatibility. ## Running Tests @@ -19,69 +19,153 @@ npm run lint ESLint must pass locally before submitting your PR, as GitHub Actions CI/CD will block any PR that fails the lint check. -### Functional Testing +### Unit Testing -Run the test suite: +To run unit tests only: + +```bash +npm run test:unit +``` + +Unit tests verify individual functions and components work correctly in isolation. + +### End-to-End Testing + +To run end-to-end tests only: + +```bash +npm run test:e2e +``` + +End-to-end tests validate the entire translation process with real inputs and outputs. + +### All Tests + +Run the complete test suite: ```bash npm run test ``` -The test suite runs comprehensive checks across all parsers and formats, with support for multi-file outputs. Testing structure: +This will execute both unit tests and end-to-end tests to ensure comprehensive coverage. + +## Test Suite Structure + +The test suite is organized in a hierarchical structure: ```bash test/ -├── docker-compose-files/ # Test docker-compose files -│ ├── file1.yml -│ ├── file2.yaml -│ └── ... -├── docker-run-files/ # Test docker run commands -│ ├── nginx.txt -│ ├── redis.txt -│ └── ... -├── output/ # Generated test outputs -│ ├── docker-compose/ # Docker Compose test outputs -│ │ └── [filename]/ -│ │ ├── services.json -│ │ ├── cfn/ # AWS CloudFormation outputs -│ │ │ └── aws-cloudformation.cf.yml -│ │ ├── rnd/ # Render outputs -│ │ │ └── render.yaml -│ │ └── dop/ # DigitalOcean outputs -│ │ └── .do/ -│ │ └── deploy.template.yaml -│ └── docker-run/ # Docker run test outputs -│ └── [filename]/ -│ ├── services.json -│ ├── cfn/ -│ │ └── aws-cloudformation.cf.yml -│ ├── rnd/ -│ │ └── render.yaml -│ └── dop/ -│ └── .do/ -│ └── deploy.template.yaml -└── test.ts # Main test file +├── e2e/ # End-to-end tests +│ ├── assertions/ # Testing assertions for output validation +│ │ ├── digitalocean.ts # DigitalOcean-specific assertions +│ │ ├── do-port-assertions.ts +│ │ ├── port-assertions.ts +│ │ └── render.ts # Render-specific assertions +│ ├── docker-compose-files/ # Test docker-compose files +│ │ ├── test1.yml +│ │ ├── test2.yml +│ │ └── ... +│ ├── docker-run-files/ # Test docker run commands +│ │ ├── test1.txt +│ │ ├── test2.txt +│ │ └── ... +│ ├── output/ # Generated test outputs +│ │ └── README.md # Explanation of the output directory +│ ├── utils/ # Testing utilities +│ ├── index.ts # Main e2e test executor +│ ├── test1.ts # Environment variables and volume mapping tests +│ ├── test2.ts # Port mapping tests +│ ├── test3.ts # Environment variable substitution tests +│ └── test4.ts # Render-specific validation +├── unit/ # Unit tests +│ ├── config/ # Tests for configuration +│ ├── parsers/ # Tests for parsers +│ ├── sources/ # Tests for sources +│ └── utils/ # Tests for utility functions +└── test.ts # Main test file ``` -The test suite automatically: +## End-to-End Test Scenarios + +The end-to-end tests cover four main scenarios: + +### Test 1: Environment Variables and Volume Mapping + +Tests the translation of environment variables and volume mappings for both Docker run commands and Docker Compose files. It verifies: + +- Environment variables are correctly passed through to the output +- Environment variables with defaults are handled properly +- Volume mappings are correctly configured in the output + +### Test 2: Port Mappings + +Tests port mapping functionality for both Docker run commands and Docker Compose files. It verifies: + +- Basic port mappings are correctly translated +- Multiple port mappings are handled properly +- Database service port configurations are correctly set +- PORT environment variables are properly set + +### Test 3: Environment Variable Substitution + +Tests the functionality to substitute environment variables from a .env file. It verifies: -- Tests all parsers listed by `listAllParsers()` -- Processes all Docker Compose files in `test/docker-compose-files/` -- Processes all Docker run commands in `test/docker-run-files/` -- Generates outputs in all supported formats (JSON, YAML, text) -- Preserves proper directory structure for multi-file outputs -- Validates parser information and service listing -- Creates organized output directories for inspection +- Environment variables can be substituted with values from a .env file +- Default values are used when variables are not defined +- Substitution works in both Docker run and Docker Compose scenarios -### Adding Test Cases +### Test 4: Schema Validation -#### For Docker Compose +Tests that the generated Render.com YAML output conforms to the official Render.com schema. It verifies: -Add your test files to `test/docker-compose-files/` with `.yml` or `.yaml` extension. +- Output is valid according to the Render.com schema +- Required fields are present and correctly formatted +- Service configurations are properly structured -#### For Docker Run Commands +## Test Output Structure -Add your test commands to `test/docker-run-files/` with `.txt` extension. Each file should contain a single Docker run command. You can use line continuations with `\` for readability: +End-to-end tests generate organized output directories: + +```bash +test/e2e/output/ +├── test1/ # Environment variables and volume mapping test +│ ├── docker-compose/ # Docker Compose test outputs +│ │ ├── dop/ # DigitalOcean outputs +│ │ │ └── .do/ +│ │ │ └── deploy.template.yaml +│ │ └── rnd/ # Render outputs +│ │ └── render.yaml +│ └── docker-run/ # Docker run test outputs +│ ├── dop/ +│ │ └── .do/ +│ │ └── deploy.template.yaml +│ └── rnd/ +│ └── render.yaml +├── test2/ # Port mapping test +│ ├── docker-compose/ +│ │ ├── dop/ +│ │ │ └── .do/ +│ │ │ └── deploy.template.yaml +│ │ └── rnd/ +│ │ └── render.yaml +│ └── docker-run/ +│ ├── dop/ +│ │ └── .do/ +│ │ └── deploy.template.yaml +│ └── rnd/ +│ └── render.yaml +└── ... +``` + +## Adding Test Cases + +### For Docker Compose Tests + +Add your test files to `test/e2e/docker-compose-files/` with `.yml` or `.yaml` extension. Each file should represent a specific test scenario. + +### For Docker Run Commands + +Add your test commands to `test/e2e/docker-run-files/` with `.txt` extension. Each file should contain a single Docker run command. You can use line continuations with `\` for readability: ```bash docker run -d \ @@ -91,41 +175,39 @@ docker run -d \ nginx:alpine ``` -### Adding Tests for New Parsers +### Adding New End-to-End Tests -When adding a new parser: +To add a new end-to-end test: -1. Add Docker Compose test files to `test/docker-compose-files/` -2. Add Docker run test files to `test/docker-run-files/` -3. The test suite will automatically include your parser in testing -4. Check outputs in `test/output/docker-compose/` and `test/output/docker-run/` +1. Create a new test file (e.g., `test5.ts`) in `test/e2e/` +2. Follow the pattern of existing test files: + - Define the test scenario + - Create test functions for Docker run and Docker Compose + - Use assertions to validate the output +3. Add your test to `test/e2e/index.ts` to ensure it gets executed -For parsers that generate multiple files: +### Adding Assertions -1. Ensure your `parseFiles` method returns the correct file structure -2. The test suite will automatically preserve directory structure -3. Verify that nested directories are created correctly in the output +For new validation requirements: -## Examining Test Output +1. Add assertion functions to the appropriate file in `test/e2e/assertions/` +2. Use these assertions in your test functions +3. For provider-specific assertions, create new files if needed -After running tests, check the output directory structure to verify that your parser correctly generates files: +## Unit Tests -```bash -$ tree -a test/output/docker-run/simple-1/ -test/output/docker-run/simple-1/ -├── cfn -│ └── aws-cloudformation.cf.yml -├── dop -│ └── .do -│ └── deploy.template.yaml -├── rnd -│ └── render.yaml -└── services.json - -5 directories, 4 files -``` +Unit tests validate individual components of the codebase: + +- **Config tests**: Verify configuration files and settings +- **Parser tests**: Check that parsers handle input correctly +- **Source tests**: Validate source handling (Docker run, Docker Compose) +- **Utility tests**: Ensure utility functions work as expected + +To add a new unit test: -This shows how each parser generates its files with the appropriate directory structure. +1. Create a new test file in the appropriate directory under `test/unit/` +2. Use the Vitest framework for testing (similar to Jest) +3. Follow the naming convention: `*.test.ts` ## Local Testing with `npm link` @@ -135,7 +217,7 @@ Test locally using `npm link`. Development environment setup: some-root-dir/ |-- docker-to-iac/ |-- my-dev-env/ -| |-- index.ts +| |-- index.js | |-- docker-compose.yml | |-- docker-run.txt | |-- node_modules/ @@ -145,7 +227,7 @@ some-root-dir/ Setup steps: 1. In `docker-to-iac/`: `npm link` -2. In `my-dev-env`: `npm link ../docker-to-iac/` +2. In `my-dev-env`: `npm link @deploystack/docker-to-iac` ### Setting up my-dev-env @@ -162,7 +244,7 @@ import { join, dirname } from 'path'; const dockerComposeContent = readFileSync('docker-compose.yml', 'utf8'); const composeResult = translate(dockerComposeContent, { source: 'compose', - target: 'CFN', + target: 'RND', // Render.com output templateFormat: 'yaml' }); @@ -184,7 +266,7 @@ Object.entries(composeResult.files).forEach(([path, fileData]) => { const dockerRunContent = readFileSync('docker-run.txt', 'utf8'); const runResult = translate(dockerRunContent, { source: 'run', - target: 'CFN', + target: 'DOP', // DigitalOcean output templateFormat: 'yaml' }); @@ -211,10 +293,35 @@ Object.entries(runResult.files).forEach(([path, fileData]) => { ## Test Results -The test suite shows success (✓) or failure (❌) for each test. On failure: +The test suite shows detailed results for each test: + +- Unit tests show individual function validation results +- E2E tests provide a summary of passed and failed tests +- Test failures include specific error messages for debugging + +On failure: - Error details are logged - Process exits with code 1 - GitHub Actions fails PR check -Check both Docker Compose and Docker run outputs in `test/output/` to verify your parser produces expected results across all formats and maintains the correct directory structure for multi-file outputs. +## Code Coverage + +To generate code coverage reports: + +```bash +npm run test:coverage +``` + +This will create coverage reports in the `coverage/` directory, including HTML reports you can view in a browser. + +## Troubleshooting Test Failures + +If tests fail: + +1. Check the test output for specific error messages +2. Review the actual and expected values in assertion failures +3. Check the generated files in `test/e2e/output/` to see what was produced +4. For schema validation failures, check the error details against the provider's schema documentation + +By following these steps, you can ensure your changes are fully tested and compatible with all supported output formats.