name: Docker Static

on:
  push:
    branches:
      - 'main'
    paths-ignore:
      - 'docs/**'
      - 'example_configs/**'
  release:
    types:
      - 'published'
  pull_request:
    branches:
      - 'main'
    paths-ignore:
      - 'docs/**'
      - 'example_configs/**'
  workflow_dispatch:
    inputs:
      msg:
        description: "Set message"
        default: "Manual trigger"

env:
  CARGO_TERM_COLOR: always


### CI Docs

# build-ui , create/compile the web
### install wasm
### install rollup
### run app/build.sh
### upload artifacts

# build-bin
## build-armhf, build-aarch64, build-amd64 , create binary for respective arch
#######################################################################################
# GitHub actions randomly timeout when downloading musl-gcc, using custom dev image   #
# Look into .github/workflows/Dockerfile.dev for development image details            #
# Using lldap dev image based on https://hub.docker.com/_/rust and musl-gcc bundled   #
#######################################################################################
### Cargo build
### aarch64 and amd64 is musl based
### armv7 is glibc based, musl had issue with time_t when cross compile https://github.com/rust-lang/libc/issues/1848

# build-ui,builds-armhf, build-aarch64, build-amd64 will upload artifacts will be used next job

# lldap-test
### will run lldap with postgres, mariadb and sqlite backend, do selfcheck command.

# Build docker image
### Triplet docker image arch with debian base
### amd64 & aarch64 with alpine base
# build-docker-image job will fetch artifacts and run Dockerfile.ci then push the image.
### Look into .github/workflows/Dockerfile.ci.debian or .github/workflowds/Dockerfile.ci.alpine

# create release artifacts
### Fetch artifacts
### Clean up web artifact
### Setup folder structure
### Compress
### Upload

# cache based on Cargo.lock per cargo target

jobs:
  pre_job:
    continue-on-error: true
    runs-on: ubuntu-latest
    outputs:
      should_skip: ${{ steps.skip_check.outputs.should_skip }}
    steps:
      - id: skip_check
        uses: fkirc/skip-duplicate-actions@master
        with:
          concurrent_skipping: 'outdated_runs'
          skip_after_successful_duplicate: ${{ github.ref != 'refs/heads/main' }}
          paths_ignore: '["**/*.md", "**/docs/**", "example_configs/**", "*.sh", ".gitignore", "lldap_config.docker_template.toml"]'
          do_not_skip: '["workflow_dispatch", "schedule"]'
          cancel_others: true

  build-ui:
    runs-on: ubuntu-latest
    needs: pre_job
    if: ${{ needs.pre_job.outputs.should_skip != 'true' || github.event_name == 'release' }}
    container:
      image: nitnelave/rust-dev:latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3.5.0
      - uses: actions/cache@v3
        with:
          path: |
            /usr/local/cargo/bin
            /usr/local/cargo/registry/index
            /usr/local/cargo/registry/cache
            /usr/local/cargo/git/db
            target
          key: lldap-ui-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            lldap-ui-
      - name: Install rollup (nodejs)
        run: npm install -g rollup
      - name: Add wasm target (rust)
        run: rustup target add wasm32-unknown-unknown
      - name: Install wasm-pack with cargo
        run: cargo install wasm-pack || true
        env:
          RUSTFLAGS: ""
      - name: Build frontend
        run: ./app/build.sh
      - name: Check build path
        run: ls -al app/
      - name: Upload ui artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ui
          path: app/


  build-bin:
    runs-on: ubuntu-latest
    needs: pre_job
    if: ${{ needs.pre_job.outputs.should_skip != 'true' || github.event_name == 'release' }}
    strategy:
      matrix:
        target: [armv7-unknown-linux-gnueabihf, aarch64-unknown-linux-musl, x86_64-unknown-linux-musl]
    container:
      image: nitnelave/rust-dev:latest
      env:
        CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
        CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-musl-gcc
        CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: x86_64-linux-musl-gcc
        CARGO_TERM_COLOR: always
        RUSTFLAGS: -Ctarget-feature=+crt-static
        CARGO_HOME: ${GITHUB_WORKSPACE}/.cargo
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3.5.0
      - uses: actions/cache@v3
        with:
          path: |
            .cargo/bin
            .cargo/registry/index
            .cargo/registry/cache
            .cargo/git/db
            target
          key: lldap-bin-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            lldap-bin-${{ matrix.target }}-
      - name: Compile ${{ matrix.target }} lldap and tools
        run: cargo build --target=${{ matrix.target }} --release -p lldap -p migration-tool -p lldap_set_password
      - name: Check path
        run: ls -al target/release
      - name: Upload ${{ matrix.target}} lldap artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.target}}-lldap-bin
          path: target/${{ matrix.target }}/release/lldap
      - name: Upload ${{ matrix.target }} migration tool artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.target }}-migration-tool-bin
          path: target/${{ matrix.target }}/release/migration-tool
      - name: Upload ${{ matrix.target }} password tool artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.target }}-lldap_set_password-bin
          path: target/${{ matrix.target }}/release/lldap_set_password

  lldap-database-init-test:
    needs: [build-ui,build-bin]
    name: LLDAP database init test
    runs-on: ubuntu-latest
    services:
        mariadb:
          image: mariadb:latest
          ports:
            - 3306:3306
          env:
            MARIADB_USER: lldapuser
            MARIADB_PASSWORD: lldappass
            MARIADB_DATABASE: lldap
            MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
          options: >-
            --name mariadb
            --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3

        postgresql:
           image: postgres:latest
           ports:
             - 5432:5432
           env:
             POSTGRES_USER: lldapuser
             POSTGRES_PASSWORD: lldappass
             POSTGRES_DB: lldap
           options: >-
             --health-cmd pg_isready
             --health-interval 10s
             --health-timeout 5s
             --health-retries 5
             --name postgresql

    steps:
       - name: Download artifacts
         uses: actions/download-artifact@v3
         with:
           name: x86_64-unknown-linux-musl-lldap-bin
           path: bin/

       - name: Set executables to LLDAP
         run: chmod +x bin/lldap

       - name: Run lldap with postgres DB and healthcheck
         run: |
              bin/lldap run &
              sleep 10s
              bin/lldap healthcheck
         env:
           LLDAP_database_url: postgres://lldapuser:lldappass@localhost/lldap
           LLDAP_ldap_port: 3890
           LLDAP_http_port: 17170


       - name: Run lldap with mariadb DB (MySQL Compatible) and healthcheck
         run: |
              bin/lldap run &
              sleep 10s
              bin/lldap healthcheck
         env:
           LLDAP_database_url: mysql://lldapuser:lldappass@localhost/lldap
           LLDAP_ldap_port: 3891
           LLDAP_http_port: 17171


       - name: Run lldap with sqlite DB and healthcheck
         run: |
              bin/lldap run &
              sleep 10s
              bin/lldap healthcheck
         env:
           LLDAP_database_url: sqlite://users.db?mode=rwc
           LLDAP_ldap_port: 3892
           LLDAP_http_port: 17172

       - name: Check DB container logs
         run: |
              docker logs -n 20 mariadb
              docker logs -n 20 postgresql

  lldap-database-migration-test:
    needs: [build-ui,build-bin]
    name: LLDAP database migration test
    runs-on: ubuntu-latest
    services:
        postgresql:
           image: postgres:latest
           ports:
             - 5432:5432
           env:
             POSTGRES_USER: lldapuser
             POSTGRES_PASSWORD: lldappass
             POSTGRES_DB: lldap
           options: >-
             --health-cmd pg_isready
             --health-interval 10s
             --health-timeout 5s
             --health-retries 5
             --name postgresql

        mariadb:
          image: mariadb:latest
          ports:
            - 3306:3306
          env:
            MARIADB_USER: lldapuser
            MARIADB_PASSWORD: lldappass
            MARIADB_DATABASE: lldap
            MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
          options: >-
            --name mariadb
            --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3


        mysql:
          image: mysql:latest
          ports:
            - 3307:3306
          env:
            MYSQL_USER: lldapuser
            MYSQL_PASSWORD: lldappass
            MYSQL_DATABASE: lldap
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
          options: >-
            --name mysql
            --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3


    steps:
       - name: Download LLDAP artifacts
         uses: actions/download-artifact@v3
         with:
           name: x86_64-unknown-linux-musl-lldap-bin
           path: bin/

       - name: Download LLDAP set password
         uses: actions/download-artifact@v3
         with:
           name: x86_64-unknown-linux-musl-lldap_set_password-bin
           path: bin/

       - name: Set executables to LLDAP and LLDAP set password
         run: |
              chmod +x bin/lldap
              chmod +x bin/lldap_set_password

       - name: Install sqlite3 and ldap-utils for exporting and searching dummy user
         run: sudo apt update && sudo apt install -y sqlite3 ldap-utils

       - name: Run lldap with sqlite DB and healthcheck
         run: |
              bin/lldap run &
              sleep 10s
              bin/lldap healthcheck
         env:
           LLDAP_database_url: sqlite://users.db?mode=rwc
           LLDAP_ldap_port: 3890
           LLDAP_http_port: 17170
           LLDAP_LDAP_USER_PASS: ldappass
           LLDAP_JWT_SECRET: somejwtsecret

       - name: Create dummy user
         run: |
              TOKEN=$(curl -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "ldappass"}' http://localhost:17170/auth/simple/login | jq -r .token)
              echo "$TOKEN"
              curl 'http://localhost:17170/api/graphql' -H 'Content-Type: application/json' -H "Authorization: Bearer ${TOKEN//[$'\t\r\n ']}" --data-binary '{"query":"mutation{\n  createUser(user:\n    {\n      id: \"dummyuser\",\n      email: \"dummyuser@example.com\"\n    }\n  )\n  {\n    id\n    email\n  }\n}\n\n\n"}' --compressed
              bin/lldap_set_password --base-url http://localhost:17170 --admin-username admin --admin-password ldappass --token $TOKEN --username dummyuser --password dummypassword

       - name: Test Dummy User, This will be checked again after importing
         run: |
              ldapsearch -H ldap://localhost:3890 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"

       - name: Stop LLDAP sqlite
         run: pkill lldap

       - name: Export and Converting to Postgress
         run: |
              curl -L https://raw.githubusercontent.com/lldap/lldap/main/scripts/sqlite_dump_commands.sh -o helper.sh
              chmod +x ./helper.sh
              ./helper.sh | sqlite3 ./users.db > ./dump.sql
              sed -i -r -e "s/X'([[:xdigit:]]+'[^'])/'\\\x\\1/g" -e '1s/^/BEGIN;\n/' -e '$aCOMMIT;' ./dump.sql

       - name: Create schema on postgres
         run: |
              bin/lldap create_schema -d postgres://lldapuser:lldappass@localhost:5432/lldap

       - name: Copy converted db to postgress and import
         run: |
              docker ps -a
              docker cp ./dump.sql postgresql:/tmp/dump.sql
              docker exec postgresql bash -c "psql -U lldapuser -d lldap < /tmp/dump.sql"
              rm ./dump.sql

       - name: Export and Converting to mariadb
         run: |
              curl -L https://raw.githubusercontent.com/lldap/lldap/main/scripts/sqlite_dump_commands.sh -o helper.sh
              chmod +x ./helper.sh
              ./helper.sh | sqlite3 ./users.db > ./dump.sql
              cp ./dump.sql ./dump-no-sed.sql
              sed -i -r -e "s/([^']'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{9})\+00:00'([^'])/\1'\2/g" \-e 's/^INSERT INTO "?([a-zA-Z0-9_]+)"?/INSERT INTO `\1`/' -e '1s/^/START TRANSACTION;\n/' -e '$aCOMMIT;' ./dump.sql
              sed  -i '1 i\SET FOREIGN_KEY_CHECKS = 0;' ./dump.sql

       - name: Create schema on mariadb
         run: bin/lldap create_schema -d mysql://lldapuser:lldappass@localhost:3306/lldap

       - name: Copy converted db to mariadb and import
         run: |
              docker ps -a
              docker cp ./dump.sql mariadb:/tmp/dump.sql
              docker exec mariadb bash -c "mariadb -ulldapuser -plldappass -f lldap < /tmp/dump.sql"
              rm ./dump.sql

       - name: Export and Converting to mysql
         run: |
              curl -L https://raw.githubusercontent.com/lldap/lldap/main/scripts/sqlite_dump_commands.sh -o helper.sh
              chmod +x ./helper.sh
              ./helper.sh | sqlite3 ./users.db > ./dump.sql
              sed -i -r -e 's/^INSERT INTO "?([a-zA-Z0-9_]+)"?/INSERT INTO `\1`/' -e '1s/^/START TRANSACTION;\n/' -e '$aCOMMIT;' ./dump.sql
              sed  -i '1 i\SET FOREIGN_KEY_CHECKS = 0;' ./dump.sql

       - name: Create schema on mysql
         run: bin/lldap create_schema -d mysql://lldapuser:lldappass@localhost:3307/lldap

       - name: Copy converted db to mysql and import
         run: |
              docker ps -a
              docker cp ./dump.sql mysql:/tmp/dump.sql
              docker exec mysql bash -c "mysql -ulldapuser -plldappass -f lldap < /tmp/dump.sql"
              rm ./dump.sql

       - name: Run lldap with postgres DB and healthcheck again
         run: |
              bin/lldap run &
              sleep 10s
              bin/lldap healthcheck
         env:
           LLDAP_database_url: postgres://lldapuser:lldappass@localhost:5432/lldap
           LLDAP_ldap_port: 3891
           LLDAP_http_port: 17171
           LLDAP_LDAP_USER_PASS: ldappass
           LLDAP_JWT_SECRET: somejwtsecret

       - name: Run lldap with mariaDB and healthcheck again
         run: |
              bin/lldap run &
              sleep 10s
              bin/lldap healthcheck
         env:
           LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3306/lldap
           LLDAP_ldap_port: 3892
           LLDAP_http_port: 17172
           LLDAP_JWT_SECRET: somejwtsecret

       - name: Run lldap with mysql and healthcheck again
         run: |
              bin/lldap run &
              sleep 10s
              bin/lldap healthcheck
         env:
           LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3307/lldap
           LLDAP_ldap_port: 3893
           LLDAP_http_port: 17173
           LLDAP_JWT_SECRET: somejwtsecret

       - name: Test Dummy User
         run: |
              ldapsearch -H ldap://localhost:3891 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"
              ldapsearch -H ldap://localhost:3892 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"
              ldapsearch -H ldap://localhost:3893 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"

  build-docker-image:
    needs: [build-ui, build-bin]
    name: Build Docker image
    runs-on: ubuntu-latest
    strategy:
      matrix:
        container: ["debian","alpine"]
        include:
          - container: alpine
            platforms: linux/amd64,linux/arm64
            tags: |
                  type=ref,event=pr
                  type=semver,pattern=v{{version}}
                  type=semver,pattern=v{{major}}
                  type=semver,pattern=v{{major}}.{{minor}}
                  type=semver,pattern=v{{version}},suffix=
                  type=semver,pattern=v{{major}},suffix=
                  type=semver,pattern=v{{major}}.{{minor}},suffix=
                  type=raw,value=latest,enable={{ is_default_branch }}
                  type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
                  type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/v') }},suffix=
                  type=raw,value=latest,enable={{ is_default_branch }},suffix=
          - container: debian
            platforms: linux/amd64,linux/arm64,linux/arm/v7
            tags: |
                  type=ref,event=pr
                  type=semver,pattern=v{{version}}
                  type=semver,pattern=v{{major}}
                  type=semver,pattern=v{{major}}.{{minor}}
                  type=raw,value=latest,enable={{ is_default_branch }}
                  type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/v') }}

    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3.5.0
      - name: Download all artifacts
        uses: actions/download-artifact@v3
        with:
          path: bin

      - name: Download llap ui artifacts
        uses: actions/download-artifact@v3
        with:
          name: ui
          path: web

      - name: Setup QEMU
        uses: docker/setup-qemu-action@v2
      - uses: docker/setup-buildx-action@v2

      - name: Docker ${{ matrix.container }} meta
        id: meta
        uses: docker/metadata-action@v4
        with:
          # list of Docker images to use as base name for tags
          images: |
            nitnelave/lldap
            lldap/lldap
            ghcr.io/lldap/lldap
          # Wanted Docker tags
          # vX-alpine
          # vX.Y-alpine
          # vX.Y.Z-alpine
          # latest
          # latest-alpine
          # stable
          # stable-alpine
          #################
          # vX-debian
          # vX.Y-debian
          # vX.Y.Z-debian
          # latest-debian
          # stable-debian
          #################
          # Check matrix for tag list definition
          flavor: |
            latest=false
            suffix=-${{ matrix.container }}
          tags: ${{ matrix.tags }}

      # Docker login to nitnelave/lldap and lldap/lldap
      - name: Login to Nitnelave/LLDAP Docker Hub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: nitnelave
          password: ${{ secrets.GITHUB_TOKEN }}


########################################
####       docker image build       ####
########################################
      - name: Build ${{ matrix.container }} Docker Image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          platforms: ${{ matrix.platforms  }}
          file: ./.github/workflows/Dockerfile.ci.${{ matrix.container  }}
          tags: |
                ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha,mode=max
          cache-to: type=gha,mode=max

      - name: Update repo description
        if: github.event_name != 'pull_request'
        uses: peter-evans/dockerhub-description@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
          repository: nitnelave/lldap

      - name: Update lldap repo description
        if: github.event_name != 'pull_request'
        uses: peter-evans/dockerhub-description@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
          repository: lldap/lldap

###############################################################
### Download artifacts, clean up ui, upload to release page ###
###############################################################
  create-release-artifacts:
     needs: [build-ui, build-bin]
     name: Create release artifacts
     if: github.event_name == 'release'
     runs-on: ubuntu-latest
     permissions:
       contents: write
     steps:
      - name: Download all artifacts
        uses: actions/download-artifact@v3
        with:
          path: bin/
      - name: Check file
        run: ls -alR bin/
      - name: Fixing Filename
        run: |
             mv bin/aarch64-unknown-linux-musl-lldap-bin/lldap bin/aarch64-lldap
             mv bin/x86_64-unknown-linux-musl-lldap-bin/lldap bin/amd64-lldap
             mv bin/armv7-unknown-linux-gnueabihf-lldap-bin/lldap bin/armhf-lldap
             mv bin/aarch64-unknown-linux-musl-migration-tool-bin/migration-tool bin/aarch64-migration-tool
             mv bin/x86_64-unknown-linux-musl-migration-tool-bin/migration-tool bin/amd64-migration-tool
             mv bin/armv7-unknown-linux-gnueabihf-migration-tool-bin/migration-tool bin/armhf-migration-tool
             mv bin/aarch64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password bin/aarch64-lldap_set_password
             mv bin/x86_64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password bin/amd64-lldap_set_password
             mv bin/armv7-unknown-linux-gnueabihf-lldap_set_password-bin/lldap_set_password bin/armhf-lldap_set_password
             chmod +x bin/*-lldap
             chmod +x bin/*-migration-tool
             chmod +x bin/*-lldap_set_password

      - name: Download llap ui artifacts
        uses: actions/download-artifact@v3
        with:
          name: ui
          path: web
      - name: UI (web) artifacts cleanup
        run: mkdir app && mv web/index.html app/index.html && mv web/static app/static && mv web/pkg app/pkg
      - name: Fetch web components
        run: |
             sudo apt update
             sudo apt install wget
             for file in $(cat app/static/libraries.txt); do wget -P app/static "$file"; done
             for file in $(cat app/static/fonts/fonts.txt); do wget -P app/static/fonts "$file"; done
             chmod a+r -R .

      - name: Setup LLDAP dir for packing
        run: |
             mkdir aarch64-lldap
             mkdir amd64-lldap
             mkdir armhf-lldap
             mv bin/aarch64-lldap aarch64-lldap/lldap
             mv bin/amd64-lldap amd64-lldap/lldap
             mv bin/armhf-lldap armhf-lldap/lldap
             mv bin/aarch64-migration-tool aarch64-lldap/migration-tool
             mv bin/amd64-migration-tool amd64-lldap/migration-tool
             mv bin/armhf-migration-tool armhf-lldap/migration-tool
             mv bin/aarch64-lldap_set_password aarch64-lldap/lldap_set_password
             mv bin/amd64-lldap_set_password amd64-lldap/lldap_set_password
             mv bin/armhf-lldap_set_password armhf-lldap/lldap_set_password
             cp -r app aarch64-lldap/
             cp -r app amd64-lldap/
             cp -r app armhf-lldap/
             ls -alR aarch64-lldap/
             ls -alR amd64-lldap/
             ls -alR armhf-lldap/

      - name: Packing LLDAP and Web UI
        run: |
             tar -czvf aarch64-lldap.tar.gz aarch64-lldap/
             tar -czvf amd64-lldap.tar.gz amd64-lldap/
             tar -czvf armhf-lldap.tar.gz armhf-lldap/


      - name: Upload compressed release
        uses: ncipollo/release-action@v1
        id: create_release
        with:
          allowUpdates: true
          artifacts:  aarch64-lldap.tar.gz,
                      amd64-lldap.tar.gz,
                      armhf-lldap.tar.gz
        env:
         GITHUB_TOKEN: ${{ github.token }}
