From 6b103e99fc92e015eb06a4c2f04859e603e11f46 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:11:08 +0000 Subject: [PATCH 1/3] Add GitHub Actions CI and mocked-AWS CD pipelines --- .github/dependabot.yml | 25 +++++ .github/workflows/cd.yml | 222 +++++++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 118 +++++++++++++++++++++ docs/CICD.md | 100 ++++++++++++++++++ 4 files changed, 465 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/ci.yml create mode 100644 docs/CICD.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..400c6d35 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,25 @@ +version: 2 +updates: + - package-ecosystem: maven + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + labels: [dependencies, java] + + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + labels: [dependencies, javascript] + groups: + angular: + patterns: ['@angular*', 'angular-eslint', 'zone.js'] + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + labels: [dependencies, github-actions] diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 00000000..ae5fe200 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,222 @@ +name: CD + +# DEMO PIPELINE — every AWS identifier below defaults to an obviously fake value +# (account 123456789012, role gha-deploy-ts-java-angular-jhipster, cluster +# demo-staging / demo-prod, ...). While those mock defaults are in use the +# pipeline runs in DRY RUN mode: the image is built to a local tarball instead +# of being pushed to ECR, and no ECS API call is made. Replace the repository +# variables listed in docs/CICD.md with real values to arm the pipeline. + +on: + push: + branches: [main] + workflow_dispatch: + inputs: + environment: + description: Target environment + type: choice + options: [staging, production] + default: staging + required: true + +concurrency: + group: cd-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + +env: + JAVA_VERSION: '21' + NODE_VERSION: '24.13.0' + # Mock AWS coordinates. Override with repository/environment variables. + AWS_REGION: ${{ vars.AWS_REGION || 'us-east-1' }} + AWS_ACCOUNT_ID: ${{ vars.AWS_ACCOUNT_ID || '123456789012' }} + AWS_DEPLOY_ROLE_ARN: ${{ vars.AWS_DEPLOY_ROLE_ARN || 'arn:aws:iam::123456789012:role/gha-deploy-ts-java-angular-jhipster' }} + ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY || 'demo/jhipster-sample-application' }} + +jobs: + # Single source of truth for "are we pointed at real AWS or at the mocks?". + configure: + name: Resolve deployment mode + runs-on: ubuntu-latest + outputs: + dry_run: ${{ steps.mode.outputs.dry_run }} + image_tag: ${{ steps.mode.outputs.image_tag }} + registry: ${{ steps.mode.outputs.registry }} + steps: + - id: mode + # Dry run whenever the mock account ID is still configured. + run: | + if [ "${AWS_ACCOUNT_ID}" = "123456789012" ]; then + echo "dry_run=true" >> "$GITHUB_OUTPUT" + echo "::notice::Mock AWS account detected - running in DRY RUN mode (no ECR push, no ECS deploy)." + else + echo "dry_run=false" >> "$GITHUB_OUTPUT" + fi + echo "image_tag=${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" + echo "registry=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com" >> "$GITHUB_OUTPUT" + + build-image: + name: Build container image (Jib) + needs: configure + runs-on: ubuntu-latest + timeout-minutes: 45 + permissions: + contents: read + id-token: write # GitHub OIDC -> AWS role assumption (no long-lived keys) + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + cache: maven + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + + # ---- Real deployments only ------------------------------------------- + - name: Configure AWS credentials (OIDC) + if: needs.configure.outputs.dry_run == 'false' + uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 + with: + role-to-assume: ${{ env.AWS_DEPLOY_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + role-session-name: gha-cd-${{ github.run_id }} + - name: Log in to Amazon ECR + if: needs.configure.outputs.dry_run == 'false' + uses: aws-actions/amazon-ecr-login@03f1aad4c6c7ffd436567f42f9384779290529bd # v2.1.7 + - name: Build and push image to ECR + if: needs.configure.outputs.dry_run == 'false' + run: | + ./mvnw -ntp --batch-mode -Pprod -DskipTests verify jib:build \ + -Djib.to.image=${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }} \ + -Djib.to.tags=latest + + # ---- Mock/dry-run path ------------------------------------------------ + # jib:buildTar writes target/jib-image.tar locally; nothing leaves the runner. + - name: Build image to local tarball (dry run) + if: needs.configure.outputs.dry_run == 'true' + run: | + ./mvnw -ntp --batch-mode -Pprod -DskipTests verify jib:buildTar \ + -Djib.to.image=${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }} + echo "::notice::DRY RUN - image built locally, not pushed to ${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}" + - name: Upload image tarball (dry run) + if: needs.configure.outputs.dry_run == 'true' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: jib-image-tar + path: target/jib-image.tar + if-no-files-found: error + retention-days: 3 + + deploy-staging: + name: Deploy to staging + needs: [configure, build-image] + if: github.event_name == 'push' || inputs.environment == 'staging' + runs-on: ubuntu-latest + timeout-minutes: 30 + # GitHub Environment "staging": holds the (mock) variables and any reviewers. + environment: + name: staging + permissions: + contents: read + id-token: write + env: + ECS_CLUSTER: ${{ vars.ECS_CLUSTER_STAGING || 'demo-staging' }} + ECS_SERVICE: ${{ vars.ECS_SERVICE_STAGING || 'demo-staging-jhipster-sample-application' }} + ECS_TASK_FAMILY: ${{ vars.ECS_TASK_FAMILY_STAGING || 'demo-staging-jhipster-sample-application' }} + ECS_CONTAINER_NAME: ${{ vars.ECS_CONTAINER_NAME || 'jhipster-sample-application' }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Configure AWS credentials (OIDC) + if: needs.configure.outputs.dry_run == 'false' + uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 + with: + role-to-assume: ${{ env.AWS_DEPLOY_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + role-session-name: gha-cd-staging-${{ github.run_id }} + - name: Render ECS task definition + if: needs.configure.outputs.dry_run == 'false' + id: taskdef + uses: aws-actions/amazon-ecs-render-task-definition@138c24f321fdbdf7edee4a685519d253cae2cdea # v1.9.0 + with: + task-definition-family: ${{ env.ECS_TASK_FAMILY }} + container-name: ${{ env.ECS_CONTAINER_NAME }} + image: ${{ needs.configure.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ needs.configure.outputs.image_tag }} + - name: Deploy to Amazon ECS + if: needs.configure.outputs.dry_run == 'false' + uses: aws-actions/amazon-ecs-deploy-task-definition@c465972ecbd160473f22e683363b422a5412a3de # v2.6.3 + with: + task-definition: ${{ steps.taskdef.outputs.task-definition }} + cluster: ${{ env.ECS_CLUSTER }} + service: ${{ env.ECS_SERVICE }} + wait-for-service-stability: true + - name: Dry-run deployment plan + if: needs.configure.outputs.dry_run == 'true' + run: | + echo "DRY RUN - would deploy to ECS:" + echo " region: ${AWS_REGION}" + echo " cluster: ${ECS_CLUSTER}" + echo " service: ${ECS_SERVICE}" + echo " image: ${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }}" + + deploy-production: + name: Deploy to production + needs: [configure, build-image, deploy-staging] + # Always runs after staging on push; on manual dispatch it also runs when + # production is selected (staging is skipped, hence always() + result check). + if: | + always() && + needs.build-image.result == 'success' && + (needs.deploy-staging.result == 'success' || needs.deploy-staging.result == 'skipped') && + (github.event_name == 'push' || inputs.environment == 'production') + runs-on: ubuntu-latest + timeout-minutes: 30 + # GitHub Environment "production" must have "Required reviewers" enabled - + # that is the manual approval gate between staging and production. + environment: + name: production + permissions: + contents: read + id-token: write + env: + ECS_CLUSTER: ${{ vars.ECS_CLUSTER_PRODUCTION || 'demo-prod' }} + ECS_SERVICE: ${{ vars.ECS_SERVICE_PRODUCTION || 'demo-prod-jhipster-sample-application' }} + ECS_TASK_FAMILY: ${{ vars.ECS_TASK_FAMILY_PRODUCTION || 'demo-prod-jhipster-sample-application' }} + ECS_CONTAINER_NAME: ${{ vars.ECS_CONTAINER_NAME || 'jhipster-sample-application' }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Configure AWS credentials (OIDC) + if: needs.configure.outputs.dry_run == 'false' + uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 + with: + role-to-assume: ${{ env.AWS_DEPLOY_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + role-session-name: gha-cd-production-${{ github.run_id }} + - name: Render ECS task definition + if: needs.configure.outputs.dry_run == 'false' + id: taskdef + uses: aws-actions/amazon-ecs-render-task-definition@138c24f321fdbdf7edee4a685519d253cae2cdea # v1.9.0 + with: + task-definition-family: ${{ env.ECS_TASK_FAMILY }} + container-name: ${{ env.ECS_CONTAINER_NAME }} + image: ${{ needs.configure.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ needs.configure.outputs.image_tag }} + - name: Deploy to Amazon ECS + if: needs.configure.outputs.dry_run == 'false' + uses: aws-actions/amazon-ecs-deploy-task-definition@c465972ecbd160473f22e683363b422a5412a3de # v2.6.3 + with: + task-definition: ${{ steps.taskdef.outputs.task-definition }} + cluster: ${{ env.ECS_CLUSTER }} + service: ${{ env.ECS_SERVICE }} + wait-for-service-stability: true + - name: Dry-run deployment plan + if: needs.configure.outputs.dry_run == 'true' + run: | + echo "DRY RUN - would deploy to ECS:" + echo " region: ${AWS_REGION}" + echo " cluster: ${ECS_CLUSTER}" + echo " service: ${ECS_SERVICE}" + echo " image: ${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..ad01125c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,118 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +# Cancel superseded runs on the same ref. +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + JAVA_VERSION: '21' + # Matches in pom.xml (frontend-maven-plugin). + NODE_VERSION: '24.13.0' + MAVEN_OPTS: -Djava.awt.headless=true + +jobs: + backend: + name: Backend build & tests + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Set up JDK ${{ env.JAVA_VERSION }} + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + cache: maven + # -Dskip.installnodenpm -Dskip.npm: the frontend is built and tested in the + # `frontend` job, so the Maven build does not need Node. + # -Pprod exercises the production profile; integration tests start a + # PostgreSQL container through Testcontainers (Docker is available on + # ubuntu-latest runners). + - name: Build and run unit + integration tests + run: ./mvnw -ntp --batch-mode -Dskip.installnodenpm -Dskip.npm -Pprod verify + - name: Upload backend test reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: backend-test-reports + path: | + target/surefire-reports/** + target/failsafe-reports/** + target/site/jacoco/** + if-no-files-found: warn + retention-days: 7 + + frontend: + name: Frontend lint, tests & prod build + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Set up Node ${{ env.NODE_VERSION }} + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + - name: Install dependencies + run: npm ci + - name: Lint + run: npm run lint + # `npm test` is `ng test --coverage` (Vitest); its `pretest` hook re-runs + # lint, which is cheap and keeps parity with local runs. + - name: Unit tests + run: npm test + - name: Production build + run: npm run webapp:build:prod + - name: Upload frontend test reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: frontend-test-reports + path: | + target/test-results/** + coverage/** + if-no-files-found: warn + retention-days: 7 + + code-quality: + name: Code quality & validation + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Set up JDK ${{ env.JAVA_VERSION }} + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + cache: maven + - name: Set up Node ${{ env.NODE_VERSION }} + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + - name: Install dependencies + run: npm ci + - name: Prettier check + run: npm run prettier:check + - name: Maven version info + run: ./mvnw --version + # nohttp checkstyle rules configured in checkstyle.xml. + - name: Checkstyle (nohttp) + run: ./mvnw -ntp --batch-mode -Dskip.installnodenpm -Dskip.npm checkstyle:check + - name: Javadoc + run: ./mvnw -ntp --batch-mode -Dskip.installnodenpm -Dskip.npm javadoc:javadoc + - name: Validate workflow files (actionlint) + uses: docker://rhysd/actionlint@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 # 1.7.12 + with: + args: -color diff --git a/docs/CICD.md b/docs/CICD.md new file mode 100644 index 00000000..9b89edf6 --- /dev/null +++ b/docs/CICD.md @@ -0,0 +1,100 @@ +# CI/CD + +Two GitHub Actions workflows live in `.github/workflows/`. + +> **Demo notice:** the CD pipeline ships with mock AWS identifiers and never +> touches a real AWS account. See [Mock vs. real](#mock-vs-real). + +## CI — `.github/workflows/ci.yml` + +Runs on every push to `main` and on every pull request. Superseded runs on the +same ref are cancelled (`concurrency`), and the workflow has read-only +`permissions`. + +| Job | What it does | +| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `backend` | `./mvnw -Pprod verify` with the frontend skipped (`-Dskip.installnodenpm -Dskip.npm`); unit + integration tests (Testcontainers PostgreSQL). Maven cache via `setup-java`. | +| `frontend` | `npm ci`, `npm run lint`, `npm test` (Vitest, with coverage), `npm run webapp:build:prod`. npm cache via `setup-node`. | +| `code-quality` | `npm run prettier:check`, `./mvnw checkstyle:check` (nohttp rules from `checkstyle.xml`), `./mvnw javadoc:javadoc`, and `actionlint` over the workflow files. | + +Surefire/Failsafe/JaCoCo reports and the frontend coverage report are uploaded +as artifacts (7-day retention). No step uses `continue-on-error` — a real +failure fails the run. + +Java 21 (Temurin) and Node 24.13.0 mirror `` / `` +in `pom.xml`. + +## CD — `.github/workflows/cd.yml` + +Triggered by pushes to `main` and by `workflow_dispatch` with an +`environment` input (`staging` | `production`). + +``` +configure ──> build-image (Jib) ──> deploy-staging ──> deploy-production + (dry-run?) (ECR) (env: staging) (env: production, + required reviewers) +``` + +- **configure** resolves the deployment mode and the image tag + (`${GITHUB_SHA::12}`). +- **build-image** builds the production image with the Jib Maven plugin already + configured in `pom.xml`. +- **deploy-staging / deploy-production** render an ECS task definition and + update the ECS service. The `production` job uses the `production` GitHub + Environment; enable **Required reviewers** on that environment to get the + manual approval gate between staging and production. +- AWS authentication is **GitHub OIDC only** + (`aws-actions/configure-aws-credentials` with `role-to-assume`). No long-lived + access keys are stored or used; jobs request `id-token: write` and nothing else + beyond `contents: read`. +- All actions are pinned to commit SHAs (with the version in a trailing comment). + +## Mock vs. real + +`configure` sets `dry_run=true` whenever `AWS_ACCOUNT_ID` is still the mock +`123456789012`. In dry-run mode: + +- the image is built with `jib:buildTar` into `target/jib-image.tar` and uploaded + as an artifact — **nothing is pushed to ECR**; +- the deploy jobs only print the deployment plan — **no ECS/AWS API call is made**. + +Setting a real `AWS_ACCOUNT_ID` repository variable flips every guarded step on. + +### Variables (repository or environment scoped) + +| Variable | Mock default | +| ---------------------------- | -------------------------------------------------------------------- | +| `AWS_REGION` | `us-east-1` | +| `AWS_ACCOUNT_ID` | `123456789012` | +| `AWS_DEPLOY_ROLE_ARN` | `arn:aws:iam::123456789012:role/gha-deploy-ts-java-angular-jhipster` | +| `ECR_REPOSITORY` | `demo/jhipster-sample-application` | +| `ECS_CLUSTER_STAGING` | `demo-staging` | +| `ECS_SERVICE_STAGING` | `demo-staging-jhipster-sample-application` | +| `ECS_TASK_FAMILY_STAGING` | `demo-staging-jhipster-sample-application` | +| `ECS_CLUSTER_PRODUCTION` | `demo-prod` | +| `ECS_SERVICE_PRODUCTION` | `demo-prod-jhipster-sample-application` | +| `ECS_TASK_FAMILY_PRODUCTION` | `demo-prod-jhipster-sample-application` | +| `ECS_CONTAINER_NAME` | `jhipster-sample-application` | + +### Secrets + +None. OIDC role assumption replaces static credentials; the only AWS trust +configuration lives in the IAM role's trust policy +(`token.actions.githubusercontent.com`, `sub` restricted to this repository and +the `staging` / `production` environments). + +### To go live + +1. Create the ECR repository, ECS cluster/service and task definition family. +2. Create an IAM role trusting the GitHub OIDC provider, scoped to this repo, + with permissions for ECR push and `ecs:UpdateService` / + `ecs:RegisterTaskDefinition` / `iam:PassRole` on the task roles only. +3. Create the `staging` and `production` GitHub Environments; add required + reviewers to `production`. +4. Replace the variables in the table above with the real values (at minimum + `AWS_ACCOUNT_ID`, which is what disables dry-run mode). + +## Dependabot + +`.github/dependabot.yml` watches Maven, npm (Angular packages grouped) and +GitHub Actions weekly. From 8a2ffa14cef9a5b39b22c458899f537d3aab2434 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:14:42 +0000 Subject: [PATCH 2/3] Include integration-test jacoco report in CI artifacts --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad01125c..e695963e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: target/surefire-reports/** target/failsafe-reports/** target/site/jacoco/** + target/site/jacoco-it/** if-no-files-found: warn retention-days: 7 @@ -80,7 +81,6 @@ jobs: name: frontend-test-reports path: | target/test-results/** - coverage/** if-no-files-found: warn retention-days: 7 From 78900282a88af3b0ad8e6bf55ad225a228f55b9e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:17:51 +0000 Subject: [PATCH 3/3] Quote jib image argument to satisfy shellcheck --- .github/workflows/cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ae5fe200..c3d1e3c7 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -92,7 +92,7 @@ jobs: if: needs.configure.outputs.dry_run == 'false' run: | ./mvnw -ntp --batch-mode -Pprod -DskipTests verify jib:build \ - -Djib.to.image=${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }} \ + "-Djib.to.image=${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }}" \ -Djib.to.tags=latest # ---- Mock/dry-run path ------------------------------------------------ @@ -101,7 +101,7 @@ jobs: if: needs.configure.outputs.dry_run == 'true' run: | ./mvnw -ntp --batch-mode -Pprod -DskipTests verify jib:buildTar \ - -Djib.to.image=${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }} + "-Djib.to.image=${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}:${{ needs.configure.outputs.image_tag }}" echo "::notice::DRY RUN - image built locally, not pushed to ${{ needs.configure.outputs.registry }}/${ECR_REPOSITORY}" - name: Upload image tarball (dry run) if: needs.configure.outputs.dry_run == 'true'