├── .circleci └── config.yml ├── .github ├── FUNDING.yml └── workflows │ ├── macOS.yml │ └── ubuntu.yml ├── .gitignore ├── .gitmodules ├── .hound.yml ├── .swift-version ├── .swiftformat ├── .swiftlint.yml ├── .travis.yml ├── Brewfile ├── Documentation └── Reference │ ├── README.md │ └── structs │ └── ArchiveItem.md ├── LICENSE ├── Package.swift ├── README.md ├── Scripts ├── before_install.sh └── script.sh ├── Sources ├── EggSeedKit │ ├── Controllers │ │ ├── ArchiveExpander.swift │ │ ├── EggSeedRunner.swift │ │ ├── LicenseDownloadIssuer.swift │ │ ├── ProcessGitterface.swift │ │ ├── ProcessLauncher.swift │ │ ├── ProcessPackageFactory.swift │ │ └── URLDownloader.swift │ ├── EggSeed.swift │ ├── Extensions │ │ ├── Process.swift │ │ └── Result.swift │ ├── Models │ │ ├── ArchiveEntry.swift │ │ ├── CISystem.swift │ │ ├── CPU.swift │ │ ├── EggSeedError.swift │ │ ├── LauncherError.swift │ │ ├── License.swift │ │ ├── Platform.swift │ │ ├── SupportedPlatforms.swift │ │ └── SwiftPackageType.swift │ └── Protocols │ │ ├── Cancellable.swift │ │ ├── Downloader.swift │ │ ├── EggSeedConfiguration.swift │ │ ├── Expander.swift │ │ ├── ExpansionEntry.swift │ │ ├── Gitterface.swift │ │ ├── Launcher.swift │ │ ├── LicenseIssuer.swift │ │ ├── PackageFactory.swift │ │ ├── Runner.swift │ │ └── Session.swift └── eggseed │ └── main.swift ├── Tests ├── EggSeedTests │ ├── EggSeedTests.swift │ └── XCTestManifests.swift └── LinuxMain.swift ├── bitrise.yml ├── codecov.yml ├── eggseed.png ├── eggseed.svg ├── setup.sh.reference └── word.svg /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # For a detailed guide to building and testing on iOS, read the docs: 2 | # https://circleci.com/docs/2.0/testing-ios/ 3 | 4 | version: 2.1 5 | parameters: 6 | package-name: 7 | type: string 8 | default: "EggSeed" 9 | swift-ver: 10 | type: string 11 | default: "5.2.4" 12 | codecov-upload-file: 13 | type: string 14 | default: "info.lcov" 15 | orbs: 16 | codecov: codecov/codecov@1.0.5 17 | jobs: 18 | build-xenial: 19 | machine: 20 | image: ubuntu-1604:201903-01 21 | environment: 22 | PACKAGE_NAME: << pipeline.parameters.package-name >> 23 | SWIFT_VER: << pipeline.parameters.swift-ver >> 24 | steps: 25 | - checkout 26 | - run: 27 | name: Update PATH and Define Environment Variable at Runtime 28 | command: | 29 | echo 'export RELEASE_DOT=$(lsb_release -sr)' >> $BASH_ENV 30 | echo 'export RELEASE_NAME=$(lsb_release -sc)' >> $BASH_ENV 31 | echo 'export RELEASE_NUM=${RELEASE_DOT//[-._]/}' >> $BASH_ENV 32 | source $BASH_ENV 33 | - run: 34 | name: Download Swift 5.2 35 | command: wget -q https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz 36 | - run: 37 | name: Extract Swift 5.2 38 | command: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz 39 | - run: 40 | name: Add Path 41 | command: echo 'export PATH=${PWD}/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin:$PATH' >> $BASH_ENV 42 | - run: 43 | name: Resolve 44 | command: swift package resolve 45 | - run: 46 | name: Build 47 | command: swift build -v 48 | - run: 49 | name: Run tests 50 | command: swift test --enable-test-discovery --enable-code-coverage 51 | - run: 52 | name: Prepare Code Coverage 53 | command: llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/debug/${PACKAGE_NAME}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov 54 | - codecov/upload: 55 | file: << pipeline.parameters.codecov-upload-file >> 56 | flags: circleci,${RELEASE_NAME} 57 | workflows: 58 | version: 2 59 | build: 60 | jobs: 61 | - build-xenial 62 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: brightdigit 4 | -------------------------------------------------------------------------------- /.github/workflows/macOS.yml: -------------------------------------------------------------------------------- 1 | name: macOS 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | - 'feature/*' 8 | - 'release/*' 9 | tags: '*' 10 | 11 | jobs: 12 | build: 13 | env: 14 | PACKAGE_NAME: EggSeed 15 | 16 | runs-on: macos-latest 17 | if: "!contains(github.event.head_commit.message, 'ci skip')" 18 | 19 | strategy: 20 | matrix: 21 | runs-on: [macos-10.15,macos-11.0] 22 | xcode: ["/Applications/Xcode_11.7.app","/Applications/Xcode_12.app","/Applications/Xcode_12.1.app","/Applications/Xcode_12.2.app","/Applications/Xcode_12.3.app"] 23 | include: 24 | - os: macos-10.15 25 | xcode: "/Applications/Xcode_11.5.app" 26 | 27 | steps: 28 | - uses: actions/checkout@v2 29 | - name: Set Xcode Name 30 | run: echo "XCODE_NAME=$(basename -- ${{ matrix.xcode }} | sed 's/\.[^.]*$//' | cut -d'_' -f2)" >> $GITHUB_ENV 31 | - name: Setup Xcode 32 | run: sudo xcode-select -s ${{ matrix.xcode }}/Contents/Developer 33 | - name: Build 34 | run: swift build 35 | - name: Lint 36 | if: startsWith(github.ref, 'refs/tags/') != true 37 | run: swift run swiftformat --lint . && swift run swiftlint 38 | - name: Run tests 39 | run: swift test -v --enable-code-coverage 40 | - name: Prepare Code Coverage 41 | run: xcrun llvm-cov export -format="lcov" .build/debug/${{ env.PACKAGE_NAME }}PackageTests.xctest/Contents/MacOS/${{ env.PACKAGE_NAME }}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov 42 | - name: Upload to CodeCov.io 43 | run: bash <(curl https://codecov.io/bash) -F github -F macOS -F ${{ matrix.runs-on }} -n ${{ github.sha }} 44 | env: 45 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 46 | - name: Build Documentation 47 | if: ${{ matrix.os == 'macos-11.0' && matrix.xcode == '/Applications/Xcode_12.3.app' && !startsWith(github.ref, 'refs/tags/') }} 48 | run: | 49 | swift run sourcedocs generate build -cra 50 | git config --local user.email "action@github.com" 51 | git config --local user.name "GitHub Action" 52 | git status 53 | git add Documentation 54 | git diff-index --quiet HEAD || git commit -m "[github action] Update Docs" 55 | git push 56 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | env: 8 | PACKAGE_NAME: EggSeed 9 | SWIFT_VER: ${{ matrix.swift-version }} 10 | 11 | runs-on: ${{ matrix.runs-on }} 12 | if: "!contains(github.event.head_commit.message, 'ci skip')" 13 | 14 | strategy: 15 | matrix: 16 | runs-on: [ubuntu-18.04,ubuntu-20.04] 17 | swift-version: [5.2.4, 5.3.1] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set Ubuntu Release DOT 21 | run: echo "RELEASE_DOT=$(lsb_release -sr)" >> $GITHUB_ENV 22 | - name: Set Ubuntu Release NUM 23 | run: echo "RELEASE_NUM=${RELEASE_DOT//[-._]/}" >> $GITHUB_ENV 24 | - name: Set Ubuntu Codename 25 | run: echo "RELEASE_NAME=$(lsb_release -sc)" >> $GITHUB_ENV 26 | - name: Download Swift 27 | run: curl -O https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz 28 | - name: Extract Swift 29 | run: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz 30 | - name: Add Path 31 | run: echo "$GITHUB_WORKSPACE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin" >> $GITHUB_PATH 32 | - name: Build 33 | run: swift build 34 | - name: Run tests 35 | run: swift test --enable-test-discovery --enable-code-coverage 36 | - name: Prepare Code Coverage 37 | run: llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/debug/${{ env.PACKAGE_NAME }}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov 38 | - name: Upload to CodeCov.io 39 | run: bash <(curl https://codecov.io/bash) -F github -F ${RELEASE_NAME} -F ${SWIFT_VER} -n ${{ github.sha }} 40 | env: 41 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/swiftpm,swiftpackagemanager,xcode,swift,macos 2 | # Edit at https://www.gitignore.io/?templates=swiftpm,swiftpackagemanager,xcode,swift,macos 3 | 4 | 5 | ### macOS ### 6 | # General 7 | .DS_Store 8 | *.DS_Store 9 | 10 | .AppleDouble 11 | .LSOverride 12 | 13 | # Icon must end with two \r 14 | Icon 15 | 16 | # Thumbnails 17 | ._* 18 | 19 | # Files that might appear in the root of a volume 20 | .DocumentRevisions-V100 21 | .fseventsd 22 | .Spotlight-V100 23 | .TemporaryItems 24 | .Trashes 25 | .VolumeIcon.icns 26 | .com.apple.timemachine.donotpresent 27 | 28 | # Directories potentially created on remote AFP share 29 | .AppleDB 30 | .AppleDesktop 31 | Network Trash Folder 32 | Temporary Items 33 | .apdisk 34 | 35 | ### Swift ### 36 | # Xcode 37 | # 38 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 39 | 40 | ## Build generated 41 | build/ 42 | DerivedData/ 43 | 44 | ## Various settings 45 | # Xcode 46 | # 47 | build/ 48 | 49 | *.pbxuser 50 | !default.pbxuser 51 | *.mode1v3 52 | !default.mode1v3 53 | *.mode2v3 54 | !default.mode2v3 55 | *.perspectivev3 56 | !default.perspectivev3 57 | xcuserdata/ 58 | 59 | ## Other 60 | *.moved-aside 61 | *.xccheckout 62 | *.xcuserstate 63 | 64 | *.xcscmblueprint 65 | 66 | ## Obj-C/Swift specific 67 | *.hmap 68 | *.ipa 69 | *.dSYM.zip 70 | *.dSYM 71 | 72 | ## Playgrounds 73 | timeline.xctimeline 74 | playground.xcworkspace 75 | 76 | # Swift Package Manager 77 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 78 | # Packages/ 79 | # Package.pins 80 | # Package.resolved 81 | .build/ 82 | xcuserdata 83 | *.xccheckout 84 | *.moved-aside 85 | DerivedData 86 | *.hmap 87 | *.ipa 88 | *.xcuserstate 89 | 90 | # CocoaPods 91 | 92 | # We recommend against adding the Pods directory to your .gitignore. However 93 | # you should judge for yourself, the pros and cons are mentioned at: 94 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 95 | 96 | Example/Pods/ 97 | Example/*.xcworkspace 98 | 99 | # Carthage 100 | 101 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 102 | # Carthage/Checkouts 103 | 104 | Carthage/Build 105 | 106 | # Accio dependency management 107 | Dependencies/ 108 | .accio/ 109 | 110 | # fastlane 111 | 112 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 113 | # screenshots whenever they are needed. 114 | # For more information about the recommended setup visit: 115 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 116 | 117 | fastlane/report.xml 118 | fastlane/Preview.html 119 | fastlane/screenshots/**/*.png 120 | fastlane/test_output 121 | 122 | # Code Injection 123 | # After new code Injection tools there's a generated folder /iOSInjectionProject 124 | # https://github.com/johnno1962/injectionforxcode 125 | VCS.swift 126 | 127 | iOSInjectionProject/ 128 | Products 129 | 130 | 131 | ### SwiftPackageManager ### 132 | Packages 133 | xcuserdata 134 | /*.xcodeproj 135 | .tmp 136 | 137 | 138 | ### SwiftPM ### 139 | 140 | 141 | ### Xcode ### 142 | # Xcode 143 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 144 | 145 | ## User settings 146 | 147 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 148 | 149 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 150 | 151 | ## Xcode Patch 152 | /*.xcodeproj/* 153 | .swiftpm 154 | 155 | !*.xcodeproj/project.pbxproj 156 | !*.xcodeproj/xcshareddata/ 157 | !*.xcworkspace/contents.xcworkspacedata 158 | /*.gcno 159 | 160 | ### Xcode Patch ### 161 | **/xcshareddata/WorkspaceSettings.xcsettings 162 | 163 | # End of https://www.gitignore.io/api/swiftpm,swiftpackagemanager,xcode,swift,macos 164 | *.xcworkspace 165 | Pods 166 | *.lcov 167 | Brewfile.lock.json 168 | Example 169 | Package.resolved 170 | Sample 171 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Template"] 2 | path = Template 3 | url = https://github.com/brightdigit/eggseed-template 4 | branch = feature/templating 5 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | swiftlint: 2 | config_file: .swiftlint.yml 3 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5 2 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --indent 2 2 | --header strip 3 | --commas inline 4 | --exclude .build, DerivedData 5 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | cyclomatic_complexity: 12 2 | file_length: 550 3 | function_body_length: 80 4 | function_parameter_count: 8 5 | line_length: 150 6 | type_body_length: 300 7 | identifier_name: 8 | excluded: # excluded via string array 9 | - id 10 | excluded: 11 | - Tests/*/XCTestManifests.swift 12 | - DerivedData 13 | - .build 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | include: 3 | - os: linux 4 | dist: bionic 5 | arch: amd64 6 | - os: linux 7 | dist: focal 8 | arch: amd64 9 | - os: linux 10 | dist: bionic 11 | arch: arm64 12 | - os: linux 13 | dist: focal 14 | arch: arm64 15 | - os: osx 16 | osx_image: xcode11.4 17 | - os: osx 18 | osx_image: xcode11.5 19 | - os: osx 20 | osx_image: xcode11.6 21 | - os: osx 22 | osx_image: xcode12 23 | env: 24 | global: 25 | - FRAMEWORK_NAME=EggSeed 26 | - SWIFT_VER=5.2.4 27 | before_install: 28 | - bash -e ./Scripts/before_install.sh 29 | script: 30 | - bash -e ./Scripts/script.sh 31 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | brew "swiftformat" 2 | brew "swiftlint" 3 | brew "sourcedocs" 4 | -------------------------------------------------------------------------------- /Documentation/Reference/README.md: -------------------------------------------------------------------------------- 1 | # Reference Documentation 2 | 3 | This file was generated by [SourceDocs](https://github.com/eneko/SourceDocs) -------------------------------------------------------------------------------- /Documentation/Reference/structs/ArchiveItem.md: -------------------------------------------------------------------------------- 1 | **STRUCT** 2 | 3 | # `ArchiveItem` 4 | 5 | ```swift 6 | public struct ArchiveItem: TemplateExtractorItem 7 | ``` 8 | 9 | ## Properties 10 | ### `data` 11 | 12 | ```swift 13 | public let data: Data 14 | ``` 15 | 16 | ### `relativePath` 17 | 18 | ```swift 19 | public let relativePath: String 20 | ``` 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 brightdigit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "EggSeed", 8 | platforms: [ 9 | .macOS(.v10_13) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .executable( 14 | name: "eggseed", 15 | targets: ["eggseed"] 16 | ), 17 | .library(name: "EggSeedKit", targets: ["EggSeedKit"]) 18 | ], 19 | dependencies: [ 20 | .package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1"), 21 | .package(url: "https://github.com/weichsel/ZIPFoundation", .upToNextMajor(from: "0.9.0")), 22 | .package(url: "https://github.com/mxcl/PromiseKit.git", from: "7.0.0-alpha.3"), 23 | .package(name: "SwiftSyntax", url: "https://github.com/apple/swift-syntax.git", .exact("0.50200.0")), 24 | .package(name: "mustache", url: "https://github.com/AlwaysRightInstitute/mustache", from: "0.5.9"), 25 | .package(url: "https://github.com/shibapm/Komondor", from: "1.0.6"), // dev 26 | .package(url: "https://github.com/eneko/SourceDocs", from: "1.2.1"), // dev 27 | .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.47.0"), // dev 28 | .package(url: "https://github.com/realm/SwiftLint", from: "0.41.0"), // dev 29 | .package(url: "https://github.com/brightdigit/Rocket", .branch("feature/yams-4.0.0")), // dev 30 | .package(url: "https://github.com/mattpolzin/swift-test-codecov", .branch("master")) // dev 31 | ], 32 | targets: [ 33 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 34 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 35 | .target( 36 | name: "eggseed", 37 | dependencies: [ 38 | "EggSeedKit" 39 | ] 40 | ), 41 | .target( 42 | name: "EggSeedKit", 43 | dependencies: [ 44 | .product(name: "ArgumentParser", package: "swift-argument-parser"), 45 | "ZIPFoundation", "PromiseKit", "SwiftSyntax", "mustache" 46 | ] 47 | ), 48 | .testTarget( 49 | name: "EggSeedTests", 50 | dependencies: ["eggseed"] 51 | ) 52 | ] 53 | ) 54 | 55 | #if canImport(PackageConfig) 56 | import PackageConfig 57 | 58 | let config = PackageConfiguration([ 59 | "komondor": [ 60 | "pre-push": "swift test --enable-code-coverage --enable-test-discovery", 61 | "pre-commit": [ 62 | "swift test --enable-code-coverage --enable-test-discovery --generate-linuxmain", 63 | "swift run swiftformat .", 64 | "swift run swiftlint autocorrect", 65 | "swift run sourcedocs generate build -cra", 66 | "git add .", 67 | "swift run swiftformat --lint .", 68 | "swift run swiftlint" 69 | ] 70 | ] 71 | ]).write() 72 | #endif 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |   2 | 3 | [![SwiftPM](https://img.shields.io/badge/SPM-Linux%20%7C%20iOS%20%7C%20macOS%20%7C%20watchOS%20%7C%20tvOS-success?logo=swift)](https://swift.org) 4 | [![Twitter](https://img.shields.io/badge/twitter-@leogdion-blue.svg?style=flat)](http://twitter.com/leogdion) 5 | ![GitHub](https://img.shields.io/github/license/brightdigit/EggSeed) 6 | ![GitHub issues](https://img.shields.io/github/issues/brightdigit/EggSeed) 7 | 8 | [![macOS](https://github.com/brightdigit/EggSeed/workflows/macOS/badge.svg)](https://github.com/brightdigit/EggSeed/actions?query=workflow%3AmacOS) 9 | [![ubuntu](https://github.com/brightdigit/EggSeed/workflows/ubuntu/badge.svg)](https://github.com/brightdigit/EggSeed/actions?query=workflow%3Aubuntu) 10 | [![arm](https://github.com/brightdigit/EggSeed/workflows/arm/badge.svg)](https://github.com/brightdigit/EggSeed/actions?query=workflow%3Aarm) 11 | [![Travis (.com)](https://img.shields.io/travis/com/brightdigit/EggSeed?logo=travis)](https://travis-ci.com/brightdigit/EggSeed) 12 | [![CircleCI](https://img.shields.io/circleci/build/github/brightdigit/EggSeed?label=xenial&logo=circleci&token=8772831917d1744b175dd1d52ded916373f9a3ec)](https://circleci.com/gh/brightdigit/EggSeed) 13 | [![Bitrise](https://img.shields.io/bitrise/238176596b2afbd3?label=macOS&logo=bitrise&token=dRGT3cqlMSHKC93wAK01ww)](https://app.bitrise.io/app/238176596b2afbd3) 14 | 15 | [![Codecov](https://img.shields.io/codecov/c/github/brightdigit/EggSeed)](https://codecov.io/gh/brightdigit/EggSeed) 16 | [![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/brightdigit/EggSeed)](https://www.codefactor.io/repository/github/brightdigit/EggSeed) 17 | [![codebeat badge](https://codebeat.co/badges/4f86fb90-f8de-40c5-ab63-e6069cde5002)](https://codebeat.co/projects/github-com-brightdigit-EggSeed-master) 18 | [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/brightdigit/EggSeed)](https://codeclimate.com/github/brightdigit/EggSeed) 19 | [![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/brightdigit/EggSeed?label=debt)](https://codeclimate.com/github/brightdigit/EggSeed) 20 | [![Code Climate issues](https://img.shields.io/codeclimate/issues/brightdigit/EggSeed)](https://codeclimate.com/github/brightdigit/EggSeed) 21 | [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) 22 | 23 | --- 24 | 25 | **EggSeed** is a command-line tool for creating swift pacakges with continous integration support. While `swift package init`, creates simple packages, there is no guarantee that your package will work on everyone else's device. That's where _continuous integration_ goes in. 26 | 27 | By using `eggseed`, you can create a package with full integration into CI services such as: _[GitHub Actions](https://github.com/actions), [Travis-CI](https://travis-ci.com/), [BitRise](https://www.bitrise.io), [CircleCI](https://circleci.com)_ and more. Not only that but **EggSeed** also sets up code documentation, linting, and more... 28 | 29 | Check out the [roadmap below](#roadmap) for more details on future integrations. 30 | 31 | # Installation 32 | 33 | ### [Mint](https://github.com/yonaskolb/mint) 34 | ```sh 35 | mint install brightdigit/EggSeed 36 | ``` 37 | 38 | ### Swift Package Manager 39 | 40 | **Use as CLI** 41 | 42 | ```shell 43 | git clone https://github.com/brightdigit/EggSeed.git 44 | cd EggSeed 45 | swift run eggseed 46 | ``` 47 | 48 | **Use as dependency** 49 | 50 | Add the following to your Package.swift file's dependencies: 51 | 52 | ```swift 53 | .package(url: "https://github.com/brightdigit/EggSeed.git", from: "0.2.0"), 54 | ``` 55 | 56 | And then import wherever needed: `import EggSeed` 57 | 58 | # Usage 59 | 60 | ``` 61 | USAGE: eggseed [--package-type ] [--user-name ] [--path ] 62 | 63 | OPTIONS: 64 | --package-type 65 | Swift Package Type (default: library) 66 | --user-name User name or Owner of Repostory. 67 | --path Root path of the Swift Package. 68 | -h, --help Show help information. 69 | ``` 70 | 71 | **Eggseed** can be run without any options. However there are a few options which can help customize your package: 72 | 73 | ## Package Type `--package-type` (library or executable) 74 | 75 | Desginates what type of package you are creating. 76 | 77 | ## User Name `--user-name` 78 | 79 | The owner to user name of the repository. If not specified, EggSeed will attempt to parse the URL for that information. 80 | 81 | ## Path `--path` 82 | 83 | Directory to create the Swift Package in, otherwise use the current directory. 84 | 85 | # Documentation 86 | 87 | All code [documentation is here.](/Documentation/Reference/README.md) 88 | 89 | # Roadmap 90 | 91 | Future Released Will Include: 92 | 93 | * Choosing a License ([MIT](https://choosealicense.com/licenses/mit/), [GNU GPLv3](https://choosealicense.com/licenses/gpl-3.0/), [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0/), etc...) 94 | * Choosing Target OS and Version for CI (macOS v10_12, watchOS v6_2, Ubuntu Focal, iOS 12, etc...) 95 | * Choosing CI Services ([GitHub Actions](https://github.com/actions), [Travis-CI](https://travis-ci.com/), [BitRise](https://www.bitrise.io), [CircleCI](https://circleci.com), etc...) 96 | * Custom Template URLs 97 | * Adding [Cocoapod](https://cocoapods.org) Support 98 | * Adding [Carthage](https://github.com/Carthage/Carthage) Support 99 | * Adding [Homebrew](https://brew.sh) Support 100 | * Choosing Code Documentation Tool ([SourceDocs](https://github.com/eneko/SourceDocs), [Jazzy](https://github.com/realm/jazzy), etc...) 101 | * Choosing Linting Support ([SwiftFormat](https://github.com/nicklockwood/SwiftFormat), [SwiftLint](https://github.com/realm/SwiftLint), etc...) 102 | * Allow For Multiple Products On Setup 103 | * Choosing Architecture Support (amd64, aarch64, etc...) 104 | * Support for [Komondor](https://github.com/shibapm/Komondor) 105 | * Support for [Rocket](https://github.com/shibapm/Rocket) 106 | * Support for [Swift Package Index](https://swiftpackageindex.com) 107 | * Automated Code Quality Integrations ([codebeat](https://codebeat.co), [Code Climate](https://codeclimate.com), [Code Factory](https://www.codefactor.io), etc...) 108 | * README template and badges 109 | 110 | Feel free to [add an issue for any suggestions](https://github.com/brightdigit/EggSeed/issues). 111 | 112 | ## Contact 113 | 114 | Follow and contact me on [Twitter](http://twitter.com/leogdion). If you find an issue, 115 | just [open a ticket](https://github.com/brightdigit/EggSeed/issues/new) on it. Pull 116 | requests are warmly welcome as well. 117 | 118 | # License 119 | 120 | EggSeed is licensed under the MIT license. See [LICENSE](LICENSE) for more info. 121 | -------------------------------------------------------------------------------- /Scripts/before_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $TRAVIS_OS_NAME = 'osx' ]]; then 4 | : 5 | elif [[ $TRAVIS_OS_NAME = 'linux' ]]; then 6 | RELEASE_DOT=$(lsb_release -sr) 7 | RELEASE_NUM=${RELEASE_DOT//[-._]/} 8 | 9 | if [[ $RELEASE_DOT == "20.04" ]]; then 10 | sudo apt-get update 11 | sudo apt-get -y install \ 12 | binutils \ 13 | git \ 14 | gnupg2 \ 15 | libc6-dev \ 16 | libcurl4 \ 17 | libedit2 \ 18 | libgcc-9-dev \ 19 | libpython2.7 \ 20 | libsqlite3-0 \ 21 | libstdc++-9-dev \ 22 | libxml2 \ 23 | libz3-dev \ 24 | pkg-config \ 25 | tzdata \ 26 | zlib1g-dev 27 | fi 28 | 29 | if [[ $TRAVIS_CPU_ARCH == "arm64" ]]; then 30 | curl -s https://packagecloud.io/install/repositories/swift-arm/release/script.deb.sh | sudo bash 31 | sudo apt-get install swift-lang 32 | else 33 | wget https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz 34 | tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz 35 | fi 36 | fi 37 | -------------------------------------------------------------------------------- /Scripts/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $TRAVIS_OS_NAME = 'osx' ]]; then 4 | swift run swiftformat --lint . && swift run swiftlint 5 | elif [[ $TRAVIS_OS_NAME = 'linux' ]]; then 6 | # What to do in Ubunutu 7 | RELEASE_DOT=$(lsb_release -sr) 8 | RELEASE_NUM=${RELEASE_DOT//[-._]/} 9 | RELEASE_NAME=$(lsb_release -sc) 10 | [[ $TRAVIS_CPU_ARCH = "arm64" ]] && ARCH_PREFIX="aarch64" || ARCH_PREFIX="x86_64" 11 | export PATH="${PWD}/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin:$PATH" 12 | fi 13 | 14 | ARCH=${TRAVIS_CPU_ARCH:-amd64} 15 | [[ $TRAVIS_CPU_ARCH = "arm64" ]] && ARCH_PREFIX="aarch64" || ARCH_PREFIX="x86_64" 16 | 17 | swift build 18 | swift test --enable-code-coverage --enable-test-discovery 19 | 20 | if [[ $TRAVIS_OS_NAME = 'osx' ]]; then 21 | xcrun llvm-cov export -format="lcov" .build/debug/${FRAMEWORK_NAME}PackageTests.xctest/Contents/MacOS/${FRAMEWORK_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov 22 | bash <(curl https://codecov.io/bash) -F travis -F macOS -n $TRAVIS_JOB_NUMBER-$TRAVIS_OS_NAME 23 | else 24 | llvm-cov export -format="lcov" .build/${ARCH_PREFIX}-unknown-linux-gnu/debug/${FRAMEWORK_NAME}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov 25 | bash <(curl https://codecov.io/bash) -F travis -F $RELEASE_NAME -F $ARCH -n $TRAVIS_JOB_NUMBER-$TRAVIS_OS_NAME 26 | fi -------------------------------------------------------------------------------- /Sources/EggSeedKit/Controllers/ArchiveExpander.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ZIPFoundation 3 | 4 | #if canImport(FoundationNetworking) 5 | import FoundationNetworking 6 | #endif 7 | 8 | struct ArchiveExpander: Expander { 9 | func extract( 10 | fromURL sourceURL: URL, 11 | toURL destinationURL: URL, 12 | forEach: (ExpansionEntry, (Result) -> Void) -> Void, 13 | completion: @escaping (Error?) -> Void 14 | ) { 15 | guard let archive = Archive(url: sourceURL, accessMode: .read) else { 16 | completion(EggSeedError.invalidData(sourceURL)) 17 | return 18 | } 19 | let items = archive.enumerated() 20 | for entryItem in items { 21 | do { 22 | _ = try archive.extract(entryItem.element) { data in 23 | let path = entryItem.element.path.components(separatedBy: "/").dropFirst().joined(separator: "/") 24 | let item = ArchiveEntry(data: data, relativePath: path) 25 | let destinationFileURL = destinationURL.appendingPathComponent(path) 26 | if entryItem.element.type == Entry.EntryType.file { 27 | try? FileManager.default.createDirectory( 28 | at: destinationFileURL.deletingLastPathComponent(), 29 | withIntermediateDirectories: true, 30 | attributes: nil 31 | ) 32 | forEach(item) { result in 33 | if (try? result.get()) != true { 34 | do { 35 | try data.write(to: destinationFileURL) 36 | } catch { 37 | completion(error) 38 | return 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } catch { 45 | completion(error) 46 | return 47 | } 48 | } 49 | completion(nil) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Controllers/EggSeedRunner.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import mustache 3 | import PromiseKit 4 | 5 | #if canImport(FoundationNetworking) 6 | import FoundationNetworking 7 | #endif 8 | 9 | public class EggSeedRunner: Runner { 10 | let session: Session = URLSession.shared 11 | let downloader: Downloader = URLDownloader() 12 | let gitterface: Gitterface = ProcessGitterface() 13 | let expander: Expander = ArchiveExpander() 14 | let packageFactory: PackageFactory = ProcessPackageFactory() 15 | let licenseIssuer: LicenseIssuer = LicenseDownloadIssuer() 16 | 17 | public init() {} 18 | 19 | public func run( 20 | withConfiguration configuration: EggSeedConfiguration, 21 | _ completion: @escaping (EggSeedError?) -> Void 22 | ) -> Cancellable { 23 | let url = configuration.templateURL 24 | let filesFilter = [".eggseed/.github/workflows/macOS.yml", 25 | ".eggseed/.github/workflows/ubuntu.yml", 26 | ".eggseed/.travis.yml", 27 | ".eggseed/bitrise.yml", 28 | ".eggseed/.circleci/config.yml", 29 | "README.md", 30 | "Example/project.yml"] 31 | let destinationFolderURL = URL(fileURLWithPath: configuration.path ?? FileManager.default.currentDirectoryPath) 32 | 33 | let packageName = destinationFolderURL.lastPathComponent 34 | 35 | let userNamePromise = Promise { resolver in 36 | if let readUserName = configuration.userName { 37 | resolver.fulfill(readUserName) 38 | } else { 39 | gitterface.getSpecs(for: "origin", at: destinationFolderURL) { result in 40 | resolver.resolve(result.map { $0.owner }.mapError { _ in EggSeedError.missingValue("userName") 41 | }) 42 | } 43 | } 44 | } 45 | 46 | let downloadPromise = Promise { resolver in 47 | self.downloader.downloadURL(url, with: self.session) { result in 48 | resolver.resolve(result.mapError { error in 49 | EggSeedError.downloadFailure(error) 50 | }) 51 | } 52 | } 53 | 54 | let parser = MustacheParser() 55 | parser.openCharacter = "[" 56 | parser.closeCharacter = "]" 57 | 58 | var savedUserName: String! 59 | 60 | let queue = DispatchQueue.global(qos: .userInitiated) 61 | let cancellable = when(fulfilled: userNamePromise, downloadPromise).then(on: queue) { (args) -> Promise in 62 | let (userName, url) = args 63 | savedUserName = userName 64 | 65 | return Promise { resolver in 66 | self.expander.extract(fromURL: url, toURL: destinationFolderURL, forEach: { item, completed in 67 | if filesFilter.contains(item.relativePath) || item.relativePath.hasPrefix(".eggseed/"), let templateText = String(data: item.data, encoding: .utf8) { 68 | let relativePath: String 69 | if item.relativePath.hasPrefix(".eggseed/") { 70 | relativePath = item.relativePath.components(separatedBy: "/").dropFirst().joined(separator: "/") 71 | } else { 72 | relativePath = item.relativePath 73 | } 74 | let url = destinationFolderURL.appendingPathComponent(relativePath) 75 | let result = Result { 76 | if !FileManager.default.fileExists(atPath: url.deletingLastPathComponent().path) { 77 | try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) 78 | } 79 | let template = parser.parse(string: templateText) 80 | let text = template.render(object: ["packageName": packageName, "userName": userName]) 81 | try text.write(to: url, atomically: true, encoding: .utf8) 82 | return true 83 | } 84 | completed(result) 85 | } else { 86 | completed(.success(false)) 87 | } 88 | }, completion: resolver.resolve) 89 | } 90 | }.then(on: queue) { _ in 91 | Promise { resolver in 92 | self.packageFactory.create(atURL: destinationFolderURL, withType: configuration.packageType, resolver.resolve) 93 | } 94 | }.then(on: queue) { _ in 95 | Promise { resolver in 96 | self.licenseIssuer.issue(configuration.license, to: destinationFolderURL, withSession: self.session, usingFullName: savedUserName, resolver.resolve) 97 | } 98 | }.map(on: queue) { (_) -> EggSeedError? in 99 | nil 100 | } 101 | .recover(only: EggSeedError.self, on: queue) { error -> Promise in 102 | Promise { resolver in 103 | resolver.fulfill(error) 104 | } 105 | }.done(on: queue, completion) 106 | 107 | return cancellable 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Controllers/LicenseDownloadIssuer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import mustache 3 | 4 | public struct LicenseData: Codable { 5 | let body: String 6 | } 7 | 8 | public struct LicenseDownloadIssuer: LicenseIssuer { 9 | public let fileManager = FileManager.default 10 | public let baseURL = URL(string: "https://api.github.com/licenses")! 11 | public func issue(_ license: License, to destinationURL: URL, withSession session: Session, usingFullName fullName: String, _ completion: @escaping (Error?) -> Void) { 12 | let year = Calendar.current.component(.year, from: Date()) 13 | let data = ["year": year, "fullname": fullName] as [String: Any] 14 | let licenseURL = destinationURL.appendingPathComponent("LICENSE") 15 | let sourceURL = baseURL.appendingPathComponent(license.rawValue) 16 | let decoder = JSONDecoder() 17 | 18 | let parser = MustacheParser() 19 | parser.openCharacter = "[" 20 | parser.closeCharacter = "]" 21 | session.decode(LicenseData.self, fromURL: sourceURL, withDecoder: decoder) { result in 22 | let completed = result.map { $0.body.replacingOccurrences(of: "[", with: "[[").replacingOccurrences(of: "]", with: "]]") }.map(parser.parse(string:)).map { 23 | $0.render(object: data) 24 | }.flatMap { text in 25 | Result { try text.write(to: licenseURL, atomically: true, encoding: .utf8) } 26 | }.mapError(EggSeedError.decodeFailure) 27 | completion(completed.error) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Controllers/ProcessGitterface.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | struct ProcessGitterface: Gitterface { 8 | let launcher: Launcher = ProcessLauncher() 9 | func getRemote(for _: String, at url: URL, _ completion: @escaping (Result) -> Void) { 10 | launcher.bash("git remote get-url origin", at: url) { remoteURLString in 11 | let result: Result = remoteURLString.map { (data) -> String? in 12 | String(bytes: data, encoding: .utf8) 13 | }.map { string in 14 | string.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } 15 | }.mapError { (esError) -> Error in 16 | esError as Error 17 | }.flatMap { (string) -> Result in 18 | guard let string = string else { 19 | return .failure(EggSeedError.empty) 20 | } 21 | return .success(string) 22 | } 23 | completion(result) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Controllers/ProcessLauncher.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | struct ProcessLauncher: Launcher { 8 | let bashExecutableURL = URL(fileURLWithPath: "/bin/bash") 9 | func bash( 10 | _ command: String, 11 | at url: URL?, 12 | _ completion: @escaping ((Result) -> Void) 13 | ) -> Cancellable { 14 | let process = Process() 15 | process.executableURL = bashExecutableURL 16 | process.arguments = ["-c", command] 17 | process.currentDirectoryURL = url 18 | let outputPipe = Pipe() 19 | process.standardOutput = outputPipe 20 | 21 | let errorPipe = Pipe() 22 | process.standardError = errorPipe 23 | process.terminationHandler = { _ in 24 | completion( 25 | process.getResult( 26 | fromOutput: outputPipe.fileHandleForReading, 27 | andError: errorPipe.fileHandleForReading 28 | )) 29 | } 30 | process.launch() 31 | return process 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Controllers/ProcessPackageFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | struct ProcessPackageFactory: PackageFactory { 8 | let launcher: Launcher = ProcessLauncher() 9 | 10 | func create(atURL _: URL, withType type: SwiftPackageType, _ completion: @escaping (Error?) -> Void) { 11 | launcher.bash("swift package init --type \(type.rawValue)", at: nil) { 12 | completion($0.mapError(EggSeedError.packageFailure).error) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Controllers/URLDownloader.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | struct URLDownloader: Downloader { 8 | func downloadURL( 9 | _ url: URL, 10 | with session: Session, 11 | _ completion: @escaping ((Result) -> Void) 12 | ) -> Cancellable { 13 | return session.downloadURL(url, completion) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/EggSeed.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | import Foundation 3 | 4 | public enum StylingTool: Int, CaseIterable { 5 | case swiftformat = 1 6 | case swiftlint = 2 7 | } 8 | 9 | public struct StylingToolOption: OptionSet, ExpressibleByArgument { 10 | public let rawValue: StylingTool.RawValue 11 | public init(rawValue: StylingTool.RawValue) { 12 | self.rawValue = rawValue 13 | } 14 | 15 | public static let swiftformat = Self(rawValue: StylingTool.swiftformat.rawValue) 16 | public static let swiftlint = Self(rawValue: StylingTool.swiftlint.rawValue) 17 | public static let none = Self() 18 | public static let all = Self(StylingTool.allCases.map { Self(rawValue: $0.rawValue) }) 19 | } 20 | 21 | public enum DocumentationTooling: Int, ExpressibleByArgument { 22 | case sourcedocs = 1 23 | } 24 | 25 | public struct EggSeed: ParsableCommand, EggSeedConfiguration { 26 | public static var configuration = CommandConfiguration( 27 | commandName: "eggseed") 28 | static var runner: Runner! 29 | 30 | static func setupRunner(_ runner: Runner) { 31 | Self.runner = runner 32 | } 33 | 34 | public init() {} 35 | 36 | // Licence 37 | @Option(help: "Software License.") public var license: License = .mit 38 | 39 | // OS 40 | @Option(help: "Platforms and OSes") public var platforms: [SupportedPlatform] = [] 41 | 42 | // CI 43 | #warning("Add CI targets") 44 | @Option(default: ContinuousIntegration.all, help: "Continuous Integration Services") 45 | public var ci: ContinuousIntegration 46 | // Template 47 | 48 | @Option() 49 | public var template: String = "https://github.com/brightdigit/eggseed-template/archive/master.zip" 50 | 51 | public var templateURL: URL { 52 | return URL(string: template) ?? URL(string: "https://github.com/brightdigit/eggseed-template/archive/master.zip")! 53 | } 54 | 55 | // cocoapods support 56 | #warning("Add Cocoapods Support Flag") 57 | @Flag(help: "Supports Cocoapods") 58 | public var cocoapods: Bool = false 59 | 60 | @Option(help: "Use Komondor") 61 | public var komondor: Bool = true 62 | 63 | // sourcedocs or jazzy 64 | #warning("Add Documentation Tool Option") 65 | @Option(help: "Documentation Tool") 66 | public var documentation: DocumentationTooling = .sourcedocs 67 | 68 | // swiftformat or/and swiftlint danger etc... 69 | #warning("Add Linting Options") 70 | @Option(help: "Formatting, Linters, Styling Tools") 71 | public var linters: StylingToolOption = .all 72 | 73 | #warning("Allow Multiple Products") 74 | @Option(help: "Swift Package Type") public var packageType: SwiftPackageType = .library 75 | @Option(help: "User name or Owner of Repostory.") public var userName: String? 76 | @Option(help: "Root path of the Swift Package.") public var path: String? 77 | 78 | public static func main(by runner: Runner, _ arguments: [String]? = nil) { 79 | Self.setupRunner(runner) 80 | return Self.main(arguments) 81 | } 82 | 83 | public func run() throws { 84 | let semaphore = DispatchSemaphore(value: 0) 85 | var error: Error? 86 | Self.runner.run(withConfiguration: self) { 87 | error = $0 88 | semaphore.signal() 89 | } 90 | semaphore.wait() 91 | Self.exit(withError: error) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Extensions/Process.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Process: Cancellable { 4 | func getResult( 5 | fromOutput output: FileHandle, 6 | andError error: FileHandle 7 | ) -> Result { 8 | let outputData = output.readDataToEndOfFile() 9 | 10 | if terminationStatus == 0 { 11 | return .success(outputData) 12 | } 13 | 14 | return .failure(LauncherError( 15 | terminationStatus: terminationStatus, 16 | outputData: outputData, 17 | errorData: error.readDataToEndOfFile() 18 | )) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Extensions/Result.swift: -------------------------------------------------------------------------------- 1 | extension Result { 2 | init(success: Success?, failure: Failure?, fallback: () -> Failure) { 3 | if let success = success { 4 | self = .success(success) 5 | } else { 6 | self = .failure(failure ?? fallback()) 7 | } 8 | } 9 | 10 | var error: Failure? { 11 | guard case let .failure(error) = self else { 12 | return nil 13 | } 14 | return error 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/ArchiveEntry.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct ArchiveEntry: ExpansionEntry { 4 | public let data: Data 5 | public let relativePath: String 6 | } 7 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/CISystem.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | import Foundation 3 | 4 | #if canImport(FoundationNetworking) 5 | import FoundationNetworking 6 | #endif 7 | 8 | public enum ContinuousIntegrationSystem: Int, CaseIterable { 9 | case bitrise = 1 10 | case travisci = 2 11 | case github = 4 12 | case circleci = 8 13 | } 14 | 15 | public struct ContinuousIntegration: OptionSet, ExpressibleByArgument { 16 | public let rawValue: ContinuousIntegrationSystem.RawValue 17 | 18 | public typealias RawValue = ContinuousIntegrationSystem.RawValue 19 | 20 | public init(rawValue: RawValue) { 21 | self.rawValue = rawValue 22 | } 23 | 24 | public static let bitrise = Self(rawValue: ContinuousIntegrationSystem.bitrise.rawValue) 25 | public static let travisci = Self(rawValue: ContinuousIntegrationSystem.travisci.rawValue) 26 | public static let github = Self(rawValue: ContinuousIntegrationSystem.github.rawValue) 27 | public static let circleci = Self(rawValue: ContinuousIntegrationSystem.circleci.rawValue) 28 | public static let none = Self() 29 | public static let all = Self(ContinuousIntegrationSystem.allCases.map { Self(rawValue: $0.rawValue) }) 30 | } 31 | 32 | protocol CISystem { 33 | func remove(fromURL url: URL, _ completion: @escaping (Error?) -> Void) 34 | func verify(atURL url: URL, for platforms: SupportedPlatform, _ completion: @escaping (Error?) -> Void) 35 | } 36 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/CPU.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum CPU { 4 | case amd64 5 | case aarch64 6 | } 7 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/EggSeedError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | public enum EggSeedError: Error { 8 | case missingValue(String) 9 | case downloadFailure(Error?) 10 | case invalidData(URL) 11 | case decodeFailure(Error) 12 | case extractionFailure(Error) 13 | case packageFailure(Error) 14 | case destinationFailure(Error) 15 | case empty 16 | 17 | static func fallback() -> Error { 18 | return EggSeedError.empty 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/LauncherError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct LauncherError: Error, CustomStringConvertible { 4 | let terminationStatus: Int32 5 | let outputData: Data 6 | let errorData: Data 7 | 8 | var errorString: String? { 9 | String(bytes: errorData, encoding: .utf8) 10 | } 11 | 12 | var outputString: String? { 13 | String(bytes: outputData, encoding: .utf8) 14 | } 15 | 16 | var description: String { 17 | let errorString = self.errorString ?? errorData.description 18 | let outputString = self.outputString ?? outputData.description 19 | return "\terminated with \(terminationStatus): (error: \"\(errorString)\", output: \"\(outputString)\"" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/License.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | 3 | // swiftlint:disable identifier_name 4 | public enum License: String, ExpressibleByArgument { 5 | case agpl3_0 = "agpl-3.0" 6 | case apache2_0 = "apache-2.0" 7 | case bsd2clause = "bsd-2-clause" 8 | case bsd3clause = "bsd-3-clause" 9 | case cc01_0 = "cc0-1.0" 10 | case epl2_0 = "epl-2.0" 11 | case gpl2_0 = "gpl-2.0" 12 | case gpl3_0 = "gpl-3.0" 13 | case lgpl2_1 = "lgpl-2.1" 14 | case lgpl3_0 = "lgpl-3.0" 15 | case mit 16 | case mpl2_0 = "mpl-2.0" 17 | case unlicense 18 | } 19 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/Platform.swift: -------------------------------------------------------------------------------- 1 | enum Platform: String { 2 | case macOS 3 | case iOS 4 | case watchOS 5 | case tvOS 6 | case ubuntu 7 | 8 | var minimumVersion: Version? { 9 | guard self != .ubuntu else { 10 | return nil 11 | } 12 | let version: Version? 13 | switch self { 14 | case .iOS: 15 | version = Version(argument: "8.0") 16 | case .macOS: 17 | version = Version(argument: "10.10") 18 | case .tvOS: 19 | version = Version(argument: "9.0") 20 | case .watchOS: 21 | version = Version(argument: "2.0") 22 | default: 23 | version = nil 24 | } 25 | precondition(version != nil, "Invalid Platform or Version String.") 26 | return version 27 | } 28 | 29 | func spmPlatform(fromVersion version: Version) -> String? { 30 | guard let minimumVersion = self.minimumVersion else { 31 | return nil 32 | } 33 | 34 | let actualVersion = max(version, minimumVersion) 35 | return ".\(rawValue)(\"\(actualVersion)\")" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/SupportedPlatforms.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | 3 | struct Version: Comparable, CustomStringConvertible { 4 | var description: String { 5 | [majorVersion, minorVersion].compactMap { $0 }.map(String.init).joined(separator: ".") 6 | } 7 | 8 | static func < (lhs: Version, rhs: Version) -> Bool { 9 | guard lhs.majorVersion == rhs.majorVersion else { 10 | return lhs.majorVersion < rhs.majorVersion 11 | } 12 | guard let lmv = lhs.minorVersion, let rmv = rhs.minorVersion else { 13 | return lhs.minorVersion == nil || rhs.minorVersion == nil 14 | } 15 | return lmv < rmv 16 | } 17 | 18 | let majorVersion: Int 19 | let minorVersion: Int? 20 | init?(argument: String) { 21 | let components = argument.components(separatedBy: ".").prefix(2) 22 | let numbers = components.compactMap(Int.init) 23 | guard let majorVersion = numbers.first, components.count == numbers.count else { 24 | return nil 25 | } 26 | self.majorVersion = majorVersion 27 | minorVersion = numbers.count > 1 ? numbers.last : nil 28 | } 29 | } 30 | 31 | public struct SupportedPlatform: ExpressibleByArgument { 32 | public init?(argument: String) { 33 | let components = argument.components(separatedBy: "v") 34 | guard components.count < 3 else { 35 | return nil 36 | } 37 | guard let platform = components.first.flatMap(Platform.init) else { 38 | return nil 39 | } 40 | let version: Version? 41 | if let last = components.last { 42 | guard let lastVersion = Version(argument: last) else { 43 | return nil 44 | } 45 | version = lastVersion 46 | } else { 47 | version = nil 48 | } 49 | self.platform = platform 50 | self.version = version 51 | } 52 | 53 | let platform: Platform 54 | let version: Version? 55 | 56 | init(platform: Platform, version: Version?) { 57 | self.platform = platform 58 | self.version = version 59 | } 60 | 61 | // static let macOS = SupportedPlatforms(platform: .macOS) 62 | // static let iOS = SupportedPlatforms(platform: .iOS) 63 | // static let watchOS = SupportedPlatforms(platform: .watchOS) 64 | // static let tvOS = SupportedPlatforms(platform: .tvOS) 65 | // #warning("allow for multiple versions (ubuntus, amazon, centos)") 66 | // static let ubuntu = SupportedPlatforms(platform: .ubuntu) 67 | } 68 | 69 | // let platforms = Set(arrayLiteral: SupportedPlatform(platform: .iOS, version: "13")) 70 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Models/SwiftPackageType.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | 3 | public enum SwiftPackageType: String, ExpressibleByArgument { 4 | case library 5 | case executable 6 | } 7 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/Cancellable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PromiseKit 3 | 4 | #if canImport(FoundationNetworking) 5 | import FoundationNetworking 6 | #endif 7 | 8 | public protocol Cancellable {} 9 | 10 | extension URLSessionTask: Cancellable {} 11 | 12 | extension Promise: Cancellable {} 13 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/Downloader.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | public protocol Downloader { 8 | @discardableResult 9 | func downloadURL( 10 | _ url: URL, 11 | with session: Session, 12 | _ completion: @escaping ((Result) -> Void) 13 | ) -> Cancellable 14 | } 15 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/EggSeedConfiguration.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | public protocol EggSeedConfiguration { 8 | var userName: String? { get } 9 | var path: String? { get } 10 | var packageType: SwiftPackageType { get } 11 | var license: License { get } 12 | var templateURL: URL { get } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/Expander.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | protocol Expander { 8 | func extract( 9 | fromURL url: URL, 10 | toURL url: URL, 11 | forEach: (ExpansionEntry, (Result) -> Void) -> Void, 12 | completion: @escaping (Error?) -> Void 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/ExpansionEntry.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol ExpansionEntry { 4 | var data: Data { get } 5 | var relativePath: String { get } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/Gitterface.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | struct InvalidFormatError: Error {} 8 | 9 | public struct GitHubSpec { 10 | let owner: String 11 | let repo: String 12 | 13 | public init(string: String) throws { 14 | if let url = URL(string: string), url.scheme != nil { 15 | repo = url.deletingPathExtension().lastPathComponent 16 | owner = url.deletingLastPathComponent().lastPathComponent 17 | } else { 18 | guard let components = string.components(separatedBy: ":").last?.components(separatedBy: "/") else { 19 | throw InvalidFormatError() 20 | } 21 | guard components.count == 2 else { 22 | throw InvalidFormatError() 23 | } 24 | guard let owner = components.first else { 25 | throw InvalidFormatError() 26 | } 27 | guard let repo = components.last?.components(separatedBy: ".").first else { 28 | throw InvalidFormatError() 29 | } 30 | self.repo = repo 31 | self.owner = owner 32 | } 33 | } 34 | } 35 | 36 | public protocol Gitterface { 37 | func getRemote(for name: String, at url: URL, _ completion: @escaping (Result) -> Void) 38 | } 39 | 40 | public extension Gitterface { 41 | func getSpecs(for name: String, at url: URL, _ completion: @escaping (Result) -> Void) { 42 | getRemote(for: name, at: url) { result in 43 | let newResult = result.flatMap { string in 44 | Result { 45 | try GitHubSpec(string: string) 46 | } 47 | } 48 | completion(newResult) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/Launcher.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | protocol Launcher { 8 | @discardableResult 9 | func bash( 10 | _ string: String, 11 | at url: URL?, 12 | _ completion: @escaping ((Result) -> Void) 13 | ) -> Cancellable 14 | } 15 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/LicenseIssuer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol LicenseIssuer { 4 | func issue(_ license: License, to destinationURL: URL, withSession session: Session, usingFullName fullName: String, _ completion: @escaping (Error?) -> Void) 5 | } 6 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/PackageFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | protocol PackageFactory { 8 | func create(atURL url: URL, withType type: SwiftPackageType, _ completion: @escaping (Error?) -> Void) 9 | } 10 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/Runner.swift: -------------------------------------------------------------------------------- 1 | public protocol Runner { 2 | @discardableResult 3 | func run(withConfiguration configuration: EggSeedConfiguration, _ completion: @escaping (EggSeedError?) -> Void) -> Cancellable 4 | } 5 | -------------------------------------------------------------------------------- /Sources/EggSeedKit/Protocols/Session.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(FoundationNetworking) 4 | import FoundationNetworking 5 | #endif 6 | 7 | public protocol Session { 8 | @discardableResult 9 | func downloadURL(_ url: URL, _ completion: @escaping ((Result) -> Void)) -> Cancellable 10 | 11 | @discardableResult 12 | func decode(_ type: Success.Type, fromURL url: URL, withDecoder decoder: JSONDecoder, _ completion: @escaping ((Result) -> Void)) -> Cancellable 13 | } 14 | 15 | extension URLSession: Session { 16 | public func decode(_ type: Success.Type, fromURL url: URL, withDecoder decoder: JSONDecoder, _ completion: @escaping ((Result) -> Void)) -> Cancellable where Success: Decodable { 17 | let task = dataTask(with: url) { data, _, error in 18 | let result = Result(success: data, failure: error, fallback: EggSeedError.fallback).flatMap { data in 19 | Result { 20 | try decoder.decode(type, from: data) 21 | } 22 | } 23 | completion(result) 24 | } 25 | task.resume() 26 | return task 27 | } 28 | 29 | @discardableResult 30 | public func downloadURL(_ url: URL, _ completion: @escaping ((Result) -> Void)) -> Cancellable { 31 | let task = downloadTask(with: url) { url, _, error in 32 | let result: Result 33 | if let url = url { 34 | result = .success(url) 35 | } else { 36 | result = .failure(error ?? EggSeedError.empty) 37 | } 38 | completion(result) 39 | } 40 | task.resume() 41 | return task 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/eggseed/main.swift: -------------------------------------------------------------------------------- 1 | import EggSeedKit 2 | 3 | EggSeed.main(by: EggSeedRunner()) 4 | -------------------------------------------------------------------------------- /Tests/EggSeedTests/EggSeedTests.swift: -------------------------------------------------------------------------------- 1 | // @testable import eggseed 2 | import XCTest 3 | 4 | final class EggSeedTests: XCTestCase { 5 | func testExample() { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | // XCTAssertEqual(EggSeed().text, "Hello, World!") 10 | } 11 | 12 | // static var allTests = [ 13 | // //("testExample", testExample), 14 | // ] 15 | } 16 | -------------------------------------------------------------------------------- /Tests/EggSeedTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | #if !canImport(ObjectiveC) 2 | import XCTest 3 | 4 | extension EggSeedTests { 5 | // DO NOT MODIFY: This is autogenerated, use: 6 | // `swift test --generate-linuxmain` 7 | // to regenerate. 8 | static let __allTests__EggSeedTests = [ 9 | ("testExample", testExample) 10 | ] 11 | } 12 | 13 | public func __allTests() -> [XCTestCaseEntry] { 14 | return [ 15 | testCase(EggSeedTests.__allTests__EggSeedTests) 16 | ] 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import EggSeedTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += EggSeedTests.__allTests() 7 | 8 | XCTMain(tests) 9 | -------------------------------------------------------------------------------- /bitrise.yml: -------------------------------------------------------------------------------- 1 | --- 2 | format_version: '8' 3 | default_step_lib_source: 'https://github.com/bitrise-io/bitrise-steplib.git' 4 | project_type: other 5 | app: 6 | envs: 7 | - PACKAGE_NAME: EggSeed 8 | workflows: 9 | ci: 10 | steps: 11 | - git-clone@4: {} 12 | - script@1: 13 | inputs: 14 | - content: >- 15 | #!/usr/bin/env bash # fail if any commands fails 16 | 17 | set -e 18 | 19 | # debug log 20 | 21 | set -x 22 | 23 | pwd 24 | 25 | ls 26 | 27 | swift run swiftformat --lint . && swift run swiftlint 28 | 29 | swift build 30 | 31 | swift test --enable-code-coverage 32 | 33 | 34 | xcrun llvm-cov export -format="lcov" .build/debug/${PACKAGE_NAME}PackageTests.xctest/Contents/MacOS/${PACKAGE_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov 35 | 36 | bash <(curl https://codecov.io/bash) -F bitrise -F macOS -n 37 | $BITRISE_BUILD_NUMBER 38 | trigger_map: 39 | - push_branch: '*' 40 | workflow: ci 41 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "Tests" 3 | -------------------------------------------------------------------------------- /eggseed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brightdigit/EggSeed/1fdf171b2011a9a85cc6e19c5cebb0c93f108d3d/eggseed.png -------------------------------------------------------------------------------- /eggseed.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 58 | 62 | 66 | 70 | 74 | 76 | 80 | 81 | 85 | 86 | 91 | 94 | 98 | 102 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /setup.sh.reference: -------------------------------------------------------------------------------- 1 | # Get the package name based on the directory name 2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 3 | PACKAGE_NAME=`basename $DIR` 4 | 5 | # Get the user name based on the git repo url 6 | USER_NAME=`basename $(dirname $(git remote get-url origin))` 7 | 8 | # Create the Swift Package Library 9 | swift package init --type library 10 | 11 | # Use `sed` to replace the package name 12 | # in the Github Action CI Workflows 13 | sed -i '' "s/_PACKAGE_NAME/$PACKAGE_NAME/g" .github/workflows/macOS.yml 14 | sed -i '' "s/_PACKAGE_NAME/$PACKAGE_NAME/g" .github/workflows/ubuntu.yml 15 | # in the Travis CI Setup 16 | sed -i '' "s/_PACKAGE_NAME/$PACKAGE_NAME/g" .travis.yml 17 | # in the README.md 18 | sed -i '' "s/_PACKAGE_NAME/$PACKAGE_NAME/g" README.md 19 | # in the License 20 | sed -i '' "s/_PACKAGE_NAME/$PACKAGE_NAME/g" LICENSE 21 | 22 | # Use `sed` to replace the package name 23 | # in the Github Action CI Workflows 24 | sed -i '' "s/_USER_NAME/$USER_NAME/g" .github/workflows/macOS.yml 25 | sed -i '' "s/_USER_NAME/$USER_NAME/g" .github/workflows/ubuntu.yml 26 | # in the Travis CI Setup 27 | sed -i '' "s/_USER_NAME/$USER_NAME/g" .travis.yml 28 | # in the README.md 29 | sed -i '' "s/_USER_NAME/$USER_NAME/g" README.md 30 | # in the License 31 | sed -i '' "s/_USER_NAME/$USER_NAME/g" LICENSE 32 | # in the xcodegen Example project for Cocoapods 33 | sed -i '' "s/_USER_NAME/$USER_NAME/g" Example/project.yml 34 | 35 | # Create a CocoaPod spec 36 | pod spec create $(git remote get-url origin) --silent 37 | 38 | # Update the CocoaPod Spec with valid settings for all operating systems 39 | perl -pi -e '$_ .= qq(Lorem Description\n) if /spec.description = <<-DESC/' $PACKAGE_NAME.podspec 40 | sed -i '' 's|spec.summary.*|spec.summary = "Lorem Ipsum"|g' $PACKAGE_NAME.podspec 41 | sed -i '' 's|"MIT (example)"|{ :type => "MIT", :file => "LICENSE" }|g' $PACKAGE_NAME.podspec 42 | sed -i '' 's|spec.source_files =.*|spec.source_files = "Sources/**/*.swift"|g' $PACKAGE_NAME.podspec 43 | sed -i '' 's|spec.exclude_files.*|spec.swift_versions = "5"|g' $PACKAGE_NAME.podspec 44 | 45 | sed -i '' 's|spec.ios.deployment_target.*|spec.ios.deployment_target = "8.0"|g' $PACKAGE_NAME.podspec 46 | sed -i '' 's|spec.osx.deployment_target.*|spec.osx.deployment_target = "10.9"|g' $PACKAGE_NAME.podspec 47 | sed -i '' 's|spec.watchos.deployment_target.*|spec.watchos.deployment_target = "2.0"|g' $PACKAGE_NAME.podspec 48 | sed -i '' 's|spec.tvos.deployment_target.*|spec.tvos.deployment_target = "9.0"|g' $PACKAGE_NAME.podspec 49 | 50 | # Build the Swift Package 51 | swift build 52 | # Test the Swift Package 53 | swift test 54 | # Generate the first set of docs 55 | sourcedocs generate --spm-module $PACKAGE_NAME --output-folder docs 56 | # Generate the Example project using xcodegen 57 | pushd Example 58 | xcodegen generate 59 | # Generate Podfile 60 | pod init 61 | # Setup the Podfile for using the new Pod 62 | sed -i '' "s|# Pods for.*|pod '$PACKAGE_NAME', :path => '../'|g" Podfile 63 | # Setup the Example workspace 64 | pod install 65 | popd 66 | 67 | # Build all the schemes on the example workspace 68 | xcodebuild -workspace Example/Example.xcworkspace -scheme "iOS_Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO & 69 | xcodebuild -workspace Example/Example.xcworkspace -scheme "tvOS_Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO & 70 | xcodebuild -workspace Example/Example.xcworkspace -scheme "macOS_Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO & 71 | wait 72 | -------------------------------------------------------------------------------- /word.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 55 | 59 | 63 | 67 | 71 | 75 | 79 | 83 | 87 | 88 | 89 | 90 | --------------------------------------------------------------------------------