-
-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatically building and deploying in Github Actions for Nightlies and Releases. Triggered by the following: - every day at 01:32am (nightly mode) - manualy (nightly mode) - on release publication (release mode) This workflow makes extensive use of secrets with no additional safe-guard, given: - `schedule` (nightly) runs only off `main` branch. - `workflow_dispatch` (manual) can run on any in-repo branch (but uses the workflow from `main`) - Release publication requires push access to repo. There are thus two *modes*: Release and Nightly (also used on manual dispatch). The mode sets the `VERSION` either to the YYYY-MM-DD date for nightly or the tag-name for the release. It has four *targets*: `macOS dmg`, `macOS app-store`, `iOS ipa` and `iOS app-store` - **macOS dmg**: universal notarized macOS App in a dmg uploaded to `Kiwix-$VERSION.dmg` - **macOS app-store**: universal notarized macOS App uploaded to the App Store. - **iOS ipa**: iOS App uploaded to `Kiwix-$VERSION.ipa` - **iOS app-store**: iOS App uploaded to the App Store Code Signing is *automatic* (xcode decides which one to use based on availability). We use Apple Distribution one for the app-store targets. IPA uses Apple Development and dmg uses Developer ID.⚠️ This allows updates CI workflow to make use of the shared xcbuild action
- Loading branch information
Showing
9 changed files
with
656 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) |
Oops, something went wrong.