From 58189984da3cc2ed948694cf1a781395b1d731a0 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Sun, 2 Feb 2025 17:30:31 +0900 Subject: [PATCH] feat: Build (Archive) on CI (#259) * ci: add archive workflow * fix * fix * fix? * tweak * upload * use xcbeautify * Revert "use xcbeautify" This reverts commit b2cd254c4576919a69c42abdfa000bfc93c16dd9. * chore: Use run_id for CFBundleVersion --- .github/export_archive_stats.py | 85 +++++++++++++++++++++++++++++++++ .github/workflows/archive.yml | 53 ++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 .github/export_archive_stats.py create mode 100644 .github/workflows/archive.yml diff --git a/.github/export_archive_stats.py b/.github/export_archive_stats.py new file mode 100644 index 00000000..7f6017c4 --- /dev/null +++ b/.github/export_archive_stats.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +import subprocess +import glob +import sys +import re +from typing import Any +import json +import os + +if len(sys.argv) < 2: + print("Usage: " + sys.argv[0] + " [Path to XCArchive]") + exit(1) + + +def call_process(args: list[str]): + process = subprocess.run(args, capture_output=True, text=True) + process.check_returncode() + return process.stdout.strip() + +def list_get(src: list, i: int, fallback): + if len(src) <= i: + return fallback + return src[i] + +def semver_to_parts(input: str): + parts = input.split(".", maxsplit=2) + return { + "major": list_get(parts, 0, "0"), + "minor": list_get(parts, 1, "0"), + "patch": list_get(parts, 2, "0"), + } + +output: dict[str, Any] = { + "build_os_version": call_process(["sw_vers", "--productVersion"]), + "build_os_version_extra": call_process(["sw_vers", "--productVersionExtra"]), + "build_os_build": call_process(["sw_vers", "--buildVersion"]), + "build_machine_arch": call_process(["uname", "-m"]), + "build_machine_name": call_process(["uname", "-n"]), +} + +output["build_os_version_parts"] = semver_to_parts(output["build_os_version"]) + +# Step 1. Get Xcode Version +RE_XCODEBUILD_VERSION = re.compile(r"^Xcode ([0-9\.]+)\nBuild version ([0-9A-Za-z]+)$") +xcodebuild_version_process = call_process(["xcodebuild", "-version"]) +xcodebuild_version = RE_XCODEBUILD_VERSION.match(xcodebuild_version_process) +xcode_version_parts = xcodebuild_version.group(1).split(".", maxsplit=2) +output["xcode_version"] = xcodebuild_version.group(1) +output["xcode_version_parts"] = semver_to_parts(xcodebuild_version.group(1)) +output["xcode_build"] = xcodebuild_version.group(2) + +# Step 2. Get File Sizes + +files = {} +executables = {} +directories = {} +prefix = sys.argv[1] + "/Products/Applications" + +entire_size = 0 +executable_size = 0 + +for file in glob.iglob(prefix + "/*.app/**", recursive=True): + if not os.path.isfile(file): + if os.path.isdir(file): + dir_size = 0 + for f in glob.iglob(file + "/**", recursive=True): + if not os.path.isfile(f): + continue + dir_size += os.path.getsize(f) + directories[file[len(prefix):]] = dir_size + continue + files[file[len(prefix):]] = os.path.getsize(file) + entire_size += os.path.getsize(file) + if os.access(file, os.X_OK): + executables[file[len(prefix):]] = os.path.getsize(file) + executable_size += os.path.getsize(file) + +output["files"] = files +output["directories"] = directories +output["executables"] = executables +output["entire_size"] = entire_size +output["entire_executable_size"] = executable_size +output["type"] = "archive" + +print(json.dumps(output, indent=4)) \ No newline at end of file diff --git a/.github/workflows/archive.yml b/.github/workflows/archive.yml new file mode 100644 index 00000000..67bec347 --- /dev/null +++ b/.github/workflows/archive.yml @@ -0,0 +1,53 @@ +name: Archive + +on: + push: + +jobs: + archive: + strategy: + matrix: + xcode: ["16.2"] + machine: + - "macOS-15" + runs-on: ${{ matrix.machine }} + steps: + - uses: actions/checkout@v4 + - name: Select Xcode + run: sudo xcode-select --switch /Applications/Xcode_${{ matrix.xcode }}.app + - name: bundle install + run: bundle update --bundler && bundle install + - name: Install CocoaPods dependencies + run: bundle exec pod install + - run: git config --global core.quotepath false # for Ikemen on SwiftPM + - run: xcrun agvtool new-version -all ${{ github.run_id }} + - name: Build + run: | + set -o pipefail + xcodebuild \ + -workspace iMast.xcworkspace -scheme "iMast iOS" -destination "generic/platform=iOS" \ + archive -archivePath "./archive.xcarchive" \ + CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" AD_HOC_CODE_SIGNING_ALLOWED=YES | tee ./xcodebuild.log | xcpretty -c + - uses: actions/upload-artifact@v4 + if: always() + with: + name: xcodebuild.Xcode.${{ matrix.xcode }}.${{ matrix.machine }}.log + path: ./xcodebuild.log + - uses: actions/upload-artifact@v4 + with: + name: iMast.iOS.GHA.run.${{ github.run_id }}.Xcode.${{ matrix.xcode }}.${{ matrix.machine }}.xcarchive + path: ./archive.xcarchive + - name: Export Stats + run: python3 .github/export_archive_stats.py ./archive.xcarchive | tee stats.json + - name: Upload Stats + run: | + curl -fvX POST --data-binary "@stats.json" -H "Content-Type: application/json" \ + -H "X-Space-App-Key: ${{ secrets.BINSTATSD_API_KEY }}" \ + -H "X-IBS-Branch: ${{ github.ref_name }}" \ + -H "X-IBS-Commit: $(git rev-parse HEAD)" \ + -H "X-IBS-Parent: $(git rev-parse HEAD^)" \ + -H "X-IBS-Repo: ${{ github.repository }}" \ + -H "X-IBS-RunID: ${{ github.run_id }}" \ + -H "X-IBS-Platform: iOS" \ + -H "User-Agent: iMast_BuildStatsUploader/1.0" \ + https://${{ secrets.BINSTATSD_HOST }}/api/v1/register