diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d9209b35b6..e3f55ab191 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -221,6 +221,16 @@ jobs:
done
strip /out/simplex-chat
+ - name: Build CLI deb
+ if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
+ shell: docker exec -t builder sh -eu {0}
+ run: |
+ version=${{ github.ref }}
+ version=${version#refs/tags/v}
+ version=${version%-*}
+
+ ./scripts/desktop/build-cli-deb.sh "$version"
+
- name: Copy tests from container
if: matrix.should_run == true
shell: bash
@@ -232,21 +242,41 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
shell: bash
run: |
- docker cp builder:/out/simplex-chat ./simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}
- path="${{ github.workspace }}/simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}"
- echo "bin_path=$path" >> $GITHUB_OUTPUT
- echo "bin_hash=$(echo SHA2-256\(simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
+ cli_name="simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}"
+ cli_deb_name="${cli_name}.deb"
+ cli_path="${{ github.workspace }}"
+
+ docker cp builder:/out/simplex-chat "./${cli_name}"
+ docker cp builder:/out/deb-build/simplex-chat.deb "./${cli_deb_name}"
+
+ echo "bin_name=${cli_name}" >> $GITHUB_OUTPUT
+ echo "bin_path=${cli_path}/${cli_name}" >> $GITHUB_OUTPUT
+ echo "bin_hash=$(echo SHA2-256\(${cli_name}\)= $(openssl sha256 "${cli_path}/${cli_name}" | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
+
+ echo "deb_name=${cli_deb_name}" >> $GITHUB_OUTPUT
+ echo "deb_path=${cli_path}/${cli_deb_name}" >> $GITHUB_OUTPUT
+ echo "deb_hash=$(echo SHA2-256\(${cli_deb_name}\)= $(openssl sha256 "${cli_path}/${cli_deb_name}" | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
- name: Upload CLI
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
uses: ./.github/actions/prepare-release
with:
+ bin_name: ${{ steps.linux_cli_prepare.outputs.bin_name }}
bin_path: ${{ steps.linux_cli_prepare.outputs.bin_path }}
- bin_name: simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}
bin_hash: ${{ steps.linux_cli_prepare.outputs.bin_hash }}
github_ref: ${{ github.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Upload CLI deb
+ if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
+ uses: ./.github/actions/prepare-release
+ with:
+ bin_name: ${{ steps.linux_cli_prepare.outputs.deb_name }}
+ bin_path: ${{ steps.linux_cli_prepare.outputs.deb_path }}
+ bin_hash: ${{ steps.linux_cli_prepare.outputs.deb_hash }}
+ github_ref: ${{ github.ref }}
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+
- name: Build Desktop
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
shell: docker exec -t builder sh -eu {0}
diff --git a/.github/workflows/reproduce-schedule.yml b/.github/workflows/reproduce-schedule.yml
index 7d28d6f70c..0febed4c87 100644
--- a/.github/workflows/reproduce-schedule.yml
+++ b/.github/workflows/reproduce-schedule.yml
@@ -9,9 +9,6 @@ jobs:
reproduce:
runs-on: ubuntu-latest
steps:
- - name: Checkout code
- uses: actions/checkout@v3
-
- name: Get latest release
shell: bash
run: |
@@ -23,6 +20,16 @@ jobs:
grep -i "tag_name" | \
awk -F \" '{print "TAG="$4}' >> $GITHUB_ENV
+ - name: Checkout code
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ env.TAG }}
+ repository: simplex-chat/simplex-chat
+
+ # 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: Execute reproduce script
run: |
${GITHUB_WORKSPACE}/scripts/simplex-chat-reproduce-builds.sh "$TAG" || :
diff --git a/Dockerfile.build b/Dockerfile.build
index 3c841cfb25..fddc96b6c2 100644
--- a/Dockerfile.build
+++ b/Dockerfile.build
@@ -6,7 +6,9 @@ FROM ubuntu:${TAG} AS build
ARG GHC=9.6.3
ARG CABAL=3.10.2.0
-ARG JAVA=17
+ARG JAVA_VER=17.0.17.10.1
+ARG JAVA_HASH_AMD64=e3e11daa5c22a45153bbeff1a0c21bf08631791e4e8d8ed14deba31c7cf9af1a
+ARG JAVA_HASH_ARM64=2b460859b681757b33a7591b6238ecaf51569d05d2684984e5f0a89c6514acbc
ENV TZ=Etc/UTC \
DEBIAN_FRONTEND=noninteractive
@@ -44,10 +46,26 @@ RUN apt-get update && \
# depends on libjpeg.so.8 and liblcms2.so.2 which are NOT copied into final
# /usr/lib/runtime/lib directory and I do not have time to figure out gradle.kotlin
# to fix this :(
-RUN curl --proto '=https' --tlsv1.2 -sSf 'https://apt.corretto.aws/corretto.key' | gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg &&\
- echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" > /etc/apt/sources.list.d/corretto.list &&\
- apt update &&\
- apt install -y java-${JAVA}-amazon-corretto-jdk
+RUN export JAVA_FILENAME='java-corretto.deb' \
+ JAVA_VER_MAJOR=$(printf "${JAVA_VER}" | awk -F. '{print $1}') \
+ JAVA_VER_DEB=$(printf "${JAVA_VER}" | sed 's/\.1$/-1/') && \
+ case "$(uname -m)" in \
+ x86_64) export JAVA_ARCH='amd64' JAVA_HASH="$JAVA_HASH_AMD64" ;; \
+ aarch64) export JAVA_ARCH='arm64' JAVA_HASH="$JAVA_HASH_ARM64" ;; \
+ *) echo "unknown arch $(uname -m)" && exit 1 ;; \
+ esac && \
+ curl --proto '=https' --tlsv1.2 -sSf \
+ "https://corretto.aws/downloads/resources/${JAVA_VER}/java-${JAVA_VER_MAJOR}-amazon-corretto-jdk_${JAVA_VER_DEB}_${JAVA_ARCH}.deb" \
+ -o "${JAVA_FILENAME}" && \
+ if echo "${JAVA_HASH} ${JAVA_FILENAME}" | sha256sum -c -; then \
+ if apt install -y ./"${JAVA_FILENAME}"; then \
+ rm ./"${JAVA_FILENAME}"; \
+ else \
+ echo "Failed to install Java Corretto" && exit 1; \
+ fi \
+ else \
+ echo "Checksum mismatch" && exit 1; \
+ fi
# Specify bootstrap Haskell versions
ENV BOOTSTRAP_HASKELL_GHC_VERSION=${GHC}
diff --git a/README.md b/README.md
index 1e80c14424..2f0f92f5f7 100644
--- a/README.md
+++ b/README.md
@@ -24,15 +24,15 @@
## Install the app
-[
](https://apps.apple.com/us/app/simplex-chat/id1605771084)
+[
](https://apps.apple.com/us/app/simplex-chat/id1605771084)
-[](https://play.google.com/store/apps/details?id=chat.simplex.app)
+[](https://play.google.com/store/apps/details?id=chat.simplex.app)
-[
](https://app.simplex.chat)
+[
](https://app.simplex.chat)
-[
](https://testflight.apple.com/join/DWuT2LQu)
+[
](https://testflight.apple.com/join/DWuT2LQu)
-[
](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk)
+[
](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk)
- 🖲 Protects your messages and metadata - who you talk to and when.
- 🔐 Double ratchet end-to-end encryption, with additional encryption layer.
@@ -102,7 +102,7 @@ You need to share a link with your friend or scan a QR code from their phone, in
The channel through which you share the link does not have to be secure - it is enough that you can confirm who sent you the message and that your SimpleX connection is established.
-
+
After you connect, you can [verify connection security code](./blog/20230103-simplex-chat-v4.4-disappearing-messages.md#connection-security-verification).
@@ -443,12 +443,12 @@ Please do NOT report security vulnerabilities via GitHub issues.
This software is licensed under the GNU Affero General Public License version 3 (AGPLv3). See the [LICENSE](./LICENSE) file for details. The SimpleX and SimpleX Chat name, logo, and associated branding materials are not covered by this license and are subject to the terms outlined in the [TRADEMARK](./docs/TRADEMARK.md) file.
-[
](https://apps.apple.com/us/app/simplex-chat/id1605771084)
+[
](https://apps.apple.com/us/app/simplex-chat/id1605771084)
-[](https://play.google.com/store/apps/details?id=chat.simplex.app)
+[](https://play.google.com/store/apps/details?id=chat.simplex.app)
-[
](https://app.simplex.chat)
+[
](https://app.simplex.chat)
-[
](https://testflight.apple.com/join/DWuT2LQu)
+[
](https://testflight.apple.com/join/DWuT2LQu)
-[
](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk)
+[
](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk)
diff --git a/apps/ios/SimpleX SE/ShareAPI.swift b/apps/ios/SimpleX SE/ShareAPI.swift
index 4dad9d5d15..6495d09b03 100644
--- a/apps/ios/SimpleX SE/ShareAPI.swift
+++ b/apps/ios/SimpleX SE/ShareAPI.swift
@@ -48,7 +48,7 @@ func apiSetEncryptLocalFiles(_ enable: Bool) throws {
throw r.unexpected
}
-func apiGetChats(userId: User.ID) throws -> Array {
+func apiGetChats(userId: User.ID) throws -> Array {
let r: APIResult = sendSimpleXCmd(SEChatCommand.apiGetChats(userId: userId))
if case let .result(.apiChats(user: _, chats: chats)) = r { return chats }
throw r.unexpected
@@ -170,7 +170,7 @@ enum SEChatResponse: Decodable, ChatAPIResult {
case activeUser(user: User)
case chatStarted
case chatRunning
- case apiChats(user: UserRef, chats: [ChatData])
+ case apiChats(user: UserRef, chats: [SEChatData])
case newChatItems(user: UserRef, chatItems: [AChatItem])
case cmdOk(user_: UserRef?)
@@ -199,7 +199,7 @@ enum SEChatResponse: Decodable, ChatAPIResult {
}
static func fallbackResult(_ type: String, _ json: NSDictionary) -> SEChatResponse? {
- if type == "apiChats", let r = parseApiChats(json) {
+ if type == "apiChats", let r = seParseApiChats(json) {
.apiChats(user: r.user, chats: r.chats)
} else {
nil
@@ -239,3 +239,35 @@ enum SEChatEvent: Decodable, ChatAPIResult {
}
}
}
+
+public struct SEChatData: Decodable, Identifiable, Hashable, ChatLike {
+ public var chatInfo: ChatInfo
+
+ public var id: ChatId { get { chatInfo.id } }
+
+ public init(chatInfo: ChatInfo) {
+ self.chatInfo = chatInfo
+ }
+
+ public static func invalidJSON(_ json: Data?) -> SEChatData {
+ SEChatData(chatInfo: .invalidJSON(json: json))
+ }
+}
+
+public func seParseApiChats(_ jResp: NSDictionary) -> (user: UserRef, chats: [SEChatData])? {
+ if let jApiChats = jResp["apiChats"] as? NSDictionary,
+ let user: UserRef = try? decodeObject(jApiChats["user"] as Any),
+ let jChats = jApiChats["chats"] as? NSArray {
+ let chats: [SEChatData] = jChats.map { jChat in
+ if let jChatDict = jChat as? NSDictionary,
+ let jChatInfo = jChatDict["chatInfo"],
+ let chatInfo: ChatInfo = try? decodeObject(jChatInfo) {
+ return SEChatData(chatInfo: chatInfo)
+ }
+ return SEChatData.invalidJSON(serializeJSON(jChat, options: .prettyPrinted))
+ }
+ return (user, chats)
+ } else {
+ return nil
+ }
+}
diff --git a/apps/ios/SimpleX SE/ShareModel.swift b/apps/ios/SimpleX SE/ShareModel.swift
index 5080cf2040..fd5c4c990f 100644
--- a/apps/ios/SimpleX SE/ShareModel.swift
+++ b/apps/ios/SimpleX SE/ShareModel.swift
@@ -19,11 +19,11 @@ private let MAX_DOWNSAMPLE_SIZE: Int64 = 2000
class ShareModel: ObservableObject {
@Published var sharedContent: SharedContent?
- @Published var chats: [ChatData] = []
+ @Published var chats: [SEChatData] = []
@Published var profileImages: [ChatInfo.ID: UIImage] = [:]
@Published var search = ""
@Published var comment = ""
- @Published var selected: ChatData?
+ @Published var selected: SEChatData?
@Published var isLoaded = false
@Published var bottomBar: BottomBar = .loadingSpinner
@Published var errorAlert: ErrorAlert?
@@ -60,13 +60,13 @@ class ShareModel: ObservableObject {
}
}
- func isProhibited(_ chat: ChatData?) -> Bool {
+ func isProhibited(_ chat: SEChatData?) -> Bool {
if let chat, let sharedContent {
sharedContent.prohibited(in: chat, hasSimplexLink: hasSimplexLink)
} else { false }
}
- var filteredChats: [ChatData] {
+ var filteredChats: [SEChatData] {
search.isEmpty
? filterChatsToForwardTo(chats: chats)
: filterChatsToForwardTo(chats: chats)
@@ -253,7 +253,7 @@ class ShareModel: ObservableObject {
}
}
- private func fetchChats() -> Result, ErrorAlert> {
+ private func fetchChats() -> Result, ErrorAlert> {
do {
guard let user = try apiGetActiveUser() else {
return .failure(
@@ -396,7 +396,7 @@ enum SharedContent {
}
}
- func prohibited(in chatData: ChatData, hasSimplexLink: Bool) -> Bool {
+ func prohibited(in chatData: SEChatData, hasSimplexLink: Bool) -> Bool {
chatData.prohibitedByPref(
hasSimplexLink: hasSimplexLink,
isMediaOrFileAttachment: cryptoFile != nil,
diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj
index e226b696e6..ce4785c7ab 100644
--- a/apps/ios/SimpleX.xcodeproj/project.pbxproj
+++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj
@@ -178,8 +178,8 @@
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
- 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a */; };
- 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a */; };
+ 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a */; };
+ 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a */; };
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
@@ -545,8 +545,8 @@
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; };
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; };
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; };
- 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a"; sourceTree = ""; };
- 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a"; sourceTree = ""; };
+ 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a"; sourceTree = ""; };
+ 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a"; sourceTree = ""; };
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; };
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; };
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = ""; };
@@ -708,8 +708,8 @@
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
- 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a in Frameworks */,
- 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a in Frameworks */,
+ 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a in Frameworks */,
+ 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -795,8 +795,8 @@
64C829992D54AEEE006B9E89 /* libffi.a */,
64C829982D54AEED006B9E89 /* libgmp.a */,
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
- 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a */,
- 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a */,
+ 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a */,
+ 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a */,
);
path = Libraries;
sourceTree = "";
@@ -2003,7 +2003,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -2053,7 +2053,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -2095,7 +2095,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2115,7 +2115,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2140,7 +2140,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
@@ -2177,7 +2177,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
@@ -2214,7 +2214,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2265,7 +2265,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2316,7 +2316,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -2350,7 +2350,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 310;
+ CURRENT_PROJECT_VERSION = 311;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
diff --git a/apps/ios/SimpleXChat/ChatUtils.swift b/apps/ios/SimpleXChat/ChatUtils.swift
index 98ee9cd5d4..451ac8b4ef 100644
--- a/apps/ios/SimpleXChat/ChatUtils.swift
+++ b/apps/ios/SimpleXChat/ChatUtils.swift
@@ -9,9 +9,7 @@
import Foundation
public protocol ChatLike {
- var chatInfo: ChatInfo { get}
- var chatItems: [ChatItem] { get }
- var chatStats: ChatStats { get }
+ var chatInfo: ChatInfo { get }
}
extension ChatLike {
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt
index 28b46f592d..6ef56a9124 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt
@@ -21,7 +21,7 @@ sealed class WriteFileResult {
* */
fun writeCryptoFile(path: String, data: ByteArray): CryptoFileArgs {
- val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized")
+ val ctrl = ChatController.getChatCtrl() ?: throw Exception("Controller is not initialized")
val buffer = ByteBuffer.allocateDirect(data.size)
buffer.put(data)
buffer.rewind()
@@ -44,7 +44,7 @@ fun readCryptoFile(path: String, cryptoArgs: CryptoFileArgs): ByteArray {
}
fun encryptCryptoFile(fromPath: String, toPath: String): CryptoFileArgs {
- val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized")
+ val ctrl = ChatController.getChatCtrl() ?: throw Exception("Controller is not initialized")
val str = chatEncryptFile(ctrl, fromPath, toPath)
val d = json.decodeFromString(WriteFileResult.serializer(), str)
return when (d) {
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
index 09aae9a7d3..6a80e50285 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
@@ -482,17 +482,26 @@ class AppPreferences {
private const val MESSAGE_TIMEOUT: Int = 300_000_000
object ChatController {
- var ctrl: ChatCtrl? = -1
+ private var chatCtrl: ChatCtrl? = -1
val appPrefs: AppPreferences by lazy { AppPreferences() }
val messagesChannel: Channel = Channel()
val chatModel = ChatModel
- private var receiverStarted = false
+ private var receiverJob: Job? = null
var lastMsgReceivedTimestamp: Long = System.currentTimeMillis()
private set
- fun hasChatCtrl() = ctrl != -1L && ctrl != null
+ fun hasChatCtrl() = chatCtrl != -1L && chatCtrl != null
+
+ fun getChatCtrl(): ChatCtrl? = chatCtrl
+
+ fun setChatCtrl(ctrl: ChatCtrl?) {
+ val wasRunning = receiverJob != null
+ stopReceiver()
+ chatCtrl = ctrl
+ if (wasRunning && ctrl != null) startReceiver()
+ }
suspend fun getAgentSubsTotal(rh: Long?): Pair? {
val userId = currentUserId("getAgentSubsTotal")
@@ -639,17 +648,16 @@ object ChatController {
private fun startReceiver() {
Log.d(TAG, "ChatController startReceiver")
- if (receiverStarted) return
- receiverStarted = true
- CoroutineScope(Dispatchers.IO).launch {
+ if (receiverJob != null || chatCtrl == null) return
+ receiverJob = CoroutineScope(Dispatchers.IO).launch {
var releaseLock: (() -> Unit) = {}
- while (true) {
+ while (isActive) {
/** Global [ctrl] can be null. It's needed for having the same [ChatModel] that already made in [ChatController] without the need
* to change it everywhere in code after changing a database.
* Since it can be changed in background thread, making this check to prevent NullPointerException */
- val ctrl = ctrl
+ val ctrl = chatCtrl
if (ctrl == null) {
- receiverStarted = false
+ stopReceiver()
break
}
try {
@@ -689,6 +697,15 @@ object ChatController {
}
}
+ private fun stopReceiver() {
+ Log.d(TAG, "ChatController stopReceiver")
+ val job = receiverJob
+ if (job != null) {
+ receiverJob = null
+ job.cancel()
+ }
+ }
+
private suspend fun sendCmdWithRetry(rhId: Long?, cmd: CC, inProgress: MutableState? = null, retryNum: Int = 0): API? {
val r = sendCmd(rhId, cmd, retryNum = retryNum)
val alert = if (r is API.Error) retryableNetworkErrorAlert(r.err) else null
@@ -773,7 +790,7 @@ object ChatController {
}
suspend fun sendCmd(rhId: Long?, cmd: CC, otherCtrl: ChatCtrl? = null, retryNum: Int = 0, log: Boolean = true): API {
- val ctrl = otherCtrl ?: ctrl ?: throw Exception("Controller is not initialized")
+ val ctrl = otherCtrl ?: chatCtrl ?: throw Exception("Controller is not initialized")
return withContext(Dispatchers.IO) {
val c = cmd.cmdString
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt
index 959e4749dc..d0ce703033 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt
@@ -56,6 +56,7 @@ fun initChatControllerOnStart() {
}
suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: () -> CompletableDeferred = { CompletableDeferred(true) }) {
+ Log.d(TAG, "initChatController")
try {
if (chatModel.ctrlInitInProgress.value) return
chatModel.ctrlInitInProgress.value = true
@@ -92,7 +93,7 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
val ctrl = if (res is DBMigrationResult.OK) {
migrated[1] as Long
} else null
- chatController.ctrl = ctrl
+ chatController.setChatCtrl(ctrl)
chatModel.chatDbEncrypted.value = dbKey != ""
chatModel.chatDbStatus.value = res
if (res != DBMigrationResult.OK) {
@@ -206,7 +207,7 @@ fun chatInitControllerRemovingDatabases() {
}.getOrElse { DBMigrationResult.Unknown(migrated[0] as String) }
val ctrl = migrated[1] as Long
- chatController.ctrl = ctrl
+ chatController.setChatCtrl(ctrl)
// We need only controller, not databases
File(dbPath + "_chat.db").delete()
File(dbPath + "_agent.db").delete()
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt
index 01e19ea478..df9af7fbf6 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt
@@ -804,7 +804,8 @@ fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) {
LocalAppColors provides rememberedAppColors,
LocalAppWallpaper provides rememberedWallpaper,
LocalDensity provides density,
- content = content)
+ content = content
+ )
}
)
}
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt
index 13351a2111..ed74e083e7 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt
@@ -131,7 +131,7 @@ fun UserPicker(
}
LaunchedEffect(Unit) {
// Controller.ctrl can be null when self-destructing activates
- if (controller.ctrl != null && controller.ctrl != -1L) {
+ if (controller.hasChatCtrl()) {
withBGApi {
controller.reloadRemoteHosts()
}
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt
index 4a911fa6f0..d55d89f26b 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt
@@ -366,6 +366,7 @@ fun startChat(
chatDbChanged: MutableState,
progressIndicator: MutableState? = null
) {
+ Log.d(TAG, "startChat")
withLongRunningApi {
try {
progressIndicator?.value = true
@@ -532,7 +533,7 @@ fun deleteChatDatabaseFilesAndState() {
appPrefs.newDatabaseInitialized.set(false)
chatModel.desktopOnboardingRandomPassword.value = false
controller.appPrefs.storeDBPassphrase.set(true)
- controller.ctrl = null
+ controller.setChatCtrl(null)
// Clear sensitive data on screen just in case ModalManager will fail to prevent hiding its modals while database encrypts itself
chatModel.chatId.value = null
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt
index 8021a605db..f86edb0388 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt
@@ -33,7 +33,7 @@ fun LocalAuthView(m: ChatModel, authRequest: LocalAuthRequest) {
}
} else {
val r: LAResult = if (passcode.value == authRequest.password) {
- if (authRequest.selfDestruct && sdPassword != null && controller.ctrl == -1L) {
+ if (authRequest.selfDestruct && sdPassword != null && controller.getChatCtrl() == -1L) {
initChatControllerOnStart()
}
LAResult.Success
@@ -58,7 +58,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (
if (m.chatRunning.value == true) {
stopChatAsync(m)
}
- val ctrl = m.controller.ctrl
+ val ctrl = m.controller.getChatCtrl()
if (ctrl != null && ctrl != -1L) {
/**
* The following sequence can bring a user here:
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt
index d74846f8a3..6199621c39 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt
@@ -633,7 +633,7 @@ private fun MutableState.startDownloading(
private fun MutableState.importArchive(archivePath: String, netCfg: NetCfg, networkProxy: NetworkProxy?) {
withLongRunningApi {
try {
- if (ChatController.ctrl == null || ChatController.ctrl == -1L) {
+ if (!ChatController.hasChatCtrl()) {
chatInitControllerRemovingDatabases()
}
controller.apiDeleteStorage()
diff --git a/scripts/desktop/build-cli-deb.sh b/scripts/desktop/build-cli-deb.sh
new file mode 100755
index 0000000000..7422520d71
--- /dev/null
+++ b/scripts/desktop/build-cli-deb.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env sh
+set -eu
+
+export SOURCE_DATE_EPOCH=1764547200
+
+CLI_VERSION="$1"
+CLI_PATH_TO_BIN="${2:-/out/simplex-chat}"
+BUILD_FOLDER="${3:-/out/deb-build}"
+
+size=$(stat -c '%s' "$CLI_PATH_TO_BIN" | awk '{printf "%.0f\n", ($1+1023)/1024}')
+arch=$(case "$(uname -m)" in x86_64) printf "amd64" ;; aarch64) printf "arm64" ;; *) printf "unknown" ;; esac)
+package='simplex-chat'
+
+mkdir "$BUILD_FOLDER"
+cd "$BUILD_FOLDER"
+
+mkdir -p ./${package}/DEBIAN
+mkdir -p ./${package}/usr/bin
+cat > ./${package}/DEBIAN/control << EOF
+Package: ${package}
+Version: ${CLI_VERSION}
+Section: Messenger
+Priority: optional
+Architecture: ${arch}
+Maintainer: SimpleX Chat
+Description: SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design! (CLI)
+Installed-Size: ${size}
+EOF
+
+cp "$CLI_PATH_TO_BIN" ./${package}/usr/bin/simplex-chat
+chmod +x ./${package}/usr/bin/simplex-chat
+
+find ./${package} -exec touch -d "@${SOURCE_DATE_EPOCH}" {} +
+
+dpkg-deb --build --root-owner-group --uniform-compression ./${package}
+
+strip-nondeterminism "./${package}.deb"
diff --git a/scripts/simplex-chat-reproduce-builds.sh b/scripts/simplex-chat-reproduce-builds.sh
index c7d9170c4a..e1a62dc73a 100755
--- a/scripts/simplex-chat-reproduce-builds.sh
+++ b/scripts/simplex-chat-reproduce-builds.sh
@@ -20,115 +20,133 @@ package direct-sqlcipher
export DOCKER_BUILDKIT=1
+version=${TAG#v}
+version=${version%-*}
+
cleanup() {
- docker exec -t "${container_name}" sh -c 'rm -rf ./dist-newstyle ./apps' 2>/dev/null || :
- rm -rf -- "${tempdir}"
- docker rm --force "${container_name}" 2>/dev/null || :
- docker image rm "${image_name}" 2>/dev/null || :
- cd "${init_dir}"
+ docker exec -t "${container_name}" sh -c 'rm -rf ./dist-newstyle ./apps' 2>/dev/null || :
+ rm -rf -- "${tempdir}"
+ docker rm --force "${container_name}" 2>/dev/null || :
+ docker image rm "${image_name}" 2>/dev/null || :
+ cd "${init_dir}"
}
trap 'cleanup' EXIT INT
mkdir -p "${init_dir}/${TAG}-${repo_name}/from-source" "${init_dir}/${TAG}-${repo_name}/prebuilt"
git -C "${tempdir}" clone "${repo}.git" &&\
- cd "${tempdir}/${repo_name}" &&\
- git checkout "${TAG}"
+ cd "${tempdir}/${repo_name}" &&\
+ git checkout "${TAG}"
for os in '22.04' '24.04'; do
- os_url="$(printf '%s' "${os}" | tr '.' '_')"
+ os_url="$(printf '%s' "${os}" | tr '.' '_')"
- cli_name="simplex-chat-ubuntu-${os_url}-x86_64"
- deb_name="simplex-desktop-ubuntu-${os_url}-x86_64.deb"
- appimage_name="simplex-desktop-x86_64.AppImage"
+ cli_name="simplex-chat-ubuntu-${os_url}-x86_64"
+ deb_name="simplex-desktop-ubuntu-${os_url}-x86_64.deb"
+ appimage_name="simplex-desktop-x86_64.AppImage"
- # Build image
- docker build \
- --no-cache \
- --build-arg TAG="${os}" \
- --build-arg GHC="${ghc}" \
- -f "${tempdir}/${repo_name}/Dockerfile.build" \
- -t "${image_name}" \
- .
+ # Build image
+ docker build \
+ --no-cache \
+ --build-arg TAG="${os}" \
+ --build-arg GHC="${ghc}" \
+ -f "${tempdir}/${repo_name}/Dockerfile.build" \
+ -t "${image_name}" \
+ .
- printf '%s' "${cabal_local}" > "${tempdir}/${repo_name}/cabal.project.local"
+ printf '%s' "${cabal_local}" > "${tempdir}/${repo_name}/cabal.project.local"
- # Run container in background
- docker run -t -d \
- --name "${container_name}" \
- --device /dev/fuse \
- --cap-add SYS_ADMIN \
- --security-opt apparmor:unconfined \
- -v "${tempdir}/${repo_name}:/project" \
- "${image_name}"
+ # Run container in background
+ docker run -t -d \
+ --name "${container_name}" \
+ --device /dev/fuse \
+ --cap-add SYS_ADMIN \
+ --security-opt apparmor:unconfined \
+ -v "${tempdir}/${repo_name}:/project" \
+ "${image_name}"
- # Consistent permissions
- docker exec \
- -t "${container_name}" \
- sh -c 'find /project -type d -exec chmod 755 {} \; ; find /project -type f -perm /111 -exec chmod 755 {} \; ; find /project -type f ! -perm /111 -exec chmod 644 {} \;'
+ # Consistent permissions
+ docker exec \
+ -t "${container_name}" \
+ sh -c 'find /project -type d -exec chmod 755 {} \; ; find /project -type f -perm /111 -exec chmod 755 {} \; ; find /project -type f ! -perm /111 -exec chmod 644 {} \;'
- # CLI
- docker exec \
- -t "${container_name}" \
- sh -c 'cabal clean && cabal update && cabal build -j && 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'
+ # CLI
+ docker exec \
+ -t "${container_name}" \
+ sh -c 'cabal clean && cabal update && cabal build -j && 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'
- # Copy CLI
- docker cp \
- "${container_name}":/out/simplex-chat \
- "${init_dir}/${TAG}-${repo_name}/from-source/${cli_name}"
+ # Copy CLI
+ docker cp \
+ "${container_name}":/out/simplex-chat \
+ "${init_dir}/${TAG}-${repo_name}/from-source/${cli_name}"
- # Download prebuilt CLI binary
- curl -L \
- --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \
- -O "${repo}/releases/download/${TAG}/${cli_name}"
+ # Download prebuilt CLI binary
+ curl -L \
+ --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \
+ -O "${repo}/releases/download/${TAG}/${cli_name}"
- # Desktop: deb
- docker exec \
- -t "${container_name}" \
- sh -c './scripts/desktop/make-deb-linux.sh'
+ # CLI: deb
+ docker exec \
+ -t "${container_name}" \
+ sh -c "./scripts/desktop/build-cli-deb.sh ${version}"
- # Copy deb
- docker cp \
- "${container_name}":/project/apps/multiplatform/release/main/deb/simplex_x86_64.deb \
- "${init_dir}/${TAG}-${repo_name}/from-source/${deb_name}"
+ # Copy CLI: deb
+ docker cp \
+ "${container_name}":/out/deb-build/simplex-chat.deb \
+ "${init_dir}/${TAG}-${repo_name}/from-source/${cli_name}.deb"
- # Download prebuilt deb package
- curl -L \
- --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \
- -O "${repo}/releases/download/${TAG}/${deb_name}"
+ # Download prebuilt CLI: deb binary
+ curl -L \
+ --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \
+ -O "${repo}/releases/download/${TAG}/${cli_name}.deb"
- # Desktop: appimage. Build only on 22.04
- case "$os" in
- 22.04)
- # Appimage
- docker exec \
- -t "${container_name}" \
- sh -c './scripts/desktop/make-appimage-linux.sh && mv ./apps/multiplatform/release/main/*imple*.AppImage ./apps/multiplatform/release/main/simplex.appimage'
+ # Desktop: deb
+ docker exec \
+ -t "${container_name}" \
+ sh -c './scripts/desktop/make-deb-linux.sh'
- # Copy appimage
- docker cp \
- "${container_name}":/project/apps/multiplatform/release/main/simplex.appimage \
- "${init_dir}/${TAG}-${repo_name}/from-source/${appimage_name}"
+ # Copy deb
+ docker cp \
+ "${container_name}":/project/apps/multiplatform/release/main/deb/simplex_x86_64.deb \
+ "${init_dir}/${TAG}-${repo_name}/from-source/${deb_name}"
- # Download prebuilt appimage binary
- curl -L \
- --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \
- -O "${repo}/releases/download/${TAG}/${appimage_name}"
- ;;
- esac
-
- # Important! Remove dist-newstyle for the next interation
- docker exec \
- -t "${container_name}" \
- sh -c 'rm -rf ./dist-newstyle ./apps/multiplatform'
+ # Download prebuilt deb package
+ curl -L \
+ --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \
+ -O "${repo}/releases/download/${TAG}/${deb_name}"
- # Also restore git to previous state
- git reset --hard && git clean -dfx
+ # Desktop: appimage. Build only on 22.04
+ case "$os" in
+ 22.04)
+ # Appimage
+ docker exec \
+ -t "${container_name}" \
+ sh -c './scripts/desktop/make-appimage-linux.sh && mv ./apps/multiplatform/release/main/*imple*.AppImage ./apps/multiplatform/release/main/simplex.appimage'
- # Stop containers, delete images
- docker stop "${container_name}"
- docker rm --force "${container_name}"
- docker image rm "${image_name}"
+ # Copy appimage
+ docker cp \
+ "${container_name}":/project/apps/multiplatform/release/main/simplex.appimage \
+ "${init_dir}/${TAG}-${repo_name}/from-source/${appimage_name}"
+
+ # Download prebuilt appimage binary
+ curl -L \
+ --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \
+ -O "${repo}/releases/download/${TAG}/${appimage_name}"
+ ;;
+ esac
+
+ # Important! Remove dist-newstyle for the next interation
+ docker exec \
+ -t "${container_name}" \
+ sh -c 'rm -rf ./dist-newstyle ./apps/multiplatform'
+
+ # Also restore git to previous state
+ git reset --hard && git clean -dfx
+
+ # Stop containers, delete images
+ docker stop "${container_name}"
+ docker rm --force "${container_name}"
+ docker image rm "${image_name}"
done
# Cleanup
@@ -145,27 +163,27 @@ bad=0
# Check hashes for all binaries
for file in "${path_bin}"/from-source/*; do
- # Extract binary name
- app="$(basename ${file})"
+ # 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}')
+ # 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
+ # 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
+ # ... 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' -e "s|-$repo_name||g" > "${path_bin}/_sha256sums"
+ sha256sum "${path_bin}"/from-source/* | sed -e "s|$PWD/||g" -e 's|from-source/||g' -e "s|-$repo_name||g" > "${path_bin}/_sha256sums"
- printf 'Checksums computed - %s\n' "${path_bin}/_sha256sums"
+ printf 'Checksums computed - %s\n' "${path_bin}/_sha256sums"
fi
diff --git a/simplex-chat.cabal b/simplex-chat.cabal
index 75ddceac5e..c66c7b51ee 100644
--- a/simplex-chat.cabal
+++ b/simplex-chat.cabal
@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
-version: 6.4.7.1
+version: 6.4.8.0
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat
diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs
index dfb0aece74..778ddec72f 100644
--- a/tests/ChatTests/Utils.hs
+++ b/tests/ChatTests/Utils.hs
@@ -93,6 +93,9 @@ xit' = if os == "linux" then xit else it
xit'' :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
xit'' = ifCI xit Hspec.it
+xitMacCI :: HasCallStack => String -> (TestParams -> Expectation) -> SpecWith (Arg (TestParams -> Expectation))
+xitMacCI = ifCI (if os == "darwin" then xit else it) it
+
xdescribe'' :: HasCallStack => String -> SpecWith a -> SpecWith a
xdescribe'' = ifCI xdescribe describe
diff --git a/tests/RemoteTests.hs b/tests/RemoteTests.hs
index e21f97d69e..9790681f3b 100644
--- a/tests/RemoteTests.hs
+++ b/tests/RemoteTests.hs
@@ -33,7 +33,7 @@ remoteTests = describe "Remote" $ do
it "connects with new pairing (stops mobile)" $ remoteHandshakeTest False
it "connects with new pairing (stops desktop)" $ remoteHandshakeTest True
it "connects with stored pairing" remoteHandshakeStoredTest
- it "connects with multicast discovery" remoteHandshakeDiscoverTest
+ xitMacCI "connects with multicast discovery" remoteHandshakeDiscoverTest
it "refuses invalid client cert" remoteHandshakeRejectTest
it "connects with stored server bindings" storedBindingsTest
it "sends messages" remoteMessageTest