Skip to content

Commit 3aa7710

Browse files
Merge branch 'main' of https://github.com/RecapAI/Recap
2 parents 9e97344 + e6d7ade commit 3aa7710

File tree

5 files changed

+357
-72
lines changed

5 files changed

+357
-72
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: CI
1+
name: Run SwiftLint
22

33
on:
44
push:

.github/workflows/release.yml

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
name: Release Build
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
workflow_dispatch:
7+
inputs:
8+
release_type:
9+
description: 'Release type (patch, minor, major)'
10+
required: false
11+
default: 'patch'
12+
type: choice
13+
options:
14+
- patch
15+
- minor
16+
- major
17+
18+
permissions:
19+
contents: write
20+
21+
concurrency:
22+
group: release-${{ github.ref }}
23+
cancel-in-progress: true
24+
25+
jobs:
26+
build-and-release:
27+
name: Build, Sign, Release
28+
runs-on: macos-15
29+
30+
steps:
31+
- name: Checkout code
32+
uses: actions/checkout@v4
33+
with:
34+
fetch-depth: 0 # Need full history for release notes
35+
36+
- name: Setup Xcode
37+
uses: maxim-lobanov/setup-xcode@v1
38+
with:
39+
xcode-version: latest-stable
40+
41+
- name: Get current version
42+
id: get_version
43+
run: |
44+
VERSION=$(xcodebuild -project Recap.xcodeproj -target Recap -showBuildSettings 2>/dev/null | grep "MARKETING_VERSION" | head -1 | sed 's/.*= //' | tr -d ' ')
45+
BUILD=$(xcodebuild -project Recap.xcodeproj -target Recap -showBuildSettings 2>/dev/null | grep "CURRENT_PROJECT_VERSION" | head -1 | sed 's/.*= //' | tr -d ' ')
46+
47+
VERSION=${VERSION:-"1.0"}
48+
BUILD=${BUILD:-"1"}
49+
50+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
51+
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then
52+
VERSION="${VERSION}.0"
53+
else
54+
VERSION="1.0.0"
55+
fi
56+
fi
57+
58+
echo "current_version=$VERSION" >> $GITHUB_OUTPUT
59+
echo "current_build=$BUILD" >> $GITHUB_OUTPUT
60+
echo "Current version: $VERSION ($BUILD)"
61+
62+
- name: Calculate next version
63+
id: next_version
64+
run: |
65+
CURRENT="${{ steps.get_version.outputs.current_version }}"
66+
TYPE="${{ github.event.inputs.release_type || 'patch' }}"
67+
68+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
69+
70+
case $TYPE in
71+
major)
72+
MAJOR=$((MAJOR + 1))
73+
MINOR=0
74+
PATCH=0
75+
;;
76+
minor)
77+
MINOR=$((MINOR + 1))
78+
PATCH=0
79+
;;
80+
patch)
81+
PATCH=$((PATCH + 1))
82+
;;
83+
esac
84+
85+
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
86+
NEW_BUILD="${{ github.run_number }}"
87+
88+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
89+
echo "build=$NEW_BUILD" >> $GITHUB_OUTPUT
90+
echo "Next version: $NEW_VERSION ($NEW_BUILD)"
91+
92+
- name: Update version in project
93+
run: |
94+
sed -i '' "s/MARKETING_VERSION = [^;]*/MARKETING_VERSION = ${{ steps.next_version.outputs.version }}/g" Recap.xcodeproj/project.pbxproj
95+
sed -i '' "s/CURRENT_PROJECT_VERSION = [^;]*/CURRENT_PROJECT_VERSION = ${{ steps.next_version.outputs.build }}/g" Recap.xcodeproj/project.pbxproj
96+
97+
echo "Updated project version to ${{ steps.next_version.outputs.version }} (${{ steps.next_version.outputs.build }})"
98+
99+
- name: Install Apple certificates
100+
env:
101+
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
102+
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
103+
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
104+
run: |
105+
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
106+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
107+
108+
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
109+
110+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
111+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
112+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
113+
114+
security import $CERTIFICATE_PATH -k $KEYCHAIN_PATH -P "$P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
115+
116+
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
117+
118+
security list-keychain -d user -s $KEYCHAIN_PATH login.keychain
119+
120+
echo "Certificates in new keychain:"
121+
security find-identity -v -p codesigning $KEYCHAIN_PATH
122+
123+
echo "✓ Certificates installed"
124+
125+
- name: Cache Swift Package Manager
126+
uses: actions/cache@v4
127+
with:
128+
path: |
129+
~/Library/Developer/Xcode/DerivedData/*/SourcePackages
130+
~/Library/Caches/org.swift.swiftpm
131+
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
132+
restore-keys: |
133+
${{ runner.os }}-spm-
134+
135+
- name: Resolve Dependencies
136+
run: |
137+
xcodebuild -resolvePackageDependencies \
138+
-project Recap.xcodeproj \
139+
-scheme Recap \
140+
-configuration Release
141+
142+
- name: Build and Archive
143+
env:
144+
DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }}
145+
CODE_SIGN_IDENTITY: ${{ secrets.CODE_SIGN_IDENTITY }}
146+
run: |
147+
xcodebuild archive \
148+
-project Recap.xcodeproj \
149+
-scheme Recap \
150+
-configuration Release \
151+
-archivePath $RUNNER_TEMP/Recap.xcarchive \
152+
-destination 'generic/platform=macOS' \
153+
-derivedDataPath $RUNNER_TEMP/DerivedData \
154+
-skipMacroValidation \
155+
-allowProvisioningUpdates \
156+
ONLY_ACTIVE_ARCH=YES \
157+
ARCHS=arm64 \
158+
VALID_ARCHS=arm64 \
159+
EXCLUDED_ARCHS=x86_64 \
160+
DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \
161+
CODE_SIGN_STYLE="Manual" \
162+
CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" \
163+
PROVISIONING_PROFILE_SPECIFIER="" \
164+
CODE_SIGNING_REQUIRED=YES \
165+
CODE_SIGNING_ALLOWED=YES \
166+
SWIFT_VERSION=5.0 \
167+
SWIFT_TREAT_WARNINGS_AS_ERRORS=NO \
168+
GCC_TREAT_WARNINGS_AS_ERRORS=NO
169+
170+
- name: Export Archive
171+
env:
172+
DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }}
173+
run: |
174+
if [ ! -d "$RUNNER_TEMP/Recap.xcarchive" ]; then
175+
echo "Archive not found at $RUNNER_TEMP/Recap.xcarchive"
176+
exit 1
177+
fi
178+
179+
echo "Available signing certificates:"
180+
security find-identity -v -p codesigning
181+
182+
cat > $RUNNER_TEMP/ExportOptions.plist <<EOF
183+
<?xml version="1.0" encoding="UTF-8"?>
184+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
185+
<plist version="1.0">
186+
<dict>
187+
<key>method</key>
188+
<string>developer-id</string>
189+
<key>teamID</key>
190+
<string>$DEVELOPMENT_TEAM</string>
191+
<key>signingStyle</key>
192+
<string>automatic</string>
193+
<key>uploadBitcode</key>
194+
<false/>
195+
<key>uploadSymbols</key>
196+
<false/>
197+
</dict>
198+
</plist>
199+
EOF
200+
201+
xcodebuild -exportArchive \
202+
-archivePath $RUNNER_TEMP/Recap.xcarchive \
203+
-exportPath $RUNNER_TEMP/export \
204+
-exportOptionsPlist $RUNNER_TEMP/ExportOptions.plist \
205+
-allowProvisioningUpdates
206+
207+
- name: Notarize app
208+
env:
209+
APPLE_ID: ${{ secrets.APPLE_ID }}
210+
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
211+
TEAM_ID: ${{ secrets.DEVELOPMENT_TEAM }}
212+
run: |
213+
if [ ! -d "$RUNNER_TEMP/export" ]; then
214+
echo "Export directory not found at $RUNNER_TEMP/export"
215+
echo "Contents of RUNNER_TEMP:"
216+
ls -la $RUNNER_TEMP
217+
exit 1
218+
fi
219+
220+
xcrun notarytool store-credentials "notarytool-profile" \
221+
--apple-id "$APPLE_ID" \
222+
--password "$APPLE_PASSWORD" \
223+
--team-id "$TEAM_ID"
224+
225+
cd $RUNNER_TEMP/export
226+
227+
if [ ! -d "Recap.app" ]; then
228+
echo "Recap.app not found in export directory"
229+
echo "Contents of export directory:"
230+
ls -la
231+
exit 1
232+
fi
233+
234+
ditto -c -k --keepParent "Recap.app" "Recap.zip"
235+
236+
xcrun notarytool submit "Recap.zip" \
237+
--keychain-profile "notarytool-profile" \
238+
--wait \
239+
--timeout 30m
240+
241+
xcrun stapler staple "Recap.app"
242+
243+
- name: Create DMG
244+
run: |
245+
brew install create-dmg
246+
247+
cd $RUNNER_TEMP
248+
249+
ICON_PATH=""
250+
if [ -f "$GITHUB_WORKSPACE/Recap/Assets.xcassets/AppIcon.appiconset/mac512.png" ]; then
251+
ICON_PATH="$GITHUB_WORKSPACE/Recap/Assets.xcassets/AppIcon.appiconset/mac512.png"
252+
elif [ -f "$GITHUB_WORKSPACE/Recap/Recap/Assets.xcassets/AppIcon.appiconset/mac512.png" ]; then
253+
ICON_PATH="$GITHUB_WORKSPACE/Recap/Recap/Assets.xcassets/AppIcon.appiconset/mac512.png"
254+
else
255+
echo "Warning: mac512.png not found, using app's icon"
256+
ICON_PATH=""
257+
fi
258+
259+
mkdir dmg_source
260+
cp -R export/Recap.app dmg_source/
261+
262+
if [ -n "$ICON_PATH" ]; then
263+
create-dmg \
264+
--volname "Recap" \
265+
--volicon "$ICON_PATH" \
266+
--window-pos 200 120 \
267+
--window-size 600 400 \
268+
--icon-size 100 \
269+
--icon "Recap.app" 150 180 \
270+
--hide-extension "Recap.app" \
271+
--app-drop-link 450 180 \
272+
--no-internet-enable \
273+
"Recap-${{ steps.next_version.outputs.version }}.dmg" \
274+
"dmg_source/"
275+
else
276+
create-dmg \
277+
--volname "Recap" \
278+
--window-pos 200 120 \
279+
--window-size 600 400 \
280+
--icon-size 100 \
281+
--icon "Recap.app" 150 180 \
282+
--hide-extension "Recap.app" \
283+
--app-drop-link 450 180 \
284+
--no-internet-enable \
285+
"Recap-${{ steps.next_version.outputs.version }}.dmg" \
286+
"dmg_source/"
287+
fi
288+
289+
xcrun notarytool submit "Recap-${{ steps.next_version.outputs.version }}.dmg" \
290+
--keychain-profile "notarytool-profile" \
291+
--wait \
292+
--timeout 30m
293+
294+
xcrun stapler staple "Recap-${{ steps.next_version.outputs.version }}.dmg"
295+
296+
- name: Generate Release Notes
297+
id: release_notes
298+
run: |
299+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)
300+
301+
cat > $RUNNER_TEMP/release_notes.md <<EOF
302+
# Recap v${{ steps.next_version.outputs.version }}
303+
304+
## What's Changed
305+
306+
EOF
307+
308+
git log --pretty=format:"- %s (%h)" $LAST_TAG..HEAD >> $RUNNER_TEMP/release_notes.md
309+
310+
cat >> $RUNNER_TEMP/release_notes.md <<EOF
311+
312+
313+
## Installation
314+
315+
1. Download the DMG file below
316+
2. Open the DMG and drag Recap to your Applications folder
317+
3. On first launch, right-click and select "Open"
318+
319+
## Checksums
320+
321+
EOF
322+
323+
cd $RUNNER_TEMP
324+
echo '```' >> release_notes.md
325+
echo "SHA256 (DMG): $(shasum -a 256 Recap-${{ steps.next_version.outputs.version }}.dmg | cut -d' ' -f1)" >> release_notes.md
326+
echo '```' >> release_notes.md
327+
328+
echo "RELEASE_NOTES<<EOF" >> $GITHUB_OUTPUT
329+
cat release_notes.md >> $GITHUB_OUTPUT
330+
echo "EOF" >> $GITHUB_OUTPUT
331+
332+
- name: Create GitHub Release
333+
uses: softprops/action-gh-release@v1
334+
with:
335+
tag_name: v${{ steps.next_version.outputs.version }}
336+
name: Recap v${{ steps.next_version.outputs.version }}
337+
body: ${{ steps.release_notes.outputs.RELEASE_NOTES }}
338+
draft: false
339+
prerelease: false
340+
files: |
341+
${{ runner.temp }}/Recap-${{ steps.next_version.outputs.version }}.dmg
342+
343+
- name: Upload Build Artifacts
344+
if: always()
345+
uses: actions/upload-artifact@v4
346+
with:
347+
name: build-artifacts
348+
path: |
349+
${{ runner.temp }}/Recap.xcarchive
350+
${{ runner.temp }}/export/
351+
${{ runner.temp }}/*.dmg
352+
353+
- name: Clean up keychain
354+
if: always()
355+
run: |
356+
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db

0 commit comments

Comments
 (0)