Merge pull request #544 from kiwix/cd

Introduce Continuous Deployment
This commit is contained in:
Kelson 2023-11-22 21:13:25 +01:00 committed by GitHub
commit eac6b3dff7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 649 additions and 62 deletions

31
.github/actions/install-cert/action.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Install Certificate in Keychain
description: Install a single cert in existing keychain
inputs:
KEYCHAIN:
required: true
KEYCHAIN_PASSWORD:
required: true
SIGNING_CERTIFICATE:
required: true
SIGNING_CERTIFICATE_P12_PASSWORD:
required: true
runs:
using: composite
steps:
- name: Install certificate
shell: bash
env:
KEYCHAIN: ${{ inputs.KEYCHAIN }}
KEYCHAIN_PASSWORD: ${{ inputs.KEYCHAIN_PASSWORD }}
CERTIFICATE_PATH: /tmp/cert.p12
SIGNING_CERTIFICATE: ${{ inputs.SIGNING_CERTIFICATE }}
SIGNING_CERTIFICATE_P12_PASSWORD: ${{ inputs.SIGNING_CERTIFICATE_P12_PASSWORD }}
run: |
security unlock-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN
echo "${SIGNING_CERTIFICATE}" | base64 --decode -o $CERTIFICATE_PATH
security import $CERTIFICATE_PATH -k $KEYCHAIN -P "${SIGNING_CERTIFICATE_P12_PASSWORD}" -A -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
rm $CERTIFICATE_PATH
security find-identity -v $KEYCHAIN
security set-key-partition-list -S apple-tool:,apple: -s -k $KEYCHAIN_PASSWORD $KEYCHAIN

133
.github/actions/xcbuild/action.yml vendored Normal file
View File

@ -0,0 +1,133 @@
name: Build with XCode
description: Run xcodebuild for Kiwix
inputs:
action:
required: true
version:
required: true
xc-destination:
required: true
upload-to:
required: true
libkiwix-version:
required: true
APPLE_DEVELOPMENT_SIGNING_CERTIFICATE:
required: true
APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD:
required: true
DEPLOYMENT_SIGNING_CERTIFICATE:
required: false
DEPLOYMENT_SIGNING_CERTIFICATE_P12_PASSWORD:
required: false
KEYCHAIN:
required: false
default: /Users/runner/build.keychain-db
KEYCHAIN_PASSWORD:
required: false
default: mysecretpassword
KEYCHAIN_PROFILE:
required: false
default: build-profile
XC_WORKSPACE:
required: false
default: Kiwix.xcodeproj/project.xcworkspace/
XC_SCHEME:
required: false
default: Kiwix
XC_CONFIG:
required: false
default: Release
EXTRA_XCODEBUILD:
required: false
default: ""
runs:
using: composite
steps:
# not necessary on github runner but serves as documentation for local setup
- name: Update Apple Intermediate Certificate
shell: bash
run: |
curl -L -o ~/Downloads/AppleWWDRCAG3.cer https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer
sudo security import ~/Downloads/AppleWWDRCAG3.cer \
-k /Library/Keychains/System.keychain \
-T /usr/bin/codesign \
-T /usr/bin/security \
-T /usr/bin/productbuild || true
- name: Set Xcode version (15.0.1)
shell: bash
# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode
run: sudo xcode-select -s /Applications/Xcode_15.0.1.app
- name: Create Keychain
shell: bash
env:
KEYCHAIN: ${{ inputs.KEYCHAIN }}
KEYCHAIN_PASSWORD: ${{ inputs.KEYCHAIN_PASSWORD }}
KEYCHAIN_PROFILE: ${{ inputs.KEYCHAIN_PROFILE }}
CERTIFICATE_PATH: /tmp/cert.p12
APPLE_DEVELOPER_CERTIFICATE_PATH: /tmp/dev-cert.p12
SIGNING_CERTIFICATE: ${{ inputs.SIGNING_CERTIFICATE }}
SIGNING_CERTIFICATE_P12_PASSWORD: ${{ inputs.SIGNING_CERTIFICATE_P12_PASSWORD }}
APPLE_DEVELOPER_ID_SIGNING_CERTIFICATE: ${{ inputs.APPLE_DEVELOPER_ID_SIGNING_CERTIFICATE }}
APPLE_DEVELOPER_ID_SIGNING_P12_PASSWORD: ${{ inputs.APPLE_DEVELOPER_ID_SIGNING_P12_PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN
security default-keychain -s $KEYCHAIN
security set-keychain-settings $KEYCHAIN
security unlock-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN
- name: Add Apple Development certificate to Keychain
uses: ./.github/actions/install-cert
with:
SIGNING_CERTIFICATE: ${{ inputs.APPLE_DEVELOPMENT_SIGNING_CERTIFICATE }}
SIGNING_CERTIFICATE_P12_PASSWORD: ${{ inputs.APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD }}
KEYCHAIN: ${{ inputs.KEYCHAIN }}
KEYCHAIN_PASSWORD: ${{ inputs.KEYCHAIN_PASSWORD }}
- name: Add Distribution certificate to Keychain
if: ${{ inputs.DEPLOYMENT_SIGNING_CERTIFICATE }}
uses: ./.github/actions/install-cert
with:
SIGNING_CERTIFICATE: ${{ inputs.DEPLOYMENT_SIGNING_CERTIFICATE }}
SIGNING_CERTIFICATE_P12_PASSWORD: ${{ inputs.DEPLOYMENT_SIGNING_CERTIFICATE_P12_PASSWORD }}
KEYCHAIN: ${{ inputs.KEYCHAIN }}
KEYCHAIN_PASSWORD: ${{ inputs.KEYCHAIN_PASSWORD }}
- name: Download CoreKiwix.xcframework
env:
XCF_URL: https://download.kiwix.org/release/libkiwix/libkiwix_xcframework-${{ inputs.libkiwix-version }}.tar.gz
shell: bash
run: curl -L -o - $XCF_URL | tar -x --strip-components 2
- name: Prepare Xcode
shell: bash
run: xcrun xcodebuild -checkFirstLaunchStatus || xcrun xcodebuild -runFirstLaunch
- name: Dump build settings
env:
XC_WORKSPACE: ${{ inputs.XC_WORKSPACE }}
XC_SCHEME: ${{ inputs.XC_SCHEME }}
shell: bash
run: xcrun xcodebuild -workspace $XC_WORKSPACE -scheme $XC_SCHEME -showBuildSettings
# build is launched up to twice as it's common the build fails, looking for CoreKiwix module
- name: Install retry command
shell: bash
run: brew install kadwanev/brew/retry
- name: Build with Xcode
env:
FRAMEWORK_SEARCH_PATHS: ${{ env.PWD }}
ACTION: ${{ inputs.action }}
VERSION: ${{ inputs.version }}
XC_WORKSPACE: ${{ inputs.XC_WORKSPACE }}
XC_SCHEME: ${{ inputs.XC_SCHEME }}
XC_CONFIG: ${{ inputs.XC_CONFIG }}
XC_DESTINATION: ${{ inputs.xc-destination }}
EXTRA_XCODEBUILD: ${{ inputs.EXTRA_XCODEBUILD }}
shell: bash
run: retry -t 2 -- xcrun xcodebuild ${EXTRA_XCODEBUILD} -workspace $XC_WORKSPACE -scheme $XC_SCHEME -destination "$XC_DESTINATION" -configuration $XC_CONFIG -onlyUsePackageVersionsFromResolvedFile -allowProvisioningUpdates -verbose -archivePath $PWD/Kiwix-$VERSION.xcarchive ${ACTION}

BIN
.github/dmg-bg.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

46
.github/dmg-settings.py vendored Normal file
View File

@ -0,0 +1,46 @@
from pathlib import Path
application = defines.get("app", "Kiwix.app") # noqa: F821
background = defines.get("bg", "bg.png") # noqa: F821
appname = Path(application).name
# Volume format (see hdiutil create -help)
format = defines.get("format", "ULMO") # noqa: F821
# Compression level (if relevant)
# compression_level = 9
# Volume size
size = defines.get("size", None) # noqa: F821
# Files to include
files = [application]
# Symlinks to create
symlinks = {"Applications": "/Applications"}
# Files to hide the extension of
hide_extension = [ "Kiwix.app" ]
# Volume icon (reuse from app)
icon = Path(application).joinpath("Contents/Resources/AppIcon.icns")
# Where to put the icons
icon_locations = {appname: (146, 180), "Applications": (481, 181)}
background = background
show_status_bar = False
show_tab_view = False
show_toolbar = False
show_pathbar = False
show_sidebar = False
sidebar_width = 180
# Window position in ((x, y), (w, h)) format
window_rect = ((200, 120), (600, 360))
default_view = "icon-view"
show_icon_preview = True
# Set these to True to force inclusion of icon/list view settings (otherwise
# we only include settings for the default view)
include_icon_view_settings = True
include_list_view_settings = True
# .. Icon view configuration ...................................................
arrange_by = None
grid_offset = (0, 0)
grid_spacing = 100
scroll_position = (0, 0)
label_pos = "bottom" # or 'right'
text_size = 16
icon_size = 100

75
.github/retry-if-retcode.py vendored Normal file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
import argparse
import subprocess
import sys
import time
def run_command(
max_attempts: int, retcode: int, sleep_seconds: int, command: str
) -> int:
attempts = 0
while True:
ps = subprocess.run(command, check=False)
attempts += 1
# either suceeded or returned an unexpected exit-code, returning.
if ps.returncode == 0 or ps.returncode != retcode:
return ps.returncode
if attempts >= max_attempts:
print(f"Reached {max_attempts=}")
return ps.returncode
print(
f"Received retcode={ps.returncode} on attempt #{attempts}. "
f"Retrying in {sleep_seconds}s."
)
if sleep_seconds:
time.sleep(sleep_seconds)
def main():
parser = argparse.ArgumentParser(
prog="retry-if-retcode", epilog=r"/!\ Append your command after those args!"
)
parser.add_argument(
"--retcode",
required=True,
help="Return code to retry when received",
type=int,
)
parser.add_argument(
"--attempts",
required=False,
help="Max number of attempts",
type=int,
default=10,
)
parser.add_argument(
"--sleep",
required=False,
help="Nb. of seconds to sleep in-between retries",
type=int,
default=1,
)
args, command = parser.parse_known_args()
if not command:
print("You must supply a command to run")
return 1
return run_command(
max_attempts=args.attempts,
retcode=args.retcode,
sleep_seconds=args.sleep,
command=command,
)
if __name__ == "__main__":
sys.exit(main())

110
.github/upload_file.py vendored Normal file
View File

@ -0,0 +1,110 @@
import argparse
import os
import pathlib
import subprocess
import sys
import urllib.parse
def main() -> int:
parser = argparse.ArgumentParser(
prog="scp-upload",
description="Upload files to Kiwix server",
)
parser.add_argument(
"--src", required=True, help="filepath to be uploaded", dest="src_path"
)
parser.add_argument(
"--dest",
required=True,
help="destination as user@host[:port]/folder/",
dest="dest",
)
parser.add_argument(
"--ssh-key",
required=False,
help="filepath to the private key to use for upload",
default=os.getenv("SSH_KEY", ""),
dest="ssh_key",
)
args = parser.parse_args()
ssh_path = (
pathlib.Path(args.ssh_key or os.getenv("SSH_KEY", "")).expanduser().resolve()
)
src_path = pathlib.Path(args.src_path).expanduser().resolve()
dest = urllib.parse.urlparse(f"ssh://{args.dest}")
dest_path = pathlib.Path(dest.path)
if not src_path.exists() or not ssh_path.is_file():
print(f"Source file “{src_path}” missing")
return 1
if not ssh_path.exists() or not ssh_path.is_file():
print(f"SSH Key “{ssh_path}” missing")
return 1
if not dest_path or dest_path == pathlib.Path("") or dest_path == pathlib.Path("/"):
print(f"Must upload in a subfoler, not “{dest_path}")
return 1
return upload(
src_path=src_path, host=dest.netloc, dest_path=dest_path, ssh_path=ssh_path
)
def upload(
src_path: pathlib.Path, host: str, dest_path: pathlib.Path, ssh_path: pathlib.Path
) -> int:
if ":" in host:
host, port = host.split(":", 1)
else:
port = "22"
# sending SFTP mkdir command to the sftp interactive mode and not batch (-b) mode
# as the latter would exit on any mkdir error while it is most likely
# the first parts of the destination is already present and thus can't be created
sftp_commands = "\n".join(
[
f"mkdir {part}"
for part in list(reversed(dest_path.parents)) + [str(dest_path)]
]
)
command = [
"sftp",
"-i",
str(ssh_path),
"-P",
port,
"-o",
"StrictHostKeyChecking=no",
host,
]
print(f"Creating dest path: {dest_path}")
subprocess.run(command, input=sftp_commands, text=True, check=True)
command = [
"scp",
"-c",
"aes128-ctr",
"-rp",
"-P",
port,
"-i",
str(ssh_path),
"-o",
"StrictHostKeyChecking=no",
str(src_path),
f"{host}:{dest_path}/",
]
print(f"Sending archive with command {' '.join(command)}")
subprocess.run(command, check=True)
return 0
if __name__ == "__main__":
sys.exit(main())

209
.github/workflows/cd.yml vendored Normal file
View File

@ -0,0 +1,209 @@
name: CD
on:
schedule:
- cron: '32 1 * * *'
workflow_dispatch:
release:
types: [published]
env:
LIBKIWIX_VERSION: "13.0.0"
KEYCHAIN: /Users/runner/build.keychain-db
KEYCHAIN_PASSWORD: mysecretpassword
KEYCHAIN_PROFILE: build-profile
SSH_KEY: /tmp/id_rsa
APPLE_STORE_AUTH_KEY_PATH: /tmp/authkey.p8
jobs:
build_and_deploy:
strategy:
fail-fast: false
matrix:
destination:
- platform: macOS
uploadto: dmg
- platform: macOS
uploadto: app-store
- platform: iOS
uploadto: ipa
xcode_extra: -sdk iphoneos
- platform: iOS
uploadto: app-store
xcode_extra: -sdk iphoneos
runs-on: macos-13
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Decide whether building nightly or release
env:
PLATFORM: ${{ matrix.destination.platform }}
UPLOAD_TO: ${{ matrix.destination.uploadto }}
EXTRA_XCODEBUILD: ${{ matrix.destination.xcode_extra }}
APPLE_STORE_AUTH_KEY_PATH: ${{ env.APPLE_STORE_AUTH_KEY_PATH }}
APPLE_STORE_AUTH_KEY_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ID }}
APPLE_STORE_AUTH_KEY_ISSUER_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ISSUER_ID }}
shell: python
run: |
import datetime
import os
if os.getenv("GITHUB_EVENT_NAME", "") == "release":
is_release = True
version = os.getenv("GITHUB_REF_NAME")
upload_folder = f"release/{version}"
else:
is_release = False
version = str(datetime.date.today())
upload_folder = f"nightly/{version}"
export_method = "developer-id" if os.getenv("UPLOAD_TO") == "dmg" else "app-store"
extra_xcode = os.getenv("EXTRA_XCODEBUILD", "")
if os.getenv("PLATFORM") == "iOS":
extra_xcode += f" -authenticationKeyPath {os.getenv('APPLE_STORE_AUTH_KEY_PATH')}"
extra_xcode += f" -authenticationKeyID {os.getenv('APPLE_STORE_AUTH_KEY_ID')}"
extra_xcode += f" -authenticationKeyIssuerID {os.getenv('APPLE_STORE_AUTH_KEY_ISSUER_ID')}"
with open(os.getenv("GITHUB_ENV"), "a") as fh:
fh.write(f"VERSION={version}\n")
fh.write(f"ISRELEASE={'yes' if is_release else ''}\n")
fh.write(f"EXPORT_METHOD={export_method}\n")
fh.write(f"UPLOAD_FOLDER={upload_folder}\n")
fh.write(f"EXTRA_XCODEBUILD={extra_xcode}\n")
- name: Prepare use of Developper ID Certificate
if: ${{ matrix.destination.uploadto == 'dmg' }}
shell: bash
env:
APPLE_DEVELOPER_ID_SIGNING_CERTIFICATE: ${{ secrets.APPLE_DEVELOPER_ID_SIGNING_CERTIFICATE }}
APPLE_DEVELOPER_ID_SIGNING_P12_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_SIGNING_P12_PASSWORD }}
APPLE_DEVELOPER_ID_SIGNING_IDENTITY: ${{ secrets.APPLE_DEVELOPER_ID_SIGNING_IDENTITY }}
run: |
echo "SIGNING_CERTIFICATE=${APPLE_DEVELOPER_ID_SIGNING_CERTIFICATE}" >> "$GITHUB_ENV"
echo "SIGNING_CERTIFICATE_P12_PASSWORD=${APPLE_DEVELOPER_ID_SIGNING_P12_PASSWORD}" >> "$GITHUB_ENV"
echo "SIGNING_IDENTITY=${APPLE_DEVELOPER_ID_SIGNING_IDENTITY}" >> "$GITHUB_ENV"
- name: Prepare use of Apple Development Certificate
if: ${{ matrix.destination.uploadto == 'ipa' }}
shell: bash
env:
APPLE_DEVELOPMENT_SIGNING_CERTIFICATE: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_CERTIFICATE }}
APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD }}
APPLE_DEVELOPMENT_SIGNING_IDENTITY: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_IDENTITY }}
run: |
echo "SIGNING_CERTIFICATE=${APPLE_DEVELOPMENT_SIGNING_CERTIFICATE}" >> "$GITHUB_ENV"
echo "SIGNING_CERTIFICATE_P12_PASSWORD=${APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD}" >> "$GITHUB_ENV"
echo "SIGNING_IDENTITY=${APPLE_DEVELOPMENT_SIGNING_IDENTITY}" >> "$GITHUB_ENV"
- name: Prepare use of Apple Distribution Certificate
if: ${{ matrix.destination.uploadto == 'app-store' }}
shell: bash
env:
APPLE_DISTRIBUTION_SIGNING_CERTIFICATE: ${{ secrets.APPLE_DISTRIBUTION_SIGNING_CERTIFICATE }}
APPLE_DISTRIBUTION_SIGNING_P12_PASSWORD: ${{ secrets.APPLE_DISTRIBUTION_SIGNING_P12_PASSWORD }}
APPLE_DEVELOPMENT_SIGNING_IDENTITY: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_IDENTITY }}
run: |
echo "SIGNING_CERTIFICATE=${APPLE_DISTRIBUTION_SIGNING_CERTIFICATE}" >> "$GITHUB_ENV"
echo "SIGNING_CERTIFICATE_P12_PASSWORD=${APPLE_DISTRIBUTION_SIGNING_P12_PASSWORD}" >> "$GITHUB_ENV"
echo "SIGNING_IDENTITY=${APPLE_DEVELOPMENT_SIGNING_IDENTITY}" >> "$GITHUB_ENV"
- name: Add Apple Store Key
env:
APPLE_STORE_AUTH_KEY_PATH: ${{ env.APPLE_STORE_AUTH_KEY_PATH }}
APPLE_STORE_AUTH_KEY: ${{ secrets.APPLE_STORE_AUTH_KEY }}
shell: bash
run: echo "${APPLE_STORE_AUTH_KEY}" | base64 --decode -o $APPLE_STORE_AUTH_KEY_PATH
- name: Build xcarchive
uses: ./.github/actions/xcbuild
with:
action: archive
xc-destination: generic/platform=${{ matrix.destination.platform }}
upload-to: ${{ matrix.destination.uploadto }}
libkiwix-version: ${{ env.LIBKIWIX_VERSION }}
version: ${{ env.VERSION }}
APPLE_DEVELOPMENT_SIGNING_CERTIFICATE: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_CERTIFICATE }}
APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD }}
DEPLOYMENT_SIGNING_CERTIFICATE: ${{ env.SIGNING_CERTIFICATE }}
DEPLOYMENT_SIGNING_CERTIFICATE_P12_PASSWORD: ${{ env.SIGNING_CERTIFICATE_P12_PASSWORD }}
KEYCHAIN: ${{ env.KEYCHAIN }}
KEYCHAIN_PASSWORD: ${{ env.KEYCHAIN_PASSWORD }}
KEYCHAIN_PROFILE: ${{ env.KEYCHAIN_PROFILE }}
EXTRA_XCODEBUILD: ${{ env.EXTRA_XCODEBUILD }}
- name: Add altool credentials to Keychain
shell: bash
env:
APPLE_SIGNING_ALTOOL_USERNAME: ${{ secrets.APPLE_SIGNING_ALTOOL_USERNAME }}
APPLE_SIGNING_ALTOOL_PASSWORD: ${{ secrets.APPLE_SIGNING_ALTOOL_PASSWORD }}
APPLE_SIGNING_TEAM: ${{ secrets.APPLE_SIGNING_TEAM }}
run: |
security find-identity -v $KEYCHAIN
security unlock-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN
xcrun notarytool store-credentials \
--apple-id "${APPLE_SIGNING_ALTOOL_USERNAME}" \
--password "${APPLE_SIGNING_ALTOOL_PASSWORD}" \
--team-id "${APPLE_SIGNING_TEAM}" \
--validate \
--keychain $KEYCHAIN \
$KEYCHAIN_PROFILE
- name: Prepare export for ${{ env.EXPORT_METHOD }}
if: ${{ matrix.destination.uploadto != 'ipa' }}
run: |
plutil -create xml1 ./export.plist
plutil -insert destination -string upload ./export.plist
plutil -insert method -string $EXPORT_METHOD ./export.plist
- name: Prepare export for IPA
if: ${{ matrix.destination.uploadto == 'ipa' }}
run: |
plutil -create xml1 ./export.plist
plutil -insert method -string ad-hoc ./export.plist
plutil -insert provisioningProfiles -dictionary ./export.plist
plutil -replace provisioningProfiles -json '{ "self.Kiwix" : "iOS Team Provisioning Profile" }' ./export.plist
- name: Upload Archive to Apple (App Store or Notarization)
env:
APPLE_STORE_AUTH_KEY_PATH: ${{ env.APPLE_STORE_AUTH_KEY_PATH }}
APPLE_STORE_AUTH_KEY_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ID }}
APPLE_STORE_AUTH_KEY_ISSUER_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ISSUER_ID }}
run: python .github/retry-if-retcode.py --sleep 60 --attempts 5 --retcode 70 xcrun xcodebuild -exportArchive -archivePath $PWD/Kiwix-$VERSION.xcarchive -exportPath $PWD/export/ -exportOptionsPlist export.plist -authenticationKeyPath $APPLE_STORE_AUTH_KEY_PATH -allowProvisioningUpdates -authenticationKeyID $APPLE_STORE_AUTH_KEY_ID -authenticationKeyIssuerID $APPLE_STORE_AUTH_KEY_ISSUER_ID
- name: Export notarized App from archive
if: ${{ matrix.destination.uploadto == 'dmg' }}
env:
APPLE_STORE_AUTH_KEY_PATH: ${{ env.APPLE_STORE_AUTH_KEY_PATH }}
APPLE_STORE_AUTH_KEY_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ID }}
APPLE_STORE_AUTH_KEY_ISSUER_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ISSUER_ID }}
run: python .github/retry-if-retcode.py --sleep 60 --attempts 20 --retcode 65 xcrun xcodebuild -exportNotarizedApp -archivePath $PWD/Kiwix-$VERSION.xcarchive -exportPath $PWD/export/ -authenticationKeyPath $APPLE_STORE_AUTH_KEY_PATH -allowProvisioningUpdates -authenticationKeyID $APPLE_STORE_AUTH_KEY_ID -authenticationKeyIssuerID $APPLE_STORE_AUTH_KEY_ISSUER_ID
- name: Create DMG
if: ${{ matrix.destination.uploadto == 'dmg' }}
run: |
pip install dmgbuild
dmgbuild -s .github/dmg-settings.py -Dapp=$PWD/export/Kiwix.app -Dbg=.github/dmg-bg.png "Kiwix-$VERSION" $PWD/Kiwix-$VERSION.dmg
- name: Notarize DMG
if: ${{ matrix.destination.uploadto == 'dmg' }}
run: |
xcrun notarytool submit --keychain $KEYCHAIN --keychain-profile $KEYCHAIN_PROFILE --wait $PWD/Kiwix-$VERSION.dmg
xcrun stapler staple $PWD/Kiwix-$VERSION.dmg
- name: Add SSH_KEY to filesystem
shell: bash
env:
PRIVATE_KEY: ${{ secrets.SSH_KEY }}
run: |
echo "${PRIVATE_KEY}" > $SSH_KEY
chmod 600 $SSH_KEY
- name: Upload DMG
if: ${{ matrix.destination.uploadto == 'dmg' }}
run: python .github/upload_file.py --src ${PWD}/Kiwix-${VERSION}.dmg --dest ci@master.download.kiwix.org:30022/data/download/${UPLOAD_FOLDER} --ssh-key ${SSH_KEY}
- name: Upload IPA
if: ${{ matrix.destination.uploadto == 'ipa' }}
run: |
mv ${PWD}/export/Kiwix.ipa ${PWD}/export/Kiwix-${VERSION}.ipa
python .github/upload_file.py --src ${PWD}/export/Kiwix-${VERSION}.ipa --dest ci@master.download.kiwix.org:30022/data/download/${UPLOAD_FOLDER} --ssh-key ${SSH_KEY}

View File

@ -8,74 +8,57 @@ on:
env:
LIBKIWIX_VERSION: "13.0.0"
APPLE_STORE_AUTH_KEY_PATH: /tmp/authkey.p8
jobs:
build:
runs-on: macos-13
strategy:
fail-fast: false
matrix:
destination:
- platform: macOS
name: Any Mac
- platform: iOS
name: Any iOS Device
runs-on: macos-13
env:
XC_WORKSPACE: Kiwix.xcodeproj/project.xcworkspace/
XC_SCHEME: Kiwix
XC_CONFIG: Release
XC_DESTINATION: platform=${{ matrix.destination.platform }},name=${{ matrix.destination.name }}
CERTIFICATE: /tmp/apple-development.p12
SIGNING_IDENTITY: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_IDENTITY }}
KEYCHAIN: /Users/runner/build.keychain-db
KEYCHAIN_PASSWORD: mysecretpassword
KEYCHAIN_PROFILE: build-profile
xcode_extra: -sdk iphoneos
steps:
- name: install Apple certificate
shell: bash
run: |
echo "${{ secrets.APPLE_DEVELOPMENT_SIGNING_CERTIFICATE }}" | base64 --decode -o $CERTIFICATE
security create-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN
security default-keychain -s $KEYCHAIN
security set-keychain-settings $KEYCHAIN
security unlock-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN
security import $CERTIFICATE -k $KEYCHAIN -P "${{ secrets.APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD }}" -A -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
rm $CERTIFICATE
security set-key-partition-list -S apple-tool:,apple: -s -k $KEYCHAIN_PASSWORD $KEYCHAIN
security find-identity -v $KEYCHAIN
xcrun notarytool store-credentials \
--apple-id "${{ secrets.APPLE_SIGNING_ALTOOL_USERNAME }}" \
--password "${{ secrets.APPLE_SIGNING_ALTOOL_PASSWORD }}" \
--team-id "${{ secrets.APPLE_SIGNING_TEAM }}" \
--validate \
--keychain $KEYCHAIN \
$KEYCHAIN_PROFILE
# not necessary on github runner but serves as documentation for local setup
- name: Update Apple Intermediate Certificate
run: |
curl -L -o ~/Downloads/AppleWWDRCAG3.cer https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer
sudo security import ~/Downloads/AppleWWDRCAG3.cer \
-k /Library/Keychains/System.keychain \
-T /usr/bin/codesign \
-T /usr/bin/security \
-T /usr/bin/productbuild || true
- name: Set Xcode version (15.0.1)
# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode
run: sudo xcode-select -s /Applications/Xcode_15.0.1.app
- name: Checkout code
uses: actions/checkout@v3
- name: Download CoreKiwix.xcframework
env:
XCF_URL: https://download.kiwix.org/release/libkiwix/libkiwix_xcframework-${{ env.LIBKIWIX_VERSION }}.tar.gz
run: curl -L -o - $XCF_URL | tar -x --strip-components 2
- name: Prepare Xcode
run: xcrun xcodebuild -checkFirstLaunchStatus || xcrun xcodebuild -runFirstLaunch
- name: Dump build settings
run: xcrun xcodebuild -workspace $XC_WORKSPACE -scheme $XC_SCHEME -showBuildSettings
# build is launched up to twice as it's common the build fails, looking for CoreKiwix module
- name: Install retry command
run: brew install kadwanev/brew/retry
- name: Build for ${{ matrix.destination.platform }}/${{ matrix.destination.name }}
env:
FRAMEWORK_SEARCH_PATHS: /Users/runner/work/apple/apple/
run: retry -t 2 -- xcrun xcodebuild -workspace $XC_WORKSPACE -scheme $XC_SCHEME -destination "$XC_DESTINATION" -configuration $XC_CONFIG -onlyUsePackageVersionsFromResolvedFile -derivedDataPath $PWD/build -allowProvisioningUpdates -verbose build
- name: Checkout code
uses: actions/checkout@v3
- name: Add Apple Store Key
if: ${{ matrix.destination.platform == 'iOS' }}
env:
APPLE_STORE_AUTH_KEY_PATH: ${{ env.APPLE_STORE_AUTH_KEY_PATH }}
APPLE_STORE_AUTH_KEY: ${{ secrets.APPLE_STORE_AUTH_KEY }}
shell: bash
run: echo "${APPLE_STORE_AUTH_KEY}" | base64 --decode -o $APPLE_STORE_AUTH_KEY_PATH
- name: Extend EXTRA_XCODEBUILD
if: ${{ matrix.destination.platform == 'iOS' }}
env:
EXTRA_XCODEBUILD: ${{ matrix.destination.xcode_extra }}
APPLE_STORE_AUTH_KEY_PATH: ${{ env.APPLE_STORE_AUTH_KEY_PATH }}
APPLE_STORE_AUTH_KEY_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ID }}
APPLE_STORE_AUTH_KEY_ISSUER_ID: ${{ secrets.APPLE_STORE_AUTH_KEY_ISSUER_ID }}
shell: python
run: |
import os
extra_xcode = os.getenv("EXTRA_XCODEBUILD", "")
extra_xcode += f" -authenticationKeyPath {os.getenv('APPLE_STORE_AUTH_KEY_PATH')}"
extra_xcode += f" -authenticationKeyID {os.getenv('APPLE_STORE_AUTH_KEY_ID')}"
extra_xcode += f" -authenticationKeyIssuerID {os.getenv('APPLE_STORE_AUTH_KEY_ISSUER_ID')}"
with open(os.getenv("GITHUB_ENV"), "a") as fh:
fh.write(f"EXTRA_XCODEBUILD={extra_xcode}\n")
- name: Build
uses: ./.github/actions/xcbuild
with:
action: build
xc-destination: generic/platform=${{ matrix.destination.platform }}
upload-to: dev
libkiwix-version: ${{ env.LIBKIWIX_VERSION }}
version: CI
APPLE_DEVELOPMENT_SIGNING_CERTIFICATE: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_CERTIFICATE }}
APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD: ${{ secrets.APPLE_DEVELOPMENT_SIGNING_P12_PASSWORD }}
EXTRA_XCODEBUILD: ${{ env.EXTRA_XCODEBUILD }}

View File

@ -200,7 +200,7 @@ struct Settings: View {
}
var miscellaneous: some View {
Section("Misc".lowercased) {
Section("Misc".localized) {
Button("Feedback".localized) { UIApplication.shared.open(URL(string: "mailto:feedback@kiwix.org")!) }
Button("Rate the App".localized) {
let url = URL(string: "itms-apps://itunes.apple.com/us/app/kiwix/id997079563?action=write-review")!