Skip to content

Commit e6a13cc

Browse files
committed
Merge branch 'release/1.6.0'
2 parents d36dfa3 + a2c181f commit e6a13cc

File tree

78 files changed

+655
-426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+655
-426
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ DerivedData
2929
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
3030
#
3131
#Pods/
32+
.BuildVersion.xcconfig

package/BuildAndPackage.command

+13-11
Original file line numberDiff line numberDiff line change
@@ -165,23 +165,25 @@ then
165165
fi
166166
fi
167167

168-
PKG_VERSION=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" "${PROJECT_DIR}/Hello IT/Info.plist")
168+
MARKETING_VERSION=$(sed -n '/MARKETING_VERSION/{s/MARKETING_VERSION = //;s/;//;s/^[[:space:]]*//;p;q;}' "${PROJECT_DIR}/Hello-IT.xcconfig")
169169

170170
if [[ "$CURRENT_BRANCH" == release* ]]
171171
then
172172
CONFIGURATION="Release"
173173
VERSION_FROM_BRANCH=$(echo "${CURRENT_BRANCH}" | awk -F'/' '{print $2}')
174174
if [[ "$VERSION_FROM_BRANCH" =~ ^[0-9]+\.[0-9]+ ]]
175175
then
176-
PKG_VERSION=$VERSION_FROM_BRANCH
177-
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $PKG_VERSION" "${PROJECT_DIR}/Hello IT/Info.plist"
178-
git add "${PROJECT_DIR}/Hello IT/Info.plist"
179-
git commit -m "Update app version number according to release branch"
176+
MARKETING_VERSION=$VERSION_FROM_BRANCH
177+
sed -i '' "/MARKETING_VERSION/c\\
178+
MARKETING_VERSION = ${MARKETING_VERSION}
179+
" "${PROJECT_DIR}/Hello-IT.xcconfig"
180+
git add "${PROJECT_DIR}/Hello-IT.xcconfig"
181+
git commit -m "Update marketing version number according to release branch"
180182
fi
181183
fi
182184

183185
BASE_RELEASE_LOCATION="${GIT_ROOT_DIR}/package/build"
184-
RELEASE_LOCATION="${BASE_RELEASE_LOCATION}/${PKG_VERSION}-${CONFIGURATION}"
186+
RELEASE_LOCATION="${BASE_RELEASE_LOCATION}/${MARKETING_VERSION}-${CONFIGURATION}"
185187
RELEASE_PRODUCT_LOCATION="${RELEASE_LOCATION}/Products"
186188
RELEASE_DSYM_LOCATION="${RELEASE_LOCATION}/dSYM"
187189

@@ -206,7 +208,7 @@ echo "####### Build project"
206208

207209
echo "### Start building Hello IT"
208210

209-
xcodebuild -UseModernBuildSystem=NO -quiet -project "${PROJECT_DIR}/Hello IT.xcodeproj" -configuration ${CONFIGURATION} -target "Hello IT" CONFIGURATION_TEMP_DIR="${BUILT_PRODUCTS_DIR}/Intermediates" CONFIGURATION_BUILD_DIR="${BUILT_PRODUCTS_DIR}/Products" DWARF_DSYM_FOLDER_PATH="${BUILT_PRODUCTS_DIR}/dSYM" OTHER_CODE_SIGN_FLAGS="--timestamp"
211+
xcodebuild -UseModernBuildSystem=NO -arch x86_64 -arch arm64 ONLY_ACTIVE_ARCH=NO -quiet -project "${PROJECT_DIR}/Hello IT.xcodeproj" -configuration ${CONFIGURATION} -target "Hello IT" CONFIGURATION_TEMP_DIR="${BUILT_PRODUCTS_DIR}/Intermediates" CONFIGURATION_BUILD_DIR="${BUILT_PRODUCTS_DIR}/Products" DWARF_DSYM_FOLDER_PATH="${BUILT_PRODUCTS_DIR}/dSYM" OTHER_CODE_SIGN_FLAGS="--timestamp"
210212

211213
if [[ $? != 0 ]]; then
212214
echo "Building failed"
@@ -242,18 +244,18 @@ pkgbuild --analyze --root "${PKG_ROOT}" "${PBK_BUILD_COMPONENT}"
242244

243245
/usr/libexec/PlistBuddy -c "Set 0:BundleIsRelocatable bool false" "${PBK_BUILD_COMPONENT}"
244246
#/usr/libexec/PlistBuddy -c "Print" "${PBK_BUILD_COMPONENT}"
245-
pkgbuild --component-plist "${PBK_BUILD_COMPONENT}" --sign "${DEVELOPER_ID_INSTALLER}" --root "${PKG_ROOT}" --scripts "${GIT_ROOT_DIR}/package/pkg_scripts" --identifier "com.github.ygini.hello-it" --version "${PKG_VERSION}" "${RELEASE_LOCATION}/Hello-IT-${PKG_VERSION}-${CONFIGURATION}.pkg"
247+
pkgbuild --component-plist "${PBK_BUILD_COMPONENT}" --sign "${DEVELOPER_ID_INSTALLER}" --root "${PKG_ROOT}" --scripts "${GIT_ROOT_DIR}/package/pkg_scripts" --identifier "com.github.ygini.hello-it" --version "${MARKETING_VERSION}" "${RELEASE_LOCATION}/Hello-IT-${MARKETING_VERSION}-${CONFIGURATION}.pkg"
246248

247-
productbuild --product "${GIT_ROOT_DIR}/package/requirements.plist" --sign "${DEVELOPER_ID_INSTALLER}" --version "${PKG_VERSION}" --package "${RELEASE_LOCATION}/Hello-IT-${PKG_VERSION}-${CONFIGURATION}.pkg" "${RELEASE_LOCATION}/Hello-IT-${PKG_VERSION}-${CONFIGURATION}-Distribution.pkg"
249+
productbuild --product "${GIT_ROOT_DIR}/package/requirements.plist" --sign "${DEVELOPER_ID_INSTALLER}" --version "${MARKETING_VERSION}" --package "${RELEASE_LOCATION}/Hello-IT-${MARKETING_VERSION}-${CONFIGURATION}.pkg" "${RELEASE_LOCATION}/Hello-IT-${MARKETING_VERSION}-${CONFIGURATION}-Distribution.pkg"
248250

249251
if [[ $? != 0 ]]; then
250252
echo "Package creation failed"
251253
exit 1
252254
fi
253255

254-
rm "${RELEASE_LOCATION}/Hello-IT-${PKG_VERSION}-${CONFIGURATION}.pkg"
256+
rm "${RELEASE_LOCATION}/Hello-IT-${MARKETING_VERSION}-${CONFIGURATION}.pkg"
255257

256-
notarizePayloadWithBundleID "${RELEASE_LOCATION}/Hello-IT-${PKG_VERSION}-${CONFIGURATION}-Distribution.pkg" "com.github.ygini.hello-it"
258+
notarizePayloadWithBundleID "${RELEASE_LOCATION}/Hello-IT-${MARKETING_VERSION}-${CONFIGURATION}-Distribution.pkg" "com.github.ygini.hello-it"
257259

258260
rm -rf "${PKG_ROOT}"
259261

src/HITDevKit/HITDevKit.xcconfig

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// HITDevKit.xcconfig
3+
// HITDevKit
4+
//
5+
// Created by Yoann Gini on 18/04/2021.
6+
// Copyright © 2021 Yoann Gini (Open Source Project). All rights reserved.
7+
//
8+
9+
// Configuration settings file format documentation can be found at:
10+
// https://help.apple.com/xcode/#/dev745c5c974
11+
12+
#include "../Hello-IT.xcconfig"

src/HITDevKit/HITDevKit.xcodeproj/project.pbxproj

+7-19
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
E1B2C9061B593957005A9936 /* Warning.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Warning.tiff; sourceTree = "<group>"; };
5656
E1B2C9141B5941AA005A9936 /* HITPeriodicPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HITPeriodicPlugin.h; sourceTree = "<group>"; };
5757
E1B2C9151B5941AA005A9936 /* HITPeriodicPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HITPeriodicPlugin.m; sourceTree = "<group>"; };
58+
F8886A0E262CC1F70039DE6C /* HITDevKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = HITDevKit.xcconfig; sourceTree = "<group>"; };
5859
/* End PBXFileReference section */
5960

6061
/* Begin PBXFrameworksBuildPhase section */
@@ -79,6 +80,7 @@
7980
E1B2C8C71B59351B005A9936 = {
8081
isa = PBXGroup;
8182
children = (
83+
F8886A0E262CC1F70039DE6C /* HITDevKit.xcconfig */,
8284
E1B2C8D31B59351B005A9936 /* HITDevKit */,
8385
E1B2C8E01B59351B005A9936 /* HITDevKitTests */,
8486
E1B2C8D21B59351B005A9936 /* Products */,
@@ -164,7 +166,6 @@
164166
isa = PBXNativeTarget;
165167
buildConfigurationList = E1B2C8E71B59351B005A9936 /* Build configuration list for PBXNativeTarget "HITDevKit" */;
166168
buildPhases = (
167-
F8B50A231D1ECEF9003CD614 /* GitVersion */,
168169
E1B2C8CC1B59351B005A9936 /* Sources */,
169170
E1B2C8CD1B59351B005A9936 /* Frameworks */,
170171
E1B2C8CE1B59351B005A9936 /* Headers */,
@@ -203,7 +204,7 @@
203204
E1B2C8C81B59351B005A9936 /* Project object */ = {
204205
isa = PBXProject;
205206
attributes = {
206-
LastUpgradeCheck = 1140;
207+
LastUpgradeCheck = 1240;
207208
ORGANIZATIONNAME = "Yoann Gini (Open Source Project)";
208209
TargetAttributes = {
209210
E1B2C8D01B59351B005A9936 = {
@@ -258,23 +259,6 @@
258259
};
259260
/* End PBXResourcesBuildPhase section */
260261

261-
/* Begin PBXShellScriptBuildPhase section */
262-
F8B50A231D1ECEF9003CD614 /* GitVersion */ = {
263-
isa = PBXShellScriptBuildPhase;
264-
buildActionMask = 2147483647;
265-
files = (
266-
);
267-
inputPaths = (
268-
);
269-
name = GitVersion;
270-
outputPaths = (
271-
);
272-
runOnlyForDeploymentPostprocessing = 0;
273-
shellPath = /bin/sh;
274-
shellScript = "set -o errexit\nset -o nounset\n\nVERSION=$(git rev-list HEAD | wc -l)\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $VERSION\" \"${INFOPLIST_FILE}\"";
275-
};
276-
/* End PBXShellScriptBuildPhase section */
277-
278262
/* Begin PBXSourcesBuildPhase section */
279263
E1B2C8CC1B59351B005A9936 /* Sources */ = {
280264
isa = PBXSourcesBuildPhase;
@@ -308,6 +292,7 @@
308292
/* Begin XCBuildConfiguration section */
309293
E1B2C8E51B59351B005A9936 /* Debug */ = {
310294
isa = XCBuildConfiguration;
295+
baseConfigurationReference = F8886A0E262CC1F70039DE6C /* HITDevKit.xcconfig */;
311296
buildSettings = {
312297
ALWAYS_SEARCH_USER_PATHS = NO;
313298
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
@@ -329,6 +314,7 @@
329314
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
330315
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
331316
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
317+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
332318
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
333319
CLANG_WARN_STRICT_PROTOTYPES = YES;
334320
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -365,6 +351,7 @@
365351
};
366352
E1B2C8E61B59351B005A9936 /* Release */ = {
367353
isa = XCBuildConfiguration;
354+
baseConfigurationReference = F8886A0E262CC1F70039DE6C /* HITDevKit.xcconfig */;
368355
buildSettings = {
369356
ALWAYS_SEARCH_USER_PATHS = NO;
370357
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
@@ -386,6 +373,7 @@
386373
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
387374
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
388375
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
376+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
389377
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
390378
CLANG_WARN_STRICT_PROTOTYPES = YES;
391379
CLANG_WARN_SUSPICIOUS_MOVE = YES;

src/HITDevKit/HITDevKit/HITAdvancedPlugin.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// Copyright (c) 2015 Yoann Gini (Open Source Project). All rights reserved.
77
//
88

9-
#import "HITSimplePlugin.h"
9+
#import <HITDevKit/HITSimplePlugin.h>
1010

1111
@interface HITAdvancedPlugin : HITSimplePlugin
1212

src/HITDevKit/HITDevKit/HITAdvancedPlugin.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ - (void)updateMenuItemState {
4646
break;
4747
case HITPluginTestStateNone:
4848
default:
49-
self.menuItem.image = nil;
49+
self.menuItem.image = [self imageForMenuItem];
5050
notificationMessage = [self localizedString:[self.settings objectForKey:kHITNotificationMessageForNoneKey]];
5151
break;
5252
}

src/HITDevKit/HITDevKit/HITSimplePlugin.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
#import <HITDevKit/HITBasicPlugin.h>
1111

1212
#define kHITSimplePluginTitleKey @"title"
13+
#define kHITSimplePluginComputedTitleKey @"computedTitle"
1314
#define kHITSimplePluginImagePathKey @"imagePath"
1415
#define kHITSimplePluginImageBaseNameKey @"imageBaseName"
1516

1617
@interface HITSimplePlugin : HITBasicPlugin
17-
1818
@end
1919

2020
@interface HITSimplePlugin (MustBeDefinedInSubclass)
2121
- (IBAction)mainAction:(id)sender;
22-
@end
22+
-(NSImage *)imageForMenuItem;
23+
@end

src/HITDevKit/HITDevKit/HITSimplePlugin.m

+108-14
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,50 @@
1010

1111
#import <asl.h>
1212

13+
@interface HITSimplePlugin ()
14+
@property NSString *script;
15+
@property BOOL scriptChecked;
16+
- (void)updateWithComputedTitle:(NSMenuItem *)menuItem;
17+
@end
18+
19+
#define kHITPCustomScriptsPath @"/Library/Application Support/com.github.ygini.hello-it/CustomScripts"
20+
#define kHITPDenyUserWritableScript @"denyUserWritableScript"
21+
1322
@implementation HITSimplePlugin
1423

15-
-(NSMenuItem *)prepareNewMenuItem {
16-
NSString *title = [self localizedString:[self.settings objectForKey:kHITSimplePluginTitleKey]];
17-
if (!title) {
18-
title = @"";
24+
25+
- (instancetype)initWithSettings:(NSDictionary*)settings
26+
{
27+
self = [super initWithSettings:settings];
28+
if (self) {
29+
30+
if ([[settings objectForKey:kHITSimplePluginComputedTitleKey] length] > 0) {
31+
_script = [[NSString stringWithFormat:kHITPCustomScriptsPath] stringByAppendingPathComponent:[settings objectForKey:kHITSimplePluginComputedTitleKey]];
32+
33+
asl_log(NULL, NULL, ASL_LEVEL_INFO, "Loading script based plugin with script at path %s", [_script cStringUsingEncoding:NSUTF8StringEncoding]);
34+
35+
if ([[NSFileManager defaultManager] fileExistsAtPath:_script]) {
36+
if ([[NSFileManager defaultManager] isWritableFileAtPath:_script] && [[NSUserDefaults standardUserDefaults] boolForKey:kHITPDenyUserWritableScript]) {
37+
#ifdef DEBUG
38+
_scriptChecked = YES;
39+
#else
40+
_scriptChecked = NO;
41+
#endif
42+
asl_log(NULL, NULL, ASL_LEVEL_ERR, "Target script is writable, security restriction deny such a scenario %s", [_script cStringUsingEncoding:NSUTF8StringEncoding]);
43+
} else {
44+
_scriptChecked = YES;
45+
}
46+
} else {
47+
_scriptChecked = NO;
48+
asl_log(NULL, NULL, ASL_LEVEL_ERR, "Target script not accessible %s", [_script cStringUsingEncoding:NSUTF8StringEncoding]);
49+
}
50+
}
51+
1952
}
20-
21-
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:title
22-
action:@selector(mainAction:)
23-
keyEquivalent:@""];
24-
menuItem.target = self;
25-
53+
return self;
54+
}
55+
56+
-(NSImage *)imageForMenuItem {
2657
NSString *imagePath = [self.settings objectForKey:kHITSimplePluginImagePathKey];
2758
NSString *imageBaseName = [self.settings objectForKey:kHITSimplePluginImageBaseNameKey];
2859

@@ -53,18 +84,81 @@ -(NSMenuItem *)prepareNewMenuItem {
5384
}
5485

5586
if (imagePath) {
56-
asl_log(NULL, NULL, ASL_LEVEL_INFO, "Menu item with title %s set with image at path %s.", [title cStringUsingEncoding:NSUTF8StringEncoding], [imagePath cStringUsingEncoding:NSUTF8StringEncoding]);
87+
asl_log(NULL, NULL, ASL_LEVEL_INFO, "Menu item set with image at path %s.", [imagePath cStringUsingEncoding:NSUTF8StringEncoding]);
5788
NSImage *accessoryImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
5889

59-
if (accessoryImage) {
60-
menuItem.image = accessoryImage;
61-
} else {
90+
if (!accessoryImage) {
6291
asl_log(NULL, NULL, ASL_LEVEL_ERR, "Impossible to load image at path %s.", [imagePath cStringUsingEncoding:NSUTF8StringEncoding]);
6392
}
93+
94+
return accessoryImage;
6495
}
96+
97+
return nil;
98+
}
99+
100+
-(NSMenuItem *)prepareNewMenuItem {
101+
NSString *title = [self localizedString:[self.settings objectForKey:kHITSimplePluginTitleKey]];
102+
if (!title) {
103+
title = @"";
104+
}
105+
106+
107+
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:title
108+
action:@selector(mainAction:)
109+
keyEquivalent:@""];
65110

111+
[self updateWithComputedTitle:menuItem];
112+
113+
menuItem.target = self;
114+
115+
menuItem.image = [self imageForMenuItem];
66116

67117
return menuItem;
68118
}
69119

120+
- (void)updateWithComputedTitle:(NSMenuItem *)menuItem {
121+
if (self.scriptChecked && self.allowedToRun) {
122+
asl_log(NULL, NULL, ASL_LEVEL_INFO, "Get computed title with script %s", [self.script cStringUsingEncoding:NSUTF8StringEncoding]);
123+
124+
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
125+
NSTask *task = [[NSTask alloc] init];
126+
[task setLaunchPath:self.script];
127+
128+
[task setStandardOutput:[NSPipe pipe]];
129+
NSFileHandle *fileToRead = [[task standardOutput] fileHandleForReading];
130+
131+
dispatch_io_t stdoutChannel = dispatch_io_create(DISPATCH_IO_STREAM, [fileToRead fileDescriptor], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(int error) {
132+
133+
});
134+
135+
__block BOOL firstLine = YES;
136+
dispatch_io_read(stdoutChannel, 0, SIZE_MAX, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(bool done, dispatch_data_t data, int error) {
137+
if (firstLine) {
138+
firstLine = NO;
139+
} else {
140+
return;
141+
}
142+
143+
NSData *stdoutData = (NSData *)data;
144+
145+
NSString *stdoutString = [[NSString alloc] initWithData:stdoutData encoding:NSUTF8StringEncoding];
146+
147+
NSArray *stdoutLines = [stdoutString componentsSeparatedByString:@"\n"];
148+
menuItem.title = [stdoutLines firstObject];
149+
});
150+
151+
@try {
152+
[task launch];
153+
154+
[task waitUntilExit];
155+
156+
asl_log(NULL, NULL, ASL_LEVEL_INFO, "Script exited with code %i", [task terminationStatus]);
157+
} @catch (NSException *exception) {
158+
asl_log(NULL, NULL, ASL_LEVEL_ERR, "Script failed to run: %s", [[exception reason] UTF8String]);
159+
}
160+
}];
161+
}
162+
}
163+
70164
@end

src/HITDevKit/HITDevKit/Info.plist

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.0</string>
18+
<string>$(MARKETING_VERSION)</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>
22-
<string>367</string>
22+
<string>$(BUILD_VERSION)</string>
2323
<key>NSHumanReadableCopyright</key>
2424
<string>Copyright © 2015 Yoann Gini (Open Source Project). All rights reserved.</string>
2525
<key>NSPrincipalClass</key>

src/HITDevKit/HITDevKitTests/Info.plist

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
<key>CFBundlePackageType</key>
1616
<string>BNDL</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.0</string>
18+
<string>$(MARKETING_VERSION)</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>
22-
<string>1</string>
22+
<string>$(BUILD_VERSION)</string>
2323
</dict>
2424
</plist>

src/HITPlugins/HITPlugins/Info.plist

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.0</string>
18+
<string>$(MARKETING_VERSION)</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>
22-
<string>$(CURRENT_PROJECT_VERSION)</string>
22+
<string>$(BUILD_VERSION)</string>
2323
<key>NSHumanReadableCopyright</key>
2424
<string>Copyright © 2015 Yoann Gini (Open Source Project). All rights reserved.</string>
2525
<key>NSPrincipalClass</key>

0 commit comments

Comments
 (0)