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 ### 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 # # lldap/rust-dev:latest # ####################################################################################### # Cargo build ### armv7, aarch64 and amd64 is musl based # 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 and 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: lldap/rust-dev:latest steps: - name: Checkout repository uses: actions/checkout@v4.1.5 - uses: actions/cache@v4 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: 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@v4 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: fail-fast: false matrix: target: [armv7-unknown-linux-musleabihf, aarch64-unknown-linux-musl, x86_64-unknown-linux-musl] container: image: lldap/rust-dev:latest env: CARGO_TERM_COLOR: always RUSTFLAGS: -Ctarget-feature=+crt-static CARGO_HOME: ${GITHUB_WORKSPACE}/.cargo steps: - name: Checkout repository uses: actions/checkout@v4.1.5 - uses: actions/cache@v4 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 lldap_migration_tool -p lldap_set_password - name: Check path run: ls -al target/release - name: Upload ${{ matrix.target}} lldap artifacts uses: actions/upload-artifact@v4 with: name: ${{ matrix.target}}-lldap-bin path: target/${{ matrix.target }}/release/lldap - name: Upload ${{ matrix.target }} migration tool artifacts uses: actions/upload-artifact@v4 with: name: ${{ matrix.target }}-lldap_migration_tool-bin path: target/${{ matrix.target }}/release/lldap_migration_tool - name: Upload ${{ matrix.target }} password tool artifacts uses: actions/upload-artifact@v4 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="mariadb-admin 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@v4 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="mariadb-admin 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: Checkout scripts uses: actions/checkout@v4.1.5 with: sparse-checkout: 'scripts' - name: Download LLDAP artifacts uses: actions/download-artifact@v4 with: name: x86_64-unknown-linux-musl-lldap-bin path: bin/ - name: Download LLDAP set password uses: actions/download-artifact@v4 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: | bash ./scripts/sqlite_dump_commands.sh | sqlite3 ./users.db > ./dump.sql sed -i -r -e "s/X'([[:xdigit:]]+'[^'])/'\\\x\\1/g" -e ":a; s/(INSERT INTO (user_attribute_schema|jwt_storage)\(.*\) VALUES\(.*),1([^']*\);)$/\1,true\3/; s/(INSERT INTO (user_attribute_schema|jwt_storage)\(.*\) VALUES\(.*),0([^']*\);)$/\1,false\3/; ta" -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 cp ./dump.sql postgresql:/tmp/dump.sql docker exec postgresql bash -c "psql -U lldapuser -d lldap < /tmp/dump.sql" | tee import.log rm ./dump.sql ! grep ERROR import.log > /dev/null - name: Export and Converting to mariadb run: | bash ./scripts/sqlite_dump_commands.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 cp ./dump.sql mariadb:/tmp/dump.sql docker exec mariadb bash -c "mariadb -ulldapuser -plldappass -f lldap < /tmp/dump.sql" | tee import.log rm ./dump.sql ! grep ERROR import.log > /dev/null - name: Export and Converting to mysql run: | bash ./scripts/sqlite_dump_commands.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 cp ./dump.sql mysql:/tmp/dump.sql docker exec mysql bash -c "mysql -ulldapuser -plldappass -f lldap < /tmp/dump.sql" | tee import.log rm ./dump.sql ! grep ERROR import.log > /dev/null - 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 Postgres 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" - name: Test Dummy User MariaDB run: 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" - name: Test Dummy User MySQL run: 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 BASE IMAGE ################## ######################################## 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,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=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= type=raw,value={{ date 'YYYY-MM-DD' }},enable={{ is_default_branch }} type=raw,value={{ date 'YYYY-MM-DD' }},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') }} type=raw,value={{ date 'YYYY-MM-DD' }},enable={{ is_default_branch }} services: registry: image: registry:2 ports: - 5000:5000 permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4.1.5 - name: Download all artifacts uses: actions/download-artifact@v4 with: path: bin - name: Download llap ui artifacts uses: actions/download-artifact@v4 with: name: ui path: web - name: Setup QEMU uses: docker/setup-qemu-action@v3 - name: Setup buildx uses: docker/setup-buildx-action@v3 with: driver-opts: network=host - name: Docker ${{ matrix.container }} Base meta id: meta-base uses: docker/metadata-action@v5 with: # list of Docker images to use as base name for tags images: | localhost:5000/lldap/lldap tags: ${{ matrix.container }}-base - name: Build ${{ matrix.container }} Base Docker Image uses: docker/build-push-action@v5 with: context: . # On PR will fail, force fully uncomment push: true, or docker image will fail for next steps #push: ${{ github.event_name != 'pull_request' }} push: true platforms: ${{ matrix.platforms }} file: ./.github/workflows/Dockerfile.ci.${{ matrix.container }}-base tags: | ${{ steps.meta-base.outputs.tags }} labels: ${{ steps.meta-base.outputs.labels }} cache-from: type=gha,mode=max cache-to: type=gha,mode=max ##################################### #### build variants docker image #### ##################################### - name: Docker ${{ matrix.container }}-rootless meta id: meta-rootless uses: docker/metadata-action@v5 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 # YYYY-MM-DD # YYYY-MM-DD-alpine ################# # vX-debian # vX.Y-debian # vX.Y.Z-debian # latest-debian # stable-debian # YYYY-MM-DD-debian ################# # Check matrix for tag list definition flavor: | latest=false suffix=-${{ matrix.container }}-rootless tags: ${{ matrix.tags }} - name: Docker ${{ matrix.container }} meta id: meta-standard uses: docker/metadata-action@v5 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 # YYYY-MM-DD # YYYY-MM-DD-alpine ################# # vX-debian # vX.Y-debian # vX.Y.Z-debian # latest-debian # stable-debian # YYYY-MM-DD-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@v3 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@v3 with: registry: ghcr.io username: nitnelave password: ${{ secrets.GITHUB_TOKEN }} - name: Build ${{ matrix.container }}-rootless Docker Image uses: docker/build-push-action@v5 with: context: . push: ${{ github.event_name != 'pull_request' }} platforms: ${{ matrix.platforms }} file: ./.github/workflows/Dockerfile.ci.${{ matrix.container }}-rootless tags: | ${{ steps.meta-rootless.outputs.tags }} labels: ${{ steps.meta-rootless.outputs.labels }} cache-from: type=gha,mode=max cache-to: type=gha,mode=max ### This docker build always the last, due :latest tag pushed multiple times, for whatever variants may added in future add docker build above this - name: Build ${{ matrix.container }} Docker Image uses: docker/build-push-action@v5 with: context: . push: ${{ github.event_name != 'pull_request' }} platforms: ${{ matrix.platforms }} file: ./.github/workflows/Dockerfile.ci.${{ matrix.container }} tags: | ${{ steps.meta-standard.outputs.tags }} labels: ${{ steps.meta-standard.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@v4 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@v4 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@v4 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-musleabihf-lldap-bin/lldap bin/armhf-lldap mv bin/aarch64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool bin/aarch64-lldap_migration_tool mv bin/x86_64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool bin/amd64-lldap_migration_tool mv bin/armv7-unknown-linux-musleabihf-lldap_migration_tool-bin/lldap_migration_tool bin/armhf-lldap_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-musleabihf-lldap_set_password-bin/lldap_set_password bin/armhf-lldap_set_password chmod +x bin/*-lldap chmod +x bin/*-lldap_migration_tool chmod +x bin/*-lldap_set_password - name: Download llap ui artifacts uses: actions/download-artifact@v4 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-lldap_migration_tool aarch64-lldap/lldap_migration_tool mv bin/amd64-lldap_migration_tool amd64-lldap/lldap_migration_tool mv bin/armhf-lldap_migration_tool armhf-lldap/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 }}