# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
on:
    workflow_call:
        inputs:
            ref:
                type: string
                required: false
                description: "The git ref to checkout, defaults to the default branch"
            arch:
                type: string
                required: true
                description: "The architecture to build for, one of 'amd64' | 'arm64'"
            version:
                type: string
                required: false
                description: "Version string to override the one in package.json, used for non-release builds"
            sqlcipher:
                type: string
                required: true
                description: "How to link sqlcipher, one of 'system' | 'static'"
            blob_report:
                type: boolean
                required: false
                description: "Whether to run the blob report"
            prepare-artifact-name:
                type: string
                required: false
                description: |
                    The name of the prepare artifact to use, defaults to 'desktop-prepare'.
                    The artifact must contain the following:
                    + webapp.asar - the asar archive of the webapp to embed in the desktop app
                    + electronVersion - the version of electron to use for cache keying
                    + hakHash - the hash of the .hak directory to use for cache keying
                    + changelog.Debian - the changelog file to embed in the Debian package
                    + variant.json - the variant configuration to use for the build

                    The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
                    for example icons in the `build/` directory to override the app icons.
                default: "desktop-prepare"
            test:
                type: boolean
                required: false
                default: true
                description: "Whether to run the test stage after building"
            test-args:
                type: string
                required: false
                description: "Additional arguments to pass to playwright"
            runs-on:
                type: string
                required: false
                description: "The runner image to use, normally set for you, may be needed for running in private repos."
            artifact-prefix:
                type: string
                required: false
                description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
                default: ""
            targets:
                type: string
                required: false
                description: "List of targets to build"
                default: "tar.gz deb"
env:
    SQLCIPHER_BUNDLED: ${{ inputs.sqlcipher == 'static' && '1' || '' }}
    MAX_GLIBC: 2.31 # bullseye-era glibc, used by glibc-check.sh
permissions: {} # No permissions required
jobs:
    build:
        name: Build Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
        # We build on native infrastructure as matrix-seshat fails to cross-compile properly
        # https://github.com/matrix-org/seshat/issues/135
        runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
        env:
            HAK_DOCKER_IMAGE: ghcr.io/element-hq/element-web/desktop-build-env:${{ case(github.event_name == 'push', inputs.ref || github.ref_name, github.event_name == 'release', 'staging', 'develop') }}
        steps:
            - uses: nbucic/variable-mapper@0673f6891a0619ba7c002ecfed0f9f4f39017b6f
              id: config
              with:
                  key: "${{ inputs.arch }}"
                  export_to: output
                  map: |
                      {
                        "amd64": {
                          "arch": "x86-64"
                        },
                        "arm64": {
                          "arch": "aarch64",
                          "build-args": "--arm64"
                        }
                      }

            - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
              with:
                  repository: element-hq/element-web
                  ref: ${{ inputs.ref }}
                  persist-credentials: false

            - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
              with:
                  name: ${{ inputs.prepare-artifact-name }}
                  path: apps/desktop

            - name: Cache .hak
              id: cache
              uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
              with:
                  key: ${{ runner.os }}-${{ github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('apps/desktop/hakHash', 'apps/desktop/electronVersion', 'apps/desktop/dockerbuild/*') }}
                  path: |
                      apps/desktop/.hak

            - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
            - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
              with:
                  node-version-file: apps/desktop/.node-version
                  cache: "pnpm"
              env:
                  # Workaround for https://github.com/actions/setup-node/issues/317
                  FORCE_COLOR: 0

            - name: Install Deps
              working-directory: apps/desktop
              run: "pnpm install --frozen-lockfile --filter element-desktop"

            - name: "Get modified files"
              id: changed_files
              if: steps.cache.outputs.cache-hit != 'true' && github.event_name == 'pull_request' && github.repository == 'element-hq/element-web'
              uses: tj-actions/changed-files@823fcebdb31bb35fdf2229d9f769b400309430d0 # v46
              with:
                  files: |
                      apps/desktop/dockerbuild/**

            # This allows contributors to test changes to the dockerbuild image within a pull request
            - name: Build docker image
              uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
              if: steps.changed_files.outputs.any_modified == 'true'
              with:
                  file: apps/desktop/dockerbuild/Dockerfile
                  context: apps/desktop
                  load: true
                  platforms: linux/${{ inputs.arch }}
                  tags: ${{ env.HAK_DOCKER_IMAGE }}

            - name: Build Natives
              if: steps.cache.outputs.cache-hit != 'true'
              run: |
                  docker run \
                    -v ${{ github.workspace }}:/work -w /work \
                    -e SQLCIPHER_BUNDLED \
                    -e CI=1 \
                    $HAK_DOCKER_IMAGE \
                    pnpm -C apps/desktop run build:native

            - name: Fix permissions
              run: |
                  # For .hak
                  sudo chown -R $USER:$USER apps/desktop/.hak
                  # For node_modules pnpm strict security
                  sudo chmod +x node_modules/7zip-bin/linux/*/7za

            - name: Check native libraries in hak dependencies
              working-directory: apps/desktop
              run: |
                  shopt -s globstar

                  for filename in ./.hak/hakModules/**/*.node; do
                      ./scripts/glibc-check.sh $filename
                  done

            - name: Generate debian files and arguments
              working-directory: apps/desktop
              run: |
                  if [ -f changelog.Debian ]; then
                      echo "ED_DEBIAN_CHANGELOG=changelog.Debian" >> $GITHUB_ENV
                  fi

            - name: Build App
              working-directory: apps/desktop
              run: pnpm run build --publish never $BUILD_ARGS -l $TARGETS
              env:
                  VARIANT_PATH: variant.json
                  # Only set for Nightly builds
                  VERSION: ${{ inputs.version }}
                  # Workaround for https://github.com/electron-userland/electron-builder/issues/5721
                  USE_HARD_LINKS: false
                  BUILD_ARGS: ${{ steps.config.outputs.build-args }}
                  TARGETS: ${{ inputs.targets }}

            - name: Check native libraries
              working-directory: apps/desktop
              run: |
                  set -x
                  shopt -s globstar

                  FILES=$(file dist/**/*.node)
                  echo $FILES

                  ! echo "$FILES" | grep -v "$ARCH"

                  LIBS=$(readelf -d dist/**/*.node | grep NEEDED)
                  echo "$LIBS"

                  set +x
                  assert_contains_string() { [[ "$1" == *"$2"* ]]; }
                  ! assert_contains_string "$LIBS" "libcrypto.so.1.1"
                  if [ "$SQLCIPHER_BUNDLED" == "1" ]; then
                      ! assert_contains_string "$LIBS" "libsqlcipher.so.0"
                  else
                      assert_contains_string "$LIBS" "libsqlcipher.so.0"
                  fi

                  ./scripts/glibc-check.sh dist/linux-*unpacked/element-desktop*
              env:
                  ARCH: ${{ steps.config.outputs.arch }}

            # We exclude *-unpacked as it loses permissions and the tarball contains it with correct permissions
            - name: Upload Artifacts
              uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
              with:
                  name: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
                  path: |
                      apps/desktop/dist
                      !apps/desktop/dist/*-unpacked/**
                  retention-days: 1

            - name: Assert deb is present and valid
              if: contains(inputs.targets, 'deb')
              working-directory: apps/desktop
              run: |
                  test -f ./dist/element-desktop*$ARCH.deb

                  DEB_LISTING=$(dpkg-deb --fsys-tarfile ./dist/element-desktop*.deb | tar -tv)
                  echo "deb listing: "
                  echo "$DEB_LISTING"
                  ! echo "$DEB_LISTING" | grep '^h'
              env:
                  ARCH: ${{ inputs.arch }}

            - name: Assert tar.gz is present
              if: contains(inputs.targets, 'tar.gz')
              working-directory: apps/desktop
              run: |
                  test -f ./dist/element-desktop*.tar.gz

                  TAR_GZ_LISTING=$(tar -tvf ./dist/element-desktop*.tar.gz)
                  echo "tar.gz listing: "
                  echo "$TAR_GZ_LISTING"
                  ! echo "$TAR_GZ_LISTING" | grep '^h'

    test:
        name: Test Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
        needs: build
        if: inputs.test && contains(inputs.targets, 'deb')
        uses: ./.github/workflows/build_desktop_test.yaml
        with:
            project: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
            artifact: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
            runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
            executable: /opt/Element*/element-desktop*
            prepare_cmd: |
                sudo apt-get -qq update
                sudo apt install ./dist/*.deb
            blob_report: ${{ inputs.blob_report }}
            args: ${{ inputs.test-args }}
