diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a6f49ad..bfd44cf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,11 +38,8 @@ jobs: run: npm run test:unit - name: Start Camunda ${{ matrix.camunda }} with Docker Compose - run: | - cd assets/c8/${{ matrix.camunda }} - DATABASE=elasticsearch docker compose --profile elasticsearch up -d - env: - DATABASE: elasticsearch + working-directory: assets/c8/${{ matrix.camunda }} + run: docker compose up -d - name: Wait for Camunda to be ready run: | @@ -79,19 +76,13 @@ jobs: - name: Stop Camunda if: always() - run: | - cd assets/c8/${{ matrix.camunda }} - DATABASE=elasticsearch docker compose --profile elasticsearch down -v - env: - DATABASE: elasticsearch + working-directory: assets/c8/${{ matrix.camunda }} + run: docker compose down -v - name: Show Camunda logs on failure if: failure() - run: | - cd assets/c8/${{ matrix.camunda }} - DATABASE=elasticsearch docker compose --profile elasticsearch logs - env: - DATABASE: elasticsearch + working-directory: assets/c8/${{ matrix.camunda }} + run: docker compose logs release: name: Release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7ad725d..66c7333 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,12 +35,9 @@ jobs: run: npm run test:unit - name: Start Camunda ${{ matrix.camunda }} with Docker Compose - run: | - cd assets/c8/${{ matrix.camunda }} - DATABASE=elasticsearch docker compose --profile elasticsearch up -d - env: - DATABASE: elasticsearch - + working-directory: assets/c8/${{ matrix.camunda }} + run: docker compose up -d + - name: Wait for Camunda to be ready run: | echo "Waiting for Camunda at localhost:8080..." @@ -76,16 +73,10 @@ jobs: - name: Stop Camunda if: always() - run: | - cd assets/c8/${{ matrix.camunda }} - DATABASE=elasticsearch docker compose --profile elasticsearch down -v - env: - DATABASE: elasticsearch + working-directory: assets/c8/${{ matrix.camunda }} + run: docker compose down -v - name: Show Camunda logs on failure if: failure() - run: | - cd assets/c8/${{ matrix.camunda }} - DATABASE=elasticsearch docker compose --profile elasticsearch logs - env: - DATABASE: elasticsearch + working-directory: assets/c8/${{ matrix.camunda }} + run: docker compose logs diff --git a/.gitignore b/.gitignore index d639728..cbc4c73 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,7 @@ web_modules/ .env.* !.env.example !.env.database.* +!assets/**/*/.env # parcel-bundler cache (https://parceljs.org/) .cache diff --git a/EXAMPLES.md b/EXAMPLES.md index fbee887..2c975d5 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -63,6 +63,9 @@ c8 create pi --id=order-process --variables='{"orderId":"12345","amount":100}' # Create and wait for completion c8 create pi --id=order-process --awaitCompletion +# Create and wait with custom timeout (30 seconds) +c8 create pi --id=order-process --awaitCompletion --requestTimeout=30000 + # Note: --fetchVariables is reserved for future API support # All variables are currently returned by default ``` @@ -71,6 +74,8 @@ c8 create pi --id=order-process --awaitCompletion The `await` command is an alias for `create` with `--awaitCompletion`. It uses the Camunda 8 API's built-in server-side waiting to create a process instance and wait for completion. +The `--requestTimeout` option specifies the maximum time in milliseconds to wait for the process instance to complete. By default (or when set to 0), the generic request timeout configured in the cluster is used. + ```bash # Create and wait for completion (shorthand) c8 await pi --id=order-process @@ -79,6 +84,9 @@ c8 await process-instance --id=order-process # With variables c8 await pi --id=order-process --variables='{"orderId":"12345"}' +# With custom timeout (60 seconds) +c8 await pi --id=order-process --requestTimeout=60000 + # Equivalent to: c8 create pi --id=order-process --awaitCompletion diff --git a/README.md b/README.md index 8d4fc97..8d442fb 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,16 @@ c8ctl create process-instance --id=myProcess # Create process instance and wait for completion c8ctl create pi --id=myProcess --awaitCompletion +# Create process instance with custom timeout (30 seconds) +c8ctl create pi --id=myProcess --awaitCompletion --requestTimeout=30000 + # Await process instance completion (alias for create with --awaitCompletion) c8ctl await pi --id=myProcess c8ctl await process-instance --id=myProcess +# Await with custom timeout +c8ctl await pi --id=myProcess --requestTimeout=60000 + # Cancel process instance c8ctl cancel pi 123456 diff --git a/assets/c8/8.8/.env b/assets/c8/8.8/.env new file mode 100644 index 0000000..a9f3a6c --- /dev/null +++ b/assets/c8/8.8/.env @@ -0,0 +1,3 @@ +# Default configuration for docker compose +# Activates the elasticsearch profile (vs opensearch) +COMPOSE_PROFILES=elasticsearch diff --git a/assets/c8/8.8/docker-compose.yml b/assets/c8/8.8/docker-compose.yml index 27121e0..59a3bc9 100644 --- a/assets/c8/8.8/docker-compose.yml +++ b/assets/c8/8.8/docker-compose.yml @@ -128,9 +128,9 @@ services: - 9600:9600 - 8080:8080 depends_on: - - ${DATABASE} + - elasticsearch networks: - zeebe_network env_file: - - envs/.env.database.${DATABASE} + - envs/.env.database.elasticsearch diff --git a/assets/c8/8.9/.env b/assets/c8/8.9/.env new file mode 100644 index 0000000..70ed0ce --- /dev/null +++ b/assets/c8/8.9/.env @@ -0,0 +1,2 @@ +# Default configuration for docker compose +CAMUNDA_VERSION=8.9.0-alpha4 diff --git a/assets/c8/8.9/docker-compose.yml b/assets/c8/8.9/docker-compose.yml index a114903..2adc985 100644 --- a/assets/c8/8.9/docker-compose.yml +++ b/assets/c8/8.9/docker-compose.yml @@ -1,135 +1,61 @@ -networks: - zeebe_network: {} - services: - postgres: - container_name: postgres - image: postgres:17.5-alpine - ports: - - '5432:5432' - environment: - POSTGRES_DB: identity - POSTGRES_USER: identity - POSTGRES_PASSWORD: 't2L@!AqSMg8%I%NmHM' - networks: - - zeebe_network - - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:8.18.4 - container_name: elasticsearch - profiles: ['elasticsearch'] - environment: - - discovery.type=single-node - - cluster.name=elasticsearch - - bootstrap.memory_lock=true - - xpack.security.enabled=false - - 'ES_JAVA_OPTS=-Xms1024m -Xmx1024m' - - path.repo=/usr/local/els-snapshots - ulimits: - memlock: - soft: -1 - hard: -1 - ports: - - 9200:9200 - - 9300:9300 - networks: - - zeebe_network - restart: always - volumes: - - ./els-snapshots:/usr/local/els-snapshots - - opensearch: - image: opensearchproject/opensearch:2.17.0 - container_name: opensearch - profiles: ['opensearch'] - environment: - - cluster.name=opensearch-cluster - - discovery.type=single-node - - plugins.security.disabled=true - - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping - - 'OPENSEARCH_JAVA_OPTS=-Xms1024m -Xmx1024m' # minimum and maximum Java heap size, recommend setting both to 50% of system RAM - - OPENSEARCH_INITIAL_ADMIN_PASSWORD=yourStrongPassword123! - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems - hard: 65536 - ports: - - 9200:9200 - - 9605:9605 # required for Performance Analyzer - networks: - - zeebe_network - - kibana: - image: docker.elastic.co/kibana/kibana:8.18.4 - profiles: ['elasticsearch'] - ports: - - 5601:5601 - environment: - - elasticsearch.hosts=http://elasticsearch:9200 - depends_on: - - elasticsearch - networks: - - zeebe_network - - keycloak: - depends_on: - - postgres - container_name: keycloak - image: quay.io/keycloak/keycloak:26.0 - command: start-dev --http-relative-path=/auth - ports: - - '18080:8080' - environment: - KC_DB: postgres - KC_DB_URL: jdbc:postgresql://postgres:5432/identity - KC_DB_USERNAME: identity - KC_DB_PASSWORD: 't2L@!AqSMg8%I%NmHM' - KEYCLOAK_ADMIN: admin - KEYCLOAK_ADMIN_PASSWORD: admin - healthcheck: - test: ['CMD-SHELL', 'exec 3<>/dev/tcp/localhost/8080 && echo -e "GET /auth/health/ready HTTP/1.1\nhost: localhost:8080\n" >&3 && timeout 1 cat <&3 | grep "200 OK"'] - interval: 30s - timeout: 15s - retries: 8 - start_period: 30s - networks: - - zeebe_network - camunda: + image: camunda/camunda:${CAMUNDA_VERSION} container_name: camunda - image: camunda/camunda:SNAPSHOT environment: - - 'JAVA_TOOL_OPTIONS=-Xms512m -Xmx1g' - - ZEEBE_BROKER_NETWORK_HOST=camunda - - SPRING_PROFILES_ACTIVE=e2e-test,consolidated-auth,tasklist,broker,operate,identity - - CAMUNDA_SECURITY_AUTHENTICATION_UNPROTECTEDAPI=false - - CAMUNDA_SECURITY_AUTHORIZATIONS_ENABLED=true - - CAMUNDA_SECURITY_AUTHENTICATION_METHOD=BASIC - - CAMUNDA_SECURITY_MULTITENANCY_CHECKSENABLED=false - - CAMUNDA_SECURITY_INITIALIZATION_USERS_0_USERNAME=demo - - CAMUNDA_SECURITY_INITIALIZATION_USERS_0_PASSWORD=demo - - CAMUNDA_SECURITY_INITIALIZATION_USERS_0_NAME=Demo - - CAMUNDA_SECURITY_INITIALIZATION_USERS_0_EMAIL=demo@example.com - - CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES_ADMIN_USERS_0=demo - - CAMUNDA_SECURITY_INITIALIZATION_USERS_1_USERNAME=lisa - - CAMUNDA_SECURITY_INITIALIZATION_USERS_1_PASSWORD=lisa - - CAMUNDA_SECURITY_INITIALIZATION_USERS_1_NAME=lisa - - CAMUNDA_SECURITY_INITIALIZATION_USERS_1_EMAIL=lisa@example.com - - CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES_ADMIN_USERS_1=lisa - - CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=elasticsearch - - CAMUNDA_DATA_SECONDARY_STORAGE_ELASTICSEARCH_URL=http://elasticsearch:9200 - - CAMUNDA_DATA_SECONDARYSTORAGE_ELASTICSEARCH_WAITFORIMPORTERS=false - - CAMUNDA_TASKLIST_V2_MODE_ENABLED=${CAMUNDA_TASKLIST_V2_MODE_ENABLED:-true} + SPRING_PROFILES_ACTIVE: 'broker,consolidated-auth' + ZEEBE_CLOCK_CONTROLLED: 'true' + ZEEBE_LOG_APPENDER: 'Stackdriver' + CAMUNDA_SECURITY_AUTHENTICATION_UNPROTECTEDAPI: 'true' + CAMUNDA_SECURITY_AUTHORIZATIONS_ENABLED: 'false' + # H2 / in-memory config (mirrors H2Configuration) + CAMUNDA_DATABASE_URL: 'jdbc:h2:mem:cpt;DB_CLOSE_DELAY=-1;MODE=PostgreSQL' + CAMUNDA_DATABASE_TYPE: 'rdbms' + CAMUNDA_DATABASE_USERNAME: 'sa' + CAMUNDA_DATABASE_PASSWORD: '' + CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: 'rdbms' + ZEEBE_BROKER_EXPORTERS_RDBMS_CLASSNAME: 'io.camunda.exporter.rdbms.RdbmsExporter' + ZEEBE_BROKER_EXPORTERS_RDBMS_ARGS_FLUSH_INTERVAL: 'PT0S' + ZEEBE_BROKER_EXPORTERS_RDBMS_ARGS_DEFAULT_HISTORY_TTL: 'PT2S' + ZEEBE_BROKER_EXPORTERS_RDBMS_ARGS_MIN_HISTORY_CLEANUP_INTERVAL: 'PT2S' + ZEEBE_BROKER_EXPORTERS_RDBMS_ARGS_MAX_HISTORY_CLEANUP_INTERVAL: 'PT5S' + LOGGING_LEVEL_IO_CAMUNDA_DB_RDBMS: 'INFO' + LOGGING_LEVEL_ORG_MYBATIS: 'INFO' ports: - - 26500:26500 - - 9600:9600 - - 8080:8080 - depends_on: - - ${DATABASE} + - '8080:8080' # REST API (ZEEBE_REST_ADDRESS -> http://localhost:8080) + - '26500:26500' # gRPC Gateway + - '9600:9600' # Monitoring / actuator / test time control + # Uncomment if you need direct access to internal broker ports: + # - "26501:26501" # Command API (internal) + # - "26502:26502" # Internal API (internal) + healthcheck: + # Mimic readiness checks: uses monitoring port + health endpoint + test: ['CMD', 'wget', '-qO', '-', 'http://localhost:9600/actuator/health/status'] + interval: 5s + timeout: 3s + retries: 30 networks: - - zeebe_network - env_file: - - envs/.env.database.${DATABASE} + - camunda-net + + # connectors: + # image: camunda/connectors-bundle:SNAPSHOT + # container_name: camunda-connectors + # depends_on: + # camunda: + # condition: service_healthy + # environment: + # # Adapted from runtime: service name + internal port + # ZEEBE_CLIENT_BROKER_GATEWAY-ADDRESS: "camunda:26500" + # ZEEBE_CLIENT_SECURITY_PLAINTEXT: "true" + # # Optional additional env you might pass via framework (placeholders): + # # CONNECTORS_LOG_APPENDER: "Stackdriver" + # # CAMUNDA_CLIENT_GRPC-ADDRESS: "camunda:26500" + # # CAMUNDA_CLIENT_REST-ADDRESS: "http://camunda:8080" + # ports: + # - "8085:8080" # Expose connectors runtime (external 8085 -> internal 8080) + # networks: + # - camunda-net + +networks: + camunda-net: + name: camunda-net diff --git a/assets/c8/8.9/envs/.env.database.elasticsearch b/assets/c8/8.9/envs/.env.database.elasticsearch deleted file mode 100644 index 4d40758..0000000 --- a/assets/c8/8.9/envs/.env.database.elasticsearch +++ /dev/null @@ -1 +0,0 @@ -# Elasticsearch database configuration for Camunda 8.9 diff --git a/package-lock.json b/package-lock.json index 0a6a7de..7042620 100644 --- a/package-lock.json +++ b/package-lock.json @@ -196,6 +196,7 @@ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -2786,6 +2787,7 @@ "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -5016,6 +5018,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5857,6 +5860,7 @@ "integrity": "sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/error": "^4.0.0", @@ -6660,6 +6664,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -6754,6 +6759,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/src/commands/completion.ts b/src/commands/completion.ts index f3bc9d4..beb08aa 100644 --- a/src/commands/completion.ts +++ b/src/commands/completion.ts @@ -49,7 +49,7 @@ _c8ctl_completions() { local help_resources="list get create complete await search deploy run watch cancel resolve fail activate publish correlate upgrade downgrade init" # Global flags - local flags="--help --version --profile --from --all --bpmnProcessId --id --processInstanceKey --processDefinitionKey --parentProcessInstanceKey --variables --state --assignee --type --correlationKey --timeToLive --maxJobsToActivate --timeout --worker --retries --errorMessage --baseUrl --clientId --clientSecret --audience --oAuthUrl --defaultTenantId --awaitCompletion --fetchVariables --name --key --elementId --errorType --value --scopeKey --fullValue --userTask --ut --processDefinition --pd --iname --iid --iassignee --ierrorMessage --itype --ivalue" + local flags="--help --version --profile --from --all --bpmnProcessId --id --processInstanceKey --processDefinitionKey --parentProcessInstanceKey --variables --state --assignee --type --correlationKey --timeToLive --maxJobsToActivate --timeout --worker --retries --errorMessage --baseUrl --clientId --clientSecret --audience --oAuthUrl --defaultTenantId --awaitCompletion --fetchVariables --requestTimeout --name --key --elementId --errorType --value --scopeKey --fullValue --userTask --ut --processDefinition --pd --iname --iid --iassignee --ierrorMessage --itype --ivalue" case \${cword} in 1) @@ -227,6 +227,7 @@ _c8ctl() { '--version[Process definition version]:version:' '--awaitCompletion[Wait for process instance to complete]' '--fetchVariables[Comma-separated variable names]:variables:' + '--requestTimeout[Timeout in milliseconds for process completion]:milliseconds:' '--name[Variable or resource name]:name:' '--key[Resource key]:key:' '--elementId[Element ID]:id:' @@ -546,6 +547,8 @@ complete -c c8ctl -l awaitCompletion -d 'Wait for process instance to complete' complete -c c8 -l awaitCompletion -d 'Wait for process instance to complete' complete -c c8ctl -l fetchVariables -d 'Comma-separated variable names' -r complete -c c8 -l fetchVariables -d 'Comma-separated variable names' -r +complete -c c8ctl -l requestTimeout -d 'Timeout in milliseconds for process completion' -r +complete -c c8 -l requestTimeout -d 'Timeout in milliseconds for process completion' -r complete -c c8ctl -l name -d 'Variable or resource name' -r complete -c c8 -l name -d 'Variable or resource name' -r complete -c c8ctl -l key -d 'Resource key' -r diff --git a/src/commands/help.ts b/src/commands/help.ts index 8bd04f1..3fc7ed9 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -90,6 +90,7 @@ Flags: --id Process definition ID (alias for --bpmnProcessId) --awaitCompletion Wait for process instance to complete (use with 'create pi') --fetchVariables Reserved for future use (all variables returned by default) + --requestTimeout Timeout in milliseconds for process completion (use with --awaitCompletion) --version, -v Show version --help, -h Show help @@ -346,6 +347,7 @@ Resources and their available flags: --variables Process variables as JSON string --awaitCompletion Wait for process instance to complete --fetchVariables Reserved for future use (all variables returned by default) + --requestTimeout Timeout in milliseconds for process completion (use with --awaitCompletion) --profile Use specific profile Examples: @@ -353,6 +355,7 @@ Examples: c8ctl create pi --id=order-process --version=2 c8ctl create pi --id=order-process --variables='{"orderId":"12345"}' c8ctl create pi --id=order-process --awaitCompletion + c8ctl create pi --id=order-process --awaitCompletion --requestTimeout=30000 `.trim()); } @@ -400,6 +403,7 @@ Resources and their available flags: --version Process definition version --variables Process variables as JSON string --fetchVariables Reserved for future use (all variables returned by default) + --requestTimeout Timeout in milliseconds for process completion --profile Use specific profile Description: @@ -410,6 +414,7 @@ Description: Examples: c8ctl await pi --id=order-process c8ctl await pi --id=order-process --variables='{"orderId":"12345"}' + c8ctl await pi --id=order-process --requestTimeout=30000 # Equivalent to: c8ctl create pi --id=order-process --awaitCompletion diff --git a/src/commands/process-instances.ts b/src/commands/process-instances.ts index bf329fd..6fa7b10 100644 --- a/src/commands/process-instances.ts +++ b/src/commands/process-instances.ts @@ -117,6 +117,7 @@ export async function createProcessInstance(options: { variables?: string; awaitCompletion?: boolean; fetchVariables?: boolean; + requestTimeout?: number; }): Promise<{ processInstanceKey: string | number; variables?: Record; @@ -152,6 +153,7 @@ export async function createProcessInstance(options: { processDefinitionVersion?: number; variables?: Record; awaitCompletion?: boolean; + requestTimeout?: number; } = { processDefinitionId: options.processDefinitionId, tenantId, @@ -176,6 +178,11 @@ export async function createProcessInstance(options: { logger.info('Waiting for process instance to complete...'); } + // Set requestTimeout if provided + if (options.requestTimeout !== undefined) { + request.requestTimeout = options.requestTimeout; + } + const result = await client.createProcessInstance(request as unknown as ProcessInstanceCreationInstructionById); if (options.awaitCompletion) { diff --git a/src/index.ts b/src/index.ts index 5bf9503..abedd66 100755 --- a/src/index.ts +++ b/src/index.ts @@ -107,6 +107,7 @@ function parseCliArgs() { errorType: { type: 'string' }, awaitCompletion: { type: 'boolean' }, fetchVariables: { type: 'boolean' }, + requestTimeout: { type: 'string' }, value: { type: 'string' }, scopeKey: { type: 'string' }, fullValue: { type: 'boolean' }, @@ -348,6 +349,7 @@ async function main() { variables: values.variables as string | undefined, awaitCompletion: values.awaitCompletion as boolean | undefined, fetchVariables: values.fetchVariables as boolean | undefined, + requestTimeout: (values.requestTimeout && typeof values.requestTimeout === 'string') ? parseInt(values.requestTimeout) : undefined, }); return; } @@ -374,6 +376,7 @@ async function main() { variables: values.variables as string | undefined, awaitCompletion: true, // Always true for await command fetchVariables: values.fetchVariables as boolean | undefined, + requestTimeout: (values.requestTimeout && typeof values.requestTimeout === 'string') ? parseInt(values.requestTimeout) : undefined, }); return; } diff --git a/tests/unit/help.test.ts b/tests/unit/help.test.ts index 6860fdd..e41f71e 100644 --- a/tests/unit/help.test.ts +++ b/tests/unit/help.test.ts @@ -69,6 +69,7 @@ describe('Help Module', () => { assert.ok(output.includes('--variables')); assert.ok(output.includes('--awaitCompletion')); assert.ok(output.includes('--fetchVariables')); + assert.ok(output.includes('--requestTimeout')); assert.ok(output.includes('--version')); assert.ok(output.includes('--help'));