Skip to content

Commit 6bf77b4

Browse files
author
Rajul
committed
Set up release versioning workflow
1 parent 4286529 commit 6bf77b4

File tree

10 files changed

+227
-41
lines changed

10 files changed

+227
-41
lines changed
-766 KB
Loading

.github/workflows/release.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ jobs:
1919
- name: Show Xcode version
2020
run: xcodebuild -version
2121

22+
- name: Sync release version and build number
23+
run: |
24+
VERSION="${GITHUB_REF_NAME#v}"
25+
./scripts/bump_version.sh "$VERSION" "$GITHUB_RUN_NUMBER"
26+
2227
- name: Build Universal Binary
2328
run: make build
2429

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ XCODEBUILD_FLAGS ?= \
2020
ONLY_ACTIVE_ARCH=NO \
2121
ARCHS="arm64 x86_64"
2222

23-
.PHONY: build dist-app dmg clean verify-app verify-universal
23+
.PHONY: build dist-app dmg clean verify-app verify-universal bump-version bump-build
2424

2525
build:
2626
xcodebuild \
@@ -67,3 +67,10 @@ dmg: dist-app
6767

6868
clean:
6969
rm -rf "$(CURDIR)/build" "$(DIST_DIR)"
70+
71+
bump-version:
72+
@test -n "$(VERSION)" || (echo "Usage: make bump-version VERSION=1.0.2" && exit 1)
73+
./scripts/bump_version.sh "$(VERSION)" "$(BUILD)"
74+
75+
bump-build:
76+
./scripts/bump_build.sh

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,30 @@ make dmg # Create DMG installer
4747

4848
Requires Xcode 16+ and macOS 14+.
4949

50+
### Versioning
51+
52+
Kutuk uses a checked-in versioning script so local releases and CI builds stay in sync.
53+
54+
To update the app version and automatically increment the local build number by 1:
55+
56+
```bash
57+
make bump-version VERSION=1.0.2
58+
```
59+
60+
To increment only the build number:
61+
62+
```bash
63+
make bump-build
64+
```
65+
66+
If needed, you can also set an explicit build number while changing the app version:
67+
68+
```bash
69+
make bump-version VERSION=1.0.2 BUILD=5
70+
```
71+
72+
Release builds set the final build number from GitHub Actions, so shipped artifacts always get a monotonically increasing CI build number.
73+
5074
## Usage
5175

5276
Once running, Kutuk appears as a small icon in your menu bar. Click it to:

kutuk.xcodeproj/project.pbxproj

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,52 @@
2929
D084F3A62F140AA0000178B2 /* kutukUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = kutukUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3030
/* End PBXFileReference section */
3131

32+
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
33+
D084F3AA2F140AA1000178B2 /* Exceptions for "kutuk" folder in "kutuk" target */ = {
34+
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
35+
membershipExceptions = (
36+
Info.plist,
37+
);
38+
target = D084F38C2F140A9F000178B2 /* kutuk */;
39+
};
40+
D084F3AB2F140AA1000178B2 /* Exceptions for "kutukTests" folder in "kutukTests" target */ = {
41+
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
42+
membershipExceptions = (
43+
Info.plist,
44+
);
45+
target = D084F39B2F140AA0000178B2 /* kutukTests */;
46+
};
47+
D084F3AC2F140AA1000178B2 /* Exceptions for "kutukUITests" folder in "kutukUITests" target */ = {
48+
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
49+
membershipExceptions = (
50+
Info.plist,
51+
);
52+
target = D084F3A52F140AA0000178B2 /* kutukUITests */;
53+
};
54+
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
55+
3256
/* Begin PBXFileSystemSynchronizedRootGroup section */
3357
D084F38F2F140A9F000178B2 /* kutuk */ = {
3458
isa = PBXFileSystemSynchronizedRootGroup;
59+
exceptions = (
60+
D084F3AA2F140AA1000178B2 /* Exceptions for "kutuk" folder in "kutuk" target */,
61+
);
3562
path = kutuk;
3663
sourceTree = "<group>";
3764
};
3865
D084F39F2F140AA0000178B2 /* kutukTests */ = {
3966
isa = PBXFileSystemSynchronizedRootGroup;
67+
exceptions = (
68+
D084F3AB2F140AA1000178B2 /* Exceptions for "kutukTests" folder in "kutukTests" target */,
69+
);
4070
path = kutukTests;
4171
sourceTree = "<group>";
4272
};
4373
D084F3A92F140AA0000178B2 /* kutukUITests */ = {
4474
isa = PBXFileSystemSynchronizedRootGroup;
75+
exceptions = (
76+
D084F3AC2F140AA1000178B2 /* Exceptions for "kutukUITests" folder in "kutukUITests" target */,
77+
);
4578
path = kutukUITests;
4679
sourceTree = "<group>";
4780
};
@@ -396,19 +429,19 @@
396429
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
397430
CODE_SIGN_STYLE = Automatic;
398431
COMBINE_HIDPI_IMAGES = YES;
399-
CURRENT_PROJECT_VERSION = 1;
432+
CURRENT_PROJECT_VERSION = 2;
400433
DEVELOPMENT_TEAM = 9Y7VECW4AU;
401434
ENABLE_APP_SANDBOX = NO;
402435
ENABLE_HARDENED_RUNTIME = YES;
403436
ENABLE_PREVIEWS = YES;
404437
ENABLE_USER_SELECTED_FILES = readonly;
405-
GENERATE_INFOPLIST_FILE = YES;
438+
INFOPLIST_FILE = kutuk/Info.plist;
406439
INFOPLIST_KEY_NSHumanReadableCopyright = "";
407440
LD_RUNPATH_SEARCH_PATHS = (
408441
"$(inherited)",
409442
"@executable_path/../Frameworks",
410443
);
411-
MARKETING_VERSION = 1.0;
444+
MARKETING_VERSION = 1.0.1;
412445
PRODUCT_BUNDLE_IDENTIFIER = io.github.irajul.kutuk;
413446
PRODUCT_MODULE_NAME = kutuk;
414447
PRODUCT_NAME = Kutuk;
@@ -429,19 +462,19 @@
429462
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
430463
CODE_SIGN_STYLE = Automatic;
431464
COMBINE_HIDPI_IMAGES = YES;
432-
CURRENT_PROJECT_VERSION = 1;
465+
CURRENT_PROJECT_VERSION = 2;
433466
DEVELOPMENT_TEAM = 9Y7VECW4AU;
434467
ENABLE_APP_SANDBOX = NO;
435468
ENABLE_HARDENED_RUNTIME = YES;
436469
ENABLE_PREVIEWS = YES;
437470
ENABLE_USER_SELECTED_FILES = readonly;
438-
GENERATE_INFOPLIST_FILE = YES;
471+
INFOPLIST_FILE = kutuk/Info.plist;
439472
INFOPLIST_KEY_NSHumanReadableCopyright = "";
440473
LD_RUNPATH_SEARCH_PATHS = (
441474
"$(inherited)",
442475
"@executable_path/../Frameworks",
443476
);
444-
MARKETING_VERSION = 1.0;
477+
MARKETING_VERSION = 1.0.1;
445478
PRODUCT_BUNDLE_IDENTIFIER = io.github.irajul.kutuk;
446479
PRODUCT_MODULE_NAME = kutuk;
447480
PRODUCT_NAME = Kutuk;
@@ -460,11 +493,11 @@
460493
buildSettings = {
461494
BUNDLE_LOADER = "$(TEST_HOST)";
462495
CODE_SIGN_STYLE = Automatic;
463-
CURRENT_PROJECT_VERSION = 1;
496+
CURRENT_PROJECT_VERSION = 2;
464497
DEVELOPMENT_TEAM = 9Y7VECW4AU;
465-
GENERATE_INFOPLIST_FILE = YES;
498+
INFOPLIST_FILE = kutukTests/Info.plist;
466499
MACOSX_DEPLOYMENT_TARGET = 14.0;
467-
MARKETING_VERSION = 1.0;
500+
MARKETING_VERSION = 1.0.1;
468501
PRODUCT_BUNDLE_IDENTIFIER = io.github.irajul.kutukTests;
469502
PRODUCT_NAME = "$(TARGET_NAME)";
470503
STRING_CATALOG_GENERATE_SYMBOLS = NO;
@@ -481,11 +514,11 @@
481514
buildSettings = {
482515
BUNDLE_LOADER = "$(TEST_HOST)";
483516
CODE_SIGN_STYLE = Automatic;
484-
CURRENT_PROJECT_VERSION = 1;
517+
CURRENT_PROJECT_VERSION = 2;
485518
DEVELOPMENT_TEAM = 9Y7VECW4AU;
486-
GENERATE_INFOPLIST_FILE = YES;
519+
INFOPLIST_FILE = kutukTests/Info.plist;
487520
MACOSX_DEPLOYMENT_TARGET = 14.0;
488-
MARKETING_VERSION = 1.0;
521+
MARKETING_VERSION = 1.0.1;
489522
PRODUCT_BUNDLE_IDENTIFIER = io.github.irajul.kutukTests;
490523
PRODUCT_NAME = "$(TARGET_NAME)";
491524
STRING_CATALOG_GENERATE_SYMBOLS = NO;
@@ -501,10 +534,10 @@
501534
isa = XCBuildConfiguration;
502535
buildSettings = {
503536
CODE_SIGN_STYLE = Automatic;
504-
CURRENT_PROJECT_VERSION = 1;
537+
CURRENT_PROJECT_VERSION = 2;
505538
DEVELOPMENT_TEAM = 9Y7VECW4AU;
506-
GENERATE_INFOPLIST_FILE = YES;
507-
MARKETING_VERSION = 1.0;
539+
INFOPLIST_FILE = kutukUITests/Info.plist;
540+
MARKETING_VERSION = 1.0.1;
508541
PRODUCT_BUNDLE_IDENTIFIER = io.github.irajul.kutukUITests;
509542
PRODUCT_NAME = "$(TARGET_NAME)";
510543
STRING_CATALOG_GENERATE_SYMBOLS = NO;
@@ -520,10 +553,10 @@
520553
isa = XCBuildConfiguration;
521554
buildSettings = {
522555
CODE_SIGN_STYLE = Automatic;
523-
CURRENT_PROJECT_VERSION = 1;
556+
CURRENT_PROJECT_VERSION = 2;
524557
DEVELOPMENT_TEAM = 9Y7VECW4AU;
525-
GENERATE_INFOPLIST_FILE = YES;
526-
MARKETING_VERSION = 1.0;
558+
INFOPLIST_FILE = kutukUITests/Info.plist;
559+
MARKETING_VERSION = 1.0.1;
527560
PRODUCT_BUNDLE_IDENTIFIER = io.github.irajul.kutukUITests;
528561
PRODUCT_NAME = "$(TARGET_NAME)";
529562
STRING_CATALOG_GENERATE_SYMBOLS = NO;

kutuk/Info.plist

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5-
<key>CFBundleName</key>
6-
<string>Kutuk</string>
7-
<key>CFBundleDisplayName</key>
8-
<string>Kutuk</string>
9-
<key>CFBundleIdentifier</key>
10-
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11-
<key>CFBundleVersion</key>
12-
<string>1</string>
13-
<key>CFBundleShortVersionString</key>
14-
<string>1.0</string>
15-
<key>CFBundlePackageType</key>
16-
<string>APPL</string>
17-
<key>CFBundleExecutable</key>
18-
<string>$(EXECUTABLE_NAME)</string>
19-
<key>LSMinimumSystemVersion</key>
20-
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
21-
<key>LSUIElement</key>
22-
<true/>
23-
<key>NSHumanReadableCopyright</key>
24-
<string>Copyright © 2026 Rajul Jain. MIT License.</string>
25-
<key>NSPrincipalClass</key>
26-
<string>NSApplication</string>
5+
<key>CFBundleDisplayName</key>
6+
<string>Kutuk</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleName</key>
12+
<string>Kutuk</string>
13+
<key>CFBundlePackageType</key>
14+
<string>APPL</string>
15+
<key>CFBundleShortVersionString</key>
16+
<string>1.0.1</string>
17+
<key>CFBundleVersion</key>
18+
<string>2</string>
19+
<key>LSMinimumSystemVersion</key>
20+
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
21+
<key>LSUIElement</key>
22+
<true/>
23+
<key>NSHumanReadableCopyright</key>
24+
<string>Copyright © 2026 Rajul Jain. MIT License.</string>
25+
<key>NSPrincipalClass</key>
26+
<string>NSApplication</string>
2727
</dict>
2828
</plist>

kutukTests/Info.plist

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleExecutable</key>
6+
<string>$(EXECUTABLE_NAME)</string>
7+
<key>CFBundleIdentifier</key>
8+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
9+
<key>CFBundleName</key>
10+
<string>$(PRODUCT_NAME)</string>
11+
<key>CFBundlePackageType</key>
12+
<string>BNDL</string>
13+
<key>CFBundleShortVersionString</key>
14+
<string>1.0.1</string>
15+
<key>CFBundleVersion</key>
16+
<string>2</string>
17+
</dict>
18+
</plist>

kutukUITests/Info.plist

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleExecutable</key>
6+
<string>$(EXECUTABLE_NAME)</string>
7+
<key>CFBundleIdentifier</key>
8+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
9+
<key>CFBundleName</key>
10+
<string>$(PRODUCT_NAME)</string>
11+
<key>CFBundlePackageType</key>
12+
<string>BNDL</string>
13+
<key>CFBundleShortVersionString</key>
14+
<string>1.0.1</string>
15+
<key>CFBundleVersion</key>
16+
<string>2</string>
17+
</dict>
18+
</plist>

scripts/bump_build.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
3+
set -eu
4+
5+
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
6+
REPO_ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)"
7+
PROJECT_FILE="$REPO_ROOT/kutuk.xcodeproj/project.pbxproj"
8+
9+
CURRENT_VERSION="$(rg -o 'MARKETING_VERSION = [^;]+' "$PROJECT_FILE" | head -n1 | sed 's/MARKETING_VERSION = //')"
10+
CURRENT_BUILD="$(rg -o 'CURRENT_PROJECT_VERSION = [^;]+' "$PROJECT_FILE" | head -n1 | sed 's/CURRENT_PROJECT_VERSION = //')"
11+
NEXT_BUILD=$((CURRENT_BUILD + 1))
12+
13+
"$SCRIPT_DIR/bump_version.sh" "$CURRENT_VERSION" "$NEXT_BUILD"

scripts/bump_version.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/bin/sh
2+
3+
set -eu
4+
5+
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
6+
REPO_ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)"
7+
cd "$REPO_ROOT"
8+
PROJECT_FILE="kutuk.xcodeproj/project.pbxproj"
9+
PLIST_FILES="kutuk/Info.plist kutukTests/Info.plist kutukUITests/Info.plist"
10+
11+
if [ "${1:-}" = "" ]; then
12+
echo "Usage: $0 <marketing-version> [build-number]" >&2
13+
exit 1
14+
fi
15+
16+
NEW_VERSION="$1"
17+
OVERRIDE_BUILD="${2:-}"
18+
19+
case "$NEW_VERSION" in
20+
*[!0-9.]* | .* | *..* | *.)
21+
echo "Invalid marketing version: $NEW_VERSION" >&2
22+
exit 1
23+
;;
24+
esac
25+
26+
CURRENT_VERSION="$(rg -o 'MARKETING_VERSION = [^;]+' "$PROJECT_FILE" | head -n1 | sed 's/MARKETING_VERSION = //')"
27+
CURRENT_BUILD="$(rg -o 'CURRENT_PROJECT_VERSION = [^;]+' "$PROJECT_FILE" | head -n1 | sed 's/CURRENT_PROJECT_VERSION = //')"
28+
29+
case "$CURRENT_BUILD" in
30+
'' | *[!0-9]*)
31+
echo "Current build number must be an integer, found: $CURRENT_BUILD" >&2
32+
exit 1
33+
;;
34+
esac
35+
36+
if [ -n "$OVERRIDE_BUILD" ]; then
37+
NEXT_BUILD="$OVERRIDE_BUILD"
38+
else
39+
if [ "$NEW_VERSION" = "$CURRENT_VERSION" ]; then
40+
NEXT_BUILD="$CURRENT_BUILD"
41+
else
42+
NEXT_BUILD=$((CURRENT_BUILD + 1))
43+
fi
44+
fi
45+
46+
case "$NEXT_BUILD" in
47+
'' | *[!0-9]*)
48+
echo "Build number must be an integer, found: $NEXT_BUILD" >&2
49+
exit 1
50+
;;
51+
esac
52+
53+
if [ -z "$CURRENT_VERSION" ] || [ -z "$CURRENT_BUILD" ]; then
54+
echo "Could not read version settings from $PROJECT_FILE" >&2
55+
exit 1
56+
fi
57+
58+
export CURRENT_VERSION CURRENT_BUILD NEW_VERSION NEXT_BUILD
59+
60+
perl -0pi -e 's/MARKETING_VERSION = \Q$ENV{CURRENT_VERSION}\E;/MARKETING_VERSION = $ENV{NEW_VERSION};/g; s/CURRENT_PROJECT_VERSION = \Q$ENV{CURRENT_BUILD}\E;/CURRENT_PROJECT_VERSION = $ENV{NEXT_BUILD};/g' "$PROJECT_FILE"
61+
62+
for plist in $PLIST_FILES; do
63+
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $NEW_VERSION" "$plist"
64+
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $NEXT_BUILD" "$plist"
65+
done
66+
67+
echo "Updated marketing version: $CURRENT_VERSION -> $NEW_VERSION"
68+
echo "Updated build number: $CURRENT_BUILD -> $NEXT_BUILD"

0 commit comments

Comments
 (0)