From 5e619c3634b813be349adca2e33c9feb91bdb6e4 Mon Sep 17 00:00:00 2001 From: lingniu Date: Fri, 26 Jun 2026 11:17:27 +0800 Subject: [PATCH] feat: add jt808 ingest app --- Dockerfile | 2 +- README.md | 2 + deploy/portainer/docker-compose.yml | 25 +++++ modules/apps/gb32960-ingest-app/pom.xml | 4 + .../src/main/resources/application.yml | 6 ++ modules/apps/jt808-ingest-app/pom.xml | 79 +++++++++++++++ .../jt808app/Jt808IngestApplication.java | 21 ++++ .../src/main/resources/application.yml | 99 +++++++++++++++++++ .../Jt808IngestAppCompositionTest.java | 69 +++++++++++++ .../jt808app/Jt808IngestAppDefaultsTest.java | 46 +++++++++ modules/apps/vehicle-analytics-app/pom.xml | 4 + .../src/main/resources/application.yml | 6 ++ modules/apps/vehicle-history-app/pom.xml | 4 + .../src/main/resources/application.yml | 6 ++ pom.xml | 6 ++ woodpecker.yml | 38 ++++++- 16 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 modules/apps/jt808-ingest-app/pom.xml create mode 100644 modules/apps/jt808-ingest-app/src/main/java/com/lingniu/ingest/jt808app/Jt808IngestApplication.java create mode 100644 modules/apps/jt808-ingest-app/src/main/resources/application.yml create mode 100644 modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppCompositionTest.java create mode 100644 modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppDefaultsTest.java diff --git a/Dockerfile b/Dockerfile index 015fa950..79786696 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,6 @@ ENV JAVA_OPTS="" WORKDIR /app COPY modules/apps/${APP_NAME}/target/${APP_NAME}.jar /app/app.jar -EXPOSE 20100 20200 20300 32960 +EXPOSE 20100 20200 20300 20400 32960 10808 ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"] diff --git a/README.md b/README.md index d4ba0b9e..9f5b84cd 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ lingniu-vehicle-ingest/ │ └── apps/ │ ├── command-gateway/ HTTP → 设备下行命令(808 位置/参数/属性/控制/区域删除/报警确认 + 1078 音视频控制) │ ├── gb32960-ingest-app/ GB32960 TCP 接入 + Kafka 投递 +│ ├── jt808-ingest-app/ JT808 TCP 接入 + Kafka 投递 │ ├── vehicle-history-app/ 历史查询 + 原始归档索引消费 │ └── vehicle-analytics-app/ 车辆状态/日统计消费 ├── docs/ 架构文档、模块图、实施计划 @@ -71,6 +72,7 @@ lingniu-vehicle-ingest/ mvn -v mvn clean install -DskipTests mvn -pl :gb32960-ingest-app -am spring-boot:run +mvn -pl :jt808-ingest-app -am spring-boot:run ``` ## 迁移说明 diff --git a/deploy/portainer/docker-compose.yml b/deploy/portainer/docker-compose.yml index ae6fd4b1..3e71b29a 100644 --- a/deploy/portainer/docker-compose.yml +++ b/deploy/portainer/docker-compose.yml @@ -19,6 +19,9 @@ x-common-env: &common-env KAFKA_TOPIC_GB32960_EVENT: ${KAFKA_TOPIC_GB32960_EVENT:-vehicle.event.gb32960.v1} KAFKA_TOPIC_GB32960_RAW: ${KAFKA_TOPIC_GB32960_RAW:-vehicle.raw.gb32960.v1} KAFKA_TOPIC_GB32960_DLQ: ${KAFKA_TOPIC_GB32960_DLQ:-vehicle.dlq.gb32960.v1} + KAFKA_TOPIC_JT808_EVENT: ${KAFKA_TOPIC_JT808_EVENT:-vehicle.event.jt808.v1} + KAFKA_TOPIC_JT808_RAW: ${KAFKA_TOPIC_JT808_RAW:-vehicle.raw.jt808.v1} + KAFKA_TOPIC_JT808_DLQ: ${KAFKA_TOPIC_JT808_DLQ:-vehicle.dlq.jt808.v1} services: gb32960-ingest-app: @@ -45,12 +48,32 @@ services: networks: - vehicle-ingest + jt808-ingest-app: + image: crpi-85r4m0ackrm3qpje-vpc.cn-shanghai.personal.cr.aliyuncs.com/oneos/jt808-ingest-app:${LINGNIU_IMAGE_VERSION:?set LINGNIU_IMAGE_VERSION} + container_name: jt808-ingest-app + restart: unless-stopped + environment: + <<: *common-env + HTTP_PORT: 20400 + JT808_PORT: 10808 + KAFKA_CONSUMER_ENABLED: "false" + KAFKA_NODE_ID: ${KAFKA_NODE_ID_JT808:-jt808-ingest-portainer} + VEHICLE_IDENTITY_FILE: /data/vehicle-identity.jsonl + ports: + - "${JT808_HTTP_PORT:-20400}:20400" + - "${JT808_TCP_PORT:-10808}:10808" + volumes: + - jt808-ingest-data:/data + networks: + - vehicle-ingest + vehicle-history-app: image: crpi-85r4m0ackrm3qpje-vpc.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-history-app:${LINGNIU_IMAGE_VERSION:?set LINGNIU_IMAGE_VERSION} container_name: vehicle-history-app restart: unless-stopped depends_on: - gb32960-ingest-app + - jt808-ingest-app environment: <<: *common-env HTTP_PORT: 20200 @@ -75,6 +98,7 @@ services: restart: unless-stopped depends_on: - gb32960-ingest-app + - jt808-ingest-app environment: <<: *common-env HTTP_PORT: 20300 @@ -98,6 +122,7 @@ networks: volumes: gb32960-ingest-data: + jt808-ingest-data: vehicle-history-archive: vehicle-history-event-store: vehicle-analytics-stat: diff --git a/modules/apps/gb32960-ingest-app/pom.xml b/modules/apps/gb32960-ingest-app/pom.xml index 2409f6fd..49bf5972 100644 --- a/modules/apps/gb32960-ingest-app/pom.xml +++ b/modules/apps/gb32960-ingest-app/pom.xml @@ -41,6 +41,10 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config + + org.springdoc + springdoc-openapi-starter-webmvc-ui + org.springframework.boot spring-boot-starter-test diff --git a/modules/apps/gb32960-ingest-app/src/main/resources/application.yml b/modules/apps/gb32960-ingest-app/src/main/resources/application.yml index a30fc25c..92f1591e 100644 --- a/modules/apps/gb32960-ingest-app/src/main/resources/application.yml +++ b/modules/apps/gb32960-ingest-app/src/main/resources/application.yml @@ -21,6 +21,12 @@ spring: server: port: ${HTTP_PORT:20100} +springdoc: + swagger-ui: + path: /swagger-ui.html + api-docs: + path: /v3/api-docs + lingniu: ingest: gb32960: diff --git a/modules/apps/jt808-ingest-app/pom.xml b/modules/apps/jt808-ingest-app/pom.xml new file mode 100644 index 00000000..c15e4b92 --- /dev/null +++ b/modules/apps/jt808-ingest-app/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + com.lingniu.ingest + lingniu-vehicle-ingest + 0.1.0-SNAPSHOT + ../../../pom.xml + + + jt808-ingest-app + jt808-ingest-app + JT808 protocol ingress runtime: TCP decode, identity mapping, Kafka production, and downlink session routing. + + + + com.lingniu.ingest + ingest-core + + + com.lingniu.ingest + session-core + + + com.lingniu.ingest + observability + + + com.lingniu.ingest + vehicle-identity + + + com.lingniu.ingest + protocol-jt808 + + + com.lingniu.ingest + sink-mq + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.springframework.boot + spring-boot-starter-test + test + + + + + jt808-ingest-app + + + org.springframework.boot + spring-boot-maven-plugin + + --sun-misc-unsafe-memory-access=allow + + + + + repackage + build-info + + + + + + + diff --git a/modules/apps/jt808-ingest-app/src/main/java/com/lingniu/ingest/jt808app/Jt808IngestApplication.java b/modules/apps/jt808-ingest-app/src/main/java/com/lingniu/ingest/jt808app/Jt808IngestApplication.java new file mode 100644 index 00000000..989826ee --- /dev/null +++ b/modules/apps/jt808-ingest-app/src/main/java/com/lingniu/ingest/jt808app/Jt808IngestApplication.java @@ -0,0 +1,21 @@ +package com.lingniu.ingest.jt808app; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages = "com.lingniu.ingest") +public class Jt808IngestApplication { + + private static final Logger log = LoggerFactory.getLogger(Jt808IngestApplication.class); + + public static void main(String[] args) { + try { + SpringApplication.run(Jt808IngestApplication.class, args); + } catch (Throwable t) { + log.error("JT808 ingest application failed to start, forcing JVM exit", t); + System.exit(1); + } + } +} diff --git a/modules/apps/jt808-ingest-app/src/main/resources/application.yml b/modules/apps/jt808-ingest-app/src/main/resources/application.yml new file mode 100644 index 00000000..63660f81 --- /dev/null +++ b/modules/apps/jt808-ingest-app/src/main/resources/application.yml @@ -0,0 +1,99 @@ +spring: + application: + name: jt808-ingest-app + config: + import: + - optional:nacos:${spring.application.name}.${NACOS_CONFIG_FILE_EXTENSION:yml}?group=${NACOS_GROUP:DEFAULT_GROUP}&refreshEnabled=${NACOS_REFRESH_ENABLED:true} + cloud: + nacos: + config: + enabled: ${NACOS_CONFIG_ENABLED:true} + server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848} + namespace: ${NACOS_NAMESPACE:} + group: ${NACOS_GROUP:DEFAULT_GROUP} + file-extension: ${NACOS_CONFIG_FILE_EXTENSION:yml} + username: ${NACOS_USERNAME:} + password: ${NACOS_PASSWORD:} + threads: + virtual: + enabled: true + +server: + port: ${HTTP_PORT:20400} + +springdoc: + swagger-ui: + path: /swagger-ui.html + api-docs: + path: /v3/api-docs + +lingniu: + ingest: + jt808: + enabled: true + port: ${JT808_PORT:10808} + worker-threads: ${JT808_WORKER_THREADS:0} + pipeline: + disruptor: + ring-buffer-size: ${PIPELINE_RING_BUFFER_SIZE:131072} + wait-strategy: ${PIPELINE_WAIT_STRATEGY:yielding} + producer-type: multi + dedup: + enabled: true + cache-size: 200000 + ttl-seconds: 600 + rate-limit: + per-vin-qps: 50 + session: + store: ${SESSION_STORE:memory} + ttl: ${SESSION_TTL:30m} + identity: + store: ${VEHICLE_IDENTITY_STORE:file} + file: + path: ${VEHICLE_IDENTITY_FILE:./data/vehicle-identity.jsonl} + sink: + mq: + enabled: ${KAFKA_ENABLED:true} + type: kafka + bootstrap-servers: ${KAFKA_BROKERS:114.55.58.251:9092} + compression-type: zstd + linger-ms: 20 + batch-size: 65536 + acks: all + enable-idempotence: true + node-id: ${KAFKA_NODE_ID:jt808-ingest-local} + topics: + realtime: ${KAFKA_TOPIC_JT808_EVENT:vehicle.event.jt808.v1} + location: ${KAFKA_TOPIC_JT808_EVENT:vehicle.event.jt808.v1} + alarm: ${KAFKA_TOPIC_JT808_EVENT:vehicle.event.jt808.v1} + session: ${KAFKA_TOPIC_JT808_EVENT:vehicle.event.jt808.v1} + media-meta: ${KAFKA_TOPIC_MEDIA_META:vehicle.media.meta.v1} + raw-archive: ${KAFKA_TOPIC_JT808_RAW:vehicle.raw.jt808.v1} + dlq: ${KAFKA_TOPIC_JT808_DLQ:vehicle.dlq.jt808.v1} + consumer: + enabled: false + event-file-store: + enabled: false + event-history: + enabled: false + vehicle-state: + enabled: false + vehicle-stat: + enabled: false + +management: + endpoints: + web: + exposure: + include: health,info,metrics,prometheus,env + health: + redis: + enabled: ${MANAGEMENT_HEALTH_REDIS_ENABLED:false} + metrics: + tags: + application: jt808-ingest-app + +logging: + level: + root: INFO + com.lingniu.ingest: INFO diff --git a/modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppCompositionTest.java b/modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppCompositionTest.java new file mode 100644 index 00000000..8e384b50 --- /dev/null +++ b/modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppCompositionTest.java @@ -0,0 +1,69 @@ +package com.lingniu.ingest.jt808app; + +import com.lingniu.ingest.core.config.IngestCoreAutoConfiguration; +import com.lingniu.ingest.identity.config.VehicleIdentityAutoConfiguration; +import com.lingniu.ingest.protocol.jt808.codec.Jt808MessageDecoder; +import com.lingniu.ingest.protocol.jt808.config.Jt808AutoConfiguration; +import com.lingniu.ingest.protocol.jt808.inbound.Jt808NettyServer; +import com.lingniu.ingest.session.config.SessionCoreAutoConfiguration; +import com.lingniu.ingest.sink.mq.KafkaEnvelopeDeadLetterSink; +import com.lingniu.ingest.sink.mq.KafkaEventSink; +import com.lingniu.ingest.sink.mq.SinkMqAutoConfiguration; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.ApplicationContext; +import org.springframework.util.ClassUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class Jt808IngestAppCompositionTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + IngestCoreAutoConfiguration.class, + SessionCoreAutoConfiguration.class, + VehicleIdentityAutoConfiguration.class, + SinkMqAutoConfiguration.class, + Jt808AutoConfiguration.class)) + .withAllowBeanDefinitionOverriding(true) + .withBean("kafkaProducer", KafkaProducer.class, Jt808IngestAppCompositionTest::kafkaProducer) + .withPropertyValues( + "lingniu.ingest.jt808.enabled=true", + "lingniu.ingest.jt808.port=0", + "lingniu.ingest.session.store=memory", + "lingniu.ingest.sink.mq.enabled=true", + "lingniu.ingest.sink.mq.type=kafka", + "lingniu.ingest.sink.mq.bootstrap-servers=localhost:9092", + "lingniu.ingest.sink.mq.consumer.enabled=false", + "lingniu.ingest.event-file-store.enabled=false", + "lingniu.ingest.event-history.enabled=false", + "lingniu.ingest.vehicle-state.enabled=false", + "lingniu.ingest.vehicle-stat.enabled=false"); + + @Test + void createsJt808ListenerWithoutHistoryStorageBoundaries() { + contextRunner.run(context -> { + assertThat(context).hasSingleBean(Jt808MessageDecoder.class); + assertThat(context).hasSingleBean(Jt808NettyServer.class); + assertThat(context).hasSingleBean(KafkaEventSink.class); + assertThat(context).hasSingleBean(KafkaEnvelopeDeadLetterSink.class); + + assertTypeNotPresent(context, "com.lingniu.ingest.sink.archive.ArchiveStore"); + assertTypeNotPresent(context, "com.lingniu.ingest.eventfilestore.EventFileStore"); + }); + } + + @SuppressWarnings("unchecked") + private static KafkaProducer kafkaProducer() { + return mock(KafkaProducer.class); + } + + private static void assertTypeNotPresent(ApplicationContext context, String className) { + assertThat(ClassUtils.isPresent(className, context.getClassLoader())) + .as("%s must stay outside the JT808 ingest runtime", className) + .isFalse(); + } +} diff --git a/modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppDefaultsTest.java b/modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppDefaultsTest.java new file mode 100644 index 00000000..5679a261 --- /dev/null +++ b/modules/apps/jt808-ingest-app/src/test/java/com/lingniu/ingest/jt808app/Jt808IngestAppDefaultsTest.java @@ -0,0 +1,46 @@ +package com.lingniu.ingest.jt808app; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.io.ClassPathResource; + +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +class Jt808IngestAppDefaultsTest { + + @Test + void applicationDefaultsKeepJt808IngestAsTcpKafkaProducerOnly() { + Properties properties = applicationProperties(); + + assertThat(properties) + .containsEntry("spring.application.name", "jt808-ingest-app") + .containsEntry( + "spring.config.import[0]", + "optional:nacos:${spring.application.name}.${NACOS_CONFIG_FILE_EXTENSION:yml}?group=${NACOS_GROUP:DEFAULT_GROUP}&refreshEnabled=${NACOS_REFRESH_ENABLED:true}") + .containsEntry("spring.cloud.nacos.config.server-addr", "${NACOS_SERVER_ADDR:127.0.0.1:8848}") + .containsEntry("server.port", "${HTTP_PORT:20400}") + .containsEntry("lingniu.ingest.jt808.enabled", true) + .containsEntry("lingniu.ingest.jt808.port", "${JT808_PORT:10808}") + .containsEntry("lingniu.ingest.sink.mq.enabled", "${KAFKA_ENABLED:true}") + .containsEntry("lingniu.ingest.sink.mq.consumer.enabled", false) + .containsEntry("lingniu.ingest.sink.mq.topics.realtime", "${KAFKA_TOPIC_JT808_EVENT:vehicle.event.jt808.v1}") + .containsEntry("lingniu.ingest.sink.mq.topics.raw-archive", "${KAFKA_TOPIC_JT808_RAW:vehicle.raw.jt808.v1}") + .containsEntry("lingniu.ingest.sink.mq.topics.dlq", "${KAFKA_TOPIC_JT808_DLQ:vehicle.dlq.jt808.v1}") + .containsEntry("lingniu.ingest.event-file-store.enabled", false) + .containsEntry("lingniu.ingest.event-history.enabled", false) + .containsEntry("lingniu.ingest.vehicle-state.enabled", false) + .containsEntry("lingniu.ingest.vehicle-stat.enabled", false); + } + + private static Properties applicationProperties() { + YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); + factory.setResources(new ClassPathResource("application.yml")); + + Properties properties = factory.getObject(); + + assertThat(properties).isNotNull(); + return properties; + } +} diff --git a/modules/apps/vehicle-analytics-app/pom.xml b/modules/apps/vehicle-analytics-app/pom.xml index d904e775..c5414f46 100644 --- a/modules/apps/vehicle-analytics-app/pom.xml +++ b/modules/apps/vehicle-analytics-app/pom.xml @@ -41,6 +41,10 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config + + org.springdoc + springdoc-openapi-starter-webmvc-ui + org.springframework.boot spring-boot-starter-test diff --git a/modules/apps/vehicle-analytics-app/src/main/resources/application.yml b/modules/apps/vehicle-analytics-app/src/main/resources/application.yml index 9435221f..e1ad81e4 100644 --- a/modules/apps/vehicle-analytics-app/src/main/resources/application.yml +++ b/modules/apps/vehicle-analytics-app/src/main/resources/application.yml @@ -21,6 +21,12 @@ spring: server: port: ${HTTP_PORT:20300} +springdoc: + swagger-ui: + path: /swagger-ui.html + api-docs: + path: /v3/api-docs + lingniu: ingest: gb32960: diff --git a/modules/apps/vehicle-history-app/pom.xml b/modules/apps/vehicle-history-app/pom.xml index c31f0509..2316864c 100644 --- a/modules/apps/vehicle-history-app/pom.xml +++ b/modules/apps/vehicle-history-app/pom.xml @@ -49,6 +49,10 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config + + org.springdoc + springdoc-openapi-starter-webmvc-ui + org.springframework.boot spring-boot-starter-test diff --git a/modules/apps/vehicle-history-app/src/main/resources/application.yml b/modules/apps/vehicle-history-app/src/main/resources/application.yml index 2f8d38e5..41312393 100644 --- a/modules/apps/vehicle-history-app/src/main/resources/application.yml +++ b/modules/apps/vehicle-history-app/src/main/resources/application.yml @@ -21,6 +21,12 @@ spring: server: port: ${HTTP_PORT:20200} +springdoc: + swagger-ui: + path: /swagger-ui.html + api-docs: + path: /v3/api-docs + lingniu: ingest: gb32960: diff --git a/pom.xml b/pom.xml index 6985812f..d6f6d776 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ modules/inbound/inbound-xinda-push modules/apps/command-gateway modules/apps/gb32960-ingest-app + modules/apps/jt808-ingest-app modules/apps/vehicle-history-app modules/apps/vehicle-analytics-app @@ -237,6 +238,11 @@ gb32960-ingest-app ${project.version} + + com.lingniu.ingest + jt808-ingest-app + ${project.version} + com.lingniu.ingest vehicle-history-app diff --git a/woodpecker.yml b/woodpecker.yml index 812ab27c..05d87d68 100644 --- a/woodpecker.yml +++ b/woodpecker.yml @@ -13,7 +13,7 @@ steps: commands: | cd $CI_WORKSPACE - mvn -B -ntp -am -pl :gb32960-ingest-app,:vehicle-history-app,:vehicle-analytics-app package -DskipTests + mvn -B -ntp -am -pl :gb32960-ingest-app,:jt808-ingest-app,:vehicle-history-app,:vehicle-analytics-app package -DskipTests BRANCH_NAME=$(echo ${CI_COMMIT_BRANCH:-local} | tr / -) PROJECT_VERSION=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version) @@ -60,6 +60,42 @@ steps: docker push $IMAGE + - name: docker-build-jt808-ingest-app + image: docker:24.0.5-cli + when: + event: + - push + - pull_request + - manual + branch: + - master + - develop + - main + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + ALIYUN_ACR_USERNAME: + from_secret: aliyun_acr_username + ALIYUN_ACR_PASSWORD: + from_secret: aliyun_acr_password + commands: | + cd $CI_WORKSPACE + APP_NAME=jt808-ingest-app + APP_VERSION=$(cat $CI_WORKSPACE/project_version.txt) + IMAGE=crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/$APP_NAME:$APP_VERSION + + docker build \ + --build-arg APP_NAME=$APP_NAME \ + --build-arg APP_VERSION=$APP_VERSION \ + -t $IMAGE . + + printf '%s' "$ALIYUN_ACR_PASSWORD" | docker login \ + --username "$ALIYUN_ACR_USERNAME" \ + --password-stdin \ + crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com + + docker push $IMAGE + - name: docker-build-vehicle-history-app image: docker:24.0.5-cli when: