Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,22 @@ See also [#185](https://github.com/syncthing/syncthing-macos/issues/185) and the

## Uninstallation

On Mac OS X you drop the application from the Application folder to your Trash.
But there are some user specific files are kept elsewhere, which are located under
On Mac OS X you drop the application from the Application folder to your Trash.
But there are some user specific files are kept elsewhere, which are located under
`$HOME/Library/Application Support/Syncthing`. The files in this folder are the configuration,
encryption/profile files and the database cache. For more information see
[docs.syncthing.net/users/config.html](https://docs.syncthing.net/users/config.html#description).

## Disable automatic update checker

By default the Syncthing macOS application checks automatically for updates. To disable (or re-enable) the
By default the Syncthing macOS application checks automatically for updates. To disable (or re-enable) the
auto update check one must set the Sparkle updater parameter from the commandline:

```
defaults write com.github.xor-gate.syncthing-macosx SUEnableAutomaticChecks 0
```

This setting is un-adviced and therefor only available from the commandline. When your system is not
This setting is un-adviced and therefor only available from the commandline. When your system is not
supported anymore and don't want to get notified of unsupported updates disabling then is recommended.

# Prerequisites for building/using everything in this repository
Expand Down Expand Up @@ -198,7 +198,7 @@ jerry@Jerrys-iMac ~ % defaults read com.github.xor-gate.syncthing-macosx
### Override Syncthing Daeomon `Executable` property (power-users only)

If you want to use the nice GUI but have your own executable located outside the `Syncthing.app` bundle,
then the `Executable` configuration parameter can be manually overwritten with the `defaults` commandline
then the `Executable` configuration parameter can be manually overwritten with the `defaults` commandline
tool using builtin Terminal or iTerm2. The last arguments should be changed to the syncthing daemon golang
application.

Expand Down Expand Up @@ -276,3 +276,35 @@ basicly these steps automatically:
* Manually download the release `.dmg` from [github.com actions](https://github.com/syncthing/syncthing-macos/actions/workflows/build-syncthing-macos.yml) which is correctly signed and notarized
* Manually [create a new Github release](https://github.com/syncthing/syncthing-macos/releases/new) with tag `v<major>.<minor>.<patch>-<bundle index>`
* Run deployment of Sparkle updater [appcast.xml](https://github.com/syncthing/syncthing-macos/actions/workflows/generate-appcast.yml). Which turns [GitHub Releases JSON into a Sparkle appcast.xml file](https://github.com/syncthing/syncthing-macos/tree/develop/cmd/ghreleases2appcast). (See also [Sparkle documentation](https://sparkle-project.org/documentation/)) to push to users.

## Support for additional languages

### 1. Add Language in Xcode

1. Open `syncthing.xcworkspace`
2. Select project root -> **Project -> Info -> Localizations**
3. Click **+** and select target language
4. Select all files when prompted

### 2. Translate

Edit files in `syncthing/[language_code].lproj/`:

```
/* Keep key, translate value */
"Up to date" = "Actualizado";
```

### 3. Update Info.plist

Add language code to `CFBundleLocalizations`:
```xml
<string>es</string> <!-- New language -->
```

### 4. Test

```bash
make debug
open ./Build/Products/Debug/Syncthing.app --args -AppleLanguages '(language_code)'
```
63 changes: 62 additions & 1 deletion cmd/update-release/update-release.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,73 @@
if 'tag_name' not in data:
raise ValueError("tag_name not present in latest_url")

import urllib.request
import json
import semver

def get_latest_v1_tag_name(repo_owner, repo_name, allow_prerelease: bool = False):
"""
Fetches the latest v1 release tag_name from a GitHub repository's releases.

Args:
repo_owner (str): The owner of the GitHub repository (e.g., 'syncthing').
repo_name (str): The name of the GitHub repository (e.g., 'syncthing').

Returns:
str or None: The tag_name of the latest v1 release, or None if not found.
"""
url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases"
try:
with urllib.request.urlopen(url) as response:
if response.getcode() == 200:
data = json.loads(response.read().decode('utf-8'))
else:
print(f"Error fetching data: HTTP {response.getcode()}")
return None
except urllib.error.URLError as e:
print(f"Error connecting to GitHub API: {e.reason}")
return None
except json.JSONDecodeError:
print("Error decoding JSON response.")
return None

v1_releases = []
for release in data:
tag_name = release.get('tag_name')
prerelease = release.get('prerelease')

if tag_name:
try:
version = semver.Version.parse(tag_name.lstrip('v')) # Remove 'v' prefix if present
if allow_prerelease and version.major == 1 and version.prerelease:
v1_releases.append(version)
elif version.major == 1:
v1_releases.append(version)
except ValueError:
# Not a valid semver string, skip
continue

if not v1_releases:
return None

# Sort the prereleases to find the latest
latest_v1_release = max(v1_releases)
return f"v{latest_v1_release}" # Re-add the 'v' prefix for consistency

###
# Parse the tag version and generate CFBundleShortVersionString and CFBundleVersion
###
owner = "syncthing"
repo = "syncthing"
latest_tag = get_latest_v1_tag_name(owner, repo)

if latest_tag:
print(f"The latest v1 release tag_name for {owner}/{repo} is: {latest_tag}")
else:
print(f"No v1 release found for {owner}/{repo}.")

# Ugly hack because of https://github.com/python-semver/python-semver/issues/137
tag_name = data['tag_name'].replace('v', '')
tag_name = latest_tag.replace('v', '')
version = semver.VersionInfo.parse(tag_name)

CFBundleShortVersionString = "{}-{:d}".format(
Expand Down
67 changes: 58 additions & 9 deletions syncthing.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@
29AF1BA3210F11BF004212DE /* DaemonProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AFA4CD210F10FA00071E5E /* DaemonProcess.swift */; };
BD71E04946BAD5B0A9FACC3D /* libPods-syncthing.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 014971905AAC00CB5291E063 /* libPods-syncthing.a */; };
C4460A801D0DD2D500200C21 /* STPreferencesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = C4460A7D1D0DD2D500200C21 /* STPreferencesWindowController.m */; };
C4460A831D0DD38F00200C21 /* STPreferencesWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4460A821D0DD38F00200C21 /* STPreferencesWindow.xib */; };
C4460A831D0DD38F00200C21 /* STPreferencesWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = D4A1B57A1E2A3F4900C8E2AA /* STPreferencesWindow.xib */; };
C469AFEB25D7E76900904DD7 /* STLoginItem.m in Sources */ = {isa = PBXBuildFile; fileRef = C469AFEA25D7E76900904DD7 /* STLoginItem.m */; };
C4946B011D5877F2008447A2 /* STAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4946B001D5877F2008447A2 /* STAboutWindow.xib */; };
C4946B011D5877F2008447A2 /* STAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = D4A1B5791E2A3F4900C8E2A9 /* STAboutWindow.xib */; };
C4946B031D587878008447A2 /* STAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = C4946B021D587878008447A2 /* STAboutWindowController.m */; };
C4A415651D0D579D00DC6018 /* STApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = C4A415641D0D579D00DC6018 /* STApplication.m */; };
C4A415681D0D579D00DC6018 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C4A415671D0D579D00DC6018 /* main.m */; };
C4A4156A1D0D579D00DC6018 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C4A415691D0D579D00DC6018 /* Assets.xcassets */; };
C4A4156D1D0D579D00DC6018 /* STApplication.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4A4156B1D0D579D00DC6018 /* STApplication.xib */; };
C4F0E82E1DA1B9CF00435310 /* STPreferencesWindowGeneralViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C4F0E82C1DA1B9CF00435310 /* STPreferencesWindowGeneralViewController.m */; };
C4F0E82F1DA1B9CF00435310 /* STPreferencesWindowGeneralView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4F0E82D1DA1B9CF00435310 /* STPreferencesWindowGeneralView.xib */; };
C4F0E82F1DA1B9CF00435310 /* STPreferencesWindowGeneralView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D4A1B57C1E2A3F4900C8E2AC /* STPreferencesWindowGeneralView.xib */; };
C4FFB0661D0D7F870015D14A /* XGSyncthing.m in Sources */ = {isa = PBXBuildFile; fileRef = C4FFB0641D0D7E4C0015D14A /* XGSyncthing.m */; };
D4A1B5701E2A3F4900C8E2A1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4A1B5721E2A3F4900C8E2A2 /* Localizable.strings */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -71,18 +72,22 @@
298A5C44210DA6C40034B89F /* LocalhostTLSDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalhostTLSDelegate.h; sourceTree = "<group>"; };
29AFA4CD210F10FA00071E5E /* DaemonProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DaemonProcess.swift; sourceTree = "<group>"; };
29CC334D4211142108D2F82C /* Pods-syncthing.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncthing.release.xcconfig"; path = "Pods/Target Support Files/Pods-syncthing/Pods-syncthing.release.xcconfig"; sourceTree = "<group>"; };
9A38F85F2E80F1B600100A57 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/STApplication.strings; sourceTree = "<group>"; };
9A38F8602E80F1B600100A57 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/STAboutWindow.strings; sourceTree = "<group>"; };
9A38F8612E80F1B600100A57 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/STPreferencesWindow.strings; sourceTree = "<group>"; };
9A38F8622E80F1B600100A57 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/STPreferencesWindowGeneralView.strings; sourceTree = "<group>"; };
9A38F8632E80F1B700100A57 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
C44262761D0DEF9F005340C2 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
C4460A7C1D0DD2D500200C21 /* STPreferencesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPreferencesWindowController.h; path = Controllers/STPreferencesWindowController.h; sourceTree = "<group>"; };
C4460A7D1D0DD2D500200C21 /* STPreferencesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STPreferencesWindowController.m; path = Controllers/STPreferencesWindowController.m; sourceTree = "<group>"; };
C4460A821D0DD38F00200C21 /* STPreferencesWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = STPreferencesWindow.xib; path = UI/STPreferencesWindow.xib; sourceTree = "<group>"; };
C4576BD51D11E3210031BCFD /* create-dmg.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "create-dmg.sh"; path = "syncthing/Scripts/create-dmg.sh"; sourceTree = "<group>"; };
C4576BE51D11E79F0031BCFD /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
C4576BE71D11E7C00031BCFD /* libobjc.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libobjc.tbd; path = usr/lib/libobjc.tbd; sourceTree = SDKROOT; };
C4576BE91D11E7E10031BCFD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
C469AFE325D7CFE400904DD7 /* syncthing-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "syncthing-Bridging-Header.h"; sourceTree = "<group>"; };
C469AFE925D7E76900904DD7 /* STLoginItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STLoginItem.h; sourceTree = "<group>"; };
C469AFEA25D7E76900904DD7 /* STLoginItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STLoginItem.m; sourceTree = "<group>"; };
C4946B001D5877F2008447A2 /* STAboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = STAboutWindow.xib; path = UI/STAboutWindow.xib; sourceTree = "<group>"; };
C4946B001D5877F2008447A2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/STAboutWindow.xib; sourceTree = "<group>"; };
C4946B021D587878008447A2 /* STAboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STAboutWindowController.m; path = Controllers/STAboutWindowController.m; sourceTree = "<group>"; };
C4946B041D587907008447A2 /* STAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STAboutWindowController.h; path = Controllers/STAboutWindowController.h; sourceTree = "<group>"; };
C4A415601D0D579D00DC6018 /* Syncthing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Syncthing.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand All @@ -96,9 +101,11 @@
C4D896121D0DF90900D42F73 /* syncthing-resource.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "syncthing-resource.sh"; path = "syncthing/Scripts/syncthing-resource.sh"; sourceTree = "<group>"; };
C4F0E82B1DA1B9CF00435310 /* STPreferencesWindowGeneralViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPreferencesWindowGeneralViewController.h; sourceTree = "<group>"; };
C4F0E82C1DA1B9CF00435310 /* STPreferencesWindowGeneralViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPreferencesWindowGeneralViewController.m; sourceTree = "<group>"; };
C4F0E82D1DA1B9CF00435310 /* STPreferencesWindowGeneralView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = STPreferencesWindowGeneralView.xib; sourceTree = "<group>"; };
C4FFB0631D0D7E440015D14A /* XGSyncthing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XGSyncthing.h; path = syncthing/XGSyncthing.h; sourceTree = SOURCE_ROOT; };
C4FFB0641D0D7E4C0015D14A /* XGSyncthing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XGSyncthing.m; path = syncthing/XGSyncthing.m; sourceTree = SOURCE_ROOT; };
D4A1B5731E2A3F4900C8E2A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
D4A1B57B1E2A3F4900C8E2AB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/STPreferencesWindow.xib; sourceTree = "<group>"; };
D4A1B57D1E2A3F4900C8E2AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/STPreferencesWindowGeneralView.xib; sourceTree = "<group>"; };
FB3E4EE48CFC608AD4FBDE77 /* Pods-syncthing.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncthing.debug.xcconfig"; path = "Pods/Target Support Files/Pods-syncthing/Pods-syncthing.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -229,9 +236,10 @@
isa = PBXGroup;
children = (
C4A4156B1D0D579D00DC6018 /* STApplication.xib */,
C4946B001D5877F2008447A2 /* STAboutWindow.xib */,
C4460A821D0DD38F00200C21 /* STPreferencesWindow.xib */,
C4F0E82D1DA1B9CF00435310 /* STPreferencesWindowGeneralView.xib */,
D4A1B5791E2A3F4900C8E2A9 /* STAboutWindow.xib */,
D4A1B57A1E2A3F4900C8E2AA /* STPreferencesWindow.xib */,
D4A1B57C1E2A3F4900C8E2AC /* STPreferencesWindowGeneralView.xib */,
D4A1B5721E2A3F4900C8E2A2 /* Localizable.strings */,
);
name = UI;
sourceTree = "<group>";
Expand Down Expand Up @@ -297,6 +305,7 @@
knownRegions = (
en,
Base,
es,
);
mainGroup = C4A415571D0D579D00DC6018;
productRefGroup = C4A415611D0D579D00DC6018 /* Products */;
Expand All @@ -319,6 +328,7 @@
C4460A831D0DD38F00200C21 /* STPreferencesWindow.xib in Resources */,
C4946B011D5877F2008447A2 /* STAboutWindow.xib in Resources */,
C4F0E82F1DA1B9CF00435310 /* STPreferencesWindowGeneralView.xib in Resources */,
D4A1B5701E2A3F4900C8E2A1 /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -423,10 +433,47 @@
isa = PBXVariantGroup;
children = (
C4A4156C1D0D579D00DC6018 /* Base */,
9A38F85F2E80F1B600100A57 /* es */,
);
name = STApplication.xib;
sourceTree = "<group>";
};
D4A1B5721E2A3F4900C8E2A2 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
D4A1B5731E2A3F4900C8E2A3 /* Base */,
9A38F8632E80F1B700100A57 /* es */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
D4A1B5791E2A3F4900C8E2A9 /* STAboutWindow.xib */ = {
isa = PBXVariantGroup;
children = (
C4946B001D5877F2008447A2 /* Base */,
9A38F8602E80F1B600100A57 /* es */,
);
name = STAboutWindow.xib;
sourceTree = "<group>";
};
D4A1B57A1E2A3F4900C8E2AA /* STPreferencesWindow.xib */ = {
isa = PBXVariantGroup;
children = (
D4A1B57B1E2A3F4900C8E2AB /* Base */,
9A38F8612E80F1B600100A57 /* es */,
);
name = STPreferencesWindow.xib;
sourceTree = "<group>";
};
D4A1B57C1E2A3F4900C8E2AC /* STPreferencesWindowGeneralView.xib */ = {
isa = PBXVariantGroup;
children = (
D4A1B57D1E2A3F4900C8E2AD /* Base */,
9A38F8622E80F1B600100A57 /* es */,
);
name = STPreferencesWindowGeneralView.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
Expand All @@ -448,6 +495,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
Expand Down Expand Up @@ -506,6 +554,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
Expand Down
Loading
Loading