---
name: Java Demo CI (unreleased plugin version)

on:
  push:
    branches:
    - main
    tags-ignore:
    - '*'
    paths:
    - build.gradle.kts
    - internal/**
    - plugin-gradle/**
    - demo/java/**
    - .github/workflows/demo-java-ci-unreleased.yml
  pull_request:
    types: [assigned, opened, synchronize, reopened]
    paths:
    - plugin-gradle/**
    - demo/java/**
    - .github/workflows/demo-java-ci-unreleased.yml

concurrency:
  # Documentation suggests ${{ github.head_ref }}, but that's only available on pull_request/pull_request_target triggers, so using ${{ github.ref }}.
  # On master, we want all builds to complete even if merging happens faster to make it easier to discover at which point something broke.
  group: ${{ github.ref == 'refs/heads/main' && format('demo-java-ci-un-main-{0}', github.sha) || format('demo-java-ci-un-{0}', github.ref) }}
  cancel-in-progress: true

env:
  GRADLE_OPTS: -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false

jobs:
  buildTest:
    name: Build Java Demos (unreleased plugin version)
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: demo/java
    strategy:
      fail-fast: false
      max-parallel: 1
      matrix:
        java_version: [8, 17]
        distribution: [zulu]
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: Install JDK ${{ matrix.distribution }} ${{ matrix.java_version }}
      uses: actions/setup-java@v4
      with:
        distribution: ${{ matrix.distribution }}
        java-version: ${{ matrix.java_version }}
        cache: gradle
    - name: Build project Java Demo (unreleased plugin version)
      run: ./gradlew build -S --scan  --warning-mode all -I ../../gradle/init.gradle.kts


Lint

name: Lint

on:
  pull_request:
    branches:
      - main
      - 'releases/**'
    types: [opened, synchronize, edited]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    name: Lint changed files
    runs-on: ubuntu-22.04
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - uses: world-federation-of-advertisers/actions/lint@v2
      with:
        license-type: apache
        copyright-holder: The Cross-Media Measurement Authors


Build and test

# Copyright 2021 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Build and test

on:
  push:
    branches:
      - main
      - 'releases/**'
  pull_request:
    branches:
      - main
      - 'releases/**'
    types: [opened, synchronize, edited]
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build-test:
    name: Build and test
    runs-on: ubuntu-22.04
    permissions:
      id-token: write
    env:
      CLUSTER_LOGS_PATH: cluster-logs
    steps:
      - name: Check out revision
        uses: actions/checkout@v4

      - uses: ./.github/actions/free-disk-space

      # Authenticate to Google Cloud for access to remote cache.
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ vars.BAZEL_BUILD_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ vars.BAZEL_BUILD_SERVICE_ACCOUNT }}

      - name: Write ~/.bazelrc
        run: |
          cat << EOF > ~/.bazelrc
          common --config=ci
          common --config=remote-cache
          build --remote_upload_local_results
          build --define container_registry=localhost:5001
          build --define image_repo_prefix=halo
          build --define image_tag=latest
          build --define kingdom_public_api_target=kingdom.example.com:8443
          build --define worker1_id=worker1
          build --define worker1_public_api_target=worker1.example.com:8443
          build --define worker2_id=worker2
          build --define worker2_public_api_target=worker2.example.com:8443
          build --define mc_name=measurementConsumers/foo
          build --define edp1_name=dataProviders/foo1
          build --define edp1_cert_name=dataProviders/foo1/certificates/bar1
          build --define edp2_name=dataProviders/foo2
          build --define edp2_cert_name=dataProviders/foo2/certificates/bar2
          build --define edp3_name=dataProviders/foo3
          build --define edp3_cert_name=dataProviders/foo3/certificates/bar3
          build --define edp4_name=dataProviders/foo4
          build --define edp4_cert_name=dataProviders/foo4/certificates/bar4
          build --define edp5_name=dataProviders/foo5
          build --define edp5_cert_name=dataProviders/foo5/certificates/bar5
          build --define edp6_name=dataProviders/foo6
          build --define edp6_cert_name=dataProviders/foo6/certificates/bar6
          build --define google_cloud_project=example-project
          build --define bigquery_dataset=example-dataset
          build --define bigquery_table=events
          EOF

      - name: Get Bazel cache params
        id: get-cache-params
        run: |
          cache_path="$(bazelisk info output_base)"
          echo "cache-path=${cache_path}" >> $GITHUB_OUTPUT

          repo_cache_path="$(bazelisk info repository_cache)"
          echo "repo-cache-path=${repo_cache_path}" >> $GITHUB_OUTPUT

      # Hack to work around disk space consumed by bazel-contrib/rules_oci#439.
      # The alternative is to use a runner with more disk space.
      - name: Mount scratch disk
        uses: ./.github/actions/mount-scratch-disk
        with:
          mount-path: ${{ steps.get-cache-params.outputs.cache-path }}

      - name: Restore repository cache
        uses: actions/cache/restore@v4
        with:
          path: ${{ steps.get-cache-params.outputs.repo-cache-path }}
          key: ${{ vars.BAZEL_REPO_CACHE_KEY }}

      - name: Build
        run: >
          bazelisk build --worker_quit_after_build
          //...
          //src/main/k8s/dev:synthetic_generator_edp_simulators

      - name: Save repository cache
        uses: actions/cache/save@v4
        continue-on-error: true
        with:
          path: ${{ steps.get-cache-params.outputs.repo-cache-path }}
          key: ${{ vars.BAZEL_REPO_CACHE_KEY }}

      - name: Run tests
        id: run-tests
        run: bazelisk test //...

      - name: Create cluster
        id: create-cluster
        run: |
          echo "KUBECONFIG=$HOME/.kube/config" >> "$GITHUB_ENV"
          curl -LOJ https://kind.sigs.k8s.io/examples/kind-with-registry.sh
          chmod +x kind-with-registry.sh
          ./kind-with-registry.sh

      - name: Run correctness test
        id: run-correctness-test
        run: >
          bazelisk test
          //src/test/kotlin/org/wfanet/measurement/integration/k8s:EmptyClusterCorrectnessTest
          --test_output=streamed

      - name: Delete Kubernetes Resources
        continue-on-error: true
        run: |
          kubectl delete all --namespace=default --all --cascade=foreground --wait
          kubectl delete networkpolicies --namespace=default --all

      - name: Run panelmatch correctness test
        id: run-panelmatch-correctness-test
        run: >
          bazelisk test
          //src/test/kotlin/org/wfanet/panelmatch/integration/k8s:EmptyClusterPanelMatchCorrectnessTest
          --test_output=streamed

      - name: Export cluster logs
        id: export-cluster-logs
        if: failure() && (steps.run-correctness-test.outcome == 'failure' || steps.run-panelmatch-correctness-test.outcome == 'failure')
        continue-on-error: true
        run: |
          mkdir -p "$CLUSTER_LOGS_PATH"
          kind export logs "$CLUSTER_LOGS_PATH"

      - name: Upload cluster logs artifact
        if: failure() && steps.export-cluster-logs.outcome == 'success'
        uses: actions/upload-artifact@v4
        continue-on-error: true
        with:
          name: cluster-logs
          path: ${{ env.CLUSTER_LOGS_PATH }}

      - name: Upload Bazel testlogs
        continue-on-error: true
        uses: world-federation-of-advertisers/actions/bazel-upload-testlogs@v2
        if: failure() && (steps.run-tests.outcome == 'failure' || steps.run-correctness-test.outcome == 'failure' || steps.run-panelmatch-correctness-test.outcome == 'failure')


Configure Kingdom

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: "Configure Kingdom"

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
          - dev
          - qa
          - head
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false

permissions:
  id-token: write

env:
  KUSTOMIZATION_PATH: "k8s/cmms"

jobs:
  update-kingdom:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4

      # Authenticate to Google Cloud. This will export some environment
      # variables, including GCLOUD_PROJECT.
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ vars.GKE_CONFIG_SERVICE_ACCOUNT }}

      - name: Write ~/.bazelrc
        env:
          IMAGE_TAG: ${{ inputs.image-tag }}
          SPANNER_INSTANCE: ${{ vars.SPANNER_INSTANCE }}
        run: |
          cat << EOF > ~/.bazelrc
          common --config=ci
          common --config=ghcr
          build --define "image_tag=$IMAGE_TAG"
          build --define "google_cloud_project=$GCLOUD_PROJECT"
          build --define "spanner_instance=$SPANNER_INSTANCE"
          build --define kingdom_public_api_address_name=kingdom-v2alpha
          build --define kingdom_system_api_address_name=kingdom-system-v1alpha
          EOF

      - name: Export BAZEL_BIN
        run: echo "BAZEL_BIN=$(bazelisk info bazel-bin)" >> $GITHUB_ENV

      - name: Get GKE cluster credentials
        uses: google-github-actions/get-gke-credentials@v2
        with:
          cluster_name: kingdom
          location: ${{ vars.GCLOUD_ZONE }}

      - name: Configure metrics
        uses: ./.github/actions/configure-metrics
        if: ${{ inputs.apply }}

      - name: Generate archives
        run: >
          bazelisk build
          //src/main/k8s/dev:kingdom.tar
          //src/main/k8s/testing/secretfiles:archive

      - name: Make Kustomization dir
        run: mkdir -p "$KUSTOMIZATION_PATH"
      - name: Extract Kustomization archive
        run: >
          tar -xf "$BAZEL_BIN/src/main/k8s/dev/kingdom.tar"
          -C "$KUSTOMIZATION_PATH"

      - name: Extract secret files archive
        run: >
          tar -xf "$BAZEL_BIN/src/main/k8s/testing/secretfiles/archive.tar"
          -C "$KUSTOMIZATION_PATH/src/main/k8s/dev/kingdom_secret"

      # Write map from configuration variable. Since it appears that GitHub
      # configuration variables use DOS (CRLF) line endings, we convert these to
      # Unix (LF) line endings.
      - name: Write AKID to principal map
        env:
          AKID_TO_PRINCIPAL_MAP: ${{ vars.AKID_TO_PRINCIPAL_MAP }}
        run: >
          echo "$AKID_TO_PRINCIPAL_MAP" | sed $'s/\r$//'  >
          "$KUSTOMIZATION_PATH/src/main/k8s/dev/config_files/authority_key_identifier_to_principal_map.textproto"

      # Run kubectl diff, treating the command as succeeded even if the exit
      # code is 1 as kubectl uses this code to indicate there's a diff.
      - name: kubectl diff
        id: kubectl-diff
        run: kubectl diff -k "$KUSTOMIZATION_PATH/src/main/k8s/dev/kingdom" || (( $? == 1 ))

      - name: kubectl apply
        if: ${{ inputs.apply }}
        run: kubectl apply -k "$KUSTOMIZATION_PATH/src/main/k8s/dev/kingdom"

      - name: Wait for rollout
        if: ${{ inputs.apply }}
        run: |
          for deployment in $(kubectl get deployments -o name); do
            kubectl rollout status "$deployment" --timeout=5m
          done


Configure Duchy

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: "Configure Duchy"

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      duchy-name:
        description: "Name (external ID) of Duchy"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      duchy-name:
        description: "Name (external ID) of Duchy"
        type: choice
        options:
        - worker1
        - aggregator
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false

permissions:
  id-token: write

env:
  KUSTOMIZATION_PATH: "k8s/cmms"
  DUCHY_NAME: ${{ inputs.duchy-name }}

jobs:
  update-duchy:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    steps:
    - uses: actions/checkout@v4

    # Authenticate to Google Cloud. This will export some environment
    # variables, including GCLOUD_PROJECT.
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.GKE_CONFIG_SERVICE_ACCOUNT }}

    - name: Export DUCHY_CERT_ID
      env:
        AGGREGATOR_DUCHY_CERT_ID: ${{ vars.AGGREGATOR_DUCHY_CERT_ID }}
        WORKER1_DUCHY_CERT_ID: ${{ vars.WORKER1_DUCHY_CERT_ID }}
        WORKER2_DUCHY_CERT_ID: ${{ vars.WORKER2_DUCHY_CERT_ID }}
      run: ./.github/workflows/export-duchy-cert-id.sh

    - name: Write ~/.bazelrc
      env:
        IMAGE_TAG: ${{ inputs.image-tag }}
        KINGDOM_SYSTEM_API_TARGET: ${{ vars.KINGDOM_SYSTEM_API_TARGET }}
        KINGDOM_PUBLIC_API_TARGET: ${{ vars.KINGDOM_PUBLIC_API_TARGET }}
        AGGREGATOR_SYSTEM_API_TARGET: ${{ vars.AGGREGATOR_SYSTEM_API_TARGET }}
        WORKER1_SYSTEM_API_TARGET: ${{ vars.WORKER1_SYSTEM_API_TARGET }}
        WORKER2_SYSTEM_API_TARGET: ${{ vars.WORKER2_SYSTEM_API_TARGET }}
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=ghcr
        build --define image_tag=$IMAGE_TAG
        build --define google_cloud_project=$GCLOUD_PROJECT
        build --define kingdom_system_api_target=$KINGDOM_SYSTEM_API_TARGET
        build --define kingdom_public_api_target=$KINGDOM_PUBLIC_API_TARGET
        build --define aggregator_system_api_target=$AGGREGATOR_SYSTEM_API_TARGET
        build --define worker1_system_api_target=$WORKER1_SYSTEM_API_TARGET
        build --define worker2_system_api_target=$WORKER2_SYSTEM_API_TARGET
        build --define duchy_cert_id=$DUCHY_CERT_ID
        build --define duchy_public_api_address_name=${DUCHY_NAME}-duchy-v2alpha
        build --define duchy_system_api_address_name=${DUCHY_NAME}-duchy-system-v1alpha
        EOF

    - name: Export BAZEL_BIN
      run: echo "BAZEL_BIN=$(bazelisk info bazel-bin)" >> $GITHUB_ENV

    - name: Get GKE cluster credentials
      uses: google-github-actions/get-gke-credentials@v2
      with:
        cluster_name: ${{ format('{0}-duchy', inputs.duchy-name) }}
        location: ${{ vars.GCLOUD_ZONE }}

    - name: Configure metrics
      uses: ./.github/actions/configure-metrics
      if: ${{ inputs.apply }}

    - name: Generate archives
      env:
        IMAGE_TAG: ${{ inputs.image-tag }}
        SPANNER_INSTANCE: ${{ vars.SPANNER_INSTANCE }}
        KINGDOM_SYSTEM_API_TARGET: ${{ vars.KINGDOM_SYSTEM_API_TARGET }}
        STORAGE_BUCKET: ${{ vars.STORAGE_BUCKET }}
      run: >
        bazelisk build
        "//src/main/k8s/dev:${DUCHY_NAME}_duchy.tar"
        //src/main/k8s/testing/secretfiles:archive
        --config ghcr
        --define "image_tag=$IMAGE_TAG"
        --define "google_cloud_project=$GCLOUD_PROJECT"
        --define "spanner_instance=$SPANNER_INSTANCE"
        --define "kingdom_system_api_target=$KINGDOM_SYSTEM_API_TARGET"
        --define "duchy_storage_bucket=$STORAGE_BUCKET"
        --define "duchy_cert_id=$DUCHY_CERT_ID"

    - name: Make Kustomization dir
      run: mkdir -p "$KUSTOMIZATION_PATH"

    - name: Extract Kustomization archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/dev/${DUCHY_NAME}_duchy.tar"
        -C "$KUSTOMIZATION_PATH"

    - name: Extract secret files archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/testing/secretfiles/archive.tar"
        -C "$KUSTOMIZATION_PATH/src/main/k8s/dev/${DUCHY_NAME}_duchy_secret"

    # Write map from configuration variable. Since it appears that GitHub
    # configuration variables use DOS (CRLF) line endings, we convert these to
    # Unix (LF) line endings.
    - name: Write AKID to principal map
      env:
        AKID_TO_PRINCIPAL_MAP: ${{ vars.AKID_TO_PRINCIPAL_MAP }}
      run: >
        echo "$AKID_TO_PRINCIPAL_MAP" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/config_files/authority_key_identifier_to_principal_map.textproto"

    - name: Export KUSTOMIZE_PATH
      run: echo "KUSTOMIZE_PATH=$KUSTOMIZATION_PATH/src/main/k8s/dev/${DUCHY_NAME}_duchy" >> $GITHUB_ENV

    # Run kubectl diff, treating the command as succeeded even if the exit
    # code is 1 as kubectl uses this code to indicate there's a diff.
    - name: kubectl diff
      id: kubectl-diff
      run: kubectl diff -k "$KUSTOMIZE_PATH" || (( $? == 1 ))

    - name: kubectl apply
      if: ${{ inputs.apply }}
      run: kubectl apply -k "$KUSTOMIZE_PATH"

    - name: Wait for rollout
      if: ${{ inputs.apply }}
      run: |
        for deployment in $(kubectl get deployments -o name); do
          kubectl rollout status "$deployment" --timeout=5m
        done


Update CMMS

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Update CMMS

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      run-tests:
        description: "Run tests"
        type: boolean
        default: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head
      run-tests:
        description: "Run tests"
        type: boolean
        default: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false
      existing-image-tag:
        description: "Tag of existing published images (skips push)"
        type: string

jobs:
  publish-images:
    uses: ./.github/workflows/maybe-push-images.yml
    with:
      existing-image-tag: ${{ inputs.existing-image-tag }}

  scan-images:
    uses: ./.github/workflows/scan-images.yml
    needs: [publish-images]
    if: inputs.existing-image-tag == ''
    with:
      container-registry: ghcr.io
      image-repo-prefix: ${{ github.repository_owner }}
      image-tag: ${{ needs.publish-images.outputs.image-tag }}

  terraform:
    uses: ./.github/workflows/terraform-cmms.yml
    secrets: inherit
    with:
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  update-kingdom:
    uses: ./.github/workflows/configure-kingdom.yml
    needs: [publish-images, terraform]
    with:
      image-tag: ${{ needs.publish-images.outputs.image-tag }}
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  update-aggregator-duchy:
    uses: ./.github/workflows/configure-duchy.yml
    needs: [publish-images, terraform]
    with:
      duchy-name: aggregator
      image-tag: ${{ needs.publish-images.outputs.image-tag }}
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  update-worker1-duchy:
    uses: ./.github/workflows/configure-duchy.yml
    needs: [publish-images, terraform]
    with:
      duchy-name: worker1
      image-tag: ${{ needs.publish-images.outputs.image-tag }}
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  update-worker2-duchy:
    uses: ./.github/workflows/configure-aws-duchy.yml
    needs: [publish-images, terraform]
    with:
      duchy-name: worker2
      image-tag: ${{ needs.publish-images.outputs.image-tag }}
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  update-simulators:
    uses: ./.github/workflows/configure-simulators.yml
    needs: [publish-images, terraform]
    with:
      image-tag: ${{ needs.publish-images.outputs.image-tag }}
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  # Update the Reporting system.
  #
  # This isn't technically part of the CMMS, but we do it here for simplicity.
  update-reporting:
    uses: ./.github/workflows/configure-reporting.yml
    secrets: inherit
    needs: [publish-images, terraform]
    with:
      image-tag: ${{ needs.publish-images.outputs.image-tag }}
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  update-reporting-v2:
    uses: ./.github/workflows/configure-reporting-v2.yml
    secrets: inherit
    needs: [ publish-images, terraform ]
    with:
      image-tag: ${{ needs.publish-images.outputs.image-tag }}
      environment: ${{ inputs.environment }}
      apply: ${{ inputs.apply }}

  run-tests:
    if: inputs.run-tests
    needs: [
      update-kingdom,
      update-aggregator-duchy,
      update-worker1-duchy,
      update-worker2-duchy,
      update-simulators,
    ]
    uses: ./.github/workflows/run-k8s-tests.yml
    secrets: inherit
    with:
      environment: ${{ inputs.environment }}


Configure Reporting

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: "Configure Reporting"

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false

permissions:
  id-token: write

env:
  KUSTOMIZATION_PATH: "k8s/reporting"

jobs:
  update-reporting:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    steps:
    - uses: actions/checkout@v4

    # Authenticate to Google Cloud. This will export some environment
    # variables, including GCLOUD_PROJECT.
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.GKE_CONFIG_SERVICE_ACCOUNT }}

    - name: Write ~/.bazelrc
      env:
        IMAGE_TAG: ${{ inputs.image-tag }}
        POSTGRES_INSTANCE: ${{ vars.POSTGRES_INSTANCE }}
        GCLOUD_REGION: ${{ vars.GCLOUD_REGION }}
        KINGDOM_PUBLIC_API_TARGET: ${{ vars.KINGDOM_PUBLIC_API_TARGET }}
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=ghcr
        build --define "image_tag=$IMAGE_TAG"
        build --define "google_cloud_project=$GCLOUD_PROJECT"
        build --define "postgres_instance=$POSTGRES_INSTANCE"
        build --define "postgres_region=$GCLOUD_REGION"
        build --define "kingdom_public_api_target=$KINGDOM_PUBLIC_API_TARGET"
        build --define reporting_public_api_address_name=reporting-v1alpha
        EOF

    - name: Export BAZEL_BIN
      run: echo "BAZEL_BIN=$(bazelisk info bazel-bin)" >> $GITHUB_ENV

    - name: Get GKE cluster credentials
      uses: google-github-actions/get-gke-credentials@v2
      with:
        cluster_name: reporting
        location: ${{ vars.GCLOUD_ZONE }}

    - name: Configure metrics
      uses: ./.github/actions/configure-metrics
      if: ${{ inputs.apply }}

    - name: Generate archives
      run: >
        bazelisk build
        //src/main/k8s/dev:reporting.tar
        //src/main/k8s/testing/secretfiles:archive

    - name: Make Kustomization dir
      run: mkdir -p "$KUSTOMIZATION_PATH"

    - name: Extract Kustomization archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/dev/reporting.tar"
        -C "$KUSTOMIZATION_PATH"

    - name: Extract secret files archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/testing/secretfiles/archive.tar"
        -C "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_secrets"

    # Write files from configuration variables. Since it appears that GitHub
    # configuration variables use DOS (CRLF) line endings, we convert these to
    # Unix (LF) line endings.

    - name: Write AKID to principal map
      env:
        AKID_TO_PRINCIPAL_MAP: ${{ vars.AKID_TO_PRINCIPAL_MAP }}
      run: >
        echo "$AKID_TO_PRINCIPAL_MAP" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_config_files/authority_key_identifier_to_principal_map.textproto"

    - name: Write encryption key-pair config
      env:
        ENCRYPTION_KEY_PAIR_CONFIG: ${{ vars.ENCRYPTION_KEY_PAIR_CONFIG }}
      run: >
        echo "$ENCRYPTION_KEY_PAIR_CONFIG" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_config_files/encryption_key_pair_config.textproto"

    - name: Copy measurement spec config
      run: >
        cp src/main/k8s/testing/secretfiles/measurement_spec_config.textproto
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_config_files/"

    - name: Write measurement consumer config
      env:
        MEASUREMENT_CONSUMER_CONFIG: ${{ secrets.MEASUREMENT_CONSUMER_CONFIG }}
      run: >
        echo "$MEASUREMENT_CONSUMER_CONFIG" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_secrets/measurement_consumer_config.textproto"

    - name: Copy secret generator
      run: >
        cp src/main/k8s/testing/secretfiles/reporting_secrets_kustomization.yaml
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_secrets/kustomization.yaml"

    - name: Export KUSTOMIZE_PATH
      run: echo "KUSTOMIZE_PATH=$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting" >> $GITHUB_ENV

    # Run kubectl diff, treating the command as succeeded even if the exit
    # code is 1 as kubectl uses this code to indicate there's a diff.
    - name: kubectl diff
      id: kubectl-diff
      run: kubectl diff -k "$KUSTOMIZE_PATH" || (( $? == 1 ))

    - name: kubectl apply
      if: ${{ inputs.apply }}
      run: kubectl apply -k "$KUSTOMIZE_PATH"

    - name: Wait for rollout
      if: ${{ inputs.apply }}
      run: |
        for deployment in $(kubectl get deployments -o name); do
          kubectl rollout status "$deployment" --timeout=5m
        done


Configure EDP simulators

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: "Configure EDP simulators"

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false

permissions:
  id-token: write

env:
  KUSTOMIZATION_PATH: "k8s/cmms"

jobs:
  update-simulators:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    steps:
    - uses: actions/checkout@v4

    # Authenticate to Google Cloud. This will export some environment
    # variables, including GCLOUD_PROJECT.
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.GKE_CONFIG_SERVICE_ACCOUNT }}

    - name: Write ~/.bazelrc
      env:
        IMAGE_TAG: ${{ inputs.image-tag }}
        KINGDOM_PUBLIC_API_TARGET: ${{ vars.KINGDOM_PUBLIC_API_TARGET }}
        WORKER1_PUBLIC_API_TARGET: ${{ vars.WORKER1_PUBLIC_API_TARGET }}
        WORKER2_PUBLIC_API_TARGET: ${{ vars.WORKER2_PUBLIC_API_TARGET }}
        MC_NAME: ${{ vars.MC_NAME }}
        EDP1_NAME: ${{ vars.EDP1_NAME }}
        EDP1_CERT_NAME: ${{ vars.EDP1_CERT_NAME }}
        EDP2_NAME: ${{ vars.EDP2_NAME }}
        EDP2_CERT_NAME: ${{ vars.EDP2_CERT_NAME }}
        EDP3_NAME: ${{ vars.EDP3_NAME }}
        EDP3_CERT_NAME: ${{ vars.EDP3_CERT_NAME }}
        EDP4_NAME: ${{ vars.EDP4_NAME }}
        EDP4_CERT_NAME: ${{ vars.EDP4_CERT_NAME }}
        EDP5_NAME: ${{ vars.EDP5_NAME }}
        EDP5_CERT_NAME: ${{ vars.EDP5_CERT_NAME }}
        EDP6_NAME: ${{ vars.EDP6_NAME }}
        EDP6_CERT_NAME: ${{ vars.EDP6_CERT_NAME }}
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=ghcr
        build --define "image_tag=$IMAGE_TAG"
        build --define "google_cloud_project=$GCLOUD_PROJECT"
        build --define "kingdom_public_api_target=$KINGDOM_PUBLIC_API_TARGET"
        build --define "worker1_id=worker1"
        build --define "worker1_public_api_target=$WORKER1_PUBLIC_API_TARGET"
        build --define "worker2_id=worker2"
        build --define "worker2_public_api_target=$WORKER2_PUBLIC_API_TARGET"
        build --define "mc_name=$MC_NAME"
        build --define "edp1_name=$EDP1_NAME"
        build --define "edp1_cert_name=$EDP1_CERT_NAME"
        build --define "edp2_name=$EDP2_NAME"
        build --define "edp2_cert_name=$EDP2_CERT_NAME"
        build --define "edp3_name=$EDP3_NAME"
        build --define "edp3_cert_name=$EDP3_CERT_NAME"
        build --define "edp4_name=$EDP4_NAME"
        build --define "edp4_cert_name=$EDP4_CERT_NAME"
        build --define "edp5_name=$EDP5_NAME"
        build --define "edp5_cert_name=$EDP5_CERT_NAME"
        build --define "edp6_name=$EDP6_NAME"
        build --define "edp6_cert_name=$EDP6_CERT_NAME"
        EOF

    - name: Export BAZEL_BIN
      run: echo "BAZEL_BIN=$(bazelisk info bazel-bin)" >> $GITHUB_ENV

    - name: Get GKE cluster credentials
      uses: google-github-actions/get-gke-credentials@v2
      with:
        cluster_name: simulators
        location: ${{ vars.GCLOUD_ZONE }}

    - name: Configure metrics
      uses: ./.github/actions/configure-metrics
      if: ${{ inputs.apply }}

    - name: Generate archives
      run: >
        bazelisk build
        //src/main/k8s/dev:synthetic_generator_edp_simulators.tar
        //src/main/k8s/testing/secretfiles:kustomization.tar

    - name: Make Kustomization dir
      run: mkdir -p "$KUSTOMIZATION_PATH"
    - name: Extract Kustomization archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/dev/synthetic_generator_edp_simulators.tar"
        -C "$KUSTOMIZATION_PATH"

    - name: Extract secret files archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/testing/secretfiles/kustomization.tar"
        -C "$KUSTOMIZATION_PATH"

    - name: Export KUSTOMIZE_PATH
      run: echo "KUSTOMIZE_PATH=$KUSTOMIZATION_PATH/src/main/k8s/dev/synthetic_generator_edp_simulators" >> $GITHUB_ENV

    # Run kubectl diff, treating the command as succeeded even if the exit
    # code is 1 as kubectl uses this code to indicate there's a diff.
    - name: kubectl diff
      id: kubectl-diff
      run: kubectl diff -k "$KUSTOMIZE_PATH" || (( $? == 1 ))

    - name: kubectl apply
      if: ${{ inputs.apply }}
      run: kubectl apply -k "$KUSTOMIZE_PATH"

    - name: Wait for rollout
      if: ${{ inputs.apply }}
      run: |
        for deployment in $(kubectl get deployments -o name); do
          kubectl rollout status "$deployment" --timeout=5m
        done


Terraform CMMS

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Terraform CMMS

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false

permissions:
  id-token: write

jobs:
  terraform:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    env:
      GCLOUD_MODULE_PATH: src/main/terraform/gcloud/cmms
      GCLOUD_REGION: ${{ vars.GCLOUD_REGION }}
      GCLOUD_ZONE: ${{ vars.GCLOUD_ZONE }}
      AWS_MODULE_PATH: src/main/terraform/aws/cmms

    steps:
    - uses: actions/checkout@v4

    # Authenticate to Google Cloud. This will export some environment
    # variables, including GCLOUD_PROJECT.
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.TF_SERVICE_ACCOUNT }}

    - name: terraform init - gcloud
      env:
        TF_STORAGE_BUCKET: ${{ vars.TF_STORAGE_BUCKET }}
      working-directory: ${{ env.GCLOUD_MODULE_PATH }}
      run: >
        terraform init
        -input=false
        -lockfile=readonly
        -backend-config="bucket=$TF_STORAGE_BUCKET"

    - name: terraform plan - gcloud
      env:
        KEY_RING: ${{ vars.KEY_RING }}
        SPANNER_INSTANCE: ${{ vars.SPANNER_INSTANCE }}
        STORAGE_BUCKET: ${{ vars.STORAGE_BUCKET }}
        POSTGRES_INSTANCE: ${{ vars.POSTGRES_INSTANCE }}
        POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
      working-directory: ${{ env.GCLOUD_MODULE_PATH }}
      run: >
        terraform plan
        -input=false
        -var="key_ring_name=$KEY_RING"
        -var="spanner_instance_name=$SPANNER_INSTANCE"
        -var="storage_bucket_name=$STORAGE_BUCKET"
        -var="postgres_instance_name=$POSTGRES_INSTANCE"
        -var="postgres_password=$POSTGRES_PASSWORD"
        -out=tfplan

    - name: terraform apply - gcloud
      if: ${{ inputs.apply }}
      working-directory: ${{ env.GCLOUD_MODULE_PATH }}
      run: terraform apply -input=false tfplan

    # Authenticate to AWS Cloud. This will export some environment
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-region: ${{ vars.AWS_REGION }}
        role-to-assume: ${{ vars.AWS_GHA_ROLE }}
        role-session-name: GitHubAction

    - name: terraform init - aws
      env:
        TF_STORAGE_BUCKET: ${{ vars.AWS_TF_STORAGE_BUCKET }}
      working-directory: ${{ env.AWS_MODULE_PATH }}
      run: >
        terraform init
        -input=false
        -lockfile=readonly
        -backend-config="bucket=$TF_STORAGE_BUCKET"
        -backend-config="region=$AWS_REGION"

    - name: terraform plan - aws
      env:
        POSTGRES_INSTANCE: ${{ vars.POSTGRES_INSTANCE }}
        AWS_S3_BUCKET: ${{ vars.AWS_S3_BUCKET }}
        ENVIRONMENT: ${{ inputs.environment }}
      working-directory: ${{ env.AWS_MODULE_PATH }}
      run: >
        terraform plan
        -input=false
        -var="aws_region=$AWS_REGION"
        -var="postgres_instance_name=$POSTGRES_INSTANCE"
        -var="aws_project_env=halo-cmm-$ENVIRONMENT"
        -var="aws_s3_bucket=$AWS_S3_BUCKET"
        -out=tfplan

    - name: terraform apply - aws
      if: ${{ inputs.apply }}
      working-directory: ${{ env.AWS_MODULE_PATH }}
      run: terraform apply -input=false tfplan


Run Kubernetes tests

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Run Kubernetes tests

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head

permissions:
  id-token: write

jobs:
  run-tests:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    steps:
    - uses: actions/checkout@v4

    # Authenticate to Google Cloud for access to remote cache.
    - name: Authenticate to Google Cloud for remote cache
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.BAZEL_BUILD_WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.BAZEL_BUILD_SERVICE_ACCOUNT }}

    - name: Write ~/.bazelrc
      env:
        KINGDOM_PUBLIC_API_TARGET: ${{ vars.KINGDOM_PUBLIC_API_TARGET }}
        MC_NAME: ${{ vars.MC_NAME }}
        MC_API_KEY: ${{ secrets.MC_API_KEY }}
        GCLOUD_PROJECT: ${{ vars.GCLOUD_PROJECT }}
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=remote-cache
        build --define kingdom_public_api_target=$KINGDOM_PUBLIC_API_TARGET
        build --define mc_name=$MC_NAME
        build --define mc_api_key=$MC_API_KEY
        build --define google_cloud_project=$GCLOUD_PROJECT
        test --test_output=streamed
        test --test_timeout=3600
        EOF

    - name: Get Bazel cache params
      id: get-cache-params
      run: |
        repo_cache_path="$(bazelisk info repository_cache)"
        echo "repo-cache-path=${repo_cache_path}" >> $GITHUB_OUTPUT

    - name: Restore repository cache
      uses: actions/cache/restore@v4
      with:
        path: ${{ steps.get-cache-params.outputs.repo-cache-path }}
        key: ${{ vars.BAZEL_REPO_CACHE_KEY }}

    - name: Run tests
      id: run-tests
      run: >
        bazelisk test
        //src/test/kotlin/org/wfanet/measurement/integration/k8s:SyntheticGeneratorCorrectnessTest

    - name: Upload Bazel testlogs
      continue-on-error: true
      uses: world-federation-of-advertisers/actions/bazel-upload-testlogs@v2
      if: failure() && steps.run-tests.outcome == 'failure'


API lint

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: API lint

on:
  pull_request:
    branches:
      - main
      - 'releases/**'
    types: [opened, synchronize, edited]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    name: API lint
    runs-on: ubuntu-22.04
    permissions:
      id-token: write
    defaults:
      run:
        shell: bash
    steps:
    - uses: actions/checkout@v4

    - uses: world-federation-of-advertisers/actions/setup-api-linter@v2
      with:
        version: 1.52.0
        sha256: 89ec88aa07974e4476f1d5a1bb626b05a65cdc76b64c7e73e15ebb77ebdb9aaf

    # Authenticate to Google Cloud for access to remote cache.
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.BAZEL_BUILD_WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.BAZEL_BUILD_SERVICE_ACCOUNT }}

    - name: Write ~/.bazelrc
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=remote-cache
        EOF

    - env:
        BAZEL: bazelisk
      run: tools/api-lint wfa/measurement/system


Build and push container images

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Build and push container images

on:
  workflow_call:
    inputs:
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
jobs:
  push-images:
    runs-on: ubuntu-22.04
    permissions:
      id-token: write
      packages: write
    env:
      CONTAINER_REGISTRY: ghcr.io
    steps:
      - uses: actions/checkout@v4

      - uses: ./.github/actions/free-disk-space

      # Authenticate to Google Cloud for access to remote cache.
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ vars.BAZEL_BUILD_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ vars.BAZEL_BUILD_SERVICE_ACCOUNT }}

      - name: Write ~/.bazelrc
        env:
          IMAGE_TAG: ${{ inputs.image-tag }}
        run: |
          cat << EOF > ~/.bazelrc
          common --config=ci
          common --config=remote-cache
          build --define container_registry=$CONTAINER_REGISTRY
          build --define image_repo_prefix=$GITHUB_REPOSITORY_OWNER
          build --define image_tag=$IMAGE_TAG
          EOF

      - name: Get Bazel cache params
        id: get-cache-params
        run: |
          repo_cache_path="$(bazelisk info repository_cache)"
          echo "repo-cache-path=${repo_cache_path}" >> $GITHUB_OUTPUT

          cache_path="$(bazelisk info output_base)"
          echo "cache-path=${cache_path}" >> $GITHUB_OUTPUT

      # Hack to work around disk space consumed by bazel-contrib/rules_oci#439.
      # The alternative is to use a runner with more disk space.
      - name: Mount scratch disk
        uses: ./.github/actions/mount-scratch-disk
        with:
          mount-path: ${{ steps.get-cache-params.outputs.cache-path }}

      - name: Restore repository cache
        uses: actions/cache/restore@v4
        with:
          path: ${{ steps.get-cache-params.outputs.repo-cache-path }}
          key: ${{ vars.BAZEL_REPO_CACHE_KEY }}

      - name: Authenticate to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.CONTAINER_REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ github.token }}

      - name: Build images
        run: >
          bazelisk build
          //src/main/docker:push_all_gke_images
          //src/main/docker/panel_exchange_client:push_all_images
          //src/main/docker:push_all_eks_images

      - name: Push images
        run: |
          bazelisk run //src/main/docker:push_all_gke_images
          bazelisk run //src/main/docker/panel_exchange_client:push_all_images
          bazelisk run //src/main/docker:push_all_eks_images


Configure AWS Duchy

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: "Configure AWS Duchy"

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      duchy-name:
        description: "Name (external ID) of Duchy"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      duchy-name:
        description: "Name (external ID) of Duchy"
        type: choice
        options:
          - worker2
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false

permissions:
  id-token: write

env:
  KUSTOMIZATION_PATH: "k8s/cmms"
  DUCHY_NAME: ${{ inputs.duchy-name }}

jobs:
  update-duchy:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    steps:
    - uses: actions/checkout@v4

    # Authenticate to AWS Cloud. This will export some environment
    # e.g. AWS_REGION, AWS_SESSION_TOKEN ...
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-region: ${{ vars.AWS_REGION }}
        role-to-assume: ${{ vars.AWS_GHA_ROLE }}
        role-session-name: GitHubAction

    - name: Export DUCHY_CERT_ID
      env:
        AGGREGATOR_DUCHY_CERT_ID: ${{ vars.AGGREGATOR_DUCHY_CERT_ID }}
        WORKER1_DUCHY_CERT_ID: ${{ vars.WORKER1_DUCHY_CERT_ID }}
        WORKER2_DUCHY_CERT_ID: ${{ vars.WORKER2_DUCHY_CERT_ID }}
      run: ./.github/workflows/export-duchy-cert-id.sh

    - name: Write ~/.bazelrc
      env:
        IMAGE_TAG: ${{ inputs.image-tag }}
        POSTGRES_HOST: ${{ vars.AWS_POSTGRES_HOST }}
        POSTGRES_CRED_SECRET_NAME: ${{ vars.AWS_POSTGRES_CRED_SECRET_NAME }}
        KINGDOM_SYSTEM_API_TARGET: ${{ vars.KINGDOM_SYSTEM_API_TARGET }}
        KINGDOM_PUBLIC_API_TARGET: ${{ vars.KINGDOM_PUBLIC_API_TARGET }}
        AGGREGATOR_SYSTEM_API_TARGET: ${{ vars.AGGREGATOR_SYSTEM_API_TARGET }}
        WORKER1_SYSTEM_API_TARGET: ${{ vars.WORKER1_SYSTEM_API_TARGET }}
        WORKER2_SYSTEM_API_TARGET: ${{ vars.WORKER2_SYSTEM_API_TARGET }}
        DUCHY_PUBLIC_API_EIP_ALLOCS: ${{ vars.AWS_DUCHY_PUBLIC_API_EIP_ALLOCS }}
        DUCHY_SYSTEM_API_EIP_ALLOCS: ${{ vars.AWS_DUCHY_SYSTEM_API_EIP_ALLOCS }}
        S3_BUCKET: ${{ vars.AWS_S3_BUCKET }}
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=ghcr
        build --define image_tag=$IMAGE_TAG
        build --define kingdom_system_api_target=$KINGDOM_SYSTEM_API_TARGET
        build --define kingdom_public_api_target=$KINGDOM_PUBLIC_API_TARGET
        build --define aggregator_system_api_target=$AGGREGATOR_SYSTEM_API_TARGET
        build --define worker1_system_api_target=$WORKER1_SYSTEM_API_TARGET
        build --define worker2_system_api_target=$WORKER2_SYSTEM_API_TARGET
        build --define s3_bucket=$S3_BUCKET
        build --define s3_region=$AWS_REGION
        build --define duchy_cert_id=$DUCHY_CERT_ID
        build --define postgres_host=$POSTGRES_HOST
        build --define postgres_port=5432
        build --define postgres_region=$AWS_REGION
        build --define postgres_credential_secret_name=$POSTGRES_CRED_SECRET_NAME
        build --define duchy_public_api_eip_allocs=$DUCHY_PUBLIC_API_EIP_ALLOCS
        build --define duchy_system_api_eip_allocs=$DUCHY_SYSTEM_API_EIP_ALLOCS
        EOF

    - name: Get EKS cluster credentials
      run: aws eks update-kubeconfig --region $AWS_REGION --name ${DUCHY_NAME}-duchy

    - name: Export BAZEL_BIN
      run: echo "BAZEL_BIN=$(bazelisk info bazel-bin)" >> $GITHUB_ENV

    - name: Configure metrics
      uses: ./.github/actions/configure-metrics-aws
      if: ${{ inputs.apply }}
      with:
        amp-ingest-endpoint: ${{ vars.AMP_INGEST_ENDPOINT }}
        amp-region: ${{ vars.AWS_REGION }}

    - name: Generate archives
      run: >
        bazelisk build
        "//src/main/k8s/dev:${DUCHY_NAME}_duchy_aws.tar"
        //src/main/k8s/testing/secretfiles:archive

    - name: Make Kustomization dir
      run: mkdir -p "$KUSTOMIZATION_PATH"

    - name: Extract Kustomization archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/dev/${DUCHY_NAME}_duchy_aws.tar"
        -C "$KUSTOMIZATION_PATH"

    - name: Extract secret files archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/testing/secretfiles/archive.tar"
        -C "$KUSTOMIZATION_PATH/src/main/k8s/dev/${DUCHY_NAME}_duchy_secret"

    # Write map from configuration variable. Since it appears that GitHub
    # configuration variables use DOS (CRLF) line endings, we convert these to
    # Unix (LF) line endings.
    - name: Write AKID to principal map
      env:
        AKID_TO_PRINCIPAL_MAP: ${{ vars.AKID_TO_PRINCIPAL_MAP }}
      run: >
        echo "$AKID_TO_PRINCIPAL_MAP" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/config_files/authority_key_identifier_to_principal_map.textproto"

    - name: Export KUSTOMIZE_PATH
      run: echo "KUSTOMIZE_PATH=$KUSTOMIZATION_PATH/src/main/k8s/dev/${DUCHY_NAME}_duchy_aws" >> $GITHUB_ENV

    # Run kubectl diff, treating the command as succeeded even if the exit
    # code is 1 as kubectl uses this code to indicate there's a diff.
    - name: kubectl diff
      id: kubectl-diff
      run: kubectl diff -k "$KUSTOMIZE_PATH" || (( $? == 1 ))

    - name: kubectl apply
      if: ${{ inputs.apply }}
      run: kubectl apply -k "$KUSTOMIZE_PATH"

    - name: Wait for rollout
      if: ${{ inputs.apply }}
      run: |
        for deployment in $(kubectl get deployments -o name); do
          kubectl rollout status "$deployment" --timeout=5m
        done


Configure Reporting V2

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: "Configure Reporting V2"

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        required: true
  workflow_dispatch:
    inputs:
      environment:
        description: "GitHub-managed environment"
        required: true
        type: choice
        options:
        - dev
        - qa
        - head
      image-tag:
        description: "Tag of container images"
        type: string
        required: true
      apply:
        description: "Apply the new configuration"
        type: boolean
        default: false

permissions:
  id-token: write

env:
  KUSTOMIZATION_PATH: "k8s/reporting_v2"

jobs:
  update-reporting-v2:
    runs-on: ubuntu-22.04
    environment: ${{ inputs.environment }}
    steps:
    - uses: actions/checkout@v4

    # Authenticate to Google Cloud. This will export some environment
    # variables, including GCLOUD_PROJECT.
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.GKE_CONFIG_SERVICE_ACCOUNT }}

    - name: Write ~/.bazelrc
      env:
        IMAGE_TAG: ${{ inputs.image-tag }}
        POSTGRES_INSTANCE: ${{ vars.POSTGRES_INSTANCE }}
        GCLOUD_REGION: ${{ vars.GCLOUD_REGION }}
        KINGDOM_PUBLIC_API_TARGET: ${{ vars.KINGDOM_PUBLIC_API_TARGET }}
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=ghcr
        build --define "image_tag=$IMAGE_TAG"
        build --define "google_cloud_project=$GCLOUD_PROJECT"
        build --define "postgres_instance=$POSTGRES_INSTANCE"
        build --define "postgres_region=$GCLOUD_REGION"
        build --define "kingdom_public_api_target=$KINGDOM_PUBLIC_API_TARGET"
        build --define reporting_public_api_address_name=reporting-v2alpha
        EOF

    - name: Export BAZEL_BIN
      run: echo "BAZEL_BIN=$(bazelisk info bazel-bin)" >> $GITHUB_ENV

    - name: Get GKE cluster credentials
      uses: google-github-actions/get-gke-credentials@v2
      with:
        cluster_name: reporting-v2
        location: ${{ vars.GCLOUD_ZONE }}

    - name: Configure metrics
      uses: ./.github/actions/configure-metrics
      if: ${{ inputs.apply }}

    - name: Generate archives
      run: >
        bazelisk build
        //src/main/k8s/dev:reporting_v2.tar
        //src/main/k8s/testing/secretfiles:archive

    - name: Make Kustomization dir
      run: mkdir -p "$KUSTOMIZATION_PATH"

    - name: Extract Kustomization archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/dev/reporting_v2.tar"
        -C "$KUSTOMIZATION_PATH"

    - name: Extract secret files archive
      run: >
        tar -xf "$BAZEL_BIN/src/main/k8s/testing/secretfiles/archive.tar"
        -C "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_v2_secrets"

    # Write files from configuration variables. Since it appears that GitHub
    # configuration variables use DOS (CRLF) line endings, we convert these to
    # Unix (LF) line endings.

    - name: Write AKID to principal map
      env:
        AKID_TO_PRINCIPAL_MAP: ${{ vars.AKID_TO_PRINCIPAL_MAP }}
      run: >
        echo "$AKID_TO_PRINCIPAL_MAP" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_v2_config_files/authority_key_identifier_to_principal_map.textproto"

    - name: Write encryption key-pair config
      env:
        ENCRYPTION_KEY_PAIR_CONFIG: ${{ vars.ENCRYPTION_KEY_PAIR_CONFIG }}
      run: >
        echo "$ENCRYPTION_KEY_PAIR_CONFIG" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_v2_config_files/encryption_key_pair_config.textproto"

    - name: Copy metric spec config
      run: >
        cp src/main/k8s/testing/secretfiles/metric_spec_config.textproto
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_v2_config_files/"

    - name: Write measurement consumer config
      env:
        MEASUREMENT_CONSUMER_CONFIG: ${{ secrets.MEASUREMENT_CONSUMER_CONFIG }}
      run: >
        echo "$MEASUREMENT_CONSUMER_CONFIG" | sed $'s/\r$//'  >
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_v2_secrets/measurement_consumer_config.textproto"

    - name: Copy secret generator
      run: >
        cp src/main/k8s/testing/secretfiles/reporting_secrets_kustomization.yaml
        "$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_v2_secrets/kustomization.yaml"

    - name: Export KUSTOMIZE_PATH
      run: echo "KUSTOMIZE_PATH=$KUSTOMIZATION_PATH/src/main/k8s/dev/reporting_v2" >> $GITHUB_ENV

    # Run kubectl diff, treating the command as succeeded even if the exit
    # code is 1 as kubectl uses this code to indicate there's a diff.
    - name: kubectl diff
      id: kubectl-diff
      run: kubectl diff -k "$KUSTOMIZE_PATH" || (( $? == 1 ))

    - name: kubectl apply
      if: ${{ inputs.apply }}
      run: kubectl apply -k "$KUSTOMIZE_PATH"

    - name: Wait for rollout
      if: ${{ inputs.apply }}
      run: |
        for deployment in $(kubectl get deployments -o name); do
          kubectl rollout status "$deployment" --timeout=5m
        done


Release test

# Copyright 2024 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Release test

on:
  release:
    types: [published]
  workflow_dispatch:

jobs:
  build-test:
    name: Build and test
    runs-on: ubuntu-22.04
    permissions:
      id-token: write
    env:
      CLUSTER_LOGS_PATH: cluster-logs
    steps:
    - name: Check out revision
      uses: actions/checkout@v4

    - uses: ./.github/actions/free-disk-space

    # Authenticate to Google Cloud for access to remote cache.
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        workload_identity_provider: ${{ vars.BAZEL_BUILD_WORKLOAD_IDENTITY_PROVIDER }}
        service_account: ${{ vars.BAZEL_BUILD_SERVICE_ACCOUNT }}

    - name: Write ~/.bazelrc
      run: |
        cat << EOF > ~/.bazelrc
        common --config=ci
        common --config=remote-cache
        EOF

    - name: Get Bazel cache params
      id: get-cache-params
      run: |
        repo_cache_path="$(bazelisk info repository_cache)"
        echo "repo-cache-path=${repo_cache_path}" >> $GITHUB_OUTPUT

    - name: Restore repository cache
      uses: actions/cache/restore@v4
      with:
        path: ${{ steps.get-cache-params.outputs.repo-cache-path }}
        key: ${{ vars.BAZEL_REPO_CACHE_KEY }}

    - name: Run tests
      id: run-tests
      run: >
        bazelisk test --test_output=streamed
        //src/test/kotlin/org/wfanet/measurement/integration/deploy/gcloud:GCloudSpannerInProcessReachMeasurementAccuracyTest

    - name: Upload Bazel testlogs
      continue-on-error: true
      uses: world-federation-of-advertisers/actions/bazel-upload-testlogs@v2
      if: failure() && (steps.run-tests.outcome == 'failure')


Build and push reporting v2 container images


Deploy on release

# Copyright 2023 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Deploy on release

on:
  release:
    types: [published]

jobs:
  update-cmms:
    uses: ./.github/workflows/update-cmms.yml
    secrets: inherit
    with:
      environment: qa
      apply: true


Scan container images

# Copyright 2024 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Scan container images

on:
  workflow_call:
    inputs:
      container-registry:
        description: Container registry to pull images from
        type: string
        required: true
      image-repo-prefix:
        description: Common prefix of the image repository
        type: string
        required: true
      image-tag:
        description: Tag of container images
        type: string
        required: true
  workflow_dispatch:
    inputs:
      container-registry:
        description: Container registry to pull images from
        type: string
        required: true
      image-repo-prefix:
        description: Common prefix of the image repository
        type: string
        required: true
      image-tag:
        description: Tag of container images
        type: string
        required: true

jobs:
  scan-images:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        # List of images from https://github.com/orgs/world-federation-of-advertisers/packages?repo_name=cross-media-measurement
        # This list must be manually maintained as there is no known way to
        # populate it from the GitHub API or the build system.
        image:
          - duchy/computations-cleaner
          - kingdom/completed-measurements-deletion
          - duchy/spanner-update-schema
          - kingdom/spanner-update-schema
          - duchy/requisition-fulfillment
          - duchy/computation-control
          - kingdom/data-server
          - kingdom/v2alpha-public-api
          - duchy/liquid-legions-v2-mill
          - duchy/async-computation-control
          - kingdom/system-api
          - duchy/herald
          - duchy/spanner-computations
          - reporting/postgres-data-server
          - kingdom/exchanges-deletion
          - reporting/v1alpha-public-api
          - reporting/postgres-update-schema
          - reporting/v2/v2alpha-public-api
          - reporting/v2/postgres-update-schema
          - panel-exchange/gcloud-example-daemon
          - panel-exchange/aws-example-daemon
          - simulator/synthetic-generator-edp
          - simulator/bigquery-edp
          - reporting/v2/postgres-internal-server
          - duchy/postgres-update-schema
          - duchy/gcloud-postgres-update-schema
          - duchy/postgres-internal-server
          - duchy/aws-liquid-legions-v2-mill
          - duchy/aws-postgres-update-schema
          - duchy/aws-computation-control
          - duchy/aws-requisition-fulfillment
          - duchy/aws-postgres-internal-server
          - panel-exchange/forwarded-storage-daemon
    steps:
      - name: Get SARIF file path
        id: get-sarif-file-path
        env:
          IMAGE: ${{ matrix.image }}
        run:
          echo "path=${IMAGE//\//_}.sarif" >> $GITHUB_OUTPUT
      - name: Run Trivy image scanner
        uses: aquasecurity/trivy-action@0.19.0
        with:
          image-ref: ${{ inputs.container-registry }}/${{ inputs.image-repo-prefix }}/${{ matrix.image }}:${{ inputs.image-tag }}
          format: sarif
          output: ${{ steps.get-sarif-file-path.outputs.path }}
      - name: Upload scan results to GitHub
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: ${{ steps.get-sarif-file-path.outputs.path }}
          category: trivy-${{ matrix.image }}


# Copyright 2024 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Conditionally build and push container images

on:
  workflow_dispatch:
  workflow_call:
    inputs:
      existing-image-tag:
        description: "Tag of existing published container images"
        type: string
    outputs:
      image-tag:
        description: "Tag of container images"
        value: ${{ jobs.get-image-tag.outputs.image-tag }}

jobs:
  get-image-tag:
    runs-on: ubuntu-22.04
    outputs:
      image-tag: ${{ steps.get-image-tag.outputs.image-tag }}
    steps:
      - id: get-image-tag
        name: Get image tag
        env:
          EXISTING_IMAGE_TAG: ${{ inputs.existing-image-tag }}
        run: |
          declare image_tag
          if [[ -n "$EXISTING_IMAGE_TAG" ]]; then
            image_tag="$EXISTING_IMAGE_TAG"
          elif [[ "$GITHUB_REF_TYPE" == 'tag' ]]; then
            image_tag="${GITHUB_REF_NAME#v}"
            image_tag="${image_tag//\//-}"
          else
            image_tag="$GITHUB_SHA"
          fi
          echo "image-tag=${image_tag}" >> "$GITHUB_OUTPUT"
  push-images:
    uses: ./.github/workflows/push-images.yml
    if: inputs.existing-image-tag == ''
    needs: [get-image-tag]
    with:
      image-tag: ${{ needs.get-image-tag.outputs.image-tag }}


Deploy nightly build

# Copyright 2024 The Cross-Media Measurement Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Deploy nightly build

on:
  schedule:
    - cron: '19 6 * * 1,2,3,4,5'  # Sun-Thu night in America/Los_Angeles.

jobs:
  deploy-nightly:
    runs-on: ubuntu-22.04
    steps:
    - id: get-tag
      run: |
        date_string="$(TZ='America/Los_Angeles' date +'%Y%m%d')"
        echo "tag=nightly/${date_string}.$GITHUB_RUN_ATTEMPT" >> "$GITHUB_OUTPUT"
    - name: create-tag
      uses: actions/github-script@v7
      env:
        TAG: ${{ steps.get-tag.outputs.tag }}
      with:
        script: |
          const tag = process.env['TAG'];
          github.rest.git.createRef({
            owner: context.repo.owner,
            repo: context.repo.repo,
            ref: `refs/tags/${tag}`,
            sha: context.sha,
          });
    # Use workflow dispatch via API so that the workflow gets the right ref.
    - name: create-deploy-dispatch
      uses: actions/github-script@v7
      env:
        TAG: ${{ steps.get-tag.outputs.tag }}
      with:
        script: |
          const tag = process.env['TAG'];
          github.rest.actions.createWorkflowDispatch({
            owner: context.repo.owner,
            repo: context.repo.repo,
            workflow_id: 'update-cmms.yml',
            ref: tag,
            inputs: {
              environment: 'head',
              apply: true,
            },
          });


Testing Workflow

name: Testing Workflow

on:
  pull_request:
    branches:
      - develop

jobs:
  testing:
    name: Lint Check and Testing

    runs-on: ubuntu-latest

    steps:
      - name: Chekout Repo
        uses: actions/checkout@v2
        with:
          submodules: true

      - name: Validate Gradle Wrapper
        uses: gradle/wrapper-validation-action@v1

      - name: Set up JDK 17.0.8
        uses: actions/setup-java@v1
        with:
          java-version: 17.0.8

      - name: Run Android Linter
        run: ./gradlew :sphinx:application:sphinx:lintDebug --warning-mode=all

      - name: Run Unit Tests
        run: ./gradlew :sphinx:application:sphinx:testDebugUnitTest

      - name: Assemble Debug APK
        run: ./gradlew :sphinx:application:sphinx:assembleDebug

      - name: Assemble Debug Test APK
        run: ./gradlew :sphinx:application:sphinx:assembleDebugAndroidTest


benchmarks

name: benchmarks

on:
#  pull_request:
#     branches: [ '*' ]
  schedule:
    - cron: '0 3 * * *'
  workflow_dispatch:

env:
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

jobs:
  benchmarks:
    runs-on: macos-14
    if: github.repository == 'apollographql/apollo-kotlin'
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17
      - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2
        with:
          gradle-home-cache-cleanup: true
      - run: |
          # Build the benchmark apks
          ./gradlew -p benchmark :app:assembleRelease :microbenchmark:assembleIncubatingReleaseAndroidTest :microbenchmark:assembleStableReleaseAndroidTest
      # Step can be removed if/when gcloud is added to the macos image - See https://github.com/actions/virtual-environments/issues/4639
      - name: Setup gcloud
        uses: google-github-actions/setup-gcloud@98ddc00a17442e89a24bbf282954a3b65ce6d200 #v2.1.0
      - run: ./scripts/run-benchmarks.main.kts
        env:
          GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
          DD_API_KEY: ${{ secrets.DD_API_KEY }}
          GITHUB_TOKEN: ${{ github.token }}
      - name: nativebenchmarks
        run: |
          # Run the native benchmarks
          ./gradlew -p tests :native-benchmarks:allTests
          # Upload the results to DataDog
          ./scripts/upload-native-benchmarks.main.kts
        env:
          DD_API_KEY: ${{ secrets.DD_API_KEY }}


defer-router-tests

name: defer-router-tests

on:
  schedule:
    - cron: '0 3 * * *'
env:
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

jobs:
  defer-with-router-tests:
    runs-on: ubuntu-latest
    if: github.repository == 'apollographql/apollo-kotlin'
    steps:
      - name: Checkout project
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7

      - name: Install and run subgraph
        working-directory: tests/defer/router/subgraphs/computers
        run: |
          npm install
          APOLLO_PORT=4001 npm start &

      - name: Install router
        run: |
          curl -sSL https://router.apollo.dev/download/nix/latest | sh

      - name: Run router
        run: |
          ./router --supergraph tests/defer/router/simple-supergraph.graphqls &

      - name: Setup Java
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2

      - name: Run Apollo Kotlin @defer tests
        env:
          DEFER_WITH_ROUTER_TESTS: true
        run: |
          ./gradlew --no-daemon --console plain -p tests :defer:allTests


Deploy docs to production

# This triggers a release of all the apollo docs
# The deploy preview for a single docset are handled in netlify.toml
name: Deploy docs to production

on:
  push:
    branches:
      - main
      - release-3.x
      - release-2.x
    paths:
      - docs/**

jobs:
  publish:
    uses: apollographql/docs/.github/workflows/publish.yml@main # pga-ignore
    if: github.repository == 'apollographql/apollo-kotlin'
    secrets:
      NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
      NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}


Issue Close User Survey

name: Issue Close User Survey

on:
  issues:
    types: [closed]

jobs:
  user-survey-comment:
    permissions:
      issues: write
    runs-on: ubuntu-latest
    if: github.repository == 'apollographql/apollo-kotlin'
    steps:
      - run: |
          if [ "$STATE_REASON" != "completed" ]; then
            echo "Issue was closed as not planned, skipping user survey."
          elif (( $COMMENTS == 0 )); then
            echo "Issue with no comments, skipping user survey."
          else
            gh issue comment "$NUMBER" --body "$BODY"
          fi
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_REPO: ${{ github.repository }}
          NUMBER: ${{ github.event.issue.number }}
          STATE_REASON: ${{ github.event.issue.state_reason }}
          COMMENTS: ${{ github.event.issue.comments }}
          AUTHOR: ${{ github.event.issue.author_association }}
          BODY: >
            Do you have any feedback for the maintainers? Please tell us by taking a [one-minute survey](https://docs.google.com/forms/d/e/1FAIpQLSczNDXfJne3ZUOXjk9Ursm9JYvhTh1_nFTDfdq3XBAFWCzplQ/viewform?usp=pp_url&entry.1170701325=Apollo+Kotlin&entry.204965213=GitHub+Issue). Your responses will help us understand Apollo Kotlin usage and allow us to serve you better.


platform-api-tests

name: platform-api-tests

on:
  #  pull_request:
  #    branches: [ '*' ]
  schedule:
    - cron: '0 3 * * *'
  workflow_dispatch:

env:
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

jobs:
  platform-api-tests:
    runs-on: macos-14
    if: github.repository == 'apollographql/apollo-kotlin'
    steps:
      - name: Checkout project
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7

      - name: Setup Java
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2

      - name: Run tests against the Apollo Platform API
        env:
          PLATFORM_API_TESTS_KEY: ${{ secrets.PLATFORM_API_TESTS_KEY }}
        run: |
          ./gradlew --no-daemon -p tests :platform-api:platformApiTests


pr

name: pr

on:
  pull_request:
    paths-ignore:
      - 'docs/**'
      - '*.md'
env:
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

# Cancel any current or previous job from the same PR
concurrency:
  group: ${{ github.head_ref }}
  cancel-in-progress: true

jobs:
  tests-gradle:
    if: "!startsWith(github.head_ref, 'release-')"
    runs-on: macos-14
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17
      - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2
        with:
          gradle-home-cache-cleanup: true
      - name: Build with Gradle
        run: |
          ./gradlew ciTestsGradle
      - name: Collect Diagnostics
        if: always()
        run: ./scripts/collect-diagnostics.main.kts
      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
        if: always()
        with:
          name: tests-gradle.zip
          path: diagnostics.zip

  tests-no-gradle:
    if: "!startsWith(github.head_ref, 'release-')"
    runs-on: macos-14
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17
      - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2
        with:
          gradle-home-cache-cleanup: true
      - name: Build with Gradle
        run: |
          ./gradlew ciTestsNoGradle --stacktrace
      - name: Collect Diagnostics
        if: always()
        run: ./scripts/collect-diagnostics.main.kts
      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
        if: always()
        with:
          name: tests-no-gradle.zip
          path: diagnostics.zip

  tests-integration:
    if: "!startsWith(github.head_ref, 'release-')"
    runs-on: macos-14
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17
      - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2
        with:
          gradle-home-cache-cleanup: true
      - name: Build with Gradle
        run: |
          ./gradlew -p tests ciBuild
      - name: Collect Diagnostics
        if: always()
        run: ./scripts/collect-diagnostics.main.kts
      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
        if: always()
        with:
          name: tests-integration.zip
          path: diagnostics.zip

  intellij-plugin:
    if: "!startsWith(github.head_ref, 'release-')"
    name: Build IntelliJ Plugin
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.properties.outputs.version }}
    steps:
      # Free GitHub Actions Environment Disk Space
      - name: Maximize Build Space
        run: |
          sudo rm -rf /usr/share/dotnet
          sudo rm -rf /usr/local/lib/android
          sudo rm -rf /opt/ghc

      # Check out current repository
      - name: Fetch Sources
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7

      # Setup Java 11 environment for the next steps
      - name: Setup Java
        uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17

      # Set environment variables
      - name: Export Properties
        id: properties
        shell: bash
        run: |
          PROPERTIES="$(./gradlew :intellij-plugin:properties --console=plain -q)"
          VERSION="$(echo "$PROPERTIES" | grep "^version:" | cut -f2- -d ' ')"
          NAME="$(echo "$PROPERTIES" | grep "^pluginName:" | cut -f2- -d ' ')"

          echo "::set-output name=version::$VERSION"
          echo "::set-output name=pluginVerifierHomeDir::~/.pluginVerifier"

          ./gradlew :intellij-plugin:listProductsReleases # prepare list of IDEs for Plugin Verifier

      # Trigger code generation in test project, as the plugin's tests depend on it
      - name: Test project codegen
        run: ./gradlew -p tests :intellij-plugin-test-project:generateApolloSources

      # Run tests
      - name: Run Tests
        run: ./gradlew :intellij-plugin:check

      # Collect Tests Result of failed tests
      - name: Collect Tests Result
        if: ${{ failure() }}
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
        with:
          name: tests-result
          path: ${{ github.workspace }}/intellij-plugin/build/reports/tests

      # Cache Plugin Verifier IDEs
      - name: Setup Plugin Verifier IDEs Cache
        uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
        with:
          path: ${{ steps.properties.outputs.pluginVerifierHomeDir }}/ides
          key: plugin-verifier-${{ hashFiles('intellij-plugin/build/listProductsReleases.txt') }}

      # Run Verify Plugin task and IntelliJ Plugin Verifier tool
      - name: Run Plugin Verification tasks
        run: ./gradlew :intellij-plugin:runPluginVerifier -Pplugin.verifier.home.dir=${{ steps.properties.outputs.pluginVerifierHomeDir }}

      # Collect Plugin Verifier Result
      - name: Collect Plugin Verifier Result
        if: ${{ always() }}
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
        with:
          name: pluginVerifier-result
          path: ${{ github.workspace }}/intellij-plugin/build/reports/pluginVerifier

      # Prepare plugin archive content for creating artifact
      - name: Prepare Plugin Artifact
        id: artifact
        shell: bash
        run: |
          cd ${{ github.workspace }}/intellij-plugin/build/distributions
          FILENAME=`ls *.zip`
          unzip "$FILENAME" -d content

          echo "::set-output name=filename::${FILENAME:0:-4}"

      # Store already-built plugin as an artifact for downloading
      - name: Upload artifact
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
        with:
          name: ${{ steps.artifact.outputs.filename }}
          path: ./intellij-plugin/build/distributions/content/*/*


Publish IntelliJ plugin snapshot

name: Publish IntelliJ plugin snapshot

on:
  schedule:
    - cron: '0 0 * * 0'
  workflow_dispatch:
env:
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

jobs:
  publish-intellij-plugin:
    name: Publish IntelliJ plugin
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7

      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17

      - name: Publish snapshot to Jetbrains snapshots
        run: ./gradlew --no-build-cache :intellij-plugin:publishPlugin
        env:
          IJ_PLUGIN_SNAPSHOT: true
          PUBLISH_TOKEN: ${{ secrets.IJ_PLUGIN_PUBLISH_TOKEN }}
          CERTIFICATE_CHAIN: ${{ secrets.IJ_PLUGIN_CERTIFICATE_CHAIN }}
          PRIVATE_KEY: ${{ secrets.IJ_PLUGIN_PRIVATE_KEY }}
          PRIVATE_KEY_PASSWORD: ${{ secrets.IJ_PLUGIN_PRIVATE_KEY_PASSWORD }}


push

name: push

on:
  push:
    branches: [ main ]
    paths-ignore:
      - 'docs/**'
      - '*.md'
env:
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

jobs:
  deploy:
    runs-on: macos-14
    if: github.repository == 'apollographql/apollo-kotlin'
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17
      - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2
        with:
          gradle-home-cache-cleanup: true
      - name: Build with Gradle
        #--no-configuration-cache for https://youtrack.jetbrains.com/issue/KT-65879
        run: |
          ./gradlew -p tests ciBuild -i
          ./gradlew :apollo-kdoc:dokkatooGeneratePublicationHtml --no-build-cache
          ./gradlew ciPublishSnapshot --no-configuration-cache
        env:
          SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
          SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
          COM_APOLLOGRAPHQL_PROFILE_ID: ${{ secrets.COM_APOLLOGRAPHQL_PROFILE_ID }}
      - name: Collect Diagnostics
        if: always()
        run: ./scripts/collect-diagnostics.main.kts
      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
        if: always()
        with:
          name: push.zip
          path: diagnostics.zip
      - name: Deploy Kdoc to github pages
        uses: JamesIves/github-pages-deploy-action@5c6e9e9f3672ce8fd37b9856193d2a537941e66c #v4.6.1
        with:
          branch: gh-pages # The branch the action should deploy to.
          folder: libraries/apollo-kdoc/build/dokka/html # The folder the action should deploy.


tag

name: tag

on:
  push:
    tags:
      - '*'
env:
  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

jobs:
  publish-libraries:
    name: Publish libraries
    runs-on: macos-14
    if: github.repository == 'apollographql/apollo-kotlin'
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17
      - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2
        with:
          gradle-home-cache-cleanup: true
      #--no-configuration-cache for https://youtrack.jetbrains.com/issue/KT-65879
      - name: Publish to Maven Central
        run: |
          ./gradlew --no-build-cache ciPublishRelease -Pgradle.publish.key="${{ secrets.GRADLE_PUBLISH_KEY }}" -Pgradle.publish.secret="${{ secrets.GRADLE_PUBLISH_SECRET }}" --no-configuration-cache
        env:
          SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
          SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
          COM_APOLLOGRAPHQL_PROFILE_ID: ${{ secrets.COM_APOLLOGRAPHQL_PROFILE_ID }}
          GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
          GPG_PRIVATE_KEY_PASSWORD: ${{ secrets.GPG_PRIVATE_KEY_PASSWORD }}

  publish-intellij-plugin:
    name: Publish IntelliJ plugin
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
      - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1
        with:
          distribution: 'temurin'
          java-version: 17
      - name: Publish to JetBrains marketplace
        run: ./gradlew --no-build-cache :intellij-plugin:publishPlugin
        env:
          PUBLISH_TOKEN: ${{ secrets.IJ_PLUGIN_PUBLISH_TOKEN }}
          CERTIFICATE_CHAIN: ${{ secrets.IJ_PLUGIN_CERTIFICATE_CHAIN }}
          PRIVATE_KEY: ${{ secrets.IJ_PLUGIN_PRIVATE_KEY }}
          PRIVATE_KEY_PASSWORD: ${{ secrets.IJ_PLUGIN_PRIVATE_KEY_PASSWORD }}


Publish to the Maven Central Repository

name: Publish to the Maven Central Repository

on:
  push:
    branches:
      - develop
  release:
    types: [ published ]

concurrency:
  group: publish-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

jobs:
  publish:
    name: Publish to the Maven Central Repository
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'
          cache: gradle
      - name: Validate Gradle wrapper
        uses: gradle/wrapper-validation-action@v3
      - name: Build with Grade
        run: ./gradlew build
      - name: Publish to the Maven Central Repository
        run: ./gradlew --max-workers=1 publish closeAndReleaseStagingRepository
        env:
          ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
          ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
          ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.GPG_SECRET_KEY_ID }}
          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SECRET_KEY }}
          ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SECRET_PASSWORD }}
      - name: Cleanup Gradle Cache
        # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
        # Restoring these files from a GitHub Actions cache might cause problems for future builds.
        run: |
          rm -f ~/.gradle/caches/modules-2/modules-2.lock
          rm -f ~/.gradle/caches/modules-2/gc.properties


Test

name: Test

on:
  push:
    branches:
      - main
      - develop
  pull_request:

concurrency:
  group: test-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

jobs:
  test-report:
    name: Test Report
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'
          cache: gradle
      - name: Validate Gradle wrapper
        uses: gradle/wrapper-validation-action@v3
      - name: CodeCoverage(with test) with Gradle
        run: ./gradlew koverXmlReport
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: ./build/reports/kover/report.xml
      - name: Cleanup Gradle Cache
        run: |
          rm -f ~/.gradle/caches/modules-2/modules-2.lock
          rm -f ~/.gradle/caches/modules-2/gc.properties


Lint

name: Lint
on:
  pull_request:
    branches:
      - main
      - develop

concurrency:
  group: lint-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

jobs:
  commit-lint:
    name: Lint Commit Message
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 16.x
      - name: Install dependencies
        run: npm install
      - name: Validate all commits from PR
        run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
  ktlint:
    name: Lint Kotlin
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'
          cache: gradle
      - name: Validate Gradle wrapper
        uses: gradle/wrapper-validation-action@v3
      - name: Lint Kotlin with Gradle
        run: ./gradlew lintKotlin
      - name: Cleanup Gradle Cache
        run: |
          rm -f ~/.gradle/caches/modules-2/modules-2.lock
          rm -f ~/.gradle/caches/modules-2/gc.properties


Baseline Profile Updater

name: Baseline Profile Updater

on: workflow_dispatch

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          lfs: 'true'

      - name: Set up JDK 18
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 18

      - name: Set up Android SDK
        uses: android-actions/setup-android@v3

      - name: Enable KVM
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

      - name: Generate baseline profiles
        run: |
          ./gradlew :catalog:generateReleaseBaselineProfile :icons:generateBaselineProfile :illustrations:generateBaselineProfile :ui:generateBaselineProfile \
            -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile

      - name: Check for changes
        id: check_changes
        run: |
          if git diff --exit-code; then
            echo "changes_exist=false" >> "$GITHUB_OUTPUT"
          else
            echo "changes_exist=true" >> "$GITHUB_OUTPUT"
          fi

      - name: Commit and push files
        if: ${{ steps.check_changes.outputs.changes_exist == 'true' }}
        env:
          SSH_AUTH_SOCK: /tmp/ssh_agent.sock
        run: |
          ssh-agent -a $SSH_AUTH_SOCK > /dev/null
          ssh-add - <<< "${{ secrets.GH_ACTION_DEPLOY_PRIVATE_KEY }}"
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add -A
          git commit -m "Baseline and startup profiles generated."
          git push git@github.com:$GITHUB_REPOSITORY.git


Build

name: Build

on:
  pull_request:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          lfs: 'true'

      - name: Check LFS file (install git lfs and commit screenshots again if failing)
        run: git lfs fsck --pointers

      - name: set up JDK 18
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 18

      - name: ktlint
        run: ./gradlew lintKotlin

      - name: Tests & Detekt & Android lint
        run: ./gradlew :catalog:assembleDebug lintDebug :ui:detektAndroidDebug :ui:testDebugUnitTest :lint:test

      - name: Run Screenshot Tests
        run: ./gradlew :ui:verifyPaparazziDebug

      - uses: yutailang0119/action-android-lint@v3.1.0
        name: App Lint errors to annotations
        if: ${{ failure() }}
        continue-on-error: true # lint may be ok
        with:
          xml_path: app/build/reports/lint-results-debug.xml
      - uses: yutailang0119/action-android-lint@v3.1.0
        name: Lib Lint errors to annotations
        if: ${{ failure() }}
        continue-on-error: true # lint may be ok
        with:
          xml_path: lib/build/reports/lint-results-debug.xml
      - name: KTLint errors to annotations
        if: ${{ failure() }}
        run: |
          jq --raw-output '[.[] | ({ f: .file } + ( .errors[] | { l: .line, c: .column, m: .message, r: .rule } )) | "::error file=catalog/\(.f),line=\(.l),col=\(.c)::\(.m) [\(.r)]" ] | join("\n")' catalog/build/reports/ktlint/main-lint.json || true
          jq --raw-output '[.[] | ({ f: .file } + ( .errors[] | { l: .line, c: .column, m: .message, r: .rule } )) | "::error file=generator/\(.f),line=\(.l),col=\(.c)::\(.m) [\(.r)]" ] | join("\n")' generator/build/reports/ktlint/main-lint.json || true
          jq --raw-output '[.[] | ({ f: .file } + ( .errors[] | { l: .line, c: .column, m: .message, r: .rule } )) | "::error file=icons/\(.f),line=\(.l),col=\(.c)::\(.m) [\(.r)]" ] | join("\n")' icons/build/reports/ktlint/main-lint.json || true
          jq --raw-output '[.[] | ({ f: .file } + ( .errors[] | { l: .line, c: .column, m: .message, r: .rule } )) | "::error file=illustrations/\(.f),line=\(.l),col=\(.c)::\(.m) [\(.r)]" ] | join("\n")' illustrations/build/reports/ktlint/main-lint.json || true
          jq --raw-output '[.[] | ({ f: .file } + ( .errors[] | { l: .line, c: .column, m: .message, r: .rule } )) | "::error file=ui/\(.f),line=\(.l),col=\(.c)::\(.m) [\(.r)]" ] | join("\n")' ui/build/reports/ktlint/main-lint.json || true
      - uses: actions/upload-artifact@v4
        if: ${{ failure() }}
        with:
          name: shots-results
          path: ui/build/paparazzi/failures
      - name: Build Dokka HTML
        if: ${{ github.ref == 'refs/heads/main' }}
        run: |
          ./gradlew :dokkaHtmlMultiModule --no-configuration-cache
      - name: Deploy Dokka HTML
        uses: peaceiris/actions-gh-pages@v3
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build/dokka/htmlMultiModule