mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-06 23:39:54 +00:00
Merge branch 'master' into master-android
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
name: "Prebuilt steps for build"
|
||||
description: "Reusable steps for multiple jobs"
|
||||
inputs:
|
||||
java_ver:
|
||||
required: true
|
||||
description: "Java version to install"
|
||||
ghc_ver:
|
||||
required: true
|
||||
description: "GHC version to install"
|
||||
github_ref:
|
||||
required: true
|
||||
description: "Git reference"
|
||||
os:
|
||||
required: true
|
||||
description: "Target OS"
|
||||
cache_path:
|
||||
required: false
|
||||
default: "~/.cabal/store"
|
||||
description: "Cache path"
|
||||
cabal_ver:
|
||||
required: false
|
||||
default: 3.10.1.0
|
||||
description: "GHC version to install"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Skip unreliable ghc 8.10.7 build on stable branch
|
||||
shell: bash
|
||||
if: inputs.ghc_ver == '8.10.7' && inputs.github_ref == 'refs/heads/stable'
|
||||
run: exit 0
|
||||
|
||||
- name: Setup Haskell
|
||||
uses: simplex-chat/setup-haskell-action@v2
|
||||
with:
|
||||
ghc-version: ${{ inputs.ghc_ver }}
|
||||
cabal-version: ${{ inputs.cabal_ver }}
|
||||
|
||||
- name: Setup Java
|
||||
if: startsWith(inputs.github_ref, 'refs/tags/v')
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'corretto'
|
||||
java-version: ${{ inputs.java_ver }}
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Restore cached build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ inputs.cache_path }}
|
||||
dist-newstyle
|
||||
key: ${{ inputs.os }}-ghc${{ inputs.ghc_ver }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}
|
||||
@@ -0,0 +1,39 @@
|
||||
name: "Upload binary and update hash"
|
||||
description: "Reusable steps for multiple jobs"
|
||||
inputs:
|
||||
bin_path:
|
||||
required: true
|
||||
description: "Path to binary to upload"
|
||||
bin_name:
|
||||
required: true
|
||||
description: "Name of uploaded binary"
|
||||
bin_hash:
|
||||
required: true
|
||||
description: "Message with SHA to include in release"
|
||||
github_ref:
|
||||
required: true
|
||||
description: "Github reference"
|
||||
github_token:
|
||||
required: true
|
||||
description: "Github token"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Linux upload AppImage to release
|
||||
if: startsWith(inputs.github_ref, 'refs/tags/v')
|
||||
uses: simplex-chat/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ inputs.github_token }}
|
||||
file: ${{ inputs.bin_path }}
|
||||
asset_name: ${{ inputs.bin_name }}
|
||||
tag: ${{ inputs.github_ref }}
|
||||
|
||||
- name: Linux update AppImage hash
|
||||
if: startsWith(inputs.github_ref, 'refs/tags/v')
|
||||
uses: simplex-chat/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ inputs.bin_hash }}
|
||||
+324
-262
@@ -22,17 +22,58 @@ on:
|
||||
- "README.md"
|
||||
- "PRIVACY.md"
|
||||
|
||||
# This workflow uses custom actions (prepare-build and prepare-release) defined in:
|
||||
#
|
||||
# .github/actions/
|
||||
# ├── prepare-build
|
||||
# │ └── action.yml
|
||||
# └── prepare-release
|
||||
# └── action.yml
|
||||
|
||||
# Important!
|
||||
# Do not use always(), it makes build unskippable.
|
||||
# See: https://github.com/actions/runner/issues/1846#issuecomment-1246102753
|
||||
|
||||
jobs:
|
||||
prepare-release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
# =============================
|
||||
# Global variables
|
||||
# =============================
|
||||
|
||||
# That is the only and less hacky way to setup global variables
|
||||
# to use in strategy matrix (env:/YAML anchors doesn't work).
|
||||
# See: https://github.com/orgs/community/discussions/56787#discussioncomment-6041789
|
||||
# https://github.com/actions/runner/issues/1182
|
||||
# https://stackoverflow.com/a/77549656
|
||||
|
||||
variables:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
GHC_VER: 9.6.3
|
||||
JAVA_VER: 17
|
||||
steps:
|
||||
- name: Dummy job when we have just simple variables
|
||||
if: false
|
||||
run: echo
|
||||
|
||||
# =============================
|
||||
# Create release
|
||||
# =============================
|
||||
|
||||
# Create release, but only if it's triggered by tag push.
|
||||
# On pull requests/commits push, this job will always complete.
|
||||
|
||||
maybe-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone project
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build changelog
|
||||
id: build_changelog
|
||||
uses: mikepenz/release-changelog-builder-action@v4
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: simplex-chat/release-changelog-builder-action@v4
|
||||
with:
|
||||
configuration: .github/changelog_conf.json
|
||||
failOnError: true
|
||||
@@ -42,7 +83,8 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: simplex-chat/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.build_changelog.outputs.changelog }}
|
||||
prerelease: true
|
||||
@@ -52,183 +94,259 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build:
|
||||
name: build-${{ matrix.os }}-${{ matrix.ghc }}
|
||||
if: always()
|
||||
needs: prepare-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
# =========================
|
||||
# Linux Build
|
||||
# =========================
|
||||
|
||||
build-linux:
|
||||
name: "ubuntu-${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}"
|
||||
needs: [maybe-release, variables]
|
||||
runs-on: ubuntu-${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
- os: 20.04
|
||||
ghc: "8.10.7"
|
||||
cache_path: ~/.cabal/store
|
||||
- os: ubuntu-20.04
|
||||
ghc: "9.6.3"
|
||||
cache_path: ~/.cabal/store
|
||||
asset_name: simplex-chat-ubuntu-20_04-x86-64
|
||||
- os: 20.04
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-ubuntu-20_04-x86-64
|
||||
desktop_asset_name: simplex-desktop-ubuntu-20_04-x86_64.deb
|
||||
- os: ubuntu-22.04
|
||||
ghc: "9.6.3"
|
||||
cache_path: ~/.cabal/store
|
||||
asset_name: simplex-chat-ubuntu-22_04-x86-64
|
||||
- os: 22.04
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-ubuntu-22_04-x86-64
|
||||
desktop_asset_name: simplex-desktop-ubuntu-22_04-x86_64.deb
|
||||
- os: macos-latest
|
||||
ghc: "9.6.3"
|
||||
cache_path: ~/.cabal/store
|
||||
asset_name: simplex-chat-macos-aarch64
|
||||
desktop_asset_name: simplex-desktop-macos-aarch64.dmg
|
||||
- os: macos-13
|
||||
ghc: "9.6.3"
|
||||
cache_path: ~/.cabal/store
|
||||
asset_name: simplex-chat-macos-x86-64
|
||||
desktop_asset_name: simplex-desktop-macos-x86_64.dmg
|
||||
- os: windows-latest
|
||||
ghc: "9.6.3"
|
||||
cache_path: C:/cabal
|
||||
asset_name: simplex-chat-windows-x86-64
|
||||
desktop_asset_name: simplex-desktop-windows-x86_64.msi
|
||||
|
||||
steps:
|
||||
- name: Skip unreliable ghc 8.10.7 build on stable branch
|
||||
if: matrix.ghc == '8.10.7' && github.ref == 'refs/heads/stable'
|
||||
run: exit 0
|
||||
|
||||
- name: Configure pagefile (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
with:
|
||||
minimum-size: 16GB
|
||||
maximum-size: 16GB
|
||||
disk-root: "C:"
|
||||
|
||||
- name: Clone project
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Haskell
|
||||
uses: haskell-actions/setup@v2
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
cabal-version: "3.10.1.0"
|
||||
# Otherwise we run out of disk space with Docker build
|
||||
- name: Free disk space
|
||||
shell: bash
|
||||
run: ./scripts/ci/linux_util_free_space.sh
|
||||
|
||||
- name: Restore cached build
|
||||
id: restore_cache
|
||||
uses: actions/cache/restore@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.cache_path }}
|
||||
~/.cabal/store
|
||||
dist-newstyle
|
||||
key: ${{ matrix.os }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}
|
||||
key: ubuntu-${{ matrix.os }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}
|
||||
|
||||
# / Unix
|
||||
- name: Set up Docker Buildx
|
||||
uses: simplex-chat/docker-setup-buildx-action@v3
|
||||
|
||||
- name: Unix prepare cabal.project.local for Mac
|
||||
if: matrix.os == 'macos-latest'
|
||||
- name: Build and cache Docker image
|
||||
uses: simplex-chat/docker-build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
load: true
|
||||
file: Dockerfile.build
|
||||
tags: build/${{ matrix.os }}:latest
|
||||
build-args: |
|
||||
TAG=${{ matrix.os }}
|
||||
GHC=${{ matrix.ghc }}
|
||||
|
||||
# Docker needs these flags for AppImage build:
|
||||
# --device /dev/fuse
|
||||
# --cap-add SYS_ADMIN
|
||||
# --security-opt apparmor:unconfined
|
||||
- name: Start container
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ignore-project: False" >> cabal.project.local
|
||||
echo "package simplexmq" >> cabal.project.local
|
||||
echo " extra-include-dirs: /opt/homebrew/opt/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: /opt/homebrew/opt/openssl@3.0/lib" >> cabal.project.local
|
||||
echo "" >> cabal.project.local
|
||||
echo "package direct-sqlcipher" >> cabal.project.local
|
||||
echo " extra-include-dirs: /opt/homebrew/opt/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: /opt/homebrew/opt/openssl@3.0/lib" >> cabal.project.local
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
docker run -t -d \
|
||||
--device /dev/fuse \
|
||||
--cap-add SYS_ADMIN \
|
||||
--security-opt apparmor:unconfined \
|
||||
--name builder \
|
||||
-v ~/.cabal:/root/.cabal \
|
||||
-v /home/runner/work/_temp:/home/runner/work/_temp \
|
||||
-v ${{ github.workspace }}:/project \
|
||||
build/${{ matrix.os }}:latest
|
||||
|
||||
- name: Unix prepare cabal.project.local for Mac
|
||||
if: matrix.os == 'macos-13'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ignore-project: False" >> cabal.project.local
|
||||
echo "package simplexmq" >> cabal.project.local
|
||||
echo " extra-include-dirs: /usr/local/opt/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: /usr/local/opt/openssl@3.0/lib" >> cabal.project.local
|
||||
echo "" >> cabal.project.local
|
||||
echo "package direct-sqlcipher" >> cabal.project.local
|
||||
echo " extra-include-dirs: /usr/local/opt/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: /usr/local/opt/openssl@3.0/lib" >> cabal.project.local
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
|
||||
- name: Install AppImage dependencies
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
run: sudo apt install -y desktop-file-utils
|
||||
|
||||
- name: Install openssl for Mac
|
||||
if: matrix.os == 'macos-latest' || matrix.os == 'macos-13'
|
||||
run: brew install openssl@3.0
|
||||
|
||||
- name: Unix prepare cabal.project.local for Ubuntu
|
||||
if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04'
|
||||
- name: Prepare cabal.project.local
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ignore-project: False" >> cabal.project.local
|
||||
echo "package direct-sqlcipher" >> cabal.project.local
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
|
||||
- name: Unix build CLI
|
||||
id: unix_cli_build
|
||||
if: matrix.os != 'windows-latest'
|
||||
# chmod/git commands are used to workaround permission issues when cache is restored
|
||||
- name: Build CLI
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
chmod -R 777 dist-newstyle ~/.cabal && git config --global --add safe.directory '*'
|
||||
cabal clean
|
||||
cabal update
|
||||
cabal build -j --enable-tests
|
||||
mkdir -p /out
|
||||
for i in simplex-chat simplex-chat-test; do
|
||||
bin=$(find /project/dist-newstyle -name "$i" -type f -executable)
|
||||
chmod +x "$bin"
|
||||
mv "$bin" /out/
|
||||
done
|
||||
strip /out/simplex-chat
|
||||
|
||||
- name: Copy tests from container
|
||||
shell: bash
|
||||
run: |
|
||||
cabal build --enable-tests
|
||||
path=$(cabal list-bin simplex-chat)
|
||||
echo "bin_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-512\(${{ matrix.asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
docker cp builder:/out/simplex-chat-test .
|
||||
|
||||
- name: Unix upload CLI binary to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os != 'windows-latest'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.unix_cli_build.outputs.bin_path }}
|
||||
asset_name: ${{ matrix.asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Unix update CLI binary hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os != 'windows-latest'
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ steps.unix_cli_build.outputs.bin_hash }}
|
||||
|
||||
- name: Setup Java
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'corretto'
|
||||
java-version: '17'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Linux build desktop
|
||||
id: linux_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
- name: Copy CLI from container and prepare it
|
||||
id: linux_cli_prepare
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.cli_asset_name
|
||||
shell: bash
|
||||
run: |
|
||||
docker cp builder:/out/simplex-chat ./${{ matrix.cli_asset_name }}
|
||||
path="${{ github.workspace }}/${{ matrix.cli_asset_name }}"
|
||||
echo "bin_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-512\(${{ matrix.cli_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload CLI
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.cli_asset_name
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.linux_cli_prepare.outputs.bin_path }}
|
||||
bin_name: ${{ matrix.cli_asset_name }}
|
||||
bin_hash: ${{ steps.linux_cli_prepare.outputs.bin_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Desktop
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.desktop_asset_name
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
scripts/desktop/build-lib-linux.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew packageDeb
|
||||
path=$(echo $PWD/release/main/deb/simplex_*_amd64.deb)
|
||||
|
||||
- name: Prepare Desktop
|
||||
id: linux_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.desktop_asset_name
|
||||
shell: bash
|
||||
run: |
|
||||
path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/deb/simplex_*_amd64.deb )
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Linux make AppImage
|
||||
id: linux_appimage_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
shell: bash
|
||||
- name: Upload Desktop
|
||||
uses: ./.github/actions/prepare-release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.desktop_asset_name
|
||||
with:
|
||||
bin_path: ${{ steps.linux_desktop_build.outputs.package_path }}
|
||||
bin_name: ${{ matrix.desktop_asset_name }}
|
||||
bin_hash: ${{ steps.linux_desktop_build.outputs.package_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build AppImage
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.desktop_asset_name && matrix.os == '20.04'
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
scripts/desktop/make-appimage-linux.sh
|
||||
path=$(echo $PWD/apps/multiplatform/release/main/*imple*.AppImage)
|
||||
|
||||
- name: Prepare AppImage
|
||||
id: linux_appimage_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.desktop_asset_name && matrix.os == '20.04'
|
||||
shell: bash
|
||||
run: |
|
||||
path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/*imple*.AppImage)
|
||||
echo "appimage_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "appimage_hash=$(echo SHA2-512\(simplex-desktop-x86_64.AppImage\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Mac build desktop
|
||||
- name: Upload AppImage
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.desktop_asset_name && matrix.os == '20.04'
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.linux_appimage_build.outputs.appimage_path }}
|
||||
bin_name: "simplex-desktop-x86_64.AppImage"
|
||||
bin_hash: ${{ steps.linux_appimage_build.outputs.appimage_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Fix permissions for cache
|
||||
shell: bash
|
||||
run: |
|
||||
sudo chmod -R 777 dist-newstyle ~/.cabal
|
||||
sudo chown -R $(id -u):$(id -g) dist-newstyle ~/.cabal
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
./simplex-chat-test
|
||||
|
||||
# =========================
|
||||
# MacOS Build
|
||||
# =========================
|
||||
|
||||
build-macos:
|
||||
name: "${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}"
|
||||
needs: [maybe-release, variables]
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-latest
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-macos-aarch64
|
||||
desktop_asset_name: simplex-desktop-macos-aarch64.dmg
|
||||
openssl_dir: "/opt/homebrew/opt"
|
||||
- os: macos-13
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-macos-x86-64
|
||||
desktop_asset_name: simplex-desktop-macos-x86_64.dmg
|
||||
openssl_dir: "/usr/local/opt"
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare build
|
||||
uses: ./.github/actions/prepare-build
|
||||
with:
|
||||
java_ver: ${{ needs.variables.outputs.JAVA_VER }}
|
||||
ghc_ver: ${{ matrix.ghc }}
|
||||
os: ${{ matrix.os }}
|
||||
github_ref: ${{ github.ref }}
|
||||
|
||||
- name: Install OpenSSL
|
||||
run: brew install openssl@3.0
|
||||
|
||||
- name: Prepare cabal.project.local
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ignore-project: False" >> cabal.project.local
|
||||
echo "package simplexmq" >> cabal.project.local
|
||||
echo " extra-include-dirs: ${{ matrix.opnessl_dir }}/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: ${{ matrix.openssl_dir}}/openssl@3.0/lib" >> cabal.project.local
|
||||
echo "" >> cabal.project.local
|
||||
echo "package direct-sqlcipher" >> cabal.project.local
|
||||
echo " extra-include-dirs: ${{ matrix.openssl_dir }}/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: ${{ matrix.openssl_dir }}/openssl@3.0/lib" >> cabal.project.local
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
|
||||
- name: Build CLI
|
||||
id: mac_cli_build
|
||||
shell: bash
|
||||
run: |
|
||||
cabal build -j --enable-tests
|
||||
path=$(cabal list-bin simplex-chat)
|
||||
echo "bin_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-512\(${{ matrix.cli_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload CLI
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.mac_cli_build.outputs.bin_path }}
|
||||
bin_name: ${{ matrix.cli_asset_name }}
|
||||
bin_hash: ${{ steps.mac_cli_build.outputs.bin_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Desktop
|
||||
id: mac_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'macos-latest' || matrix.os == 'macos-13')
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
shell: bash
|
||||
env:
|
||||
APPLE_SIMPLEX_SIGNING_KEYCHAIN: ${{ secrets.APPLE_SIMPLEX_SIGNING_KEYCHAIN }}
|
||||
@@ -240,86 +358,59 @@ jobs:
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Linux upload desktop package to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
- name: Upload Desktop
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.linux_desktop_build.outputs.package_path }}
|
||||
asset_name: ${{ matrix.desktop_asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
bin_path: ${{ steps.mac_desktop_build.outputs.package_path }}
|
||||
bin_name: ${{ matrix.desktop_asset_name }}
|
||||
bin_hash: ${{ steps.mac_desktop_build.outputs.package_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Linux update desktop package hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ steps.linux_desktop_build.outputs.package_hash }}
|
||||
|
||||
- name: Linux upload AppImage to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.linux_appimage_build.outputs.appimage_path }}
|
||||
asset_name: simplex-desktop-x86_64.AppImage
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Linux update AppImage hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ steps.linux_appimage_build.outputs.appimage_hash }}
|
||||
|
||||
- name: Mac upload desktop package to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'macos-latest' || matrix.os == 'macos-13')
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.mac_desktop_build.outputs.package_path }}
|
||||
asset_name: ${{ matrix.desktop_asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Mac update desktop package hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'macos-latest' || matrix.os == 'macos-13')
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ steps.mac_desktop_build.outputs.package_hash }}
|
||||
|
||||
- name: Cache unix build
|
||||
uses: actions/cache/save@v3
|
||||
if: matrix.os != 'windows-latest'
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.cache_path }}
|
||||
dist-newstyle
|
||||
key: ${{ steps.restore_cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Unix test
|
||||
if: matrix.os != 'windows-latest'
|
||||
- name: Run tests
|
||||
timeout-minutes: 40
|
||||
shell: bash
|
||||
run: cabal test --test-show-details=direct
|
||||
|
||||
# Unix /
|
||||
# =========================
|
||||
# Windows Build
|
||||
# =========================
|
||||
|
||||
# / Windows
|
||||
# rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing
|
||||
build-windows:
|
||||
name: "${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}"
|
||||
needs: [maybe-release, variables]
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-windows-x86-64
|
||||
desktop_asset_name: simplex-desktop-windows-x86_64.msi
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare build
|
||||
uses: ./.github/actions/prepare-build
|
||||
with:
|
||||
java_ver: ${{ needs.variables.outputs.JAVA_VER }}
|
||||
ghc_ver: ${{ matrix.ghc }}
|
||||
os: ${{ matrix.os }}
|
||||
cache_path: "C:/cabal"
|
||||
github_ref: ${{ github.ref }}
|
||||
|
||||
- name: Configure pagefile (Windows)
|
||||
uses: simplex-chat/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 16GB
|
||||
maximum-size: 16GB
|
||||
disk-root: "C:"
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: msys2/setup-msys2@v2
|
||||
uses: simplex-chat/setup-msys2@v2
|
||||
with:
|
||||
msystem: ucrt64
|
||||
update: true
|
||||
@@ -331,10 +422,9 @@ jobs:
|
||||
toolchain:p
|
||||
cmake:p
|
||||
|
||||
|
||||
- name: Windows build
|
||||
id: windows_build
|
||||
if: matrix.os == 'windows-latest'
|
||||
# rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing
|
||||
- name: Build CLI
|
||||
id: windows_cli_build
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
export PATH=$PATH:/c/ghcup/bin:$(echo /c/tools/ghc-*/bin || echo)
|
||||
@@ -349,70 +439,42 @@ jobs:
|
||||
|
||||
rm -rf dist-newstyle/src/direct-sq*
|
||||
sed -i "s/, unix /--, unix /" simplex-chat.cabal
|
||||
cabal build --enable-tests
|
||||
cabal build -j --enable-tests
|
||||
rm -rf dist-newstyle/src/direct-sq*
|
||||
path=$(cabal list-bin simplex-chat | tail -n 1)
|
||||
echo "bin_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-512\(${{ matrix.asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-512\(${{ matrix.cli_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Windows upload CLI binary to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
- name: Upload CLI
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.windows_build.outputs.bin_path }}
|
||||
asset_name: ${{ matrix.asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
bin_path: ${{ steps.windows_cli_build.outputs.bin_path }}
|
||||
bin_name: ${{ matrix.cli_asset_name }}
|
||||
bin_hash: ${{ steps.windows_cli_build.outputs.bin_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Windows update CLI binary hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ steps.windows_build.outputs.bin_hash }}
|
||||
|
||||
- name: Windows build desktop
|
||||
- name: Build Desktop
|
||||
id: windows_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
export PATH=$PATH:/c/ghcup/bin:$(echo /c/tools/ghc-*/bin || echo)
|
||||
scripts/desktop/build-lib-windows.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew packageMsi
|
||||
rm -rf dist-newstyle/src/direct-sq*
|
||||
path=$(echo $PWD/release/main/msi/*imple*.msi | sed 's#/\([a-z]\)#\1:#' | sed 's#/#\\#g')
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Windows upload desktop package to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
- name: Upload Desktop
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.windows_desktop_build.outputs.package_path }}
|
||||
asset_name: ${{ matrix.desktop_asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Windows update desktop package hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ steps.windows_desktop_build.outputs.package_hash }}
|
||||
|
||||
- name: Cache windows build
|
||||
uses: actions/cache/save@v3
|
||||
if: matrix.os == 'windows-latest'
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.cache_path }}
|
||||
dist-newstyle
|
||||
key: ${{ steps.restore_cache.outputs.cache-primary-key }}
|
||||
|
||||
# Windows /
|
||||
bin_path: ${{ steps.windows_desktop_build.outputs.package_path }}
|
||||
bin_name: ${{ matrix.desktop_asset_name }}
|
||||
bin_hash: ${{ steps.windows_desktop_build.outputs.package_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Reproduce latest release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # every day at 02:00 night
|
||||
|
||||
jobs:
|
||||
reproduce:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Get latest release
|
||||
shell: bash
|
||||
run: |
|
||||
curl --proto '=https' \
|
||||
--tlsv1.2 \
|
||||
-sSf -L \
|
||||
'https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest' \
|
||||
2>/dev/null | \
|
||||
grep -i "tag_name" | \
|
||||
awk -F \" '{print "TAG="$4}' >> $GITHUB_ENV
|
||||
|
||||
- name: Execute reproduce script
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/scripts/reproduce-builds.sh "$TAG"
|
||||
|
||||
- name: Check if build has been reproduced
|
||||
env:
|
||||
url: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_URL }}
|
||||
user: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_USER }}
|
||||
pass: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_PASS }}
|
||||
run: |
|
||||
if [ -f "${GITHUB_WORKSPACE}/$TAG/_sha256sums" ]; then
|
||||
exit 0
|
||||
else
|
||||
curl --proto '=https' --tlsv1.2 -sSf \
|
||||
-u "${user}:${pass}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"title": "👾 GitHub: Runner", "description": "⛔️ '"$TAG"' did not reproduce."}' \
|
||||
"$url"
|
||||
exit 1
|
||||
fi
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
./website/web.sh
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
uses: simplex-chat/actions-gh-pages@v3
|
||||
with:
|
||||
publish_dir: ./website/_site
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# syntax=docker/dockerfile:1.7.0-labs
|
||||
ARG TAG=24.04
|
||||
FROM ubuntu:${TAG} AS build
|
||||
|
||||
### Build stage
|
||||
|
||||
ARG GHC=9.6.3
|
||||
ARG CABAL=3.10.1.0
|
||||
ARG JAVA=17
|
||||
|
||||
ENV TZ=Etc/UTC \
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install curl, git and and simplexmq dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl \
|
||||
libpq-dev \
|
||||
git \
|
||||
sqlite3 \
|
||||
libsqlite3-dev \
|
||||
build-essential \
|
||||
libgmp3-dev \
|
||||
zlib1g-dev \
|
||||
llvm \
|
||||
cmake \
|
||||
llvm-dev \
|
||||
libnuma-dev \
|
||||
libssl-dev \
|
||||
desktop-file-utils \
|
||||
openjdk-${JAVA}-jdk-headless \
|
||||
patchelf \
|
||||
ca-certificates \
|
||||
zip \
|
||||
wget \
|
||||
fuse3 \
|
||||
file \
|
||||
appstream \
|
||||
gpg \
|
||||
unzip &&\
|
||||
export JAVA_HOME=$(update-java-alternatives -l | head -n 1 | awk -F ' ' '{print $NF}') &&\
|
||||
ln -s /bin/fusermount /bin/fusermount3 || :
|
||||
|
||||
# Specify bootstrap Haskell versions
|
||||
ENV BOOTSTRAP_HASKELL_GHC_VERSION=${GHC}
|
||||
ENV BOOTSTRAP_HASKELL_CABAL_VERSION=${CABAL}
|
||||
|
||||
# Do not install Stack
|
||||
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK=true
|
||||
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=true
|
||||
|
||||
# Install ghcup
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh
|
||||
|
||||
# Adjust PATH
|
||||
ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH"
|
||||
|
||||
# Set both as default
|
||||
RUN ghcup set ghc "${GHC}" && \
|
||||
ghcup set cabal "${CABAL}"
|
||||
|
||||
#=====================
|
||||
# Install Android SDK
|
||||
#=====================
|
||||
ARG SDK_VERSION=13114758
|
||||
|
||||
ENV SDK_VERSION=$SDK_VERSION \
|
||||
ANDROID_HOME=/root
|
||||
|
||||
RUN curl -L -o tools.zip "https://dl.google.com/android/repository/commandlinetools-linux-${SDK_VERSION}_latest.zip" && \
|
||||
unzip tools.zip && rm tools.zip && \
|
||||
mv cmdline-tools tools && mkdir "$ANDROID_HOME/cmdline-tools" && mv tools "$ANDROID_HOME/cmdline-tools/" && \
|
||||
ln -s "$ANDROID_HOME/cmdline-tools/tools" "$ANDROID_HOME/cmdline-tools/latest"
|
||||
|
||||
ENV PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/cmdline-tools/tools/bin"
|
||||
|
||||
# https://askubuntu.com/questions/885658/android-sdk-repositories-cfg-could-not-be-loaded
|
||||
RUN mkdir -p ~/.android ~/.gradle && \
|
||||
touch ~/.android/repositories.cfg && \
|
||||
echo 'org.gradle.console=plain' > ~/.gradle/gradle.properties &&\
|
||||
yes | sdkmanager --licenses >/dev/null
|
||||
|
||||
ENV PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools
|
||||
|
||||
WORKDIR /project
|
||||
@@ -1973,7 +1973,7 @@
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -2023,7 +2023,7 @@
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -2065,7 +2065,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
@@ -2085,7 +2085,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
@@ -2110,7 +2110,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = s;
|
||||
@@ -2147,7 +2147,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_CODE_COVERAGE = NO;
|
||||
@@ -2184,7 +2184,7 @@
|
||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -2235,7 +2235,7 @@
|
||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -2286,7 +2286,7 @@
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
@@ -2320,7 +2320,7 @@
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 270;
|
||||
CURRENT_PROJECT_VERSION = 271;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
|
||||
@@ -11,6 +11,7 @@ module Directory.Events
|
||||
( DirectoryEvent (..),
|
||||
DirectoryCmd (..),
|
||||
ADirectoryCmd (..),
|
||||
DirectoryHelpSection (..),
|
||||
DirectoryRole (..),
|
||||
SDirectoryRole (..),
|
||||
crDirectoryEvent,
|
||||
@@ -25,6 +26,7 @@ import qualified Data.Attoparsec.Text as A
|
||||
import Data.Char (isSpace)
|
||||
import Data.Either (fromRight)
|
||||
import Data.Functor (($>))
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Text.Encoding (encodeUtf8)
|
||||
@@ -45,7 +47,7 @@ data DirectoryEvent
|
||||
= DEContactConnected Contact
|
||||
| DEGroupInvitation {contact :: Contact, groupInfo :: GroupInfo, fromMemberRole :: GroupMemberRole, memberRole :: GroupMemberRole}
|
||||
| DEServiceJoinedGroup {contactId :: ContactId, groupInfo :: GroupInfo, hostMember :: GroupMember}
|
||||
| DEGroupUpdated {contactId :: ContactId, fromGroup :: GroupInfo, toGroup :: GroupInfo}
|
||||
| DEGroupUpdated {member :: GroupMember, fromGroup :: GroupInfo, toGroup :: GroupInfo}
|
||||
| DEPendingMember GroupInfo GroupMember
|
||||
| DEPendingMemberMsg GroupInfo GroupMember ChatItemId Text
|
||||
| DEContactRoleChanged GroupInfo ContactId GroupMemberRole -- contactId here is the contact whose role changed
|
||||
@@ -66,7 +68,7 @@ crDirectoryEvent = \case
|
||||
CRContactConnected {contact} -> Just $ DEContactConnected contact
|
||||
CRReceivedGroupInvitation {contact, groupInfo, fromMemberRole, memberRole} -> Just $ DEGroupInvitation {contact, groupInfo, fromMemberRole, memberRole}
|
||||
CRUserJoinedGroup {groupInfo, hostMember} -> (\contactId -> DEServiceJoinedGroup {contactId, groupInfo, hostMember}) <$> memberContactId hostMember
|
||||
CRGroupUpdated {fromGroup, toGroup, member_} -> (\contactId -> DEGroupUpdated {contactId, fromGroup, toGroup}) <$> (memberContactId =<< member_)
|
||||
CRGroupUpdated {fromGroup, toGroup, member_} -> (\member -> DEGroupUpdated {member, fromGroup, toGroup}) <$> member_
|
||||
CRJoinedGroupMember {groupInfo, member = m}
|
||||
| pending m -> Just $ DEPendingMember groupInfo m
|
||||
| otherwise -> Nothing
|
||||
@@ -137,8 +139,11 @@ deriving instance Show (DirectoryCmdTag r)
|
||||
|
||||
data ADirectoryCmdTag = forall r. ADCT (SDirectoryRole r) (DirectoryCmdTag r)
|
||||
|
||||
data DirectoryHelpSection = DHSRegistration | DHSCommands
|
||||
deriving (Show)
|
||||
|
||||
data DirectoryCmd (r :: DirectoryRole) where
|
||||
DCHelp :: DirectoryCmd 'DRUser
|
||||
DCHelp :: DirectoryHelpSection -> DirectoryCmd 'DRUser
|
||||
DCSearchGroup :: Text -> DirectoryCmd 'DRUser
|
||||
DCSearchNext :: DirectoryCmd 'DRUser
|
||||
DCAllGroups :: DirectoryCmd 'DRUser
|
||||
@@ -180,7 +185,7 @@ directoryCmdP =
|
||||
(tagP >>= \(ADCT u t) -> ADC u <$> (cmdP t <|> pure (DCCommandError t)))
|
||||
<|> pure (ADC SDRUser DCUnknownCommand)
|
||||
tagP =
|
||||
A.takeTill (== ' ') >>= \case
|
||||
A.takeTill isSpace >>= \case
|
||||
"help" -> u DCHelp_
|
||||
"h" -> u DCHelp_
|
||||
"next" -> u DCSearchNext_
|
||||
@@ -213,11 +218,19 @@ directoryCmdP =
|
||||
su = pure . ADCT SDRSuperUser
|
||||
cmdP :: DirectoryCmdTag r -> Parser (DirectoryCmd r)
|
||||
cmdP = \case
|
||||
DCHelp_ -> pure DCHelp
|
||||
DCHelp_ -> DCHelp . fromMaybe DHSRegistration <$> optional (A.takeWhile isSpace *> helpSectionP)
|
||||
where
|
||||
helpSectionP =
|
||||
A.takeText >>= \case
|
||||
"registration" -> pure DHSRegistration
|
||||
"r" -> pure DHSRegistration
|
||||
"commands" -> pure DHSCommands
|
||||
"c" -> pure DHSCommands
|
||||
_ -> fail "bad help section"
|
||||
DCSearchNext_ -> pure DCSearchNext
|
||||
DCAllGroups_ -> pure DCAllGroups
|
||||
DCRecentGroups_ -> pure DCRecentGroups
|
||||
DCSubmitGroup_ -> fmap DCSubmitGroup . strDecode . encodeUtf8 <$?> (A.takeWhile1 isSpace *> A.takeText)
|
||||
DCSubmitGroup_ -> fmap DCSubmitGroup . strDecode . encodeUtf8 <$?> (spacesP *> A.takeText)
|
||||
DCConfirmDuplicateGroup_ -> gc DCConfirmDuplicateGroup
|
||||
DCListUserGroups_ -> pure DCListUserGroups
|
||||
DCDeleteGroup_ -> gc DCDeleteGroup
|
||||
@@ -228,7 +241,7 @@ directoryCmdP =
|
||||
DCGroupFilter_ -> do
|
||||
(groupId, displayName_) <- gc_ (,)
|
||||
acceptance_ <-
|
||||
(A.takeWhile (== ' ') >> A.endOfInput) $> Nothing
|
||||
(A.takeWhile isSpace >> A.endOfInput) $> Nothing
|
||||
<|> Just <$> (acceptancePresetsP <|> acceptanceFiltersP)
|
||||
pure $ DCGroupFilter groupId displayName_ acceptance_
|
||||
where
|
||||
@@ -272,15 +285,15 @@ directoryCmdP =
|
||||
where
|
||||
gc f = f <$> (spacesP *> A.decimal) <*> (A.char ':' *> displayNameTextP)
|
||||
gc_ f = f <$> (spacesP *> A.decimal) <*> optional (A.char ':' *> displayNameTextP)
|
||||
-- wordP = spacesP *> A.takeTill (== ' ')
|
||||
spacesP = A.takeWhile1 (== ' ')
|
||||
-- wordP = spacesP *> A.takeTill isSpace
|
||||
spacesP = A.takeWhile1 isSpace
|
||||
|
||||
viewName :: Text -> Text
|
||||
viewName n = if T.any (== ' ') n then "'" <> n <> "'" else n
|
||||
viewName n = if T.any isSpace n then "'" <> n <> "'" else n
|
||||
|
||||
directoryCmdTag :: DirectoryCmd r -> Text
|
||||
directoryCmdTag = \case
|
||||
DCHelp -> "help"
|
||||
DCHelp _ -> "help"
|
||||
DCSearchGroup _ -> "search"
|
||||
DCSearchNext -> "next"
|
||||
DCAllGroups -> "all"
|
||||
|
||||
@@ -48,6 +48,7 @@ import Simplex.Chat.Bot
|
||||
import Simplex.Chat.Bot.KnownContacts
|
||||
import Simplex.Chat.Controller
|
||||
import Simplex.Chat.Core
|
||||
import Simplex.Chat.Markdown (FormattedText (..), Format (..), parseMaybeMarkdownList)
|
||||
import Simplex.Chat.Messages
|
||||
import Simplex.Chat.Options
|
||||
import Simplex.Chat.Protocol (MsgContent (..))
|
||||
@@ -61,6 +62,7 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.View (serializeChatResponse, simplexChatContact, viewContactName, viewGroupName)
|
||||
import Simplex.Messaging.Agent.Store.Common (withTransaction)
|
||||
import Simplex.Messaging.Agent.Protocol (AConnectionRequestUri (..), SConnectionMode (..), sameConnReqContact)
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.TMap (TMap)
|
||||
@@ -185,13 +187,13 @@ useMemberFilter img_ = \case
|
||||
Nothing -> False
|
||||
|
||||
readBlockedWordsConfig :: DirectoryOpts -> IO BlockedWordsConfig
|
||||
readBlockedWordsConfig DirectoryOpts {blockedFragmentsFile, blockedWordsFile, nameSpellingFile, blockedExtensionRules} = do
|
||||
readBlockedWordsConfig DirectoryOpts {blockedFragmentsFile, blockedWordsFile, nameSpellingFile, blockedExtensionRules, testing} = do
|
||||
extensionRules <- maybe (pure []) (fmap read . readFile) blockedExtensionRules
|
||||
spelling <- maybe (pure M.empty) (fmap (M.fromList . read) . readFile) nameSpellingFile
|
||||
blockedFragments <- S.fromList <$> maybe (pure []) (fmap T.lines . T.readFile) blockedFragmentsFile
|
||||
bws <- maybe (pure []) (fmap lines . readFile) blockedWordsFile
|
||||
let blockedWords = S.fromList $ concatMap (wordVariants extensionRules) bws
|
||||
putStrLn $ "Blocked fragments: " <> show (length blockedFragments) <> ", blocked words: " <> show (length blockedWords) <> ", spelling rules: " <> show (M.size spelling)
|
||||
unless testing $ putStrLn $ "Blocked fragments: " <> show (length blockedFragments) <> ", blocked words: " <> show (length blockedWords) <> ", spelling rules: " <> show (M.size spelling)
|
||||
pure BlockedWordsConfig {blockedFragments, blockedWords, extensionRules, spelling}
|
||||
|
||||
directoryServiceEvent :: DirectoryStore -> DirectoryOpts -> ServiceState -> User -> ChatController -> ChatResponse -> IO ()
|
||||
@@ -200,7 +202,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
|
||||
DEContactConnected ct -> deContactConnected ct
|
||||
DEGroupInvitation {contact = ct, groupInfo = g, fromMemberRole, memberRole} -> deGroupInvitation ct g fromMemberRole memberRole
|
||||
DEServiceJoinedGroup ctId g owner -> deServiceJoinedGroup ctId g owner
|
||||
DEGroupUpdated {contactId, fromGroup, toGroup} -> deGroupUpdated contactId fromGroup toGroup
|
||||
DEGroupUpdated {member, fromGroup, toGroup} -> deGroupUpdated member fromGroup toGroup
|
||||
DEPendingMember g m -> dePendingMember g m
|
||||
DEPendingMemberMsg g m ciId t -> dePendingMemberMsg g m ciId t
|
||||
DEContactRoleChanged g ctId role -> deContactRoleChanged g ctId role
|
||||
@@ -253,17 +255,25 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
|
||||
getDuplicateGroup GroupInfo {groupId, groupProfile = GroupProfile {displayName, fullName}} =
|
||||
getGroups fullName >>= mapM duplicateGroup
|
||||
where
|
||||
sameGroup (GroupInfo {groupId = gId, groupProfile = GroupProfile {displayName = n, fullName = fn}}, _) =
|
||||
gId /= groupId && n == displayName && fn == fullName
|
||||
sameGroupNotRemoved (g@GroupInfo {groupId = gId, groupProfile = GroupProfile {displayName = n, fullName = fn}}, _) =
|
||||
gId /= groupId && n == displayName && fn == fullName && not (memberRemoved $ membership g)
|
||||
duplicateGroup [] = pure DGUnique
|
||||
duplicateGroup groups = do
|
||||
let gs = filter sameGroup groups
|
||||
let gs = filter sameGroupNotRemoved groups
|
||||
if null gs
|
||||
then pure DGUnique
|
||||
else do
|
||||
(lgs, rgs) <- atomically $ (,) <$> readTVar (listedGroups st) <*> readTVar (reservedGroups st)
|
||||
let reserved = any (\(GroupInfo {groupId = gId}, _) -> gId `S.member` lgs || gId `S.member` rgs) gs
|
||||
pure $ if reserved then DGReserved else DGRegistered
|
||||
if reserved
|
||||
then pure DGReserved
|
||||
else do
|
||||
removed <- foldM (\r -> fmap (r &&) . isGroupRemoved) True gs
|
||||
pure $ if removed then DGUnique else DGRegistered
|
||||
isGroupRemoved (GroupInfo {groupId = gId}, _) =
|
||||
getGroupReg st gId >>= \case
|
||||
Just GroupReg {groupRegStatus} -> groupRemoved <$> readTVarIO groupRegStatus
|
||||
Nothing -> pure True
|
||||
|
||||
processInvitation :: Contact -> GroupInfo -> IO ()
|
||||
processInvitation ct g@GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = do
|
||||
@@ -354,78 +364,95 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
|
||||
_ -> notifyOwner gr $ unexpectedError "can't create group link"
|
||||
_ -> notifyOwner gr $ unexpectedError "can't create group link"
|
||||
|
||||
deGroupUpdated :: ContactId -> GroupInfo -> GroupInfo -> IO ()
|
||||
deGroupUpdated ctId fromGroup toGroup = do
|
||||
deGroupUpdated :: GroupMember -> GroupInfo -> GroupInfo -> IO ()
|
||||
deGroupUpdated m@GroupMember {memberProfile = LocalProfile {displayName = mName}} fromGroup toGroup = do
|
||||
logInfo $ "group updated " <> viewGroupName toGroup
|
||||
unless (sameProfile p p') $ do
|
||||
withGroupReg toGroup "group updated" $ \gr -> do
|
||||
let userGroupRef = userGroupReference gr toGroup
|
||||
byMember = case memberContactId m of
|
||||
Just ctId | ctId `isOwner` gr -> "" -- group registration owner, not any group owner.
|
||||
_ -> " by " <> mName -- owner notification from directory will include the name.
|
||||
readTVarIO (groupRegStatus gr) >>= \case
|
||||
GRSPendingConfirmation -> pure ()
|
||||
GRSProposed -> pure ()
|
||||
GRSPendingUpdate ->
|
||||
groupProfileUpdate >>= \case
|
||||
GPNoServiceLink ->
|
||||
when (ctId `isOwner` gr) $ notifyOwner gr $ "The profile updated for " <> userGroupRef <> ", but the group link is not added to the welcome message."
|
||||
GPServiceLinkAdded
|
||||
| ctId `isOwner` gr -> groupLinkAdded gr
|
||||
| otherwise -> notifyOwner gr "The group link is added by another group member, your registration will not be processed.\n\nPlease update the group profile yourself."
|
||||
GPServiceLinkRemoved -> when (ctId `isOwner` gr) $ notifyOwner gr $ "The group link of " <> userGroupRef <> " is removed from the welcome message, please add it."
|
||||
GPHasServiceLink -> when (ctId `isOwner` gr) $ groupLinkAdded gr
|
||||
notifyOwner gr $ "The profile updated for " <> userGroupRef <> byMember <> ", but the group link is not added to the welcome message."
|
||||
GPServiceLinkAdded -> groupLinkAdded gr byMember
|
||||
GPServiceLinkRemoved ->
|
||||
notifyOwner gr $
|
||||
"The group link of " <> userGroupRef <> " is removed from the welcome message" <> byMember <> ", please add it."
|
||||
GPHasServiceLink -> groupLinkAdded gr byMember
|
||||
GPServiceLinkError -> do
|
||||
when (ctId `isOwner` gr) $ notifyOwner gr $ "Error: " <> serviceName <> " has no group link for " <> userGroupRef <> ". Please report the error to the developers."
|
||||
notifyOwner gr $
|
||||
("Error: " <> serviceName <> " has no group link for " <> userGroupRef)
|
||||
<> " after profile was updated" <> byMember <> ". Please report the error to the developers."
|
||||
logError $ "Error: no group link for " <> userGroupRef
|
||||
GRSPendingApproval n -> processProfileChange gr $ n + 1
|
||||
GRSActive -> processProfileChange gr 1
|
||||
GRSSuspended -> processProfileChange gr 1
|
||||
GRSSuspendedBadRoles -> processProfileChange gr 1
|
||||
GRSPendingApproval n -> processProfileChange gr byMember $ n + 1
|
||||
GRSActive -> processProfileChange gr byMember 1
|
||||
GRSSuspended -> processProfileChange gr byMember 1
|
||||
GRSSuspendedBadRoles -> processProfileChange gr byMember 1
|
||||
GRSRemoved -> pure ()
|
||||
where
|
||||
isInfix l d_ = l `T.isInfixOf` fromMaybe "" d_
|
||||
GroupInfo {groupId, groupProfile = p} = fromGroup
|
||||
GroupInfo {groupProfile = p'} = toGroup
|
||||
sameProfile
|
||||
GroupProfile {displayName = n, fullName = fn, image = i, description = d}
|
||||
GroupProfile {displayName = n', fullName = fn', image = i', description = d'} =
|
||||
n == n' && fn == fn' && i == i' && d == d'
|
||||
groupLinkAdded gr = do
|
||||
groupLinkAdded gr byMember = do
|
||||
getDuplicateGroup toGroup >>= \case
|
||||
Nothing -> notifyOwner gr "Error: getDuplicateGroup. Please notify the developers."
|
||||
Just DGReserved -> notifyOwner gr $ groupAlreadyListed toGroup
|
||||
_ -> do
|
||||
let gaId = 1
|
||||
setGroupStatus st gr $ GRSPendingApproval gaId
|
||||
notifyOwner gr $ "Thank you! The group link for " <> userGroupReference gr toGroup <> " is added to the welcome message.\nYou will be notified once the group is added to the directory - it may take up to 48 hours."
|
||||
notifyOwner gr $
|
||||
("Thank you! The group link for " <> userGroupReference gr toGroup <> " is added to the welcome message" <> byMember)
|
||||
<> ".\nYou will be notified once the group is added to the directory - it may take up to 48 hours."
|
||||
checkRolesSendToApprove gr gaId
|
||||
processProfileChange gr n' = do
|
||||
processProfileChange gr byMember n' = do
|
||||
setGroupStatus st gr GRSPendingUpdate
|
||||
let userGroupRef = userGroupReference gr toGroup
|
||||
groupRef = groupReference toGroup
|
||||
groupProfileUpdate >>= \case
|
||||
GPNoServiceLink -> do
|
||||
notifyOwner gr $ "The group profile is updated " <> userGroupRef <> ", but no link is added to the welcome message.\n\nThe group will remain hidden from the directory until the group link is added and the group is re-approved."
|
||||
notifyOwner gr $
|
||||
("The group profile is updated for " <> userGroupRef <> byMember <> ", but no link is added to the welcome message.\n\n")
|
||||
<> "The group will remain hidden from the directory until the group link is added and the group is re-approved."
|
||||
GPServiceLinkRemoved -> do
|
||||
notifyOwner gr $ "The group link for " <> userGroupRef <> " is removed from the welcome message.\n\nThe group is hidden from the directory until the group link is added and the group is re-approved."
|
||||
notifyOwner gr $
|
||||
("The group link for " <> userGroupRef <> " is removed from the welcome message" <> byMember)
|
||||
<> ".\n\nThe group is hidden from the directory until the group link is added and the group is re-approved."
|
||||
notifyAdminUsers $ "The group link is removed from " <> groupRef <> ", de-listed."
|
||||
GPServiceLinkAdded -> do
|
||||
setGroupStatus st gr $ GRSPendingApproval n'
|
||||
notifyOwner gr $ "The group link is added to " <> userGroupRef <> "!\nIt is hidden from the directory until approved."
|
||||
notifyAdminUsers $ "The group link is added to " <> groupRef <> "."
|
||||
notifyOwner gr $
|
||||
("The group link is added to " <> userGroupRef <> byMember)
|
||||
<> "!\nIt is hidden from the directory until approved."
|
||||
notifyAdminUsers $ "The group link is added to " <> groupRef <> byMember <> "."
|
||||
checkRolesSendToApprove gr n'
|
||||
GPHasServiceLink -> do
|
||||
setGroupStatus st gr $ GRSPendingApproval n'
|
||||
notifyOwner gr $ "The group " <> userGroupRef <> " is updated!\nIt is hidden from the directory until approved."
|
||||
notifyAdminUsers $ "The group " <> groupRef <> " is updated."
|
||||
notifyOwner gr $
|
||||
("The group " <> userGroupRef <> " is updated" <> byMember)
|
||||
<> "!\nIt is hidden from the directory until approved."
|
||||
notifyAdminUsers $ "The group " <> groupRef <> " is updated" <> byMember <> "."
|
||||
checkRolesSendToApprove gr n'
|
||||
GPServiceLinkError -> logError $ "Error: no group link for " <> groupRef <> " pending approval."
|
||||
groupProfileUpdate = profileUpdate <$> sendChatCmd cc (APIGetGroupLink groupId)
|
||||
where
|
||||
profileUpdate = \case
|
||||
CRGroupLink {connReqContact} ->
|
||||
let groupLink1 = strEncodeTxt connReqContact
|
||||
groupLink2 = strEncodeTxt $ simplexChatContact connReqContact
|
||||
hadLinkBefore = groupLink1 `isInfix` description p || groupLink2 `isInfix` description p
|
||||
hasLinkNow = groupLink1 `isInfix` description p' || groupLink2 `isInfix` description p'
|
||||
let hadLinkBefore = profileHasGroupLink fromGroup
|
||||
hasLinkNow = profileHasGroupLink toGroup
|
||||
profileHasGroupLink GroupInfo {groupProfile = gp} =
|
||||
maybe False (any ftHasLink) $ parseMaybeMarkdownList =<< description gp
|
||||
ftHasLink = \case
|
||||
FormattedText (Just SimplexLink {simplexUri = ACR SCMContact cr'}) _ -> sameConnReqContact connReqContact cr'
|
||||
_ -> False
|
||||
in if
|
||||
| hadLinkBefore && hasLinkNow -> GPHasServiceLink
|
||||
| hadLinkBefore -> GPServiceLinkRemoved
|
||||
@@ -617,7 +644,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
|
||||
|
||||
deUserCommand :: Contact -> ChatItemId -> DirectoryCmd 'DRUser -> IO ()
|
||||
deUserCommand ct ciId = \case
|
||||
DCHelp ->
|
||||
DCHelp DHSRegistration ->
|
||||
sendMessage cc ct $
|
||||
"You must be the owner to add the group to the directory:\n\
|
||||
\1. Invite "
|
||||
@@ -628,7 +655,16 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
|
||||
<> " bot will create a public group link for the new members to join even when you are offline.\n\
|
||||
\3. You will then need to add this link to the group welcome message.\n\
|
||||
\4. Once the link is added, service admins will approve the group (it can take up to 48 hours), and everybody will be able to find it in directory.\n\n\
|
||||
\Start from inviting the bot to your group as admin - it will guide you through the process"
|
||||
\Start from inviting the bot to your group as admin - it will guide you through the process."
|
||||
DCHelp DHSCommands ->
|
||||
sendMessage cc ct $
|
||||
"*/help commands* - receive this help message.\n\
|
||||
\*/help* - how to register your group to be added to directory.\n\
|
||||
\*/list* - list the groups you registered.\n\
|
||||
\*/delete <ID>:<NAME>* - remove the group you submitted from directory, with _ID_ and _name_ as shown by */list* command.\n\
|
||||
\*/role <ID>* - view and set default member role for your group.\n\
|
||||
\*/filter <ID>* - view and set spam filter settings for group.\n\n\
|
||||
\To search for groups, send the search text."
|
||||
DCSearchGroup s -> withFoundListedGroups (Just s) $ sendSearchResults s
|
||||
DCSearchNext ->
|
||||
atomically (TM.lookup (contactId' ct) searchRequests) >>= \case
|
||||
@@ -667,10 +703,10 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
|
||||
"0 registered groups for " <> localDisplayName' ct <> " (" <> tshow (contactId' ct) <> ") out of " <> tshow total <> " registrations"
|
||||
void . forkIO $ forM_ (reverse grs) $ \gr@GroupReg {userGroupRegId} ->
|
||||
sendGroupInfo ct gr userGroupRegId Nothing
|
||||
DCDeleteGroup ugrId gName ->
|
||||
withUserGroupReg ugrId gName $ \GroupInfo {groupProfile = GroupProfile {displayName}} gr -> do
|
||||
DCDeleteGroup gId gName ->
|
||||
(if isAdmin then withGroupAndReg sendReply else withUserGroupReg) gId gName $ \GroupInfo {groupProfile = GroupProfile {displayName}} gr -> do
|
||||
delGroupReg st gr
|
||||
sendReply $ "Your group " <> displayName <> " is deleted from the directory"
|
||||
sendReply $ (if isAdmin then "The group " else "Your group ") <> displayName <> " is deleted from the directory"
|
||||
DCMemberRole gId gName_ mRole_ ->
|
||||
(if isAdmin then withGroupAndReg_ sendReply else withUserGroupReg_) gId gName_ $ \g _gr -> do
|
||||
let GroupInfo {groupProfile = GroupProfile {displayName = n}} = g
|
||||
@@ -802,9 +838,12 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
|
||||
setGroupStatus st gr GRSActive
|
||||
let approved = "The group " <> userGroupReference' gr n <> " is approved"
|
||||
notifyOwner gr $
|
||||
(approved <> " and listed in directory!\n")
|
||||
(approved <> " and listed in directory - please moderate it!\n")
|
||||
<> "Please note: if you change the group profile it will be hidden from directory until it is re-approved.\n\n"
|
||||
<> ("Use */filter " <> tshow ugrId <> "* to configure anti-spam filter and */role " <> tshow ugrId <> "* to set default member role.")
|
||||
<> "Supported commands:\n"
|
||||
<> ("- */filter " <> tshow ugrId <> "* - to configure anti-spam filter.\n")
|
||||
<> ("- */role " <> tshow ugrId <> "* - to set default member role.\n")
|
||||
<> "- */help commands* - other commands."
|
||||
invited <-
|
||||
forM ownersGroup $ \og@KnownGroup {localDisplayName = ogName} -> do
|
||||
inviteToOwnersGroup og gr $ \case
|
||||
|
||||
@@ -25,6 +25,7 @@ module Directory.Store
|
||||
filterListedGroups,
|
||||
groupRegStatusText,
|
||||
pendingApproval,
|
||||
groupRemoved,
|
||||
fromCustomData,
|
||||
toCustomData,
|
||||
noJoinFilter,
|
||||
@@ -139,13 +140,19 @@ data GroupRegStatus
|
||||
| GRSSuspended
|
||||
| GRSSuspendedBadRoles
|
||||
| GRSRemoved
|
||||
deriving (Show)
|
||||
|
||||
pendingApproval :: GroupRegStatus -> Bool
|
||||
pendingApproval = \case
|
||||
GRSPendingApproval _ -> True
|
||||
_ -> False
|
||||
|
||||
data DirectoryStatus = DSListed | DSReserved | DSRegistered
|
||||
groupRemoved :: GroupRegStatus -> Bool
|
||||
groupRemoved = \case
|
||||
GRSRemoved -> True
|
||||
_ -> False
|
||||
|
||||
data DirectoryStatus = DSListed | DSReserved | DSRegistered | DSRemoved
|
||||
|
||||
groupRegStatusText :: GroupRegStatus -> Text
|
||||
groupRegStatusText = \case
|
||||
@@ -163,6 +170,7 @@ grDirectoryStatus = \case
|
||||
GRSActive -> DSListed
|
||||
GRSSuspended -> DSReserved
|
||||
GRSSuspendedBadRoles -> DSReserved
|
||||
GRSRemoved -> DSRemoved
|
||||
_ -> DSRegistered
|
||||
|
||||
$(JQ.deriveJSON (enumJSON $ dropPrefix "PC") ''ProfileCondition)
|
||||
@@ -200,8 +208,9 @@ addGroupReg st ct GroupInfo {groupId} grStatus = do
|
||||
| otherwise = mx
|
||||
|
||||
delGroupReg :: DirectoryStore -> GroupReg -> IO ()
|
||||
delGroupReg st GroupReg {dbGroupId = gId} = do
|
||||
delGroupReg st GroupReg {dbGroupId = gId, groupRegStatus} = do
|
||||
logGDelete st gId
|
||||
atomically $ writeTVar groupRegStatus GRSRemoved
|
||||
atomically $ unlistGroup st gId
|
||||
atomically $ modifyTVar' (groupRegs st) $ filter ((gId ==) . dbGroupId)
|
||||
|
||||
@@ -216,6 +225,7 @@ setGroupStatus st gr grStatus = do
|
||||
DSListed -> listGroup
|
||||
DSReserved -> reserveGroup
|
||||
DSRegistered -> unlistGroup
|
||||
DSRemoved -> unlistGroup
|
||||
|
||||
setGroupRegOwner :: DirectoryStore -> GroupReg -> GroupMember -> IO ()
|
||||
setGroupRegOwner st gr owner = do
|
||||
@@ -390,6 +400,7 @@ mkDirectoryStore h groups =
|
||||
DSListed -> (grs', S.insert gId listed, reserved)
|
||||
DSReserved -> (grs', listed, S.insert gId reserved)
|
||||
DSRegistered -> (grs', listed, reserved)
|
||||
DSRemoved -> (grs, listed, reserved)
|
||||
|
||||
mkDirectoryStore_ :: Maybe Handle -> ([GroupReg], Set GroupId, Set GroupId) -> IO DirectoryStore
|
||||
mkDirectoryStore_ h (grs, listed, reserved) = do
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: aace3fd2fb146097304b09ef07ff613a8b255f67
|
||||
tag: 9abc0fa88dd70a7e30a041697335bb663c1140b7
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
#
|
||||
# Taken from: https://github.com/apache/arrow/blob/main/ci/scripts/util_free_space.sh
|
||||
|
||||
set -eux
|
||||
|
||||
df -h
|
||||
echo "::group::/usr/local/*"
|
||||
du -hsc /usr/local/*
|
||||
echo "::endgroup::"
|
||||
# ~1GB
|
||||
sudo rm -rf \
|
||||
/usr/local/aws-sam-cil \
|
||||
/usr/local/julia* || :
|
||||
echo "::group::/usr/local/bin/*"
|
||||
du -hsc /usr/local/bin/*
|
||||
echo "::endgroup::"
|
||||
# ~1GB (From 1.2GB to 214MB)
|
||||
sudo rm -rf \
|
||||
/usr/local/bin/aliyun \
|
||||
/usr/local/bin/azcopy \
|
||||
/usr/local/bin/bicep \
|
||||
/usr/local/bin/cmake-gui \
|
||||
/usr/local/bin/cpack \
|
||||
/usr/local/bin/helm \
|
||||
/usr/local/bin/hub \
|
||||
/usr/local/bin/kubectl \
|
||||
/usr/local/bin/minikube \
|
||||
/usr/local/bin/node \
|
||||
/usr/local/bin/packer \
|
||||
/usr/local/bin/pulumi* \
|
||||
/usr/local/bin/sam \
|
||||
/usr/local/bin/stack \
|
||||
/usr/local/bin/terraform || :
|
||||
# 142M
|
||||
sudo rm -rf /usr/local/bin/oc || : \
|
||||
echo "::group::/usr/local/share/*"
|
||||
du -hsc /usr/local/share/*
|
||||
echo "::endgroup::"
|
||||
# 506MB
|
||||
sudo rm -rf /usr/local/share/chromium || :
|
||||
# 1.3GB
|
||||
sudo rm -rf /usr/local/share/powershell || :
|
||||
echo "::group::/usr/local/lib/*"
|
||||
du -hsc /usr/local/lib/*
|
||||
echo "::endgroup::"
|
||||
# 15GB
|
||||
sudo rm -rf /usr/local/lib/android || :
|
||||
# 341MB
|
||||
sudo rm -rf /usr/local/lib/heroku || :
|
||||
# 1.2GB
|
||||
sudo rm -rf /usr/local/lib/node_modules || :
|
||||
echo "::group::/opt/*"
|
||||
du -hsc /opt/*
|
||||
echo "::endgroup::"
|
||||
# 679MB
|
||||
sudo rm -rf /opt/az || :
|
||||
echo "::group::/opt/microsoft/*"
|
||||
du -hsc /opt/microsoft/*
|
||||
echo "::endgroup::"
|
||||
# 197MB
|
||||
sudo rm -rf /opt/microsoft/powershell || :
|
||||
echo "::group::/opt/hostedtoolcache/*"
|
||||
du -hsc /opt/hostedtoolcache/*
|
||||
echo "::endgroup::"
|
||||
# 5.3GB
|
||||
sudo rm -rf /opt/hostedtoolcache/CodeQL || :
|
||||
# 1.4GB
|
||||
sudo rm -rf /opt/hostedtoolcache/go || :
|
||||
# 489MB
|
||||
sudo rm -rf /opt/hostedtoolcache/PyPy || :
|
||||
# 376MB
|
||||
sudo rm -rf /opt/hostedtoolcache/node || :
|
||||
# Remove Web browser packages
|
||||
sudo apt purge -y \
|
||||
firefox \
|
||||
google-chrome-stable \
|
||||
microsoft-edge-stable
|
||||
df -h
|
||||
@@ -38,6 +38,27 @@
|
||||
</description>
|
||||
|
||||
<releases>
|
||||
<release version="6.3.1" date="2025-03-31">
|
||||
<url type="details">https://simplex.chat/blog/20250308-simplex-chat-v6-3-new-user-experience-safety-in-public-groups.html</url>
|
||||
<description>
|
||||
<p>New in v6.3.1:</p>
|
||||
<ul>
|
||||
<li>scrolling/navigation improvements.</li>
|
||||
<li>faster onboarding (conditions and operators are combined to one screen).</li>
|
||||
</ul>
|
||||
<p>New in v6.3.0:</p>
|
||||
<ul>
|
||||
<li>Mention members and get notified when mentioned.</li>
|
||||
<li>Send private reports to moderators.</li>
|
||||
<li>Delete, block and change role for multiple members at once</li>
|
||||
<li>Faster sending messages and faster deletion.</li>
|
||||
<li>Organize chats into lists to keep track of what's important.</li>
|
||||
<li>Jump to found and forwarded messages.</li>
|
||||
<li>Private media file names.</li>
|
||||
<li>Message expiration in chats.</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="6.3.0" date="2025-03-08">
|
||||
<url type="details">https://simplex.chat/blog/20250308-simplex-chat-v6-3-new-user-experience-safety-in-public-groups.html</url>
|
||||
<description>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."aace3fd2fb146097304b09ef07ff613a8b255f67" = "0iqdarkvlakk4xmrqsg62z0vhs3kwm02l8vpr383vf8q2hd7ky75";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."9abc0fa88dd70a7e30a041697335bb663c1140b7" = "0gaqqvhb5s9xw5mq2iy8swp7w34zrkwkbjlyhggz2q9nr5680z84";
|
||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
TAG="$1"
|
||||
|
||||
tempdir="$(mktemp -d)"
|
||||
init_dir="$PWD"
|
||||
|
||||
repo_name="simplex-chat"
|
||||
repo="https://github.com/simplex-chat/${repo_name}"
|
||||
|
||||
cabal_local='ignore-project: False
|
||||
package direct-sqlcipher
|
||||
flags: +openssl'
|
||||
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
cleanup() {
|
||||
docker exec -t builder sh -c 'rm -rf ./dist-newstyle' 2>/dev/null || :
|
||||
rm -rf -- "$tempdir"
|
||||
docker rm --force builder 2>/dev/null || :
|
||||
docker image rm local 2>/dev/null || :
|
||||
cd "$init_dir"
|
||||
}
|
||||
trap 'cleanup' EXIT INT
|
||||
|
||||
mkdir -p "$init_dir/$TAG/from-source" "$init_dir/$TAG/prebuilt"
|
||||
|
||||
git -C "$tempdir" clone "$repo.git" &&\
|
||||
cd "$tempdir/${repo_name}" &&\
|
||||
git checkout "$TAG"
|
||||
|
||||
for os in 20.04 22.04; do
|
||||
os_url="$(printf '%s' "$os" | tr '.' '_')"
|
||||
|
||||
# Build image
|
||||
docker build \
|
||||
--no-cache \
|
||||
--build-arg TAG=${os} \
|
||||
--build-arg GHC=9.6.3 \
|
||||
-f "$tempdir/${repo_name}/Dockerfile.build" \
|
||||
-t local \
|
||||
.
|
||||
|
||||
printf '%s' "$cabal_local" > "$tempdir/${repo_name}/cabal.project.local"
|
||||
|
||||
# Run container in background
|
||||
docker run -t -d \
|
||||
--name builder \
|
||||
-v "$tempdir/${repo_name}:/project" \
|
||||
local
|
||||
|
||||
docker exec \
|
||||
-t \
|
||||
builder \
|
||||
sh -c 'cabal clean && cabal update && cabal build -j --enable-tests && mkdir -p /out && for i in simplex-chat; do bin=$(find /project/dist-newstyle -name "$i" -type f -executable) && chmod +x "$bin" && mv "$bin" /out/; done && strip /out/simplex-chat'
|
||||
|
||||
docker cp \
|
||||
builder:/out/simplex-chat \
|
||||
"$init_dir/$TAG/from-source/simplex-chat-ubuntu-${os_url}-x86-64"
|
||||
|
||||
# Download prebuilt postgresql binary
|
||||
curl -L \
|
||||
--output-dir "$init_dir/$TAG/prebuilt/" \
|
||||
-O \
|
||||
"$repo/releases/download/${TAG}/simplex-chat-ubuntu-${os_url}-x86-64"
|
||||
|
||||
# Important! Remove dist-newstyle for the next interation
|
||||
docker exec \
|
||||
-t \
|
||||
builder \
|
||||
sh -c 'rm -rf ./dist-newstyle'
|
||||
|
||||
# Also restore git to previous state
|
||||
git reset --hard && git clean -dfx
|
||||
|
||||
# Stop containers, delete images
|
||||
docker stop builder
|
||||
docker rm --force builder
|
||||
docker image rm local
|
||||
done
|
||||
|
||||
# Cleanup
|
||||
rm -rf -- "$tempdir"
|
||||
cd "$init_dir"
|
||||
|
||||
# Final stage: compare hashes
|
||||
|
||||
# Path to binaries
|
||||
path_bin="$init_dir/$TAG"
|
||||
|
||||
# Assume everything is okay for now
|
||||
bad=0
|
||||
|
||||
# Check hashes for all binaries
|
||||
for file in "$path_bin"/from-source/*; do
|
||||
# Extract binary name
|
||||
app="$(basename $file)"
|
||||
|
||||
# Compute hash for compiled binary
|
||||
compiled=$(sha256sum "$path_bin/from-source/$app" | awk '{print $1}')
|
||||
# Compute hash for prebuilt binary
|
||||
prebuilt=$(sha256sum "$path_bin/prebuilt/$app" | awk '{print $1}')
|
||||
|
||||
# Compare
|
||||
if [ "$compiled" != "$prebuilt" ]; then
|
||||
# If hashes doesn't match, set bad...
|
||||
bad=1
|
||||
|
||||
# ... and print affected binary
|
||||
printf "%s - sha256sum hash doesn't match\n" "$app"
|
||||
fi
|
||||
done
|
||||
|
||||
# If everything is still okay, compute checksums file
|
||||
if [ "$bad" = 0 ]; then
|
||||
sha256sum "$path_bin"/from-source/* | sed -e "s|$PWD/||g" -e 's|from-source/||g' > "$path_bin/_sha256sums"
|
||||
|
||||
printf 'Checksums computed - %s\n' "$path_bin/_sha256sums"
|
||||
fi
|
||||
+1
-1
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 6.3.1.1
|
||||
version: 6.3.2.0
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
@@ -49,7 +49,7 @@ data Format
|
||||
| Secret
|
||||
| Colored {color :: FormatColor}
|
||||
| Uri
|
||||
| SimplexLink {linkType :: SimplexLinkType, simplexUri :: Text, smpHosts :: NonEmpty Text}
|
||||
| SimplexLink {linkType :: SimplexLinkType, simplexUri :: AConnectionRequestUri, smpHosts :: NonEmpty Text}
|
||||
| Mention {memberName :: Text}
|
||||
| Email
|
||||
| Phone
|
||||
@@ -255,12 +255,12 @@ markdownP = mconcat <$> A.many' fragmentP
|
||||
noFormat = pure . unmarked
|
||||
simplexUriFormat :: AConnectionRequestUri -> Format
|
||||
simplexUriFormat = \case
|
||||
ACR _ (CRContactUri crData) ->
|
||||
let uri = safeDecodeUtf8 . strEncode $ CRContactUri crData {crScheme = SSSimplex}
|
||||
in SimplexLink (linkType' crData) uri $ uriHosts crData
|
||||
ACR _ (CRInvitationUri crData e2e) ->
|
||||
let uri = safeDecodeUtf8 . strEncode $ CRInvitationUri crData {crScheme = SSSimplex} e2e
|
||||
in SimplexLink XLInvitation uri $ uriHosts crData
|
||||
ACR m (CRContactUri crData) ->
|
||||
let cReq = ACR m $ CRContactUri crData {crScheme = SSSimplex}
|
||||
in SimplexLink (linkType' crData) cReq $ uriHosts crData
|
||||
ACR m (CRInvitationUri crData e2e) ->
|
||||
let cReq = ACR m $ CRInvitationUri crData {crScheme = SSSimplex} e2e
|
||||
in SimplexLink XLInvitation cReq $ uriHosts crData
|
||||
where
|
||||
uriHosts ConnReqUriData {crSmpQueues} = L.map (safeDecodeUtf8 . strEncode) $ sconcat $ L.map (host . qServer) crSmpQueues
|
||||
linkType' ConnReqUriData {crClientData} = case crClientData >>= decodeJSON of
|
||||
|
||||
@@ -7,11 +7,14 @@ module Simplex.Chat.Options.Postgres where
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Foreign.C.String
|
||||
import Options.Applicative
|
||||
import Numeric.Natural (Natural)
|
||||
import Simplex.Messaging.Agent.Store.Interface (DBOpts (..))
|
||||
|
||||
data ChatDbOpts = ChatDbOpts
|
||||
{ dbConnstr :: String,
|
||||
dbSchemaPrefix :: String
|
||||
dbSchemaPrefix :: String,
|
||||
dbPoolSize :: Natural,
|
||||
dbCreateSchema :: Bool
|
||||
}
|
||||
|
||||
chatDbOptsP :: FilePath -> String -> Parser ChatDbOpts
|
||||
@@ -33,16 +36,32 @@ chatDbOptsP _appDir defaultDbName = do
|
||||
<> value "simplex_v1"
|
||||
<> showDefault
|
||||
)
|
||||
pure ChatDbOpts {dbConnstr, dbSchemaPrefix}
|
||||
dbPoolSize <-
|
||||
option
|
||||
auto
|
||||
( long "pool-size"
|
||||
<> metavar "DB_POOL_SIZE"
|
||||
<> help "Database connection pool size"
|
||||
<> value 1
|
||||
<> showDefault
|
||||
)
|
||||
dbCreateSchema <-
|
||||
switch
|
||||
( long "create-schema"
|
||||
<> help "Create database schema when it does not exist"
|
||||
)
|
||||
pure ChatDbOpts {dbConnstr, dbSchemaPrefix, dbPoolSize, dbCreateSchema}
|
||||
|
||||
dbString :: ChatDbOpts -> String
|
||||
dbString ChatDbOpts {dbConnstr} = dbConnstr
|
||||
|
||||
toDBOpts :: ChatDbOpts -> String -> Bool -> DBOpts
|
||||
toDBOpts ChatDbOpts {dbConnstr, dbSchemaPrefix} dbSuffix _keepKey =
|
||||
toDBOpts ChatDbOpts {dbConnstr, dbSchemaPrefix, dbPoolSize, dbCreateSchema} dbSuffix _keepKey =
|
||||
DBOpts
|
||||
{ connstr = B.pack dbConnstr,
|
||||
schema = if null dbSchemaPrefix then "simplex_v1" <> dbSuffix else dbSchemaPrefix <> dbSuffix
|
||||
schema = B.pack $ if null dbSchemaPrefix then "simplex_v1" <> dbSuffix else dbSchemaPrefix <> dbSuffix,
|
||||
poolSize = dbPoolSize,
|
||||
createSchema = dbCreateSchema
|
||||
}
|
||||
|
||||
chatSuffix :: String
|
||||
@@ -58,11 +77,13 @@ mobileDbOpts schemaPrefix connstr = do
|
||||
pure $
|
||||
ChatDbOpts
|
||||
{ dbConnstr,
|
||||
dbSchemaPrefix
|
||||
dbSchemaPrefix,
|
||||
dbPoolSize = 1,
|
||||
dbCreateSchema = True
|
||||
}
|
||||
|
||||
removeDbKey :: ChatDbOpts -> ChatDbOpts
|
||||
removeDbKey = id
|
||||
|
||||
errorDbStr :: DBOpts -> String
|
||||
errorDbStr DBOpts {schema} = schema
|
||||
errorDbStr DBOpts {schema} = B.unpack schema
|
||||
|
||||
+163
-77
@@ -34,6 +34,7 @@ directoryServiceTests = do
|
||||
it "should register group" testDirectoryService
|
||||
it "should suspend and resume group, send message to owner" testSuspendResume
|
||||
it "should delete group registration" testDeleteGroup
|
||||
it "admin should delete group registration" testDeleteGroupAdmin
|
||||
it "should change initial member role" testSetRole
|
||||
it "should join found group via link" testJoinGroup
|
||||
it "should support group names with spaces" testGroupNameWithSpaces
|
||||
@@ -52,10 +53,12 @@ directoryServiceTests = do
|
||||
it "should NOT allow approving if roles are incorrect" testNotApprovedBadRoles
|
||||
describe "should require re-approval if profile is changed by" $ do
|
||||
it "the registration owner" testRegOwnerChangedProfile
|
||||
it "another owner" testAnotherOwnerChangedProfile -- TODO fix - doesn't work if another owner is not connected as contact
|
||||
it "another owner" testAnotherOwnerChangedProfile
|
||||
it "another owner not connected to directory" testNotConnectedOwnerChangedProfile
|
||||
describe "should require profile update if group link is removed by " $ do
|
||||
it "the registration owner" testRegOwnerRemovedLink
|
||||
it "another owner" testAnotherOwnerRemovedLink -- TODO fix - doesn't work if another owner is not connected as contact
|
||||
it "another owner" testAnotherOwnerRemovedLink
|
||||
it "another owner not connected to directory" testNotConnectedOwnerRemovedLink
|
||||
describe "duplicate groups (same display name and full name)" $ do
|
||||
it "should ask for confirmation if a duplicate group is submitted" testDuplicateAskConfirmation
|
||||
it "should prohibit registration if a duplicate group is listed" testDuplicateProhibitRegistration
|
||||
@@ -186,10 +189,13 @@ testDirectoryService ps =
|
||||
superUser #> "@SimpleX-Directory /approve 1:PSA 1"
|
||||
superUser <# "SimpleX-Directory> > /approve 1:PSA 1"
|
||||
superUser <## " Group approved!"
|
||||
bob <# "SimpleX-Directory> The group ID 1 (PSA) is approved and listed in directory!"
|
||||
bob <# "SimpleX-Directory> The group ID 1 (PSA) is approved and listed in directory - please moderate it!"
|
||||
bob <## "Please note: if you change the group profile it will be hidden from directory until it is re-approved."
|
||||
bob <## ""
|
||||
bob <## "Use /filter 1 to configure anti-spam filter and /role 1 to set default member role."
|
||||
bob <## "Supported commands:"
|
||||
bob <## "- /filter 1 - to configure anti-spam filter."
|
||||
bob <## "- /role 1 - to set default member role."
|
||||
bob <## "- /help commands - other commands."
|
||||
search bob "privacy" welcomeWithLink'
|
||||
search bob "security" welcomeWithLink'
|
||||
cath `connectVia` dsLink
|
||||
@@ -266,6 +272,38 @@ testDeleteGroup ps =
|
||||
bob <## " Your group privacy is deleted from the directory"
|
||||
groupNotFound bob "privacy"
|
||||
|
||||
testDeleteGroupAdmin :: HasCallStack => TestParams -> IO ()
|
||||
testDeleteGroupAdmin ps =
|
||||
withDirectoryService ps $ \superUser dsLink ->
|
||||
withNewTestChat ps "bob" bobProfile $ \bob -> do
|
||||
withNewTestChat ps "cath" cathProfile $ \cath -> do
|
||||
bob `connectVia` dsLink
|
||||
registerGroup superUser bob "privacy" "Privacy"
|
||||
cath `connectVia` dsLink
|
||||
registerGroupId superUser cath "security" "Security" 2 1
|
||||
groupFound bob "privacy"
|
||||
groupFound bob "security"
|
||||
listUserGroup bob "privacy" "Privacy"
|
||||
listUserGroup cath "security" "Security"
|
||||
superUser #> "@SimpleX-Directory /last"
|
||||
superUser <# "SimpleX-Directory> > /last"
|
||||
superUser <## " 2 registered group(s)"
|
||||
memberGroupListing superUser bob 1 "privacy" "Privacy" 2 "active"
|
||||
memberGroupListing superUser cath 2 "security" "Security" 2 "active"
|
||||
-- trying to register group with the same name
|
||||
submitGroup bob "security" "Security"
|
||||
bob <# "SimpleX-Directory> The group security (Security) is already listed in the directory, please choose another name."
|
||||
bob ##> "/d #security"
|
||||
bob <## "#security: you deleted the group"
|
||||
-- admin can delete the group
|
||||
superUser #> "@SimpleX-Directory /delete 2:security"
|
||||
superUser <# "SimpleX-Directory> > /delete 2:security"
|
||||
superUser <## " The group security is deleted from the directory"
|
||||
groupFound bob "privacy"
|
||||
groupNotFound bob "security"
|
||||
-- another user can register the group with the same name
|
||||
registerGroupId superUser bob "security" "Security" 4 1
|
||||
|
||||
testSetRole :: HasCallStack => TestParams -> IO ()
|
||||
testSetRole ps =
|
||||
withDirectoryService ps $ \superUser dsLink ->
|
||||
@@ -726,13 +764,34 @@ testAnotherOwnerChangedProfile ps =
|
||||
cath <## "full name changed to: Privacy and Security"
|
||||
bob <## "cath updated group #privacy:"
|
||||
bob <## "full name changed to: Privacy and Security"
|
||||
bob <# "SimpleX-Directory> The group ID 1 (privacy) is updated!"
|
||||
bob <# "SimpleX-Directory> The group ID 1 (privacy) is updated by cath!"
|
||||
bob <## "It is hidden from the directory until approved."
|
||||
groupNotFound cath "privacy"
|
||||
superUser <# "SimpleX-Directory> The group ID 1 (privacy) is updated."
|
||||
superUser <# "SimpleX-Directory> The group ID 1 (privacy) is updated by cath."
|
||||
reapproveGroup 3 superUser bob
|
||||
groupFoundN 3 cath "privacy"
|
||||
|
||||
testNotConnectedOwnerChangedProfile :: HasCallStack => TestParams -> IO ()
|
||||
testNotConnectedOwnerChangedProfile ps =
|
||||
withDirectoryService ps $ \superUser dsLink ->
|
||||
withNewTestChat ps "bob" bobProfile $ \bob ->
|
||||
withNewTestChat ps "cath" cathProfile $ \cath -> do
|
||||
withNewTestChat ps "dan" danProfile $ \dan -> do
|
||||
bob `connectVia` dsLink
|
||||
dan `connectVia` dsLink
|
||||
registerGroup superUser bob "privacy" "Privacy"
|
||||
addCathAsOwner bob cath
|
||||
cath ##> "/gp privacy privacy Privacy and Security"
|
||||
cath <## "full name changed to: Privacy and Security"
|
||||
bob <## "cath updated group #privacy:"
|
||||
bob <## "full name changed to: Privacy and Security"
|
||||
bob <# "SimpleX-Directory> The group ID 1 (privacy) is updated by cath!"
|
||||
bob <## "It is hidden from the directory until approved."
|
||||
groupNotFound dan "privacy"
|
||||
superUser <# "SimpleX-Directory> The group ID 1 (privacy) is updated by cath."
|
||||
reapproveGroup 3 superUser bob
|
||||
groupFoundN 3 dan "privacy"
|
||||
|
||||
testRegOwnerRemovedLink :: HasCallStack => TestParams -> IO ()
|
||||
testRegOwnerRemovedLink ps =
|
||||
withDirectoryService ps $ \superUser dsLink ->
|
||||
@@ -758,14 +817,15 @@ testRegOwnerRemovedLink ps =
|
||||
cath <## "contact and member are merged: SimpleX-Directory_1, #privacy SimpleX-Directory"
|
||||
cath <## "use @SimpleX-Directory <message> to send messages"
|
||||
groupNotFound cath "privacy"
|
||||
bob ##> ("/set welcome #privacy " <> welcomeWithLink)
|
||||
let withChangedLink = T.unpack $ T.replace "contact#/?v=2-7&" "contact#/?v=3-7&" $ T.pack welcomeWithLink
|
||||
bob ##> ("/set welcome #privacy " <> withChangedLink)
|
||||
bob <## "description changed to:"
|
||||
bob <## welcomeWithLink
|
||||
bob <## withChangedLink
|
||||
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message."
|
||||
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours."
|
||||
cath <## "bob updated group #privacy:"
|
||||
cath <## "description changed to:"
|
||||
cath <## welcomeWithLink
|
||||
cath <## withChangedLink
|
||||
reapproveGroup 3 superUser bob
|
||||
groupFoundN 3 cath "privacy"
|
||||
|
||||
@@ -789,7 +849,7 @@ testAnotherOwnerRemovedLink ps =
|
||||
bob <## "cath updated group #privacy:"
|
||||
bob <## "description changed to:"
|
||||
bob <## "Welcome!"
|
||||
bob <# "SimpleX-Directory> The group link for ID 1 (privacy) is removed from the welcome message."
|
||||
bob <# "SimpleX-Directory> The group link for ID 1 (privacy) is removed from the welcome message by cath."
|
||||
bob <## ""
|
||||
bob <## "The group is hidden from the directory until the group link is added and the group is re-approved."
|
||||
superUser <# "SimpleX-Directory> The group link is removed from ID 1 (privacy), de-listed."
|
||||
@@ -800,20 +860,55 @@ testAnotherOwnerRemovedLink ps =
|
||||
bob <## "cath updated group #privacy:"
|
||||
bob <## "description changed to:"
|
||||
bob <## welcomeWithLink
|
||||
bob <# "SimpleX-Directory> The group link is added by another group member, your registration will not be processed."
|
||||
bob <## ""
|
||||
bob <## "Please update the group profile yourself."
|
||||
bob ##> ("/set welcome #privacy " <> welcomeWithLink <> " - welcome!")
|
||||
bob <## "description changed to:"
|
||||
bob <## (welcomeWithLink <> " - welcome!")
|
||||
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message."
|
||||
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message by cath."
|
||||
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours."
|
||||
cath <## "bob updated group #privacy:"
|
||||
cath <## "description changed to:"
|
||||
cath <## (welcomeWithLink <> " - welcome!")
|
||||
reapproveGroup 3 superUser bob
|
||||
groupFoundN 3 cath "privacy"
|
||||
|
||||
testNotConnectedOwnerRemovedLink :: HasCallStack => TestParams -> IO ()
|
||||
testNotConnectedOwnerRemovedLink ps =
|
||||
withDirectoryService ps $ \superUser dsLink ->
|
||||
withNewTestChat ps "bob" bobProfile $ \bob ->
|
||||
withNewTestChat ps "cath" cathProfile $ \cath -> do
|
||||
withNewTestChat ps "dan" danProfile $ \dan -> do
|
||||
bob `connectVia` dsLink
|
||||
dan `connectVia` dsLink
|
||||
registerGroup superUser bob "privacy" "Privacy"
|
||||
addCathAsOwner bob cath
|
||||
bob ##> "/show welcome #privacy"
|
||||
bob <## "Welcome message:"
|
||||
welcomeWithLink <- getTermLine bob
|
||||
cath ##> "/set welcome #privacy Welcome!"
|
||||
cath <## "description changed to:"
|
||||
cath <## "Welcome!"
|
||||
bob <## "cath updated group #privacy:"
|
||||
bob <## "description changed to:"
|
||||
bob <## "Welcome!"
|
||||
bob <# "SimpleX-Directory> The group link for ID 1 (privacy) is removed from the welcome message by cath."
|
||||
bob <## ""
|
||||
bob <## "The group is hidden from the directory until the group link is added and the group is re-approved."
|
||||
superUser <# "SimpleX-Directory> The group link is removed from ID 1 (privacy), de-listed."
|
||||
groupNotFound dan "privacy"
|
||||
cath ##> ("/set welcome #privacy " <> welcomeWithLink)
|
||||
cath <## "description changed to:"
|
||||
cath <## welcomeWithLink
|
||||
bob <## "cath updated group #privacy:"
|
||||
bob <## "description changed to:"
|
||||
bob <## welcomeWithLink
|
||||
-- bob <# "SimpleX-Directory> The group link is added by another group member, your registration will not be processed."
|
||||
-- bob <## ""
|
||||
-- bob <## "Please update the group profile yourself."
|
||||
-- bob ##> ("/set welcome #privacy " <> welcomeWithLink <> " - welcome!")
|
||||
-- bob <## "description changed to:"
|
||||
-- bob <## (welcomeWithLink <> " - welcome!")
|
||||
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message by cath."
|
||||
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours."
|
||||
-- cath <## "bob updated group #privacy:"
|
||||
-- cath <## "description changed to:"
|
||||
-- cath <## (welcomeWithLink <> " - welcome!")
|
||||
reapproveGroup 3 superUser bob
|
||||
groupFoundN 3 dan "privacy"
|
||||
|
||||
testDuplicateAskConfirmation :: HasCallStack => TestParams -> IO ()
|
||||
testDuplicateAskConfirmation ps =
|
||||
withDirectoryService ps $ \superUser dsLink ->
|
||||
@@ -937,14 +1032,7 @@ testListUserGroups ps =
|
||||
cath <## "use @SimpleX-Directory <message> to send messages"
|
||||
registerGroupId superUser bob "security" "Security" 2 2
|
||||
registerGroupId superUser cath "anonymity" "Anonymity" 3 1
|
||||
cath #> "@SimpleX-Directory /list"
|
||||
cath <# "SimpleX-Directory> > /list"
|
||||
cath <## " 1 registered group(s)"
|
||||
cath <# "SimpleX-Directory> 1. anonymity (Anonymity)"
|
||||
cath <## "Welcome message:"
|
||||
cath <##. "Link to join the group anonymity: "
|
||||
cath <## "2 members"
|
||||
cath <## "Status: active"
|
||||
listUserGroup cath "anonymity" "Anonymity"
|
||||
-- with de-listed group
|
||||
groupFound cath "anonymity"
|
||||
cath ##> "/mr anonymity SimpleX-Directory member"
|
||||
@@ -1076,27 +1164,11 @@ testCaptcha _ps = do
|
||||
|
||||
listGroups :: HasCallStack => TestCC -> TestCC -> TestCC -> IO ()
|
||||
listGroups superUser bob cath = do
|
||||
bob #> "@SimpleX-Directory /list"
|
||||
bob <# "SimpleX-Directory> > /list"
|
||||
bob <## " 2 registered group(s)"
|
||||
bob <# "SimpleX-Directory> 1. privacy (Privacy)"
|
||||
bob <## "Welcome message:"
|
||||
bob <##. "Link to join the group privacy: "
|
||||
bob <## "3 members"
|
||||
bob <## "Status: active"
|
||||
bob <# "SimpleX-Directory> 2. security (Security)"
|
||||
bob <## "Welcome message:"
|
||||
bob <##. "Link to join the group security: "
|
||||
bob <## "2 members"
|
||||
bob <## "Status: active"
|
||||
cath #> "@SimpleX-Directory /list"
|
||||
cath <# "SimpleX-Directory> > /list"
|
||||
cath <## " 1 registered group(s)"
|
||||
cath <# "SimpleX-Directory> 1. anonymity (Anonymity)"
|
||||
cath <## "Welcome message:"
|
||||
cath <##. "Link to join the group anonymity: "
|
||||
cath <## "2 members"
|
||||
cath <## "Status: suspended because roles changed"
|
||||
sendListCommand bob 2
|
||||
groupListing bob 1 "privacy" "Privacy" 3 "active"
|
||||
groupListing bob 2 "security" "Security" 2 "active"
|
||||
sendListCommand cath 1
|
||||
groupListing cath 1 "anonymity" "Anonymity" 2 "suspended because roles changed"
|
||||
-- superuser lists all groups
|
||||
bob #> "@SimpleX-Directory /last"
|
||||
bob <# "SimpleX-Directory> > /last"
|
||||
@@ -1104,34 +1176,42 @@ listGroups superUser bob cath = do
|
||||
superUser #> "@SimpleX-Directory /last"
|
||||
superUser <# "SimpleX-Directory> > /last"
|
||||
superUser <## " 3 registered group(s)"
|
||||
superUser <# "SimpleX-Directory> 1. privacy (Privacy)"
|
||||
superUser <## "Welcome message:"
|
||||
superUser <##. "Link to join the group privacy: "
|
||||
superUser <## "Owner: bob"
|
||||
superUser <## "3 members"
|
||||
superUser <## "Status: active"
|
||||
superUser <# "SimpleX-Directory> 2. security (Security)"
|
||||
superUser <## "Welcome message:"
|
||||
superUser <##. "Link to join the group security: "
|
||||
superUser <## "Owner: bob"
|
||||
superUser <## "2 members"
|
||||
superUser <## "Status: active"
|
||||
superUser <# "SimpleX-Directory> 3. anonymity (Anonymity)"
|
||||
superUser <## "Welcome message:"
|
||||
superUser <##. "Link to join the group anonymity: "
|
||||
superUser <## "Owner: cath"
|
||||
superUser <## "2 members"
|
||||
superUser <## "Status: suspended because roles changed"
|
||||
memberGroupListing superUser bob 1 "privacy" "Privacy" 3 "active"
|
||||
memberGroupListing superUser bob 2 "security" "Security" 2 "active"
|
||||
memberGroupListing superUser cath 3 "anonymity" "Anonymity" 2 "suspended because roles changed"
|
||||
-- showing last 1 group
|
||||
superUser #> "@SimpleX-Directory /last 1"
|
||||
superUser <# "SimpleX-Directory> > /last 1"
|
||||
superUser <## " 3 registered group(s), showing the last 1"
|
||||
superUser <# "SimpleX-Directory> 3. anonymity (Anonymity)"
|
||||
superUser <## "Welcome message:"
|
||||
superUser <##. "Link to join the group anonymity: "
|
||||
superUser <## "Owner: cath"
|
||||
superUser <## "2 members"
|
||||
superUser <## "Status: suspended because roles changed"
|
||||
memberGroupListing superUser cath 3 "anonymity" "Anonymity" 2 "suspended because roles changed"
|
||||
|
||||
listUserGroup :: HasCallStack => TestCC -> String -> String -> IO ()
|
||||
listUserGroup u n fn = do
|
||||
sendListCommand u 1
|
||||
groupListing u 1 n fn 2 "active"
|
||||
|
||||
sendListCommand :: HasCallStack => TestCC -> Int -> IO ()
|
||||
sendListCommand u count = do
|
||||
u #> "@SimpleX-Directory /list"
|
||||
u <# "SimpleX-Directory> > /list"
|
||||
u <## (" " <> show count <> " registered group(s)")
|
||||
|
||||
groupListing :: HasCallStack => TestCC -> Int -> String -> String -> Int -> String -> IO ()
|
||||
groupListing u = groupListing_ u Nothing
|
||||
|
||||
memberGroupListing :: HasCallStack => TestCC -> TestCC -> Int -> String -> String -> Int -> String -> IO ()
|
||||
memberGroupListing su owner = groupListing_ su (Just owner)
|
||||
|
||||
groupListing_ :: HasCallStack => TestCC -> Maybe TestCC -> Int -> String -> String -> Int -> String -> IO ()
|
||||
groupListing_ su owner_ gId n fn count status = do
|
||||
su <# ("SimpleX-Directory> " <> show gId <> ". " <> n <> " (" <> fn <> ")")
|
||||
su <## "Welcome message:"
|
||||
su <##. ("Link to join the group " <> n <> ": ")
|
||||
forM_ owner_ $ \owner -> do
|
||||
ownerName <- userName owner
|
||||
su <## ("Owner: " <> ownerName)
|
||||
su <## (show count <> " members")
|
||||
su <## ("Status: " <> status)
|
||||
|
||||
reapproveGroup :: HasCallStack => Int -> TestCC -> TestCC -> IO ()
|
||||
reapproveGroup count superUser bob = do
|
||||
@@ -1146,10 +1226,13 @@ reapproveGroup count superUser bob = do
|
||||
superUser #> "@SimpleX-Directory /approve 1:privacy 1"
|
||||
superUser <# "SimpleX-Directory> > /approve 1:privacy 1"
|
||||
superUser <## " Group approved!"
|
||||
bob <# "SimpleX-Directory> The group ID 1 (privacy) is approved and listed in directory!"
|
||||
bob <# "SimpleX-Directory> The group ID 1 (privacy) is approved and listed in directory - please moderate it!"
|
||||
bob <## "Please note: if you change the group profile it will be hidden from directory until it is re-approved."
|
||||
bob <## ""
|
||||
bob <## "Use /filter 1 to configure anti-spam filter and /role 1 to set default member role."
|
||||
bob <## "Supported commands:"
|
||||
bob <## "- /filter 1 - to configure anti-spam filter."
|
||||
bob <## "- /role 1 - to set default member role."
|
||||
bob <## "- /help commands - other commands."
|
||||
|
||||
addCathAsOwner :: HasCallStack => TestCC -> TestCC -> IO ()
|
||||
addCathAsOwner bob cath = do
|
||||
@@ -1293,10 +1376,13 @@ approveRegistrationId su u n gId ugId = do
|
||||
su #> ("@SimpleX-Directory " <> approve)
|
||||
su <# ("SimpleX-Directory> > " <> approve)
|
||||
su <## " Group approved!"
|
||||
u <# ("SimpleX-Directory> The group ID " <> show ugId <> " (" <> n <> ") is approved and listed in directory!")
|
||||
u <# ("SimpleX-Directory> The group ID " <> show ugId <> " (" <> n <> ") is approved and listed in directory - please moderate it!")
|
||||
u <## "Please note: if you change the group profile it will be hidden from directory until it is re-approved."
|
||||
u <## ""
|
||||
u <## ("Use /filter " <> show ugId <> " to configure anti-spam filter and /role " <> show ugId <> " to set default member role.")
|
||||
u <## "Supported commands:"
|
||||
u <## ("- /filter " <> show ugId <> " - to configure anti-spam filter.")
|
||||
u <## ("- /role " <> show ugId <> " - to set default member role.")
|
||||
u <## "- /help commands - other commands."
|
||||
|
||||
connectVia :: TestCC -> String -> IO ()
|
||||
u `connectVia` dsLink = do
|
||||
|
||||
+3
-1
@@ -117,7 +117,9 @@ testCoreOpts =
|
||||
{ dbConnstr = testDBConnstr,
|
||||
-- dbSchemaPrefix is not used in tests (except bot tests where it's redefined),
|
||||
-- instead different schema prefix is passed per client so that single test database is used
|
||||
dbSchemaPrefix = ""
|
||||
dbSchemaPrefix = "",
|
||||
dbPoolSize = 3,
|
||||
dbCreateSchema = True
|
||||
#else
|
||||
{ dbFilePrefix = "./simplex_v1", -- dbFilePrefix is not used in tests (except bot tests where it's redefined)
|
||||
dbKey = "", -- dbKey = "this is a pass-phrase to encrypt the database",
|
||||
|
||||
@@ -8,7 +8,9 @@ module MarkdownTests where
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Text.Encoding (encodeUtf8)
|
||||
import Simplex.Chat.Markdown
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import System.Console.ANSI.Types
|
||||
import Test.Hspec
|
||||
|
||||
@@ -169,7 +171,12 @@ uri :: Text -> Markdown
|
||||
uri = Markdown $ Just Uri
|
||||
|
||||
simplexLink :: SimplexLinkType -> Text -> NonEmpty Text -> Text -> Markdown
|
||||
simplexLink linkType simplexUri smpHosts = Markdown $ Just SimplexLink {linkType, simplexUri, smpHosts}
|
||||
simplexLink linkType uriText smpHosts t = Markdown (simplexLinkFormat linkType uriText smpHosts) t
|
||||
|
||||
simplexLinkFormat :: SimplexLinkType -> Text -> NonEmpty Text -> Maybe Format
|
||||
simplexLinkFormat linkType uriText smpHosts = case strDecode $ encodeUtf8 uriText of
|
||||
Right simplexUri -> Just SimplexLink {linkType, simplexUri, smpHosts}
|
||||
Left e -> error e
|
||||
|
||||
textWithUri :: Spec
|
||||
textWithUri = describe "text with Uri" do
|
||||
@@ -275,6 +282,6 @@ multilineMarkdownList = describe "multiline markdown" do
|
||||
it "multiline with simplex link" do
|
||||
("https://simplex.chat" <> inv <> "\ntext")
|
||||
<<==>>
|
||||
[ FormattedText (Just $ SimplexLink XLInvitation ("simplex:" <> inv) ["smp.simplex.im"]) ("https://simplex.chat" <> inv),
|
||||
[ FormattedText (simplexLinkFormat XLInvitation ("simplex:" <> inv) ["smp.simplex.im"]) ("https://simplex.chat" <> inv),
|
||||
"\ntext"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user