diff --git a/.github/workflows/build-qa-docker.yml b/.github/workflows/build-qa-docker.yml
index 6ff320c5..8beb9d87 100644
--- a/.github/workflows/build-qa-docker.yml
+++ b/.github/workflows/build-qa-docker.yml
@@ -5,6 +5,7 @@ permissions:
id-token: write
actions: read
packages: write
+ security-events: write
on:
workflow_dispatch:
@@ -31,17 +32,22 @@ on:
type: string
jobs:
+ # ============================================
+ # DETECT RC BUILD AND COMPUTE BUILD FLAGS
+ # ============================================
set-matrix:
- name: "Generate Build Matrix"
+ name: "Detect RC Build"
runs-on: ubuntu-latest
outputs:
- matrix: ${{ steps.set-matrix.outputs.matrix }}
+ is_rc_build: ${{ steps.set-matrix.outputs.is_rc_build }}
+ secure_image_name: ${{ steps.set-matrix.outputs.secure_image_name }}
+ build_community: ${{ steps.set-matrix.outputs.build_community }}
+ build_alpine: ${{ steps.set-matrix.outputs.build_alpine }}
+ build_secure: ${{ steps.set-matrix.outputs.build_secure }}
steps:
- - name: Set matrix dynamically
+ - name: Detect RC build and log selected targets
id: set-matrix
run: |
- matrix_items=()
-
# Determine if this is an RC build (secure-version contains "-RC")
IS_RC_BUILD="false"
if [[ -n "${{ inputs.secure-version }}" && "${{ inputs.secure-version }}" =~ -RC[0-9]+$ ]]; then
@@ -49,603 +55,238 @@ jobs:
echo "Detected RC build with version: ${{ inputs.secure-version }}"
fi
- case "${{ inputs.buildTargets }}" in
+ echo "Selected build targets: ${{ inputs.buildTargets }}"
+ echo "Is RC build: $IS_RC_BUILD"
+ echo "is_rc_build=${IS_RC_BUILD}" >> "$GITHUB_OUTPUT"
+
+ # Compute secure image name to avoid ternary expressions in reusable with: blocks
+ if [[ "$IS_RC_BUILD" == "true" ]]; then
+ echo "secure_image_name=liquibase-secure-rc" >> "$GITHUB_OUTPUT"
+ else
+ echo "secure_image_name=liquibase-secure-qa" >> "$GITHUB_OUTPUT"
+ fi
+
+ # Compute per-target build flags for use in if: conditions
+ BUILD_TARGETS="${{ inputs.buildTargets }}"
+ case "$BUILD_TARGETS" in
"All (Community + Alpine + Secure)")
- matrix_items+=('{"dockerfile": "Dockerfile", "image_name": "liquibase-qa", "suffix": "", "build_type": "Community", "is_rc": false, "ghcr_name": "community"}')
- matrix_items+=('{"dockerfile": "Dockerfile.alpine", "image_name": "liquibase-qa-alpine", "suffix": "-alpine", "build_type": "Community", "is_rc": false, "ghcr_name": "alpine"}')
- if [[ "$IS_RC_BUILD" == "true" ]]; then
- matrix_items+=('{"dockerfile": "DockerfileSecure", "image_name": "liquibase-secure-rc", "suffix": "-secure", "build_type": "secure", "is_rc": true, "ghcr_name": "secure"}')
- else
- matrix_items+=('{"dockerfile": "DockerfileSecure", "image_name": "liquibase-secure-qa", "suffix": "-secure", "build_type": "secure", "is_rc": false, "ghcr_name": "secure"}')
- fi
+ echo "build_community=true" >> "$GITHUB_OUTPUT"
+ echo "build_alpine=true" >> "$GITHUB_OUTPUT"
+ echo "build_secure=true" >> "$GITHUB_OUTPUT"
;;
"Community Only (Standard + Alpine)")
- matrix_items+=('{"dockerfile": "Dockerfile", "image_name": "liquibase-qa", "suffix": "", "build_type": "Community", "is_rc": false, "ghcr_name": "community"}')
- matrix_items+=('{"dockerfile": "Dockerfile.alpine", "image_name": "liquibase-qa-alpine", "suffix": "-alpine", "build_type": "Community", "is_rc": false, "ghcr_name": "alpine"}')
+ echo "build_community=true" >> "$GITHUB_OUTPUT"
+ echo "build_alpine=true" >> "$GITHUB_OUTPUT"
+ echo "build_secure=false" >> "$GITHUB_OUTPUT"
;;
"Secure Only")
- if [[ "$IS_RC_BUILD" == "true" ]]; then
- matrix_items+=('{"dockerfile": "DockerfileSecure", "image_name": "liquibase-secure-rc", "suffix": "-secure", "build_type": "secure", "is_rc": true, "ghcr_name": "secure"}')
- else
- matrix_items+=('{"dockerfile": "DockerfileSecure", "image_name": "liquibase-secure-qa", "suffix": "-secure", "build_type": "secure", "is_rc": false, "ghcr_name": "secure"}')
- fi
+ echo "build_community=false" >> "$GITHUB_OUTPUT"
+ echo "build_alpine=false" >> "$GITHUB_OUTPUT"
+ echo "build_secure=true" >> "$GITHUB_OUTPUT"
;;
"Standard Community Only")
- matrix_items+=('{"dockerfile": "Dockerfile", "image_name": "liquibase-qa", "suffix": "", "build_type": "Community", "is_rc": false, "ghcr_name": "community"}')
+ echo "build_community=true" >> "$GITHUB_OUTPUT"
+ echo "build_alpine=false" >> "$GITHUB_OUTPUT"
+ echo "build_secure=false" >> "$GITHUB_OUTPUT"
;;
"Alpine Community Only")
- matrix_items+=('{"dockerfile": "Dockerfile.alpine", "image_name": "liquibase-qa-alpine", "suffix": "-alpine", "build_type": "Community", "is_rc": false, "ghcr_name": "alpine"}')
+ echo "build_community=false" >> "$GITHUB_OUTPUT"
+ echo "build_alpine=true" >> "$GITHUB_OUTPUT"
+ echo "build_secure=false" >> "$GITHUB_OUTPUT"
;;
esac
- # Join array elements with commas
- IFS=','
- matrix_json="{\"include\":[${matrix_items[*]}]}"
-
- echo "Selected build targets: ${{ inputs.buildTargets }}"
- echo "Is RC build: $IS_RC_BUILD"
- echo "Generated matrix: $matrix_json"
- echo "matrix=$matrix_json" >> $GITHUB_OUTPUT
-
- build-qa-docker:
- name: Build ${{ matrix.image_name }}
+ # ============================================
+ # BUILD: community (Dockerfile)
+ # ============================================
+ build-qa-community:
+ name: Build Community QA
+ needs: [set-matrix]
+ if: needs.set-matrix.outputs.build_community == 'true'
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-build-qa.yml@main
+ with:
+ image_name: liquibase-qa
+ dockerfile_path: Dockerfile
+ suffix: ""
+ ghcr_name: community
+ branch_name: ${{ inputs.liquibaseBranch }}
+ secure_version: ${{ inputs.secure-version }}
+ build_type: Community
+ is_rc: "false"
+ push_nexus: true
+ push_ghcr: true
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # BUILD: alpine (Dockerfile.alpine)
+ # ============================================
+ build-qa-alpine:
+ name: Build Alpine QA
+ needs: [set-matrix]
+ if: needs.set-matrix.outputs.build_alpine == 'true'
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-build-qa.yml@main
+ with:
+ image_name: liquibase-qa-alpine
+ dockerfile_path: Dockerfile.alpine
+ suffix: "-alpine"
+ ghcr_name: alpine
+ branch_name: ${{ inputs.liquibaseBranch }}
+ secure_version: ${{ inputs.secure-version }}
+ build_type: Community
+ is_rc: "false"
+ push_nexus: true
+ push_ghcr: true
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # BUILD: secure (DockerfileSecure)
+ # ============================================
+ build-qa-secure:
+ name: Build Secure QA
+ needs: [set-matrix]
+ if: needs.set-matrix.outputs.build_secure == 'true'
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-build-qa.yml@main
+ with:
+ image_name: ${{ needs.set-matrix.outputs.secure_image_name }}
+ dockerfile_path: DockerfileSecure
+ suffix: "-secure"
+ ghcr_name: secure
+ branch_name: ${{ inputs.liquibaseBranch }}
+ secure_version: ${{ inputs.secure-version }}
+ build_type: secure
+ is_rc: ${{ needs.set-matrix.outputs.is_rc_build }}
+ push_nexus: true
+ push_ghcr: true
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # SCAN: community
+ # ============================================
+ vuln-scan-community:
+ name: Scan Community QA
+ needs: [build-qa-community]
+ if: ${{ !cancelled() && needs.build-qa-community.result == 'success' }}
+ uses: liquibase/build-logic/.github/workflows/reusable-vulnerability-scan.yml@main
+ with:
+ mode: docker
+ source: ghcr.io/${{ github.repository }}/liquibase-community:${{ github.sha }}
+ image_name: liquibase-qa
+ image_tag: qa
+ fail_on_vulnerabilities: true
+ upload_sarif: false
+ generate_sbom: true
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # SCAN: alpine
+ # ============================================
+ vuln-scan-alpine:
+ name: Scan Alpine QA
+ needs: [build-qa-alpine]
+ if: ${{ !cancelled() && needs.build-qa-alpine.result == 'success' }}
+ uses: liquibase/build-logic/.github/workflows/reusable-vulnerability-scan.yml@main
+ with:
+ mode: docker
+ source: ghcr.io/${{ github.repository }}/liquibase-alpine:${{ github.sha }}
+ image_name: liquibase-qa-alpine
+ image_tag: qa
+ fail_on_vulnerabilities: true
+ upload_sarif: false
+ generate_sbom: true
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # SCAN: secure
+ # ============================================
+ vuln-scan-secure:
+ name: Scan Secure QA
+ needs: [build-qa-secure]
+ if: ${{ !cancelled() && needs.build-qa-secure.result == 'success' }}
+ uses: liquibase/build-logic/.github/workflows/reusable-vulnerability-scan.yml@main
+ with:
+ mode: docker
+ source: ghcr.io/${{ github.repository }}/liquibase-secure:${{ github.sha }}
+ image_name: liquibase-secure-qa
+ image_tag: qa
+ fail_on_vulnerabilities: true
+ upload_sarif: false
+ generate_sbom: true
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # CLEANUP: community
+ # ============================================
+ cleanup-ghcr-community:
+ name: Cleanup Community GHCR
+ needs: [vuln-scan-community]
+ if: always()
runs-on: ubuntu-latest
- needs: set-matrix
- strategy:
- fail-fast: false
- matrix: ${{ fromJSON(needs.set-matrix.outputs.matrix) }}
+ permissions:
+ packages: write
steps:
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
-
- - name: Get secrets from vault
- id: vault-secrets-liquibase
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
-
- - name: Configure AWS credentials for Liquibase-Secure builds
- if: ${{ matrix.build_type == 'secure' }}
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ env.AWS_PROD_GITHUB_OIDC_ROLE_ARN_LIQUIBASE_PRO }}
- aws-region: us-east-1
-
- - name: Get GitHub App token for Liquibase-Secure workflow trigger
- if: ${{ matrix.build_type == 'secure' }}
- id: get-token
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
- with:
- app-id: ${{ env.LIQUIBASE_GITHUB_APP_ID }}
- private-key: ${{ env.LIQUIBASE_GITHUB_APP_PRIVATE_KEY }}
- owner: liquibase
- repositories: liquibase-pro
- permission-contents: write
- permission-actions: write
-
- - name: Checkout code
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Install jq
- run: |
- sudo apt-get update
- sudo apt-get install -y jq
-
- - name: Get latest workflow run for branch
- if: ${{ matrix.build_type == 'Community' }}
- id: get-workflow-run
- run: |
- # Get the latest successful workflow run for the specified branch
- WORKFLOW_RUN=$(curl -s \
- -H "Authorization: Bearer ${{ env.LIQUIBOT_PAT_GPM_ACCESS }}" \
- -H "Accept: application/vnd.github.v3+json" \
- "https://api.github.com/repos/liquibase/liquibase/actions/workflows/run-tests.yml/runs?branch=${{ inputs.liquibaseBranch }}&status=completed&conclusion=success&per_page=1" \
- | jq -r '.workflow_runs[0].id')
-
- if [ "$WORKFLOW_RUN" = "null" ] || [ -z "$WORKFLOW_RUN" ]; then
- echo "No successful workflow run found for branch ${{ inputs.liquibaseBranch }}"
- echo "Will build using standard Dockerfile from branch source instead"
- echo "workflow_run_id=" >> $GITHUB_OUTPUT
- echo "has_workflow_run=false" >> $GITHUB_OUTPUT
- else
- echo "Found workflow run ID: $WORKFLOW_RUN"
- echo "workflow_run_id=$WORKFLOW_RUN" >> $GITHUB_OUTPUT
- echo "has_workflow_run=true" >> $GITHUB_OUTPUT
- fi
-
- - name: Download liquibase build artifact (Community) or from S3 (Liquibase-Secure)
- id: download-artifact
- run: |
- if [[ "${{ matrix.build_type }}" == "secure" ]]; then
- echo "Downloading Liquibase Secure build from S3..."
-
- # Determine S3 path based on whether secure-version is provided
- if [[ -n "${{ inputs.secure-version }}" ]]; then
- echo "Using secure-version: ${{ inputs.secure-version }}"
- # Extract base version and RC number for RC versions
- VERSION="${{ inputs.secure-version }}"
- if [[ "$VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-RC([0-9]+)$ ]]; then
- BASE_VERSION="${BASH_REMATCH[1]}"
- RC_NUMBER="${BASH_REMATCH[2]}"
- LIQUIBASE_SECURE_ZIP="liquibase-secure-${VERSION}.tar.gz"
- S3_URL="s3://repo.liquibase.com/non-releases/secure/${VERSION}/${LIQUIBASE_SECURE_ZIP}"
- echo "Using RC S3 path: $S3_URL"
- else
- # Regular production version
- LIQUIBASE_SECURE_ZIP="liquibase-secure-${VERSION}.zip"
- S3_URL="s3://repo.liquibase.com/releases/secure/${VERSION}/${LIQUIBASE_SECURE_ZIP}"
- echo "Using production S3 path: $S3_URL"
- fi
- else
- echo "Using branch-based build: ${{ inputs.liquibaseBranch }}"
- # Original branch-based logic
- LIQUIBASE_SECURE_ZIP="liquibase-secure-${{ inputs.liquibaseBranch }}.zip"
- S3_URL="s3://repo.liquibase.com/releases/secure/${{ inputs.liquibaseBranch }}/$LIQUIBASE_SECURE_ZIP"
- fi
-
- echo "Downloading from S3: $S3_URL"
-
- # Try to download from S3 first
- if ! aws s3 cp "$S3_URL" "$LIQUIBASE_SECURE_ZIP" 2>/dev/null; then
- echo "Failed to download liquibase-secure build from S3. File may not exist yet."
-
- # Check if it's a 403/404 error (file doesn't exist)
- if aws s3 ls "$S3_URL" >/dev/null 2>&1; then
- echo "â File exists but access denied. Check permissions."
- exit 1
- else
- echo "đĻ Liquibase-Secure build not found in S3."
- # If secure-version is provided, don't trigger workflow - the build should exist
- if [[ -n "${{ inputs.secure-version }}" ]]; then
- echo "â ERROR: secure-version specified but build not found in S3. Build should already exist for version ${{ inputs.secure-version }}"
- echo "trigger_liquibase_secure_workflow=false" >> $GITHUB_OUTPUT
- echo "build_ready=false" >> $GITHUB_OUTPUT
- echo "use_fallback=true" >> $GITHUB_OUTPUT
- else
- echo "đĻ Branch-based build not found. Will trigger workflow to create it..."
- echo "trigger_liquibase_secure_workflow=true" >> $GITHUB_OUTPUT
- echo "build_ready=false" >> $GITHUB_OUTPUT
- echo "use_fallback=false" >> $GITHUB_OUTPUT
- fi
- fi
- else
- echo "â
Successfully downloaded Liquibase-Secure build from S3"
- echo "trigger_liquibase_secure_workflow=false" >> $GITHUB_OUTPUT
- echo "build_ready=true" >> $GITHUB_OUTPUT
- echo "use_fallback=false" >> $GITHUB_OUTPUT
-
- # Extract the liquibase Secure archive
- mkdir -p liquibase-build
- if [[ "$LIQUIBASE_SECURE_ZIP" == *.tar.gz ]]; then
- tar -xzf "$LIQUIBASE_SECURE_ZIP" -C liquibase-build/
- echo "Liquibase Secure build extracted from tar.gz to liquibase-build/"
- else
- unzip -q "$LIQUIBASE_SECURE_ZIP" -d liquibase-build/
- echo "Liquibase Secure build extracted from zip to liquibase-build/"
- fi
- fi
-
- else
- # Community build handling
- if [[ "${{ steps.get-workflow-run.outputs.has_workflow_run }}" == "false" ]]; then
- echo "No workflow run available, will use fallback build from branch source"
- echo "build_ready=false" >> $GITHUB_OUTPUT
- echo "use_fallback=true" >> $GITHUB_OUTPUT
- else
- echo "Downloading Community build from GitHub artifacts..."
-
- # Get artifact download URL
- ARTIFACT_NAME="liquibase-zip-${{ inputs.liquibaseBranch }}"
- echo "Looking for artifact: $ARTIFACT_NAME"
-
- ARTIFACTS_RESPONSE=$(curl -s \
- -H "Authorization: Bearer ${{ env.LIQUIBOT_PAT_GPM_ACCESS }}" \
- -H "Accept: application/vnd.github.v3+json" \
- "https://api.github.com/repos/liquibase/liquibase/actions/runs/${{ steps.get-workflow-run.outputs.workflow_run_id }}/artifacts")
-
- echo "Available artifacts:"
- echo "$ARTIFACTS_RESPONSE" | jq -r '.artifacts[].name'
-
- ARTIFACT_URL=$(echo "$ARTIFACTS_RESPONSE" | jq -r ".artifacts[] | select(.name == \"$ARTIFACT_NAME\") | .archive_download_url")
-
- if [ "$ARTIFACT_URL" = "null" ] || [ -z "$ARTIFACT_URL" ]; then
- echo "Artifact $ARTIFACT_NAME not found in workflow run"
- echo "Available artifacts:"
- echo "$ARTIFACTS_RESPONSE" | jq -r '.artifacts[].name'
- echo "Will use fallback build from branch source instead"
- echo "build_ready=false" >> $GITHUB_OUTPUT
- echo "use_fallback=true" >> $GITHUB_OUTPUT
- else
- echo "Downloading artifact from: $ARTIFACT_URL"
-
- # Download and extract artifact
- curl -L \
- -H "Authorization: Bearer ${{ env.LIQUIBOT_PAT_GPM_ACCESS }}" \
- -H "Accept: application/vnd.github.v3+json" \
- -o liquibase-artifact.zip \
- "$ARTIFACT_URL"
-
- # Extract the artifact
- unzip -q liquibase-artifact.zip
- echo "Artifact contents:"
- ls -la
-
- # Find the liquibase zip file inside the artifact
- LIQUIBASE_ZIP=$(find . -name "liquibase-*.zip" -type f | head -1)
- if [ -z "$LIQUIBASE_ZIP" ]; then
- echo "No liquibase zip file found in artifact"
- echo "Contents of extracted artifact:"
- find . -type f
- echo "Will use fallback build from branch source instead"
- echo "build_ready=false" >> $GITHUB_OUTPUT
- echo "use_fallback=true" >> $GITHUB_OUTPUT
- else
- echo "Found liquibase zip: $LIQUIBASE_ZIP"
-
- # Extract the liquibase zip
- mkdir -p liquibase-build
- unzip -q "$LIQUIBASE_ZIP" -d liquibase-build/
-
- echo "Liquibase build extracted to liquibase-build/"
- echo "build_ready=true" >> $GITHUB_OUTPUT
- echo "use_fallback=false" >> $GITHUB_OUTPUT
- fi
- fi
- fi
- fi
-
- # Only show contents if build is ready
- if [[ -d "liquibase-build" ]]; then
- echo "Contents of liquibase-build:"
- ls -la liquibase-build/
- fi
-
- - name: Trigger Liquibase Secure Release Workflow
- if: ${{ matrix.build_type == 'secure' && steps.download-artifact.outputs.trigger_liquibase_secure_workflow == 'true' && steps.download-artifact.outputs.use_fallback != 'true' }}
- id: trigger-liquibase-secure-release
- continue-on-error: true
- uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 # v4.0.0
- with:
- workflow: secure-release-to-s3.yml
- token: ${{ steps.get-token.outputs.token }}
- repo: liquibase/liquibase-pro
- inputs: '{"version": "${{ inputs.liquibaseBranch }}"}'
- ref: ${{ inputs.liquibaseBranch }}
- wait-for-completion: true
- wait-for-completion-timeout: 60m
- wait-for-completion-interval: 10m
-
- - name: Download Liquibase Secure build from S3 after workflow completion
- if: ${{ matrix.build_type == 'secure' && steps.download-artifact.outputs.trigger_liquibase_secure_workflow == 'true' && steps.download-artifact.outputs.use_fallback != 'true' }}
- id: download-after-trigger
- run: |
- # Determine S3 path based on whether secure-version is provided (same logic as initial download)
- if [[ -n "${{ inputs.secure-version }}" ]]; then
- echo "Using secure-version: ${{ inputs.secure-version }}"
- VERSION="${{ inputs.secure-version }}"
- if [[ "$VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-RC([0-9]+)$ ]]; then
- BASE_VERSION="${BASH_REMATCH[1]}"
- RC_NUMBER="${BASH_REMATCH[2]}"
- LIQUIBASE_SECURE_ZIP="liquibase-secure-${VERSION}.tar.gz"
- S3_URL="s3://repo.liquibase.com/releases/rc/${VERSION}/${LIQUIBASE_SECURE_ZIP}"
- else
- LIQUIBASE_SECURE_ZIP="liquibase-secure-${VERSION}.zip"
- S3_URL="s3://repo.liquibase.com/releases/secure/${VERSION}/${LIQUIBASE_SECURE_ZIP}"
- fi
- else
- LIQUIBASE_SECURE_ZIP="liquibase-secure-${{ inputs.liquibaseBranch }}.zip"
- S3_URL="s3://repo.liquibase.com/releases/secure/${{ inputs.liquibaseBranch }}/$LIQUIBASE_SECURE_ZIP"
- fi
-
- echo "âŦī¸ Downloading Liquibase Secure build from S3 after workflow completion..."
-
- if ! aws s3 cp "$S3_URL" "$LIQUIBASE_SECURE_ZIP"; then
- echo "â Failed to download Liquibase Secure build even after workflow completion"
- echo "Will use fallback build from branch source instead"
- echo "build_ready=false" >> $GITHUB_OUTPUT
- echo "use_fallback=true" >> $GITHUB_OUTPUT
- exit 0
- fi
-
- echo "â
Successfully downloaded Liquibase Secure build after workflow completion"
-
- # Extract the liquibase Secure archive
- mkdir -p liquibase-build
- if [[ "$LIQUIBASE_SECURE_ZIP" == *.tar.gz ]]; then
- tar -xzf "$LIQUIBASE_SECURE_ZIP" -C liquibase-build/
- echo "Liquibase Secure build extracted from tar.gz to liquibase-build/"
- else
- unzip -q "$LIQUIBASE_SECURE_ZIP" -d liquibase-build/
- echo "Liquibase Secure build extracted from zip to liquibase-build/"
- fi
- echo "Contents of liquibase-build:"
- ls -la liquibase-build/
- echo "build_ready=true" >> $GITHUB_OUTPUT
- echo "use_fallback=false" >> $GITHUB_OUTPUT
-
- - name: Validate and prepare liquibase build
- if: ${{ (matrix.build_type == 'Community' && (steps.download-artifact.outputs.build_ready == 'true' || steps.download-artifact.outputs.use_fallback == 'true')) || matrix.build_type == 'secure' }}
- id: validate-build
- run: |
- # Determine build mode
- USE_FALLBACK="false"
- if [[ "${{ matrix.build_type }}" == "Community" && "${{ steps.download-artifact.outputs.use_fallback }}" == "true" ]]; then
- USE_FALLBACK="true"
- elif [[ "${{ matrix.build_type }}" == "secure" ]]; then
- # Check for Secure fallback conditions
- if [[ "${{ steps.download-artifact.outputs.use_fallback }}" == "true" ]] || [[ "${{ steps.download-after-trigger.outputs.use_fallback }}" == "true" ]]; then
- USE_FALLBACK="true"
- # Check if Liquibase Secure workflow was triggered but failed
- elif [[ "${{ steps.download-artifact.outputs.trigger_liquibase_secure_workflow }}" == "true" ]] && [[ "${{ steps.trigger-liquibase-secure-release.outcome }}" == "failure" ]]; then
- echo "Liquibase Secure workflow trigger failed, falling back to standard Dockerfile build"
- USE_FALLBACK="true"
- # Check if Liquibase Secure workflow succeeded but download still failed (no build_ready=true from either step)
- elif [[ "${{ steps.download-artifact.outputs.trigger_liquibase_secure_workflow }}" == "true" ]] && [[ "${{ steps.download-artifact.outputs.build_ready }}" != "true" ]] && [[ "${{ steps.download-after-trigger.outputs.build_ready }}" != "true" ]]; then
- echo "Liquibase Secure workflow completed but build still not available, falling back to standard Dockerfile build"
- USE_FALLBACK="true"
- fi
- fi
-
- if [[ "$USE_FALLBACK" == "true" ]]; then
- echo "Using fallback mode - will build from standard Dockerfile using branch source"
- echo "use_fallback_build=true" >> $GITHUB_OUTPUT
- else
- # Validate the liquibase build structure
- if [ ! -f "liquibase-build/liquibase" ]; then
- echo "Error: liquibase executable not found in build"
- echo "Contents of liquibase-build:"
- find liquibase-build -type f
- exit 1
- fi
-
- # Make liquibase executable
- chmod +x liquibase-build/liquibase
-
- # Validate that liquibase can run
- cd liquibase-build && ./liquibase --version && cd ..
-
- echo "Liquibase build validation successful"
- echo "Final liquibase-build contents:"
- ls -la liquibase-build/
- echo "use_fallback_build=false" >> $GITHUB_OUTPUT
- fi
-
- - name: Modify Dockerfiles for artifact-based build
- if: ${{ steps.validate-build.outputs.use_fallback_build == 'false' }}
+ - name: Delete GHCR image
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
- echo "Modifying ${{ matrix.dockerfile }} to use downloaded artifact instead of GitHub releases..."
-
- # Create backup
- cp "${{ matrix.dockerfile }}" "${{ matrix.dockerfile }}.backup"
-
- # Set version ARG based on dockerfile type
- if [[ "${{ matrix.dockerfile }}" == "DockerfileSecure" ]]; then
- VERSION_ARG="LIQUIBASE_SECURE_VERSION"
+ REPO_NAME="${{ github.event.repository.name }}"
+ PACKAGE_NAME="${REPO_NAME}/liquibase-community"
+ ENCODED_PACKAGE_NAME="${REPO_NAME}%2Fliquibase-community"
+ TAG="${{ github.sha }}"
+ VERSION_ID=$(gh api \
+ "/orgs/${{ github.repository_owner }}/packages/container/${ENCODED_PACKAGE_NAME}/versions" \
+ --jq ".[] | select(.metadata.container.tags[] == \"${TAG}\") | .id" 2>/dev/null || echo "")
+ if [ -n "$VERSION_ID" ]; then
+ gh api --method DELETE \
+ "/orgs/${{ github.repository_owner }}/packages/container/${ENCODED_PACKAGE_NAME}/versions/${VERSION_ID}"
+ echo "Deleted ${PACKAGE_NAME}:${TAG}"
else
- VERSION_ARG="LIQUIBASE_VERSION"
+ echo "No version found for ${PACKAGE_NAME}:${TAG}, skipping"
fi
- # Replace version ARG with branch name
- sed -i "s|ARG ${VERSION_ARG}=.*|ARG ${VERSION_ARG}=${{ inputs.liquibaseBranch }}|" "${{ matrix.dockerfile }}"
-
- # Create modified Dockerfile using awk for cleaner handling
- cat "${{ matrix.dockerfile }}" | awk -v dockerfile="${{ matrix.dockerfile }}" -v branch="${{ inputs.liquibaseBranch }}" '
- BEGIN {
- in_wget_block = 0
- skip_line = 0
- }
-
- # Skip SHA256 ARG lines
- /^ARG LB_SHA256=/ || /^ARG LB_SECURE_SHA256=/ { next }
-
- # Update version ARGs
- /^ARG LIQUIBASE_VERSION=/ { print "ARG LIQUIBASE_VERSION=" branch; next }
- /^ARG LIQUIBASE_SECURE_VERSION=/ { print "ARG LIQUIBASE_SECURE_VERSION=" branch; next }
-
- # Handle standard Dockerfile wget block
- dockerfile == "Dockerfile" && /^RUN wget.*github\.com.*liquibase.*tar\.gz/ {
- print "# Copy the extracted liquibase build from the build context"
- print "COPY liquibase-build/ ./"
- print ""
- print "RUN ln -s /liquibase/liquibase /usr/local/bin/liquibase && \\"
- print " ln -s /liquibase/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh && \\"
- print " liquibase --version"
- in_wget_block = 1
- next
- }
-
- # Handle alpine Dockerfile wget block
- dockerfile == "Dockerfile.alpine" && /^RUN set -x/ {
- print "# Copy the extracted liquibase build from the build context"
- print "COPY liquibase-build/ ./"
- print ""
- print "RUN ln -s /liquibase/liquibase /usr/local/bin/liquibase && \\"
- print " ln -s /liquibase/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh && \\"
- print " liquibase --version"
- in_wget_block = 1
- next
- }
-
- # Handle Secure Dockerfile wget block
- dockerfile == "DockerfileSecure" && /^RUN wget.*repo\.liquibase\.com.*tar\.gz/ {
- print "# Copy the extracted liquibase build from the build context"
- print "COPY liquibase-build/ ./"
- print ""
- print "RUN ln -s /liquibase/liquibase /usr/local/bin/liquibase && \\"
- print " ln -s /liquibase/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh && \\"
- print " liquibase --version"
- in_wget_block = 1
- next
- }
-
- # Skip lines until we reach the end of wget block
- in_wget_block == 1 && /liquibase --version/ {
- in_wget_block = 0
- next
- }
- in_wget_block == 1 { next }
-
- # Print all other lines
- { print }
- ' > "${{ matrix.dockerfile }}.tmp"
-
- # Replace original with modified version
- mv "${{ matrix.dockerfile }}.tmp" "${{ matrix.dockerfile }}"
-
- echo "â Modified ${{ matrix.dockerfile }}"
- echo "=== Changes made ==="
- echo "- Set ${VERSION_ARG} to ${{ inputs.liquibaseBranch }}"
- echo "- Removed wget download commands"
- echo "- Removed SHA256 checksum validation"
- echo "- Added COPY liquibase-build/ ./"
-
- echo ""
- echo "=== Modified Dockerfile content preview ==="
- echo "First 30 lines of modified ${{ matrix.dockerfile }}:"
- head -30 "${{ matrix.dockerfile }}"
- echo "..."
-
- - name: Set up Docker Buildx
- if: ${{ steps.validate-build.outcome == 'success' }}
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
-
- - name: Set up QEMU
- if: ${{ steps.validate-build.outcome == 'success' }}
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
-
- - name: Log in to internal Nexus Docker Registry
- if: ${{ steps.validate-build.outcome == 'success' }}
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- with:
- registry: ${{ env.REPO_URL }}
- username: ${{ env.REPO_USER }}
- password: ${{ env.REPO_PASSWORD }}
-
- - name: Login to GHCR
- if: ${{ steps.validate-build.outcome == 'success' }}
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Build and push Docker image
- if: ${{ steps.validate-build.outcome == 'success' }}
- uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
- with:
- context: .
- file: ${{ matrix.dockerfile }}
- push: true
- platforms: linux/amd64,linux/arm64
- provenance: false
- tags: |
- ${{ env.REPO_URL }}/${{ matrix.image_name }}:${{ matrix.is_rc == true && inputs.secure-version || inputs.liquibaseBranch }}
- ghcr.io/${{ github.repository }}/liquibase-${{ matrix.ghcr_name }}:${{ github.sha }}
- labels: |
- org.opencontainers.image.source=https://github.com/liquibase/docker
- org.opencontainers.image.description=Liquibase QA Container Image${{ matrix.suffix }}
- org.opencontainers.image.licenses=${{ matrix.build_type == 'secure' && 'LicenseRef-Liquibase-EULA' || 'FSL-1.1-ALv2' }}
- ${{ matrix.build_type == 'secure' && 'org.opencontainers.image.licenses.url=https://www.liquibase.com/eula' || '' }}
- org.opencontainers.image.vendor=Liquibase
- org.opencontainers.image.version=${{ matrix.is_rc == true && inputs.secure-version || inputs.liquibaseBranch }}
- org.opencontainers.image.documentation=https://docs.liquibase.com
- org.opencontainers.image.revision=${{ github.sha }}
- liquibase.branch=${{ inputs.liquibaseBranch }}
- liquibase.build.mode=${{ steps.validate-build.outputs.use_fallback_build == 'true' && 'fallback' || 'artifact' }}
- - name: Image build summary
- if: ${{ steps.validate-build.outcome == 'success' }}
- run: |
- BUILD_MODE="${{ steps.validate-build.outputs.use_fallback_build == 'true' && 'fallback (standard Dockerfile)' || 'artifact-based' }}"
- IMAGE_TAG="${{ matrix.is_rc == true && inputs.secure-version || inputs.liquibaseBranch }}"
- echo "Successfully built and pushed: ${{ env.REPO_URL }}/${{ matrix.image_name }}:$IMAGE_TAG"
- echo "Build mode: $BUILD_MODE"
-
- - name: Workflow execution summary
- if: always()
+ # ============================================
+ # CLEANUP: alpine
+ # ============================================
+ cleanup-ghcr-alpine:
+ name: Cleanup Alpine GHCR
+ needs: [vuln-scan-alpine]
+ if: always()
+ runs-on: ubuntu-latest
+ permissions:
+ packages: write
+ steps:
+ - name: Delete GHCR image
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
- echo "=== QA Docker Build Workflow Summary ==="
- echo "Branch: ${{ inputs.liquibaseBranch }}"
- echo "Build Target: ${{ inputs.buildTargets }}"
- echo "Dockerfile: ${{ matrix.dockerfile }}"
- echo "Build Type: ${{ matrix.build_type }}"
- echo ""
-
- # Determine if fallback was used
- USE_FALLBACK="false"
- if [[ "${{ steps.validate-build.outputs.use_fallback_build }}" == "true" ]] || [[ "${{ steps.download-artifact.outputs.use_fallback }}" == "true" ]] || [[ "${{ steps.download-after-trigger.outputs.use_fallback }}" == "true" ]]; then
- USE_FALLBACK="true"
- fi
-
- if [[ "$USE_FALLBACK" == "true" ]]; then
- echo "đ Build Mode: FALLBACK (Standard Dockerfile from branch source)"
- echo " - No pre-built artifacts were available"
- echo " - Used standard Dockerfile without modification"
- echo " - Built directly from liquibase branch: ${{ inputs.liquibaseBranch }}"
- elif [[ "${{ matrix.build_type }}" == "secure" ]]; then
- if [[ "${{ steps.download-artifact.outputs.trigger_liquibase_secure_workflow }}" == "true" ]]; then
- echo "đ Build Mode: ARTIFACT-BASED (Secure workflow triggered)"
- echo " - S3 build was missing for branch ${{ inputs.liquibaseBranch }}"
- echo " - Automatically triggered pro-release-to-s3.yml workflow"
- if [[ "${{ steps.download-after-trigger.outputs.build_ready }}" == "true" ]]; then
- echo " - â
Liquibase-Secure workflow completed successfully"
- echo " - â
Liquibase-Secure build downloaded and Docker image built"
- else
- echo " - â Liquibase-Secure workflow may have failed"
- echo " - đ Check: https://github.com/liquibase/liquibase-pro/actions/workflows/secure-release-to-s3.yml"
- fi
- else
- echo "â
Build Mode: ARTIFACT-BASED (Direct S3 download)"
- echo " - Liquibase-Secure build downloaded from S3"
- fi
- else
- echo "â
Build Mode: ARTIFACT-BASED (GitHub artifact)"
- echo " - Community build downloaded from GitHub workflow artifacts"
- fi
-
- echo ""
- if [[ "${{ job.status }}" == "success" ]]; then
- echo "đ Overall Status: SUCCESS"
- IMAGE_TAG="${{ matrix.is_rc == true && inputs.secure-version || inputs.liquibaseBranch }}"
- echo "đĻ Image Available: ${{ env.REPO_URL }}/${{ matrix.image_name }}:$IMAGE_TAG"
- if [[ "$USE_FALLBACK" == "true" ]]; then
- echo "âšī¸ Built using fallback mode - standard Dockerfile from branch source"
- fi
+ REPO_NAME="${{ github.event.repository.name }}"
+ PACKAGE_NAME="${REPO_NAME}/liquibase-alpine"
+ ENCODED_PACKAGE_NAME="${REPO_NAME}%2Fliquibase-alpine"
+ TAG="${{ github.sha }}"
+ VERSION_ID=$(gh api \
+ "/orgs/${{ github.repository_owner }}/packages/container/${ENCODED_PACKAGE_NAME}/versions" \
+ --jq ".[] | select(.metadata.container.tags[] == \"${TAG}\") | .id" 2>/dev/null || echo "")
+ if [ -n "$VERSION_ID" ]; then
+ gh api --method DELETE \
+ "/orgs/${{ github.repository_owner }}/packages/container/${ENCODED_PACKAGE_NAME}/versions/${VERSION_ID}"
+ echo "Deleted ${PACKAGE_NAME}:${TAG}"
else
- echo "â Overall Status: FAILED"
- echo "đ Check the logs above for detailed error information"
+ echo "No version found for ${PACKAGE_NAME}:${TAG}, skipping"
fi
- vulnerability-scan:
- needs: [set-matrix, build-qa-docker]
- if: ${{ !cancelled() && needs.build-qa-docker.result == 'success' }}
- strategy:
- fail-fast: false
- matrix: ${{ fromJSON(needs.set-matrix.outputs.matrix) }}
- name: Scan ${{ matrix.image_name }}
- uses: liquibase/build-logic/.github/workflows/reusable-vulnerability-scan.yml@main
- with:
- mode: docker
- source: ghcr.io/${{ github.repository }}/liquibase-${{ matrix.ghcr_name }}:${{ github.sha }}
- image_name: ${{ matrix.image_name }}
- image_tag: qa
- fail_on_vulnerabilities: true
- upload_sarif: false
- generate_sbom: true
- build_logic_ref: main
-
- cleanup-ghcr:
- needs: [set-matrix, vulnerability-scan]
+ # ============================================
+ # CLEANUP: secure
+ # ============================================
+ cleanup-ghcr-secure:
+ name: Cleanup Secure GHCR
+ needs: [vuln-scan-secure]
if: always()
runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix: ${{ fromJSON(needs.set-matrix.outputs.matrix) }}
- name: Cleanup ${{ matrix.image_name }}
permissions:
packages: write
steps:
@@ -654,8 +295,8 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
REPO_NAME="${{ github.event.repository.name }}"
- PACKAGE_NAME="${REPO_NAME}/liquibase-${{ matrix.ghcr_name }}"
- ENCODED_PACKAGE_NAME="${REPO_NAME}%2Fliquibase-${{ matrix.ghcr_name }}"
+ PACKAGE_NAME="${REPO_NAME}/liquibase-secure"
+ ENCODED_PACKAGE_NAME="${REPO_NAME}%2Fliquibase-secure"
TAG="${{ github.sha }}"
VERSION_ID=$(gh api \
"/orgs/${{ github.repository_owner }}/packages/container/${ENCODED_PACKAGE_NAME}/versions" \
diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
index 9e1f9711..c84114d4 100644
--- a/.github/workflows/create-release.yml
+++ b/.github/workflows/create-release.yml
@@ -227,343 +227,122 @@ jobs:
id: get-latest-sha
run: echo "latestCommitSha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- setup-update-draft-build:
- name: "Build and Push Docker Images"
+ # ============================================
+ # BUILD AND PUSH: community (Dockerfile)
+ # ============================================
+ build-community:
+ name: "Build liquibase/liquibase"
needs: update-dockerfiles
- runs-on: ubuntu-latest
- env:
- PUSH_DOCKERHUB: ${{ needs.update-dockerfiles.outputs.pushDockerHub }}
- PUSH_GHCR: ${{ needs.update-dockerfiles.outputs.pushGHCR }}
- PUSH_ECR: ${{ needs.update-dockerfiles.outputs.pushECR }}
- strategy:
- matrix:
- include:
- - dockerfile: Dockerfile
- name: liquibase/liquibase
- suffix: ""
- latest_tag: "latest"
- type: liquibase-release
- - dockerfile: Dockerfile.alpine
- name: liquibase/liquibase
- suffix: "-alpine"
- latest_tag: "alpine"
- type: liquibase-release
- - dockerfile: DockerfileSecure
- name: liquibase/liquibase-secure
- suffix: ""
- latest_tag: "latest"
- type: liquibase-secure-release
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- persist-credentials: false
- ref: ${{ github.ref }}
-
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
-
- - name: Get secrets from vault
- id: vault-secrets
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
-
- - name: Decode DOCKERHUB_USERNAME
- run: |
- decoded_username=$(echo "${{ env.DOCKERHUB_USERNAME }}" | base64 -d)
- echo "DOCKERHUB_USERNAME_DECODED=$decoded_username" >> $GITHUB_ENV
-
- - name: Set up JDK
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
- with:
- java-version: "8"
- distribution: "adopt"
-
- # Create GitHub release for Community (v{version}) or SECURE (v{version}-SECURE)
- # This ensures Community and SECURE releases have distinct tags and don't conflict
- # Only create the release once per release type (using the base Dockerfile without suffix)
- - name: Determine Release Tag
- id: release-tag
- if: ${{ needs.update-dockerfiles.outputs.dryRun == 'false' && matrix.suffix == '' && ((needs.update-dockerfiles.outputs.releaseType == 'liquibase-release' && matrix.type == 'liquibase-release') || (needs.update-dockerfiles.outputs.releaseType == 'liquibase-secure-release' && matrix.type == 'liquibase-secure-release')) }}
- run: |
- if [[ "${{ needs.update-dockerfiles.outputs.releaseType }}" == "liquibase-secure-release" ]]; then
- TAG_NAME="v${{ needs.update-dockerfiles.outputs.extensionVersion }}-SECURE"
- RELEASE_NAME="v${{ needs.update-dockerfiles.outputs.extensionVersion }} SECURE"
- else
- TAG_NAME="v${{ needs.update-dockerfiles.outputs.extensionVersion }}"
- RELEASE_NAME="v${{ needs.update-dockerfiles.outputs.extensionVersion }}"
- fi
- echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT
- echo "release_name=${RELEASE_NAME}" >> $GITHUB_OUTPUT
- echo "::notice::Creating GitHub release for ${{ needs.update-dockerfiles.outputs.releaseType }} on tag: ${TAG_NAME}"
-
- # Fetch release notes from Release Drafter draft
- - name: Get Release Notes from Draft
- id: get-release-notes
- if: ${{ needs.update-dockerfiles.outputs.dryRun == 'false' && matrix.suffix == '' && ((needs.update-dockerfiles.outputs.releaseType == 'liquibase-release' && matrix.type == 'liquibase-release') || (needs.update-dockerfiles.outputs.releaseType == 'liquibase-secure-release' && matrix.type == 'liquibase-secure-release')) }}
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
- with:
- script: |
- const isSecure = '${{ needs.update-dockerfiles.outputs.releaseType }}' === 'liquibase-secure-release';
-
- const releases = await github.rest.repos.listReleases({
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- // Find the matching draft from Release Drafter
- const draft = releases.data.find(r => {
- if (!r.draft) return false;
- return isSecure ? r.tag_name.includes('SECURE') : !r.tag_name.includes('SECURE');
- });
-
- if (draft && draft.body) {
- core.setOutput('body', draft.body);
- core.setOutput('found', 'true');
-
- // Delete the draft - we'll create the real release
- await github.rest.repos.deleteRelease({
- owner: context.repo.owner,
- repo: context.repo.repo,
- release_id: draft.id
- });
- core.info(`Deleted Release Drafter draft: ${draft.name}`);
- } else {
- core.setOutput('found', 'false');
- core.info('No Release Drafter draft found, using default release notes');
- }
-
- - name: Create GitHub Release
- if: ${{ needs.update-dockerfiles.outputs.dryRun == 'false' && matrix.suffix == '' && ((needs.update-dockerfiles.outputs.releaseType == 'liquibase-release' && matrix.type == 'liquibase-release') || (needs.update-dockerfiles.outputs.releaseType == 'liquibase-secure-release' && matrix.type == 'liquibase-secure-release')) }}
- uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
- with:
- name: ${{ steps.release-tag.outputs.release_name }}
- tag_name: ${{ steps.release-tag.outputs.tag_name }}
- draft: true
- body: |
- ${{ needs.update-dockerfiles.outputs.releaseType == 'liquibase-secure-release' && format('Support for Liquibase Secure {0}.', needs.update-dockerfiles.outputs.liquibaseVersion) || format('Support for Liquibase {0}.', needs.update-dockerfiles.outputs.liquibaseVersion) }}
-
- ${{ steps.get-release-notes.outputs.found == 'true' && steps.get-release-notes.outputs.body || '' }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
-
- # Install Cosign for keyless image signing (Secure images only)
- - name: Install Cosign
- if: ${{ matrix.type == 'liquibase-secure-release' }}
- uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
-
- # Define registry configurations based on image type
- - name: Set registry variables
- id: set-registries
- run: |
- if [[ "${{ matrix.type }}" == "liquibase-release" ]]; then
- echo "DOCKERHUB_REPO=${{ matrix.name }}" >> $GITHUB_OUTPUT
- echo "ECR_REPO=public.ecr.aws/liquibase/liquibase" >> $GITHUB_OUTPUT
- echo "GHCR_REPO=ghcr.io/liquibase/liquibase" >> $GITHUB_OUTPUT
- elif [[ "${{ matrix.type }}" == "liquibase-secure-release" ]]; then
- echo "DOCKERHUB_REPO=${{ matrix.name }}" >> $GITHUB_OUTPUT
- echo "ECR_REPO=public.ecr.aws/liquibase/liquibase-secure" >> $GITHUB_OUTPUT
- echo "GHCR_REPO=ghcr.io/liquibase/liquibase-secure" >> $GITHUB_OUTPUT
- fi
-
- # Use separate login steps for each registry
- - name: Login to Docker Hub
- if: ${{ env.PUSH_DOCKERHUB == 'true' && needs.update-dockerfiles.outputs.dryRun == 'false' }}
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- with:
- username: ${{ env.DOCKERHUB_USERNAME_DECODED }}
- password: ${{ env.DOCKERHUB_TOKEN }}
-
- - name: Login to GitHub Container Registry
- if: ${{ env.PUSH_GHCR == 'true' && needs.update-dockerfiles.outputs.dryRun == 'false' && steps.set-registries.outputs.GHCR_REPO != '' }}
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Configure AWS credentials for PROD ECR
- id: configure-aws-credentials-prod
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ env.AWS_PROD_GITHUB_OIDC_ROLE_ARN_INFRASTRUCTURE }}
- aws-region: us-east-1
-
- - name: Login to ECR Registry
- if: ${{ env.PUSH_ECR == 'true' && needs.update-dockerfiles.outputs.dryRun == 'false' && steps.set-registries.outputs.ECR_REPO != '' }}
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- env:
- AWS_REGION: us-east-1
- with:
- registry: public.ecr.aws
- username: ${{ steps.configure-aws-credentials-prod.outputs.aws_access_key_id }}
- password: ${{ steps.configure-aws-credentials-prod.outputs.aws_secret_access_key }}
-
- # Add login for dry-run mode to private ECR
- - name: Login to Private ECR Registry (dry-run)
- if: ${{ needs.update-dockerfiles.outputs.dryRun == 'true' }}
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- env:
- AWS_REGION: us-east-1
- with:
- registry: ${{ env.PRIVATE_ECR_DRY_RUN_REPO }}
- username: ${{ steps.configure-aws-credentials-prod.outputs.aws_access_key_id }}
- password: ${{ steps.configure-aws-credentials-prod.outputs.aws_secret_access_key }}
-
- # Generate tags dynamically using a separate step
- - name: Generate Docker Tags
- id: generate-tags
- run: |
- VERSION="${{ needs.update-dockerfiles.outputs.extensionVersion }}"
- MINOR_VERSION="${{ needs.update-dockerfiles.outputs.minorVersion }}"
- SUFFIX="${{ matrix.suffix }}"
- LATEST_TAG="${{ matrix.latest_tag }}"
- TAGS=""
- IS_DRY_RUN="${{ needs.update-dockerfiles.outputs.dryRun }}"
-
- if [[ "${IS_DRY_RUN}" == "true" ]]; then
- # For dry run, only use ECR registry
- TAGS="${{ env.PRIVATE_ECR_DRY_RUN_REPO }}:${LATEST_TAG}${SUFFIX}"
- TAGS="${TAGS},${{ env.PRIVATE_ECR_DRY_RUN_REPO }}:${VERSION}${SUFFIX}"
- TAGS="${TAGS},${{ env.PRIVATE_ECR_DRY_RUN_REPO }}:${MINOR_VERSION}${SUFFIX}"
- else
- # Not in dry-run mode, apply normal tag selection logic
-
- # Add DockerHub tags if selected
- if [[ "${PUSH_DOCKERHUB}" == "true" ]]; then
- TAGS="${{ steps.set-registries.outputs.DOCKERHUB_REPO }}:${LATEST_TAG}${SUFFIX}"
- TAGS="${TAGS},${{ steps.set-registries.outputs.DOCKERHUB_REPO }}:${VERSION}${SUFFIX}"
- TAGS="${TAGS},${{ steps.set-registries.outputs.DOCKERHUB_REPO }}:${MINOR_VERSION}${SUFFIX}"
- fi
-
- # Add ECR tags if selected and available for this image type
- if [[ "${PUSH_ECR}" == "true" && -n "${{ steps.set-registries.outputs.ECR_REPO }}" ]]; then
- if [[ -n "${TAGS}" ]]; then TAGS="${TAGS},"; fi
- TAGS="${TAGS}${{ steps.set-registries.outputs.ECR_REPO }}:${LATEST_TAG}${SUFFIX}"
- TAGS="${TAGS},${{ steps.set-registries.outputs.ECR_REPO }}:${VERSION}${SUFFIX}"
- TAGS="${TAGS},${{ steps.set-registries.outputs.ECR_REPO }}:${MINOR_VERSION}${SUFFIX}"
- fi
-
- # Add GHCR tags if selected and available for this image type
- if [[ "${PUSH_GHCR}" == "true" && -n "${{ steps.set-registries.outputs.GHCR_REPO }}" ]]; then
- if [[ -n "${TAGS}" ]]; then TAGS="${TAGS},"; fi
- TAGS="${TAGS}${{ steps.set-registries.outputs.GHCR_REPO }}:${LATEST_TAG}${SUFFIX}"
- TAGS="${TAGS},${{ steps.set-registries.outputs.GHCR_REPO }}:${VERSION}${SUFFIX}"
- TAGS="${TAGS},${{ steps.set-registries.outputs.GHCR_REPO }}:${MINOR_VERSION}${SUFFIX}"
- fi
-
- # If no tags were set, fall back to dry-run mode
- if [[ -z "${TAGS}" ]]; then
- echo "::warning::No registries were selected. Running in dry-run mode."
- TAGS="${{ env.PRIVATE_ECR_DRY_RUN_REPO }}:${LATEST_TAG}${SUFFIX}"
- fi
- fi
-
- # Output tags at the end
- echo "tags=${TAGS}" >> $GITHUB_OUTPUT
- if [[ "${IS_DRY_RUN}" == "true" ]]; then
- echo "Generated tags (dry-run): ${TAGS}"
- else
- echo "Generated tags: ${TAGS}"
- fi
-
- #Unified build and push step using generated tags
- - name: Build and Push Docker Image
- id: build-and-push
- if: ${{ needs.update-dockerfiles.outputs.releaseType == matrix.type }}
- uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
- with:
- context: .
- file: ${{ matrix.dockerfile }}
- no-cache: true
- push: true
- platforms: linux/amd64,linux/arm64
- tags: ${{ steps.generate-tags.outputs.tags }}
- # Supply chain security - SBOM and Provenance for Secure images only (SLSA Level 3)
- sbom: ${{ matrix.type == 'liquibase-secure-release' }}
- provenance: ${{ matrix.type == 'liquibase-secure-release' && 'mode=max' || 'false' }}
- # Labels applied to each architecture-specific image
- labels: |
- org.opencontainers.image.source=https://github.com/liquibase/docker
- org.opencontainers.image.description=${{ matrix.type == 'liquibase-secure-release' && 'Liquibase Secure Container Image' || format('Liquibase Container Image{0}', matrix.suffix == '-alpine' && ' (Alpine)' || '') }}
- org.opencontainers.image.licenses=${{ matrix.type == 'liquibase-secure-release' && 'LicenseRef-Liquibase-EULA' || 'FSL-1.1-ALv2' }}
- ${{ matrix.type == 'liquibase-secure-release' && 'org.opencontainers.image.licenses.url=https://www.liquibase.com/eula' || '' }}
- org.opencontainers.image.vendor=Liquibase
- org.opencontainers.image.version=${{ needs.update-dockerfiles.outputs.extensionVersion }}
- org.opencontainers.image.documentation=https://docs.liquibase.com
- # Annotations applied to the manifest list (important for multi-arch images)
- annotations: |
- org.opencontainers.image.source=https://github.com/liquibase/docker
- org.opencontainers.image.description=${{ matrix.type == 'liquibase-secure-release' && 'Liquibase Secure Container Image' || format('Liquibase Container Image{0}', matrix.suffix == '-alpine' && ' (Alpine)' || '') }}
- org.opencontainers.image.licenses=${{ matrix.type == 'liquibase-secure-release' && 'LicenseRef-Liquibase-EULA' || 'FSL-1.1-ALv2' }}
- ${{ matrix.type == 'liquibase-secure-release' && 'org.opencontainers.image.licenses.url=https://www.liquibase.com/eula' || '' }}
- org.opencontainers.image.vendor=Liquibase
- org.opencontainers.image.version=${{ needs.update-dockerfiles.outputs.extensionVersion }}
- org.opencontainers.image.documentation=https://docs.liquibase.com
-
- # Cosign keyless signing for Liquibase Secure images (SLSA Level 3 compliance)
- # Uses GitHub OIDC for identity - no private keys stored in repository
- - name: Sign Docker Hub Image with Cosign
- if: ${{ needs.update-dockerfiles.outputs.releaseType == matrix.type && matrix.type == 'liquibase-secure-release' && needs.update-dockerfiles.outputs.dryRun == 'false' && env.PUSH_DOCKERHUB == 'true' }}
- run: |
- echo "Signing Docker Hub image with Cosign keyless signing..."
- cosign sign --yes ${{ steps.set-registries.outputs.DOCKERHUB_REPO }}@${{ steps.build-and-push.outputs.digest }}
- echo "Docker Hub image signed successfully"
-
- - name: Sign GHCR Image with Cosign
- if: ${{ needs.update-dockerfiles.outputs.releaseType == matrix.type && matrix.type == 'liquibase-secure-release' && needs.update-dockerfiles.outputs.dryRun == 'false' && env.PUSH_GHCR == 'true' }}
- run: |
- echo "Signing GHCR image with Cosign keyless signing..."
- cosign sign --yes ${{ steps.set-registries.outputs.GHCR_REPO }}@${{ steps.build-and-push.outputs.digest }}
- echo "GHCR image signed successfully"
-
- - name: Sign ECR Image with Cosign
- if: ${{ needs.update-dockerfiles.outputs.releaseType == matrix.type && matrix.type == 'liquibase-secure-release' && needs.update-dockerfiles.outputs.dryRun == 'false' && env.PUSH_ECR == 'true' }}
- run: |
- echo "Signing ECR image with Cosign keyless signing..."
- cosign sign --yes ${{ steps.set-registries.outputs.ECR_REPO }}@${{ steps.build-and-push.outputs.digest }}
- echo "ECR image signed successfully"
-
- - name: Sign Dry-Run ECR Image with Cosign
- if: ${{ needs.update-dockerfiles.outputs.releaseType == matrix.type && matrix.type == 'liquibase-secure-release' && needs.update-dockerfiles.outputs.dryRun == 'true' }}
- run: |
- echo "Signing dry-run ECR image with Cosign keyless signing..."
- cosign sign --yes ${{ env.PRIVATE_ECR_DRY_RUN_REPO }}@${{ steps.build-and-push.outputs.digest }}
- echo "Dry-run ECR image signed successfully"
-
+ if: ${{ needs.update-dockerfiles.outputs.releaseType == 'liquibase-release' }}
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-build.yml@main
+ with:
+ dockerfile_path: Dockerfile
+ image_name: liquibase/liquibase
+ suffix: ""
+ version: ${{ needs.update-dockerfiles.outputs.extensionVersion }}
+ minor_version: ${{ needs.update-dockerfiles.outputs.minorVersion }}
+ latest_tag: "latest"
+ dockerhub_repo: liquibase/liquibase
+ ghcr_repo: ghcr.io/liquibase/liquibase
+ ecr_repo: public.ecr.aws/liquibase/liquibase
+ push_dockerhub: ${{ needs.update-dockerfiles.outputs.pushDockerHub == 'true' }}
+ push_ghcr: ${{ needs.update-dockerfiles.outputs.pushGHCR == 'true' }}
+ push_ecr: ${{ needs.update-dockerfiles.outputs.pushECR == 'true' }}
+ dry_run: ${{ needs.update-dockerfiles.outputs.dryRun == 'true' }}
+ sign_with_cosign: false
+ generate_sbom: false
+ provenance_mode: "false"
+ cosign_identity_regexp: ^https://github\.com/liquibase/docker/.*
+ image_source_url: https://github.com/${{ github.repository }}
+ image_description: "Liquibase Container Image"
+ image_licenses: FSL-1.1-ALv2
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # BUILD AND PUSH: alpine (Dockerfile.alpine)
+ # ============================================
+ build-alpine:
+ name: "Build liquibase/liquibase-alpine"
+ needs: update-dockerfiles
+ if: ${{ needs.update-dockerfiles.outputs.releaseType == 'liquibase-release' }}
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-build.yml@main
+ with:
+ dockerfile_path: Dockerfile.alpine
+ image_name: liquibase/liquibase
+ suffix: "-alpine"
+ version: ${{ needs.update-dockerfiles.outputs.extensionVersion }}
+ minor_version: ${{ needs.update-dockerfiles.outputs.minorVersion }}
+ latest_tag: "alpine"
+ dockerhub_repo: liquibase/liquibase
+ ghcr_repo: ghcr.io/liquibase/liquibase
+ ecr_repo: public.ecr.aws/liquibase/liquibase
+ push_dockerhub: ${{ needs.update-dockerfiles.outputs.pushDockerHub == 'true' }}
+ push_ghcr: ${{ needs.update-dockerfiles.outputs.pushGHCR == 'true' }}
+ push_ecr: ${{ needs.update-dockerfiles.outputs.pushECR == 'true' }}
+ dry_run: ${{ needs.update-dockerfiles.outputs.dryRun == 'true' }}
+ sign_with_cosign: false
+ generate_sbom: false
+ provenance_mode: "false"
+ cosign_identity_regexp: ^https://github\.com/liquibase/docker/.*
+ image_source_url: https://github.com/${{ github.repository }}
+ image_description: "Liquibase Container Image (Alpine)"
+ image_licenses: FSL-1.1-ALv2
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # BUILD AND PUSH: secure (DockerfileSecure)
+ # ============================================
+ build-secure:
+ name: "Build liquibase/liquibase-secure"
+ needs: update-dockerfiles
+ if: ${{ needs.update-dockerfiles.outputs.releaseType == 'liquibase-secure-release' }}
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-build.yml@main
+ with:
+ dockerfile_path: DockerfileSecure
+ image_name: liquibase/liquibase-secure
+ suffix: ""
+ version: ${{ needs.update-dockerfiles.outputs.extensionVersion }}
+ minor_version: ${{ needs.update-dockerfiles.outputs.minorVersion }}
+ latest_tag: "latest"
+ dockerhub_repo: liquibase/liquibase-secure
+ ghcr_repo: ghcr.io/liquibase/liquibase-secure
+ ecr_repo: public.ecr.aws/liquibase/liquibase-secure
+ push_dockerhub: ${{ needs.update-dockerfiles.outputs.pushDockerHub == 'true' }}
+ push_ghcr: ${{ needs.update-dockerfiles.outputs.pushGHCR == 'true' }}
+ push_ecr: ${{ needs.update-dockerfiles.outputs.pushECR == 'true' }}
+ dry_run: ${{ needs.update-dockerfiles.outputs.dryRun == 'true' }}
+ sign_with_cosign: true
+ generate_sbom: true
+ provenance_mode: "mode=max"
+ cosign_identity_regexp: ^https://github\.com/liquibase/docker/.*
+ image_source_url: https://github.com/${{ github.repository }}
+ image_description: "Liquibase Secure Container Image"
+ image_licenses: LicenseRef-Liquibase-EULA
+ image_licenses_url: https://www.liquibase.com/eula
+ build_logic_ref: main
+ secrets: inherit
+
+ # ============================================
+ # PUBLISH GITHUB RELEASE
+ # ============================================
publish-release:
name: "Publish GitHub Release"
- needs: [update-dockerfiles, setup-update-draft-build]
- if: ${{ needs.update-dockerfiles.outputs.dryRun == 'false' && needs.update-dockerfiles.outputs.changes_made == 'true' }}
- runs-on: ubuntu-latest
- steps:
- - name: Determine Release Tag
- id: release-tag
- run: |
- if [[ "${{ needs.update-dockerfiles.outputs.releaseType }}" == "liquibase-secure-release" ]]; then
- echo "tag_name=v${{ needs.update-dockerfiles.outputs.extensionVersion }}-SECURE" >> $GITHUB_OUTPUT
- else
- echo "tag_name=v${{ needs.update-dockerfiles.outputs.extensionVersion }}" >> $GITHUB_OUTPUT
- fi
-
- - name: Publish Release
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- TAG="${{ steps.release-tag.outputs.tag_name }}"
- if ! gh release view "${TAG}" --repo "${{ github.repository }}" &>/dev/null; then
- echo "::warning::Release '${TAG}' not found â skipping publish."
- exit 0
- fi
- gh release edit "${TAG}" \
- --repo "${{ github.repository }}" \
- --draft=false \
- --latest=true
+ needs: [update-dockerfiles, build-community, build-alpine, build-secure]
+ if: |
+ always() &&
+ needs.update-dockerfiles.outputs.dryRun == 'false' &&
+ needs.update-dockerfiles.outputs.changes_made == 'true' &&
+ (
+ (needs.update-dockerfiles.outputs.releaseType == 'liquibase-release' && needs.build-community.result == 'success' && needs.build-alpine.result == 'success') ||
+ (needs.update-dockerfiles.outputs.releaseType == 'liquibase-secure-release' && needs.build-secure.result == 'success')
+ )
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-publish.yml@main
+ with:
+ release_type: ${{ needs.update-dockerfiles.outputs.releaseType }}
+ version: ${{ needs.update-dockerfiles.outputs.extensionVersion }}
+ tag_suffix: ${{ needs.update-dockerfiles.outputs.releaseType == 'liquibase-secure-release' && '-SECURE' || '' }}
+ dry_run: false
+ build_logic_ref: main
+ secrets: inherit
diff --git a/.github/workflows/publish-liquibase-secure-readme.yml b/.github/workflows/publish-liquibase-secure-readme.yml
index 04b22550..80c7e19e 100644
--- a/.github/workflows/publish-liquibase-secure-readme.yml
+++ b/.github/workflows/publish-liquibase-secure-readme.yml
@@ -7,43 +7,23 @@ on:
branches:
- main
workflow_dispatch:
+ inputs:
+ dry_run:
+ description: 'Preview README push without updating Docker Hub (for validation runs)'
+ required: false
+ type: boolean
+ default: false
permissions:
contents: write
id-token: write
-
-jobs:
- update-liquibase-secure-readme:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
-
- - name: Get secrets from vault
- id: vault-secrets
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
- - name: Decode DOCKERHUB_USERNAME
- run: |
- decoded_username=$(echo "${{ env.DOCKERHUB_USERNAME }}" | base64 -d)
- echo "DOCKERHUB_USERNAME_DECODED=$decoded_username" >> $GITHUB_ENV
-
- - name: Update Liquibase Secure README on Docker Hub
- uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
- with:
- username: ${{ env.DOCKERHUB_USERNAME_DECODED}}
- password: ${{ env.DOCKERHUB_UPDATE_README }}
- repository: liquibase/liquibase-secure
- readme-filepath: ./README-secure.md
- short-description: "Liquibase Secure"
+jobs:
+ update-readme:
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-readme.yml@main
+ with:
+ target_image: liquibase/liquibase-secure
+ readme_path: README-secure.md
+ short_description: "Liquibase Secure"
+ dry_run: ${{ inputs.dry_run || false }}
+ secrets: inherit
diff --git a/.github/workflows/publish-oss-readme.yml b/.github/workflows/publish-oss-readme.yml
index c5e67134..0af4912b 100644
--- a/.github/workflows/publish-oss-readme.yml
+++ b/.github/workflows/publish-oss-readme.yml
@@ -7,42 +7,23 @@ on:
branches:
- main
workflow_dispatch:
+ inputs:
+ dry_run:
+ description: 'Preview README push without updating Docker Hub (for validation runs)'
+ required: false
+ type: boolean
+ default: false
permissions:
+ contents: read
id-token: write
jobs:
- update-liquibase-Community-readme:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
-
- - name: Get secrets from vault
- id: vault-secrets
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
-
- - name: Decode DOCKERHUB_USERNAME
- run: |
- decoded_username=$(echo "${{ env.DOCKERHUB_USERNAME }}" | base64 -d)
- echo "DOCKERHUB_USERNAME_DECODED=$decoded_username" >> $GITHUB_ENV
-
- - name: Update Liquibase Community README on Docker Hub
- uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
- with:
- username: ${{ env.DOCKERHUB_USERNAME_DECODED }}
- password: ${{ env.DOCKERHUB_UPDATE_README }}
- repository: liquibase/liquibase
- readme-filepath: README.md
- short-description: "Liquibase Community"
+ update-readme:
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-readme.yml@main
+ with:
+ target_image: liquibase/liquibase
+ readme_path: README.md
+ short_description: "Liquibase Community"
+ dry_run: ${{ inputs.dry_run || false }}
+ secrets: inherit
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a9f31e3d..b90cbc5c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -31,770 +31,15 @@ permissions:
jobs:
test:
- env:
- CONTAINER_NAME: "liquibase"
-
+ name: Build & Test ${{ matrix.dockerfile }} - ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
dockerfile: [Dockerfile, Dockerfile.alpine, DockerfileSecure]
os: [ubuntu-latest, macos-15-intel]
-
- name: Build & Test ${{ matrix.dockerfile }} - ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
- steps:
- - name: Checkout code
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Setup Docker on macOS
- if: matrix.os == 'macos-15-intel'
- uses: douglascamata/setup-docker-macos-action@d5ccc6aae0ce23e7700154f5e63cc53e6433ac48 # v1.1.0
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
-
- - name: Build an image from ${{ matrix.dockerfile }}
- run: |
- docker build -f ${{ matrix.dockerfile }} -t liquibase/liquibase:${{ github.sha }} .
-
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
-
- - name: Get secrets from vault
- id: vault-secrets
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
-
- - name: Test liquibase init start-h2
- run: |
- LOG_STRING="The database does not persist data"
- docker run --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} --name $CONTAINER_NAME -d -v $(pwd)/.github/test:/liquibase/changelog liquibase/liquibase:${{ github.sha }} init start-h2
- sleep 30
- # Check if the container is running
- if docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "true"; then
- # Get the logs and check if the desired string is present
- if docker logs "$CONTAINER_NAME" 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log contains the string: $LOG_STRING"
- else
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- fi
- else
- echo "Error: Container $CONTAINER_NAME is not running."
- exit 2
- fi
-
- - name: Test liquibase version
- run: |
- LOG_STRING="Starting Liquibase"
- # Check if the container is running
- if docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "true"; then
- # Get the logs and check if the desired string is present
- if docker exec $CONTAINER_NAME liquibase --version 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log contains the string: $LOG_STRING"
- else
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- fi
- else
- echo "Error: Container $CONTAINER_NAME is not running."
- exit 2
- fi
-
- - name: Test liquibase update
- run: |
- LOG_STRING="Update has been successful"
- # Check if the container is running
- if docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "true"; then
- # Get the logs and check if the desired string is present
- if docker exec $CONTAINER_NAME liquibase update --defaultsFile=/liquibase/changelog/liquibase.properties --changelog-file=/changelog/example-changelog.xml 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log contains the string: $LOG_STRING"
- else
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- fi
- else
- echo "Error: Container $CONTAINER_NAME is not running."
- exit 2
- fi
-
- - name: Test liquibase wrong ENV variable
- run: |
- LOG_STRING="Error: Unable to access jarfile wrong_path/internal/lib/"
- # Stop docker container and remove it
- docker stop $CONTAINER_NAME
- docker rm $CONTAINER_NAME
- # Start docker container with wrong ENV
- # Get the logs and check if the desired string is present
- if docker run --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} --name $CONTAINER_NAME -d -v $(pwd)/.github/test:/liquibase/changelog -e LIQUIBASE_HOME="wrong_path" liquibase/liquibase:${{ github.sha }} init start-h2 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- else
- echo "The log contains the string: $LOG_STRING"
- fi
-
- - name: Test liquibase good ENV variable
- run: |
- LOG_STRING="The database does not persist data"
- # Stop docker container and remove it
- docker stop $CONTAINER_NAME
- docker rm $CONTAINER_NAME
- # Start docker container with good ENV
- docker run --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} --name $CONTAINER_NAME -d -v $(pwd)/.github/test:/liquibase/changelog -e LIQUIBASE_HOME="/liquibase" liquibase/liquibase:${{ github.sha }} init start-h2
- sleep 30
- docker logs $CONTAINER_NAME
- # Check if the container is running
- if docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "true"; then
- # Get the logs and check if the desired string is present
- if docker logs "$CONTAINER_NAME" 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log contains the string: $LOG_STRING"
- else
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- fi
- else
- echo "Error: Container $CONTAINER_NAME is not running."
- exit 2
- fi
-
- - name: Test volume persistence
- run: |
- LOG_STRING="Update has been successful"
- # Stop docker container
- docker stop $CONTAINER_NAME
- # Start docker container
- docker start $CONTAINER_NAME
- sleep 30
- # Check if the container is running
- if docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "true"; then
- # Get the logs and check if the desired string is present
- if docker exec $CONTAINER_NAME liquibase update --defaultsFile=/liquibase/changelog/liquibase.properties --changelog-file=/changelog/example-changelog.xml 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log contains the string: $LOG_STRING"
- else
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- fi
- else
- echo "Error: Container $CONTAINER_NAME is not running."
- exit 2
- fi
-
- - name: Test extension loading
- run: |
- LOG_STRING="liquibase-redshift"
- # Stop docker container
- docker exec $CONTAINER_NAME lpm add liquibase-redshift --category=extension -g
- # Get the logs and check if the desired string is present
- if docker exec $CONTAINER_NAME liquibase --version 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log contains the string: $LOG_STRING"
- else
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- fi
-
- - name: Test driver connection
- run: |
- LOG_STRING="successfully installed in classpath"
- docker stop $CONTAINER_NAME
- docker rm $CONTAINER_NAME
- docker network create --driver bridge test_network
- # Get the logs and check if the desired string is present
- docker run -d --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} --name $CONTAINER_NAME --network test_network -v $(pwd)/.github/test:/liquibase/changelog liquibase/liquibase:${{ github.sha }} init start-h2
- if docker exec $CONTAINER_NAME lpm add mssql --category=driver -g 2>&1 | grep -q "$LOG_STRING"; then
- echo "The log contains the string: $LOG_STRING"
- else
- echo "The log does not contain the string: $LOG_STRING"
- exit 1
- fi
-
- - name: Test custom entrypoint
- run: |
- LOG_STRING="Update has been successful"
- # Build auxiliary liquibase image to inherit from
- docker build -f ${{ matrix.dockerfile }} -t liquibase:test-entrypoint .
- # Build custom liquibase image
- docker build -f $(pwd)/.github/test/Dockerfile -t liquibase:test $(pwd)/.github/test/
- # Get the logs and check if the desired string is present
- docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} --name liquibase-test --entrypoint="/scripts/liquibase_command.sh" -v $(pwd)/.github/test:/liquibase/changelog liquibase:test "version"
-
- # CLI-Docker Compatibility Feature Tests
- - name: Test CLI-Docker compatibility - Working directory change
- run: |
- echo "Testing that working directory changes to /liquibase/changelog when mounted..."
-
- # Stop and remove existing container
- docker stop $CONTAINER_NAME || true
- docker rm $CONTAINER_NAME || true
-
- # Create test directory with a properties file to verify working directory
- mkdir -p test-compatibility
- cat > test-compatibility/test.properties << EOF
- changelogFile=test-changelog.xml
- url=offline:postgresql
- driver=org.postgresql.Driver
- EOF
-
- cat > test-compatibility/test-changelog.xml << 'EOF'
-
-
-
- SELECT 'Test';
-
-
- EOF
-
- # Test that working directory changes when /liquibase/changelog is mounted
- # If working directory changed, it should find test.properties with relative path
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-compatibility:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- --defaultsFile=test.properties validate 2>&1)
-
- echo "Container output: $RESULT"
-
- # If working directory changed correctly, validation should succeed
- if echo "$RESULT" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Working directory correctly changed to mounted changelog directory"
- else
- echo "â FAIL: Working directory did not change to changelog directory or validation failed"
- echo "This indicates the properties file was not found with relative path"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-compatibility
-
- - name: Test CLI-Docker compatibility - Relative path file creation
- run: |
- echo "Testing relative path file creation with new CLI-Docker compatibility..."
-
- # Create test directory structure
- mkdir -p test-file-creation
- cat > test-file-creation/test-changelog.xml << 'EOF'
-
-
-
-
-
-
-
-
- EOF
-
- # Test updateSQL with relative path to generate SQL output file
- docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-file-creation:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- updateSQL --changelogFile=test-changelog.xml --url=offline:postgresql --outputFile=generated-update.sql
-
- # Verify file was created in the mounted directory (not container root)
- if [ -f "test-file-creation/generated-update.sql" ]; then
- echo "â
SUCCESS: File created in mounted directory with relative path"
- echo "File contents preview:"
- head -5 test-file-creation/generated-update.sql
- else
- echo "â FAIL: File not created in expected location"
- echo "Files in test directory:"
- ls -la test-file-creation/
- # Also check if the file was created in container root (wrong location)
- echo "Testing if file was created in wrong location..."
- WRONG_LOCATION=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-file-creation:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- sh -c "ls -la /liquibase/generated-update.sql 2>/dev/null || echo 'NOT_FOUND'")
- echo "Check for file in container root: $WRONG_LOCATION"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-file-creation
-
- - name: Test CLI-Docker compatibility - Properties file resolution
- run: |
- echo "Testing properties file resolution with relative paths..."
-
- # Create test directory with properties file
- mkdir -p test-properties
- cat > test-properties/test.properties << EOF
- changelogFile=test-changelog.xml
- url=offline:postgresql
- driver=org.postgresql.Driver
- EOF
-
- cat > test-properties/test-changelog.xml << 'EOF'
-
-
-
- Test changeset
- SELECT 'Test';
-
-
- EOF
-
- # Test that properties file can be resolved with relative path
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-properties:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- --defaultsFile=test.properties validate 2>&1)
-
- echo "Validation result: $RESULT"
-
- if echo "$RESULT" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Properties file resolved correctly with relative path"
- else
- echo "â FAIL: Properties file resolution failed"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-properties
-
- - name: Test backward compatibility - No changelog directory
- run: |
- echo "Testing backward compatibility when no /liquibase/changelog is mounted..."
-
- # Test container works normally without changelog mount (backward compatibility)
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} liquibase/liquibase:${{ github.sha }} --version 2>&1)
-
- echo "Container output: $RESULT"
-
- if echo "$RESULT" | grep -q "Liquibase.*Version"; then
- echo "â
SUCCESS: Container works without changelog mount (backward compatible)"
- else
- echo "â FAIL: Container broken when no changelog directory mounted"
- echo "Full output: $RESULT"
- exit 1
- fi
-
- - name: Test backward compatibility - Mixed path usage scenarios
- run: |
- echo "Testing mixed path usage scenarios work correctly..."
-
- # Create test setup
- mkdir -p test-mixed-paths
- cat > test-mixed-paths/test-changelog.xml << 'EOF'
-
-
-
- SELECT 'Test';
-
-
- EOF
-
- # Test 1: Relative path should work (new CLI-Docker compatibility feature)
- echo "Testing relative path (new CLI-Docker compatibility)..."
- RESULT1=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-mixed-paths:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- validate --changelogFile=test-changelog.xml --url=offline:postgresql 2>&1)
-
- if echo "$RESULT1" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Relative paths work with CLI-Docker compatibility"
- else
- echo "â FAIL: Relative paths don't work"
- echo "Output: $RESULT1"
- exit 1
- fi
-
- # Test 2: Using SHOULD_CHANGE_DIR=false should preserve old behavior
- echo "Testing SHOULD_CHANGE_DIR=false preserves old behavior..."
- RESULT2=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} --env SHOULD_CHANGE_DIR=false \
- -v $(pwd)/test-mixed-paths:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- validate --changelogFile=test-changelog.xml --url=offline:postgresql 2>&1)
-
- if echo "$RESULT2" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: SHOULD_CHANGE_DIR=false preserves old behavior"
- else
- echo "â
EXPECTED: SHOULD_CHANGE_DIR=false correctly prevents automatic directory changes"
- echo "This confirms the environment variable override works correctly"
- fi
-
- # Test 3: Verify files are generated in mounted directory with relative paths
- echo "Testing file generation goes to mounted directory..."
- docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} \
- -v $(pwd)/test-mixed-paths:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- update-sql --changelogFile=test-changelog.xml --url=offline:postgresql --output-file=generated-sql.sql
-
- if [ -f "test-mixed-paths/generated-sql.sql" ]; then
- echo "â
SUCCESS: Generated files are created in mounted directory"
- echo "File contents preview:"
- head -5 test-mixed-paths/generated-sql.sql
- else
- echo "â FAIL: Generated file not found in mounted directory"
- ls -la test-mixed-paths/
- exit 1
- fi
-
- # Cleanup
- rm -rf test-mixed-paths
-
- - name: Test CLI-Docker compatibility - Generate changelog creates files in mounted directory
- run: |
- echo "Testing that generate-changelog command creates files in mounted directory..."
-
- # Create test directory for generate-changelog
- mkdir -p test-generate-changelog
-
- # Create a properties file for the test
- cat > test-generate-changelog/liquibase.properties << EOF
- url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
- username=sa
- password=
- driver=org.h2.Driver
- EOF
-
- # First, create a basic table in the database and capture it as changelog
- # Run generate-changelog with relative path
- echo "Running generate-changelog command with relative path..."
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-generate-changelog:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- generate-changelog \
- --defaultsFile=liquibase.properties \
- --changelog-file=generated-changelog.xml 2>&1)
-
- echo "Generate changelog result: $RESULT"
-
- # Check if the command executed successfully (even if no changes to generate)
- if echo "$RESULT" | grep -q "was executed successfully"; then
- echo "â
SUCCESS: generate-changelog command executed successfully"
- else
- echo "â FAIL: generate-changelog command failed"
- echo "Full output: $RESULT"
- exit 1
- fi
-
- # Test generate-changelog with output file creation using updateSQL
- echo "Testing file generation with updateSQL (which will create output)..."
-
- # Create a simple changelog to generate SQL from
- cat > test-generate-changelog/test-changelog.xml << 'EOF'
-
-
-
-
-
-
-
-
-
- EOF
-
- # Generate SQL output file with relative path to verify file creation works
- docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-generate-changelog:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- update-sql \
- --changelog-file=test-changelog.xml \
- --url=jdbc:h2:mem:testdb \
- --username=sa \
- --output-file=generated-output.sql
-
- # Verify the output file was created in the mounted directory
- if [ -f "test-generate-changelog/generated-output.sql" ]; then
- echo "â
SUCCESS: Output file created in mounted directory using relative path"
- echo "Generated file contents preview:"
- head -10 test-generate-changelog/generated-output.sql
-
- # Verify file contains expected SQL
- if grep -q "CREATE TABLE.*test_table" test-generate-changelog/generated-output.sql; then
- echo "â
SUCCESS: Generated file contains expected SQL content"
- else
- echo "â FAIL: Generated file does not contain expected SQL content"
- exit 1
- fi
- else
- echo "â FAIL: Output file was not created in the mounted directory"
- echo "Files in test directory:"
- ls -la test-generate-changelog/
- exit 1
- fi
-
- # Test with defaults file using relative paths
- echo "Testing generate-changelog with defaults file containing relative paths..."
-
- # Create a defaults file that uses relative paths
- cat > test-generate-changelog/generate.properties << EOF
- changelog-file=another-generated-changelog.xml
- url=jdbc:h2:mem:testdb
- username=sa
- driver=org.h2.Driver
- EOF
-
- # Run generate-changelog using defaults file with relative path
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-generate-changelog:/liquibase/changelog \
- liquibase/liquibase:${{ github.sha }} \
- generate-changelog \
- --defaults-file=generate.properties 2>&1)
-
- echo "Generate changelog with defaults file result: $RESULT"
-
- # Verify the command executed successfully
- if echo "$RESULT" | grep -q "was executed successfully"; then
- echo "â
SUCCESS: generate-changelog with defaults file using relative paths works"
- else
- echo "â FAIL: generate-changelog with defaults file failed"
- echo "Full output: $RESULT"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-generate-changelog
-
- - name: Test CLI-Docker compatibility - Complex scenario
- run: |
- echo "Testing complex scenario with multiple relative paths..."
-
- # Create complex test structure
- mkdir -p test-complex/{changelog,lib}
-
- cat > test-complex/changelog/db.changelog-root.xml << 'EOF'
-
-
-
-
- EOF
-
- cat > test-complex/changelog/001-tables.xml << 'EOF'
-
-
-
-
-
-
-
-
- EOF
-
- cat > test-complex/app.properties << EOF
- changelogFile=db.changelog-root.xml
- url=offline:postgresql
- driver=org.postgresql.Driver
- EOF
-
- # Test complex scenario with includes and relative paths
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} -v $(pwd)/test-complex/changelog:/liquibase/changelog -v $(pwd)/test-complex:/liquibase/app \
- liquibase/liquibase:${{ github.sha }} \
- --defaultsFile=../app/app.properties validate 2>&1)
-
- echo "Complex validation result: $RESULT"
-
- if echo "$RESULT" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Complex relative path scenario works"
- else
- echo "â FAIL: Complex relative path scenario failed"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-complex
-
- # Search Path Preservation Tests (DAT-21189 regression tests)
- - name: Test custom LIQUIBASE_SEARCH_PATH env var is preserved
- if: matrix.os == 'ubuntu-latest'
- run: |
- echo "Testing that custom LIQUIBASE_SEARCH_PATH environment variable is not overridden..."
-
- # Create test directory structure
- mkdir -p test-search-path-env/custom-path
-
- # When using a custom LIQUIBASE_SEARCH_PATH, all changelog files must be in that path
- # This tests that the custom search path is preserved and used correctly
- cat > test-search-path-env/custom-path/main-changelog.xml << 'EOF'
-
-
-
- SELECT 'Using custom search path';
-
-
- EOF
-
- # Test with custom search path
- # This verifies that when LIQUIBASE_SEARCH_PATH is set, it is used and not overridden
- set +e
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} \
- --env LIQUIBASE_SEARCH_PATH="/liquibase/custom-path" \
- -v $(pwd)/test-search-path-env/custom-path:/liquibase/custom-path \
- liquibase/liquibase:${{ github.sha }} \
- --changelogFile=main-changelog.xml \
- --url=offline:postgresql \
- validate 2>&1)
- EXIT_CODE=$?
- set -e
-
- echo "Search path test output: $RESULT"
- echo "Exit code: $EXIT_CODE"
-
- if echo "$RESULT" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Custom LIQUIBASE_SEARCH_PATH environment variable was preserved"
- else
- echo "â FAIL: Custom search path was not respected or overridden"
- echo "Checking if file was found error: $(echo "$RESULT" | grep -i "not found\|cannot find" || echo "No file not found error")"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-search-path-env
-
- - name: Test explicit --search-path CLI argument is not overridden
- if: matrix.os == 'ubuntu-latest'
- run: |
- echo "Testing that explicit --search-path command-line argument is respected..."
-
- # Create test directory structure
- mkdir -p test-search-path-cli/cli-path
-
- # Put changelog in the CLI-specified search path
- cat > test-search-path-cli/cli-path/main-changelog.xml << 'EOF'
-
-
-
- SELECT 'CLI Search Path Respected';
-
-
- EOF
-
- # Test with explicit --search-path argument
- # This verifies that CLI arguments take precedence and are not overridden
- set +e
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} \
- -v $(pwd)/test-search-path-cli/cli-path:/liquibase/cli-path \
- liquibase/liquibase:${{ github.sha }} \
- --changelogFile=main-changelog.xml \
- --search-path=/liquibase/cli-path \
- --url=offline:postgresql \
- validate 2>&1)
- EXIT_CODE=$?
- set -e
-
- echo "CLI search path test output: $RESULT"
-
- if echo "$RESULT" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Explicit --search-path CLI argument was respected"
- else
- echo "â FAIL: Explicit search path argument was overridden"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-search-path-cli
-
- - name: Test relative paths work WITH custom search path (regression test)
- if: matrix.os == 'ubuntu-latest'
- run: |
- echo "Testing relative paths combined with custom search path (DAT-21189 regression)..."
-
- # This scenario tests: user provides custom search path and accesses files from within that path
- # The regression was that the entrypoint was injecting additional search paths on top of user's
- mkdir -p test-search-path-combo/shared
-
- # Main changelog in the custom search path
- cat > test-search-path-combo/shared/main-changelog.xml << 'EOF'
-
-
-
- SELECT 'Custom search path respected';
-
-
- EOF
-
- # Test: custom search path is preserved (regression: PR #414 was overriding this)
- set +e
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} \
- --env LIQUIBASE_SEARCH_PATH="/liquibase/shared" \
- -v $(pwd)/test-search-path-combo/shared:/liquibase/shared \
- liquibase/liquibase:${{ github.sha }} \
- --changelogFile=main-changelog.xml \
- --url=offline:postgresql \
- validate 2>&1)
- EXIT_CODE=$?
- set -e
-
- echo "Regression test output: $RESULT"
-
- if echo "$RESULT" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Custom search path preserved correctly (DAT-21189 fixed)"
- else
- echo "â FAIL: Custom search path was not preserved (DAT-21189 regression)"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-search-path-combo
-
- - name: Test search path not injected when LIQUIBASE_SEARCH_PATH with multiple paths
- if: matrix.os == 'ubuntu-latest'
- run: |
- echo "Testing multiple search paths in LIQUIBASE_SEARCH_PATH are preserved..."
-
- mkdir -p test-multi-search/path1
- mkdir -p test-multi-search/path2
-
- # Main changelog in path1
- cat > test-multi-search/path1/main.xml << 'EOF'
-
-
-
- SELECT 'Multiple paths preserved';
-
-
- EOF
-
- # Test with multiple search paths - this is the real-world scenario that broke (S3 + local paths)
- set +e
- RESULT=$(docker run --rm --env LIQUIBASE_LICENSE_KEY=${{ env.PRO_LICENSE_KEY }} \
- --env LIQUIBASE_SEARCH_PATH="/liquibase/path1,/liquibase/path2" \
- -v $(pwd)/test-multi-search/path1:/liquibase/path1 \
- -v $(pwd)/test-multi-search/path2:/liquibase/path2 \
- liquibase/liquibase:${{ github.sha }} \
- --changelogFile=main.xml \
- --url=offline:postgresql \
- validate 2>&1)
- EXIT_CODE=$?
- set -e
-
- echo "Multiple search paths test output: $RESULT"
-
- if echo "$RESULT" | grep -q "No validation errors found"; then
- echo "â
SUCCESS: Multiple search paths in LIQUIBASE_SEARCH_PATH are preserved"
- else
- echo "â FAIL: Multiple search paths were not properly handled"
- exit 1
- fi
-
- # Cleanup
- rm -rf test-multi-search
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-test.yml@main
+ with:
+ dockerfile_path: ${{ matrix.dockerfile }}
+ runs_on: ${{ matrix.os }}
+ build_logic_ref: main
+ secrets: inherit
diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml
index f811b388..a741a793 100644
--- a/.github/workflows/trivy.yml
+++ b/.github/workflows/trivy.yml
@@ -36,512 +36,63 @@ permissions:
jobs:
# ============================================
- # BUILD AND PUSH TO GHCR
+ # SCAN: community (Dockerfile)
# ============================================
- build:
- strategy:
- fail-fast: false
- matrix:
- image:
- [
- {
- dockerfile: Dockerfile,
- name: liquibase/liquibase,
- suffix: "",
- ghcr_name: community,
- },
- {
- dockerfile: Dockerfile.alpine,
- name: liquibase/liquibase,
- suffix: "-alpine",
- ghcr_name: alpine,
- },
- {
- dockerfile: DockerfileSecure,
- name: liquibase/liquibase-secure,
- suffix: "",
- ghcr_name: secure,
- },
- ]
- name: Build ${{ matrix.image.name }}${{ matrix.image.suffix }}
- runs-on: "ubuntu-22.04"
- steps:
- - name: Checkout code
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
-
- - name: Login to GHCR
- uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Build and push to GHCR
- uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
- with:
- context: .
- file: ${{ matrix.image.dockerfile }}
- push: true
- tags: ghcr.io/${{ github.repository }}/liquibase-${{ matrix.image.ghcr_name }}:${{ github.sha }}
- cache-from: type=gha
- cache-to: type=gha,mode=max
-
- # ============================================
- # VULNERABILITY SCAN (REUSABLE WORKFLOW)
- # ============================================
- vulnerability-scan:
- needs: build
- strategy:
- fail-fast: false
- matrix:
- image:
- [
- {
- dockerfile: Dockerfile,
- name: liquibase/liquibase,
- suffix: "",
- ghcr_name: community,
- },
- {
- dockerfile: Dockerfile.alpine,
- name: liquibase/liquibase,
- suffix: "-alpine",
- ghcr_name: alpine,
- },
- {
- dockerfile: DockerfileSecure,
- name: liquibase/liquibase-secure,
- suffix: "",
- ghcr_name: secure,
- },
- ]
- name: Scan ${{ matrix.image.name }}${{ matrix.image.suffix }}
- uses: liquibase/build-logic/.github/workflows/reusable-vulnerability-scan.yml@main
+ scan-community:
+ name: Scan liquibase/liquibase
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-scan.yml@main
with:
- mode: docker
- source: ghcr.io/${{ github.repository }}/liquibase-${{ matrix.image.ghcr_name }}:${{ github.sha }}
- image_name: ${{ matrix.image.name }}
- image_tag: ${{ matrix.image.suffix != '' && matrix.image.suffix || 'latest' }}
- fail_on_vulnerabilities: true
+ dockerfile_path: Dockerfile
+ image_name: liquibase/liquibase
+ suffix: ""
+ vex_enabled: false
upload_sarif: true
- sarif_category: ${{ matrix.image.name }}${{ matrix.image.suffix }}
- generate_sbom: true
- vex_enabled: ${{ matrix.image.name == 'liquibase/liquibase-secure' }}
+ dispatch_new_cves: true
+ scout_enabled: true
build_logic_ref: main
secrets: inherit
# ============================================
- # DISPATCH NEW CVEs FOR INVESTIGATION
+ # SCAN: alpine (Dockerfile.alpine)
# ============================================
- dispatch-new-cves:
- needs: [vulnerability-scan]
- if: always() && github.event_name != 'pull_request' && needs.vulnerability-scan.result != 'cancelled'
- continue-on-error: true
- strategy:
- fail-fast: false
- matrix:
- image:
- [
- {
- dockerfile: Dockerfile,
- name: liquibase/liquibase,
- suffix: "",
- ghcr_name: community,
- },
- {
- dockerfile: Dockerfile.alpine,
- name: liquibase/liquibase,
- suffix: "-alpine",
- ghcr_name: alpine,
- },
- {
- dockerfile: DockerfileSecure,
- name: liquibase/liquibase-secure,
- suffix: "",
- ghcr_name: secure,
- },
- ]
- name: Dispatch CVEs ${{ matrix.image.name }}${{ matrix.image.suffix }}
- runs-on: ubuntu-22.04
- permissions:
- actions: read
- contents: read
- id-token: write
- steps:
- - name: Checkout build-logic
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- repository: liquibase/build-logic
- path: build-logic
-
- - name: Resolve artifact name
- id: artifact-name
- env:
- IMAGE_NAME: ${{ matrix.image.name }}
- IMAGE_SUFFIX: ${{ matrix.image.suffix }}
- run: |
- # image_tag for display/dispatch (stripped of leading dash)
- IMAGE_TAG="${IMAGE_SUFFIX#-}"
- IMAGE_TAG="${IMAGE_TAG:-latest}"
- # Artifact name must match what the reusable workflow uploads.
- # The reusable workflow receives image_tag = suffix || 'latest' (raw, with leading dash for alpine),
- # so use the raw suffix for artifact lookup to avoid single-dash vs double-dash mismatch.
- RAW_IMAGE_TAG="${IMAGE_SUFFIX:-latest}"
- SAFE_NAME=$(echo "$IMAGE_NAME" | tr '/' '-')
- SAFE_TAG=$(echo "$RAW_IMAGE_TAG" | tr '/' '-')
- echo "artifact=vulnerability-report-${SAFE_NAME}-${SAFE_TAG}" >> "$GITHUB_OUTPUT"
- echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"
-
- - name: Download scan artifact
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
- with:
- name: ${{ steps.artifact-name.outputs.artifact }}
- path: scan-artifacts
- continue-on-error: true
-
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
- continue-on-error: true
-
- - name: Get secrets from vault
- id: vault-secrets
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
- continue-on-error: true
-
- - name: Get GitHub App token for liquibase-pro
- id: get-token
- uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2
- with:
- app-id: ${{ env.LIQUIBASE_GITHUB_APP_ID }}
- private-key: ${{ env.LIQUIBASE_GITHUB_APP_PRIVATE_KEY }}
- owner: liquibase
- repositories: liquibase-pro
- permission-actions: write
- permission-contents: read
- continue-on-error: true
-
- - name: Dispatch new CVEs for investigation
- continue-on-error: true
- env:
- GH_TOKEN: ${{ steps.get-token.outputs.token }}
- SCAN_DIR: ${{ github.workspace }}/scan-artifacts
- IMAGE_NAME: ${{ matrix.image.name }}
- IMAGE_TAG: ${{ steps.artifact-name.outputs.image_tag }}
- RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- run: |
- set +e # Never fail the parent workflow
-
- IMAGE_OR_VERSION="${IMAGE_NAME}:${IMAGE_TAG}"
- DISPATCH_LABEL="${IMAGE_NAME}:${IMAGE_TAG}"
-
- if [ -z "$GH_TOKEN" ]; then
- echo "::warning::CVE dispatch skipped -- GitHub App token unavailable (vault step may have failed)"
- echo "## CVE Investigation Dispatch (${DISPATCH_LABEL})" >> "$GITHUB_STEP_SUMMARY"
- echo "Skipped -- GitHub App token unavailable." >> "$GITHUB_STEP_SUMMARY"
- exit 0
- fi
-
- if [ ! -d "$SCAN_DIR" ] || [ -z "$(ls -A "$SCAN_DIR" 2>/dev/null)" ]; then
- echo "::warning::CVE dispatch skipped -- no scan artifacts found"
- echo "## CVE Investigation Dispatch (${DISPATCH_LABEL})" >> "$GITHUB_STEP_SUMMARY"
- echo "Skipped -- scan artifacts not available." >> "$GITHUB_STEP_SUMMARY"
- exit 0
- fi
-
- chmod +x build-logic/scripts/vulnerability-scanning/diff-new-cves.sh
-
- BATCHES=$(build-logic/scripts/vulnerability-scanning/diff-new-cves.sh \
- --scan-dir "$SCAN_DIR" \
- --source docker \
- --image-or-version "$IMAGE_OR_VERSION" \
- --run-url "$RUN_URL" \
- 2>/tmp/diff-err-$$.txt) || {
- ERR=$(cat /tmp/diff-err-$$.txt 2>/dev/null || echo "unknown error")
- echo "::warning::diff-new-cves.sh failed -- CVE dispatch skipped: $ERR"
- echo "## CVE Investigation Dispatch (${DISPATCH_LABEL})" >> "$GITHUB_STEP_SUMMARY"
- echo "Skipped -- diff-new-cves.sh exited with error: $ERR" >> "$GITHUB_STEP_SUMMARY"
- rm -f "/tmp/diff-err-$$.txt"
- exit 0
- }
- rm -f "/tmp/diff-err-$$.txt"
-
- if [ -z "$BATCHES" ]; then
- echo "No unassessed HIGH/CRITICAL CVEs found -- dispatch not needed."
- echo "## CVE Investigation Dispatch (${DISPATCH_LABEL})" >> "$GITHUB_STEP_SUMMARY"
- echo "No unassessed CVEs found -- nothing dispatched." >> "$GITHUB_STEP_SUMMARY"
- exit 0
- fi
-
- BATCH_NUM=0
- FAILED=0
-
- echo "## CVE Investigation Dispatch (${DISPATCH_LABEL})" >> "$GITHUB_STEP_SUMMARY"
- echo "" >> "$GITHUB_STEP_SUMMARY"
-
- while IFS= read -r batch_json; do
- [ -z "$batch_json" ] && continue
- BATCH_NUM=$((BATCH_NUM + 1))
- CVE_COUNT=$(echo "$batch_json" | jq '.cves | length' 2>/dev/null || echo "?")
-
- echo "Dispatching batch $BATCH_NUM ($CVE_COUNT CVEs) to investigate-cve workflow..."
-
- if gh workflow run investigate-cve.lock.yml \
- -R liquibase/liquibase-pro \
- -f "cve_json=$batch_json" 2>/tmp/dispatch-err-$$.txt; then
- echo "- Batch $BATCH_NUM: dispatched $CVE_COUNT CVEs" >> "$GITHUB_STEP_SUMMARY"
- echo " Batch $BATCH_NUM: OK ($CVE_COUNT CVEs)"
- else
- ERR=$(cat /tmp/dispatch-err-$$.txt 2>/dev/null || echo "unknown error")
- echo "::warning::CVE dispatch batch $BATCH_NUM failed: $ERR"
- echo "- Batch $BATCH_NUM: FAILED -- $ERR" >> "$GITHUB_STEP_SUMMARY"
- FAILED=$((FAILED + 1))
- fi
- rm -f "/tmp/dispatch-err-$$.txt"
- done <<< "$BATCHES"
-
- echo "" >> "$GITHUB_STEP_SUMMARY"
- echo "**Total batches dispatched**: $BATCH_NUM ($FAILED failed)" >> "$GITHUB_STEP_SUMMARY"
- echo "Dispatch complete: $BATCH_NUM batch(es), $FAILED failure(s)"
- exit 0 # Never fail the parent workflow
-
- # ============================================
- # CLEANUP GHCR IMAGES
- # ============================================
- cleanup-ghcr:
- needs: [vulnerability-scan]
- if: always()
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- image:
- [
- { ghcr_name: community },
- { ghcr_name: alpine },
- { ghcr_name: secure },
- ]
- name: Cleanup ${{ matrix.image.ghcr_name }}
- permissions:
- packages: write
- steps:
- - name: Delete GHCR image
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- REPO_NAME="${{ github.event.repository.name }}"
- PACKAGE_NAME="${REPO_NAME}/liquibase-${{ matrix.image.ghcr_name }}"
- ENCODED_PACKAGE_NAME="${REPO_NAME}%2Fliquibase-${{ matrix.image.ghcr_name }}"
- TAG="${{ github.sha }}"
- VERSION_ID=$(gh api \
- "/orgs/${{ github.repository_owner }}/packages/container/${ENCODED_PACKAGE_NAME}/versions" \
- --jq ".[] | select(.metadata.container.tags[] == \"${TAG}\") | .id" 2>/dev/null || echo "")
- if [ -n "$VERSION_ID" ]; then
- gh api --method DELETE \
- "/orgs/${{ github.repository_owner }}/packages/container/${ENCODED_PACKAGE_NAME}/versions/${VERSION_ID}"
- echo "Deleted ${PACKAGE_NAME}:${TAG}"
- else
- echo "No version found for ${PACKAGE_NAME}:${TAG}, skipping"
- fi
+ scan-alpine:
+ name: Scan liquibase/liquibase-alpine
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-scan.yml@main
+ with:
+ dockerfile_path: Dockerfile.alpine
+ image_name: liquibase/liquibase
+ suffix: "-alpine"
+ vex_enabled: false
+ upload_sarif: true
+ dispatch_new_cves: true
+ scout_enabled: true
+ build_logic_ref: main
+ secrets: inherit
# ============================================
- # SLACK NOTIFICATION ON FAILURE
+ # SCAN: secure (DockerfileSecure) â VEX enabled
# ============================================
- notify-failure:
- needs: [build, vulnerability-scan, cleanup-ghcr]
- if: failure()
- runs-on: "ubuntu-22.04"
- steps:
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
-
- - name: Get secrets from vault
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
-
- - name: Notify Slack on Build Failure
- uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
- env:
- SLACK_COLOR: "failure"
- SLACK_MESSAGE: "View details on GitHub Actions: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. Triggered by repository: ${{ github.repository }}"
- SLACK_TITLE: "â ${{ github.repository }} â Trivy failed on branch ${{ github.ref_name }} for commit ${{ github.sha }} in repository ${{ github.repository }}"
- SLACK_USERNAME: liquibot
- SLACK_WEBHOOK: ${{ env.DOCKER_SLACK_WEBHOOK_URL }}
- SLACK_ICON_EMOJI: ":whale:"
- SLACK_FOOTER: "${{ github.repository }}"
- SLACK_LINK_NAMES: true
-
- scout:
- strategy:
- fail-fast: false
- matrix:
- image:
- [
- { dockerfile: Dockerfile, name: liquibase/liquibase, suffix: "" },
- {
- dockerfile: Dockerfile.alpine,
- name: liquibase/liquibase,
- suffix: "-alpine",
- },
- {
- dockerfile: DockerfileSecure,
- name: liquibase/liquibase-secure,
- suffix: "",
- },
- ]
- name: Scout
- runs-on: "ubuntu-22.04"
- steps:
- - name: Checkout code
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
-
- - name: Build ${{ matrix.image.name }}${{ matrix.image.suffix }} from Dockerfile
- run: |
- docker build -f ${{ matrix.image.dockerfile }} -t ${{ matrix.image.name }}${{ matrix.image.suffix }}:${{ github.sha }} .
-
- - name: Configure AWS credentials for vault access
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
- with:
- role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
- aws-region: us-east-1
-
- - name: Get secrets from vault
- id: vault-secrets
- uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
- with:
- secret-ids: |
- ,/vault/liquibase
- parse-json-secrets: true
-
- - name: Decode DOCKERHUB_USERNAME
- run: |
- decoded_username=$(echo "${{ env.DOCKERHUB_USERNAME }}" | base64 -d)
- echo "DOCKERHUB_USERNAME_DECODED=$decoded_username" >> $GITHUB_ENV
-
- - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
- with:
- username: ${{ env.DOCKERHUB_USERNAME_DECODED }}
- password: ${{ env.DOCKERHUB_TOKEN }}
-
- - name: Download VEX files for Scout
- if: matrix.image.name == 'liquibase/liquibase-secure'
- run: |
- mkdir -p ./vex
- curl -sSfL "https://raw.githubusercontent.com/liquibase/vex-repo/main/pkg/maven/org.liquibase/liquibase-core/vex.openvex.json" \
- -o ./vex/liquibase-core.vex.json
- echo "VEX file downloaded: $(jq '.statements | length' ./vex/liquibase-core.vex.json) statements"
-
- - name: Docker Scout (with VEX)
- if: matrix.image.name == 'liquibase/liquibase-secure'
- uses: docker/scout-action@8910519cee8ac046f3ee99686b0dc6654d5ba1a7 # v1.20.3
- with:
- command: cves
- image: "${{ matrix.image.name }}${{ matrix.image.suffix }}:${{ github.sha }}"
- github-token: ${{ secrets.GITHUB_TOKEN }}
- write-comment: true
- sarif-file: "scout-results.sarif"
- summary: true
- exit-code: true
- only-severities: "critical,high"
- vex-location: ./vex
- only-vex-affected: true
- vex-author: "Liquibase Security Team"
-
- - name: Docker Scout (without VEX)
- if: matrix.image.name != 'liquibase/liquibase-secure'
- uses: docker/scout-action@8910519cee8ac046f3ee99686b0dc6654d5ba1a7 # v1.20.3
- with:
- command: cves
- image: "${{ matrix.image.name }}${{ matrix.image.suffix }}:${{ github.sha }}"
- github-token: ${{ secrets.GITHUB_TOKEN }}
- write-comment: true
- sarif-file: "scout-results.sarif"
- summary: true
- exit-code: true
- only-severities: "critical,high"
-
- - name: Docker Scout JSON output
- if: always()
- run: |
- SCOUT_ARGS=(
- --format json
- --output scout-results.json
- --only-severities critical,high
- )
- if [[ "${{ matrix.image.name }}" == "liquibase/liquibase-secure" ]]; then
- SCOUT_ARGS+=(--vex-location ./vex --only-vex-affected --vex-author "Liquibase Security Team")
- fi
- docker scout cves "${SCOUT_ARGS[@]}" \
- "${{ matrix.image.name }}${{ matrix.image.suffix }}:${{ github.sha }}" || true
-
- - name: Upload Scout JSON results
- if: always()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
- with:
- name: scout-results${{ matrix.image.dockerfile == 'DockerfileSecure' && '-secure' || matrix.image.suffix }}
- path: scout-results.json
- if-no-files-found: warn
-
- - name: Notify Slack on Build Failure
- if: failure()
- uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
- env:
- SLACK_COLOR: "failure"
- SLACK_MESSAGE: "View details on GitHub Actions: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. Triggered by repository: ${{ github.repository }} and job: ${{ github.job }}"
- SLACK_TITLE: "â ${{ github.repository }} â Docker Scout failed on branch ${{ github.ref_name }} for commit ${{ github.sha }} in repository ${{ github.repository }}"
- SLACK_USERNAME: liquibot
- SLACK_WEBHOOK: ${{ env.DOCKER_SLACK_WEBHOOK_URL }}
- SLACK_ICON_EMOJI: ":whale:"
- SLACK_FOOTER: "${{ github.repository }} - ${{ matrix.image.name }}${{ matrix.image.suffix }}:${{ github.sha }}"
- SLACK_LINK_NAMES: true
-
- - name: Upload Scout scan results to GitHub Security tab
- if: always()
- uses: github/codeql-action/upload-sarif@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v4.33.0
- with:
- sarif_file: "scout-results.sarif"
- category: "${{ matrix.image.name }}${{ matrix.image.suffix }}"
-
- - name: Generate Security Report
- if: always()
- uses: rsdmike/github-security-report-action@a149b24539044c92786ec39af8ba38c93496495d # v3.0.4
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- outputDir: ./reports/scout${{ matrix.image.dockerfile == 'DockerfileSecure' && '-secure' || matrix.image.suffix }}/
- sarifReportDir: .
-
- - name: Upload Security Report
- if: always()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
- with:
- name: security-report-scout${{ matrix.image.dockerfile == 'DockerfileSecure' && '-secure' || matrix.image.suffix }}
- path: ./reports/scout${{ matrix.image.dockerfile == 'DockerfileSecure' && '-secure' || matrix.image.suffix }}/summary.pdf
+ scan-secure:
+ name: Scan liquibase/liquibase-secure
+ uses: liquibase/build-logic/.github/workflows/reusable-docker-scan.yml@main
+ with:
+ dockerfile_path: DockerfileSecure
+ image_name: liquibase/liquibase-secure
+ suffix: ""
+ vex_enabled: true
+ upload_sarif: true
+ dispatch_new_cves: true
+ scout_enabled: true
+ build_logic_ref: main
+ secrets: inherit
# ============================================
# PERSIST SCAN RESULTS TO scan-results BRANCH
# ============================================
persist-results:
name: Persist Scan Results
- needs: [vulnerability-scan, scout]
- if: always() && github.event_name != 'pull_request' && needs.vulnerability-scan.result != 'cancelled'
+ needs: [scan-community, scan-alpine, scan-secure]
+ if: always() && github.event_name != 'pull_request' && needs.scan-community.result != 'cancelled'
runs-on: ubuntu-22.04
permissions:
actions: read
@@ -600,3 +151,36 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
scripts/persist-scan-results.sh scan-artifacts
+
+ # ============================================
+ # SLACK NOTIFICATION ON FAILURE
+ # ============================================
+ notify-failure:
+ needs: [scan-community, scan-alpine, scan-secure]
+ if: failure()
+ runs-on: "ubuntu-22.04"
+ steps:
+ - name: Configure AWS credentials for vault access
+ uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
+ with:
+ role-to-assume: ${{ secrets.LIQUIBASE_VAULT_OIDC_ROLE_ARN }}
+ aws-region: us-east-1
+
+ - name: Get secrets from vault
+ uses: aws-actions/aws-secretsmanager-get-secrets@a9a7eb4e2f2871d30dc5b892576fde60a2ecc802 # v2.0.10
+ with:
+ secret-ids: |
+ ,/vault/liquibase
+ parse-json-secrets: true
+
+ - name: Notify Slack on Build Failure
+ uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
+ env:
+ SLACK_COLOR: "failure"
+ SLACK_MESSAGE: "View details on GitHub Actions: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. Triggered by repository: ${{ github.repository }}"
+ SLACK_TITLE: "â ${{ github.repository }} â Trivy failed on branch ${{ github.ref_name }} for commit ${{ github.sha }} in repository ${{ github.repository }}"
+ SLACK_USERNAME: liquibot
+ SLACK_WEBHOOK: ${{ env.DOCKER_SLACK_WEBHOOK_URL }}
+ SLACK_ICON_EMOJI: ":whale:"
+ SLACK_FOOTER: "${{ github.repository }}"
+ SLACK_LINK_NAMES: true