diff --git a/docs/rules/no-duplicate-exported-ports-rule.md b/docs/rules/no-duplicate-exported-ports-rule.md index ee10dbf..4ec8e9d 100644 --- a/docs/rules/no-duplicate-exported-ports-rule.md +++ b/docs/rules/no-duplicate-exported-ports-rule.md @@ -53,6 +53,30 @@ Duplicate `ports` can often be the result of simple typographical errors. By cat developers can avoid debugging complex port conflicts and ensure their Compose configurations are valid before attempting to run them. +## Known Limitations + +This rule does not support the detection of duplicate exported ports when ports are defined using environment variables. +Since environment variables are resolved at runtime, it's impossible to statically determine their values during the +linting process. + +For example, in the following configuration: + +```yaml +services: + web: + image: image + ports: + - "${APP_PORT}:80" + db: + image: image + ports: + - "8080:80" +``` + +The `APP_PORT` environment variable is used to define the external port. However, since its value is not available at +the time of linting, the rule will not be able to detect if the port conflicts with other services. It's recommended to +ensure that environment variables used for ports are unique or manually check for conflicts in such cases. + ## Version This rule was introduced in Docker-Compose-Linter [1.0.0](https://github.com/zavoloklom/docker-compose-linter/releases). diff --git a/docs/rules/service-ports-alphabetical-order-rule.md b/docs/rules/service-ports-alphabetical-order-rule.md index 7110364..f409427 100644 --- a/docs/rules/service-ports-alphabetical-order-rule.md +++ b/docs/rules/service-ports-alphabetical-order-rule.md @@ -18,6 +18,7 @@ services: web: image: nginx ports: + - "${WEB_PORT}:80" - '81' - "79" - 80:80 @@ -36,6 +37,7 @@ services: web: image: nginx ports: + - "${WEB_PORT}:80" - "79" - 80:80 - '81' diff --git a/src/util/service-ports-parser.ts b/src/util/service-ports-parser.ts index 5fdf62b..91eed92 100644 --- a/src/util/service-ports-parser.ts +++ b/src/util/service-ports-parser.ts @@ -24,24 +24,25 @@ function extractPublishedPortValue(yamlNode: unknown): string { function parsePortsRange(port: string): string[] { const [start, end] = port.split('-').map(Number); - if (Number.isNaN(start)) { - throw new Error('Invalid port range'); + if (Number.isNaN(start) || Number.isNaN(end)) { + return []; } - if (end && !Number.isNaN(end)) { - if (start > end) { - throw new Error('Invalid port range: start port is greater than end port'); - } + if (!end) { + return [start.toString()]; + } - const ports: string[] = []; - // eslint-disable-next-line no-plusplus - for (let i = start; i <= end; i++) { - ports.push(i.toString()); - } - return ports; + if (start > end) { + // Invalid port range: start port is greater than end port + return []; } - return [start.toString()]; + const ports: string[] = []; + // eslint-disable-next-line no-plusplus + for (let i = start; i <= end; i++) { + ports.push(i.toString()); + } + return ports; } export { extractPublishedPortValue, parsePortsRange }; diff --git a/tests/rules/no-duplicate-exported-ports-rule.spec.ts b/tests/rules/no-duplicate-exported-ports-rule.spec.ts index 2b634cd..ff3ffd4 100644 --- a/tests/rules/no-duplicate-exported-ports-rule.spec.ts +++ b/tests/rules/no-duplicate-exported-ports-rule.spec.ts @@ -34,6 +34,12 @@ services: published: 8080 protocol: tcp mode: host + g-service: + image: nginx:latest + ports: + - "$WEB_PORT:80" + - "$WEB_PORT:81" + - "$WEB_PORT-9000:80-81" `; // YAML with unique exported ports using different syntax diff --git a/tests/rules/service-ports-alphabetical-order-rule.spec.ts b/tests/rules/service-ports-alphabetical-order-rule.spec.ts index f3dd12b..572e2f2 100644 --- a/tests/rules/service-ports-alphabetical-order-rule.spec.ts +++ b/tests/rules/service-ports-alphabetical-order-rule.spec.ts @@ -22,6 +22,7 @@ services: published: 8082 protocol: tcp mode: host + - "$WEB_PORT:80" `; const yamlWithCorrectPortOrder = ` @@ -29,6 +30,7 @@ services: web: image: nginx ports: + - "$WEB_PORT:80" - "79" - 80:80 - '81' diff --git a/tests/util/service-ports-parser.spec.ts b/tests/util/service-ports-parser.spec.ts index b9c0b58..db00671 100644 --- a/tests/util/service-ports-parser.spec.ts +++ b/tests/util/service-ports-parser.spec.ts @@ -27,16 +27,8 @@ test('extractPublishedPortValue should return empty string for unknown node type }); test('parsePortsRange should return array of ports for a range', (t) => { - const result = parsePortsRange('3000-3002'); - t.deepEqual(result, ['3000', '3001', '3002']); -}); - -test('parsePortsRange should throw error for invalid range', (t) => { - const error = t.throws(() => parsePortsRange('invalid-range'), { - instanceOf: Error, - message: 'Invalid port range', - }); - t.is(error?.message, 'Invalid port range'); + t.deepEqual(parsePortsRange('3000-3002'), ['3000', '3001', '3002']); + t.deepEqual(parsePortsRange('3000-3000'), ['3000']); }); test('parsePortsRange should return single port when no range is specified', (t) => { @@ -44,10 +36,14 @@ test('parsePortsRange should return single port when no range is specified', (t) t.deepEqual(result, ['8080']); }); -test('parsePortsRange should throw error when start port is greater than end port', (t) => { - const error = t.throws(() => parsePortsRange('3003-3001'), { - instanceOf: Error, - message: 'Invalid port range: start port is greater than end port', - }); - t.is(error?.message, 'Invalid port range: start port is greater than end port'); +test('parsePortsRange should return empty array for invalid range', (t) => { + t.deepEqual(parsePortsRange('$TEST'), []); + t.deepEqual(parsePortsRange('$TEST-3002'), []); + t.deepEqual(parsePortsRange('3000-$TEST'), []); + t.deepEqual(parsePortsRange('$TEST-$TEST'), []); + t.deepEqual(parsePortsRange('3000-$TEST-$TEST-5000'), []); +}); + +test('parsePortsRange should return empty array when start port is greater than end port', (t) => { + t.deepEqual(parsePortsRange('3005-3002'), []); });