From b5c1e27a3e0033a51ee85f934b4187d3148327c8 Mon Sep 17 00:00:00 2001 From: "Matthew M. Keeler" Date: Fri, 29 Dec 2023 15:53:52 -0500 Subject: [PATCH] ci: Automate release process with release please (#72) --- .github/actions/build-docs/action.yml | 19 ++++++ .github/actions/ci/action.yml | 73 ++++++++++++++++++++++ .github/actions/publish-docs/action.yml | 15 +++++ .github/actions/publish/action.yml | 14 +++++ .github/actions/update-versions/action.yml | 55 ++++++++++++++++ .github/workflows/ci.yml | 61 ++++-------------- .github/workflows/lint-pr-title.yml | 12 ++++ .github/workflows/manual-publish-docs.yml | 26 ++++++++ .github/workflows/manual-publish.yml | 35 +++++++++++ .github/workflows/release-please.yml | 64 +++++++++++++++++++ .release-please-manifest.json | 3 + CONTRIBUTING.md | 4 +- LDSwiftEventSource.podspec | 2 +- README.md | 2 + release-please-config.json | 15 +++++ 15 files changed, 347 insertions(+), 53 deletions(-) create mode 100644 .github/actions/build-docs/action.yml create mode 100644 .github/actions/ci/action.yml create mode 100644 .github/actions/publish-docs/action.yml create mode 100644 .github/actions/publish/action.yml create mode 100644 .github/actions/update-versions/action.yml create mode 100644 .github/workflows/lint-pr-title.yml create mode 100644 .github/workflows/manual-publish-docs.yml create mode 100644 .github/workflows/manual-publish.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json diff --git a/.github/actions/build-docs/action.yml b/.github/actions/build-docs/action.yml new file mode 100644 index 0000000..f55b833 --- /dev/null +++ b/.github/actions/build-docs/action.yml @@ -0,0 +1,19 @@ +name: Build Documentation +description: 'Build Documentation.' + +runs: + using: composite + steps: + - name: Install jazzy gem + shell: bash + run: gem install jazzy + + - name: Build Documentation + shell: bash + run: jazzy -o docs + + - name: Validate coverage + shell: bash + run: | + FULLDOC=`jq '.warnings | length == 0' docs/undocumented.json` + [ $FULLDOC == "true" ] diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml new file mode 100644 index 0000000..ae95084 --- /dev/null +++ b/.github/actions/ci/action.yml @@ -0,0 +1,73 @@ +# This is a composite to allow sharing these steps into other workflows. +# For instance it could be used by regular CI as well as the release process. + +name: CI Workflow +description: 'Shared CI workflow.' +inputs: + xcode-version: + description: 'Which version of xcode should be installed' + required: true + ios-sim: + description: 'iOS Simulator to use for testing' + required: true + +runs: + using: composite + steps: + - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd + with: + xcode-version: ${{ inputs.xcode-version }} + + - name: Install mint + shell: bash + run: | + brew tap mint-lang/mint-lang + brew install mint-lang + + - name: Install cocoapods + shell: bash + run: gem install cocoapods + + - name: Lint the podspec + shell: bash + run: pod spec lint LDSwiftEventSource.podspec + + - name: Build & Test on macOS Simulator + shell: bash + run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk macosx -destination 'platform=macOS' | xcpretty + + - name: Build for ARM64 macOS + shell: bash + run: xcodebuild build -scheme 'LDSwiftEventSource' -arch arm64e -sdk macosx | xcpretty + + - name: Build Tests for iOS device + shell: bash + run: xcodebuild build-for-testing -scheme 'LDSwiftEventSource' -sdk iphoneos CODE_SIGN_IDENTITY= | xcpretty + + - name: Build & Test on iOS Simulator + shell: bash + run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk iphonesimulator -destination '${{ inputs.ios-sim }}' CODE_SIGN_IDENTITY= | xcpretty + + - name: Build Tests for tvOS device + shell: bash + run: xcodebuild build-for-testing -scheme 'LDSwiftEventSource' -sdk appletvos CODE_SIGN_IDENTITY= | xcpretty + + - name: Build & Test on tvOS Simulator + shell: bash + run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' | xcpretty + + - name: Build for watchOS simulator # No XCTest testing on watchOS + shell: bash + run: xcodebuild build -scheme 'LDSwiftEventSource' -sdk watchsimulator | xcpretty + + - name: Build for watchOS device # No XCTest testing on watchOS + shell: bash + run: xcodebuild build -scheme 'LDSwiftEventSource' -sdk watchos | xcpretty + + - name: Build & Test with swiftpm + shell: bash + run: swift test -v 2>&1 | xcpretty + + - name: Run contract tests + shell: bash + run: make contract-tests diff --git a/.github/actions/publish-docs/action.yml b/.github/actions/publish-docs/action.yml new file mode 100644 index 0000000..31a360a --- /dev/null +++ b/.github/actions/publish-docs/action.yml @@ -0,0 +1,15 @@ +name: Publish Documentation +description: 'Publish the documentation to GitHub pages' +inputs: + token: + description: 'Token to use for publishing.' + required: true + +runs: + using: composite + steps: + - uses: launchdarkly/gh-actions/actions/publish-pages@publish-pages-v1.0.1 + name: 'Publish to GitHub pages' + with: + docs_path: docs + github_token: ${{ inputs.token }} diff --git a/.github/actions/publish/action.yml b/.github/actions/publish/action.yml new file mode 100644 index 0000000..58214c5 --- /dev/null +++ b/.github/actions/publish/action.yml @@ -0,0 +1,14 @@ +name: Publish Package +description: 'Publish the package to Cocoapods' +inputs: + dry_run: + description: 'Is this a dry run. If so no package will be published.' + required: true + +runs: + using: composite + steps: + - name: Push to cocoapods + if: ${{ inputs.dry_run == 'false' }} + shell: bash + run: pod trunk push LDSwiftEventSource.podspec --allow-warnings --verbose diff --git a/.github/actions/update-versions/action.yml b/.github/actions/update-versions/action.yml new file mode 100644 index 0000000..201b8f9 --- /dev/null +++ b/.github/actions/update-versions/action.yml @@ -0,0 +1,55 @@ +name: Update xcode project version numbers +description: 'Update xcode project version numbers' +inputs: + branch: + description: 'The branch to checkout and push updates to' + required: true + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + + - name: Calculate version numbers + id: version + shell: bash + run: | + version=$(jq -r '."."' .release-please-manifest.json) + major=$(echo "$version" | cut -f1 -d.) + minor=$(echo "$version" | cut -f2 -d.) + patch=$(echo "$version" | cut -f3 -d.) + # 64 + version gives us a letter offset for the framework version. + framework=$(echo $((major + 64)) | awk '{ printf("%c", $1) }') + + echo "major=${major}" >> "$GITHUB_OUTPUT" + echo "minor=${minor}" >> "$GITHUB_OUTPUT" + echo "patch=${patch}" >> "$GITHUB_OUTPUT" + echo "framework=${framework}" >> "$GITHUB_OUTPUT" + + - name: Update other version numbers + shell: bash + run: | + sed -i .bak -E \ + -e 's/MARKETING_VERSION = [^;]+/MARKETING_VERSION = ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}/' \ + -e 's/DYLIB_CURRENT_VERSION = [^;]+/DYLIB_CURRENT_VERSION = ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}/' \ + -e 's/DYLIB_COMPATIBILITY_VERSION = [^;]+/DYLIB_COMPATIBILITY_VERSION = ${{ steps.version.outputs.major }}.0.0/' \ + -e 's/FRAMEWORK_VERSION = .*/FRAMEWORK_VERSION = ${{ steps.version.outputs.framework }};/' \ + LDSwiftEventSource.xcodeproj/project.pbxproj + + sed -i .bak -E \ + -e "s/pod 'LDSwiftEventSource', '~> [0-9]+.[0-9]+'/pod 'LDSwiftEventSource', '~> ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}'/" \ + -e "s/github \"LaunchDarkly\/swift-eventsource\" ~> [0-9]+.[0-9]+/github \"LaunchDarkly\/swift-eventsource\" ~> ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}/" README.md + + rm -f LDSwiftEventSource.xcodeproj/project.pbxproj.bak README.md.bak + if [ $(git status --porcelain | wc -l) -gt 0 ]; then + git config --global user.name 'LaunchDarklyReleaseBot' + git config --global user.email 'LaunchDarklyReleaseBot@launchdarkly.com' + + git add LDSwiftEventSource.xcodeproj/project.pbxproj + git add README.md + + git commit -m 'Updating generated project and readme files' + git push + fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8855cff..04ce38c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,71 +11,32 @@ on: jobs: macos-build: - runs-on: macos-latest + runs-on: ${{ matrix.os }} strategy: matrix: include: - - xcode-version: 15.0.0 - ios-sim: 'platform=iOS Simulator,name=iPhone 15,OS=17.0.1' - - xcode-version: 14.0.1 - ios-sim: 'platform=iOS Simulator,name=iPhone 14,OS=16.0' + - xcode-version: 15.0.1 + ios-sim: 'platform=iOS Simulator,name=iPhone 17,OS=17.0' + os: macos-13 + - xcode-version: 14.3.1 + ios-sim: 'platform=iOS Simulator,name=iPhone 16,OS=16.4' + os: macos-13 - xcode-version: 13.4.1 ios-sim: 'platform=iOS Simulator,name=iPhone 11,OS=15.5' + os: macos-12 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # If you only need the current version keep this. - - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd - if: ${{ matrix.xcode-version != '15.0.0' }} + - uses: ./.github/actions/ci with: xcode-version: ${{ matrix.xcode-version }} + ios-sim: ${{ matrix.ios-sim }} - - name: Build & Test on macOS Simulator - run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk macosx -destination 'platform=macOS' | xcpretty - - - name: Build for ARM64 macOS - run: xcodebuild build -scheme 'LDSwiftEventSource' -arch arm64e -sdk macosx | xcpretty - - - name: Build Tests for iOS device - run: xcodebuild build-for-testing -scheme 'LDSwiftEventSource' -sdk iphoneos CODE_SIGN_IDENTITY= | xcpretty - - - name: Build & Test on iOS Simulator - run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk iphonesimulator -destination '${{ matrix.ios-sim }}' CODE_SIGN_IDENTITY= | xcpretty - - - name: Build Tests for tvOS device - run: xcodebuild build-for-testing -scheme 'LDSwiftEventSource' -sdk appletvos CODE_SIGN_IDENTITY= | xcpretty - - - name: Build & Test on tvOS Simulator - run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' | xcpretty - - - name: Build for watchOS simulator # No XCTest testing on watchOS - run: xcodebuild build -scheme 'LDSwiftEventSource' -sdk watchsimulator | xcpretty - - - name: Build for watchOS device # No XCTest testing on watchOS - run: xcodebuild build -scheme 'LDSwiftEventSource' -sdk watchos | xcpretty - - - name: Build & Test with swiftpm - run: swift test -v 2>&1 | xcpretty - - - name: Run contract tests - run: make contract-tests - - - name: Install jazzy gem - run: | - gem install jazzy - gem cleanup - - - name: Build Documentation - run: jazzy -o artifacts/docs - - - name: Validate coverage - run: | - FULLDOC=`jq '.warnings | length == 0' artifacts/docs/undocumented.json` - [ $FULLDOC == "true" ] - + - uses: ./.github/actions/build-docs linux-build: runs-on: ubuntu-latest diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml new file mode 100644 index 0000000..4ba79c1 --- /dev/null +++ b/.github/workflows/lint-pr-title.yml @@ -0,0 +1,12 @@ +name: Lint PR title + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + lint-pr-title: + uses: launchdarkly/gh-actions/.github/workflows/lint-pr-title.yml@main diff --git a/.github/workflows/manual-publish-docs.yml b/.github/workflows/manual-publish-docs.yml new file mode 100644 index 0000000..8c8cbea --- /dev/null +++ b/.github/workflows/manual-publish-docs.yml @@ -0,0 +1,26 @@ +on: + workflow_dispatch: + +name: Publish Documentation +jobs: + build-publish: + runs-on: macos-13 + + permissions: + id-token: write # Needed if using OIDC to get release secrets. + contents: write # Needed in this case to write github pages. + + steps: + - uses: actions/checkout@v4 + + - name: Build and Test + uses: ./.github/actions/ci + with: + xcode-version: 14.3.1 + ios-sim: 'platform=iOS Simulator,name=iPhone 16,OS=16.4' + + - uses: ./.github/actions/build-docs + + - uses: ./.github/actions/publish-docs + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml new file mode 100644 index 0000000..fcc3335 --- /dev/null +++ b/.github/workflows/manual-publish.yml @@ -0,0 +1,35 @@ +name: Publish Package +on: + workflow_dispatch: + inputs: + dry_run: + description: 'Is this a dry run. If so no package will be published.' + type: boolean + required: true + +jobs: + build-publish: + runs-on: macos-13 + + # Needed to get tokens during publishing. + permissions: + id-token: write + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.0.0 + name: 'Get Cocoapods token' + with: + aws_assume_role: ${{ vars.AWS_ROLE_ARN }} + ssm_parameter_pairs: '/production/common/releasing/cocoapods/token = COCOAPODS_TRUNK_TOKEN' + + - uses: ./.github/actions/ci + with: + xcode-version: 14.3.1 + ios-sim: 'platform=iOS Simulator,name=iPhone 16,OS=16.4' + + - uses: ./.github/actions/publish + with: + dry_run: ${{ inputs.dry_run }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..a5712d6 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,64 @@ +name: Run Release Please + +on: + push: + branches: + - main + +jobs: + release-package: + runs-on: macos-13 + + permissions: + id-token: write # Needed if using OIDC to get release secrets. + contents: write # Contents and pull-requests are for release-please to make releases. + pull-requests: write + + steps: + - uses: google-github-actions/release-please-action@v4 + id: release + with: + token: ${{ secrets.GITHUB_TOKEN }} + target-branch: ${{ github.ref_name }} + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # If you only need the current version keep this. + + # + # This step runs and updates an existing PR + # + - uses: ./.github/actions/update-versions + if: ${{ steps.release.outputs.prs_created == 'true' }} + with: + branch: ${{ fromJSON(steps.release.outputs.pr).headBranchName }} + + # + # These remaining steps are ONLY run if a release was actually created + # + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.0.0 + if: ${{ steps.release.outputs.releases_created == 'true' }} + name: 'Get Cocoapods token' + with: + aws_assume_role: ${{ vars.AWS_ROLE_ARN }} + ssm_parameter_pairs: '/production/common/releasing/cocoapods/token = COCOAPODS_TRUNK_TOKEN' + + - uses: ./.github/actions/ci + if: ${{ steps.release.outputs.releases_created == 'true' }} + with: + xcode-version: 14.3.1 + ios-sim: 'platform=iOS Simulator,name=iPhone 16,OS=16.4' + + - uses: ./.github/actions/build-docs + if: ${{ steps.release.outputs.releases_created == 'true' }} + + - uses: ./.github/actions/publish + if: ${{ steps.release.outputs.releases_created == 'true' }} + with: + token: ${{secrets.GITHUB_TOKEN}} + dry_run: false + + - uses: ./.github/actions/publish-docs + if: ${{ steps.release.outputs.releases_created == 'true' }} + with: + token: ${{secrets.GITHUB_TOKEN}} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..b4b8d0f --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "3.1.1" +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ade822..99fcd10 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ Build instructions ### Prerequisites -This library is built with [XCode](https://developer.apple.com/xcode/) or [SwiftPM](https://swift.org/package-manager/). The [CI build](https://circleci.com/gh/launchdarkly/swift-eventsource) builds and tests various configurations of the library on various systems, platforms, and devices. For details, see [the CircleCI configuration][ci-config]. +This library is built with [XCode](https://developer.apple.com/xcode/) or [SwiftPM](https://swift.org/package-manager/). The [CI build](https://github.com/launchdarkly/swift-eventsource/actions/workflows/ci.yml) builds and tests various configurations of the library on various systems, platforms, and devices. For details, see [the GitHub action CI configuration][ci-config]. ### Building And Testing @@ -41,4 +41,4 @@ make contract-tests Docs are built with [jazzy](https://github.com/realm/jazzy), which is configured [here](https://github.com/launchdarkly/swift-eventsource/blob/main/.jazzy.yaml). To build them, simply run `jazzy`. Pull requests should keep our documentation coverage at 100%. -[ci-config]: https://github.com/launchdarkly/swift-eventsource/blob/main/.circleci/config.yml +[ci-config]: https://github.com/launchdarkly/swift-eventsource/blob/main/.github/workflows/ci.yml diff --git a/LDSwiftEventSource.podspec b/LDSwiftEventSource.podspec index 7ca9bba..9067c63 100644 --- a/LDSwiftEventSource.podspec +++ b/LDSwiftEventSource.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LDSwiftEventSource" - s.version = "3.1.1" + s.version = "3.1.1" # x-release-please-version s.summary = "Swift EventSource library" s.homepage = "https://github.com/launchdarkly/swift-eventsource" s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE.txt" } diff --git a/README.md b/README.md index e604589..dd85fa1 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,13 @@ To integrate LDSwiftEventSource into an Xcode project, go to the project editor, To include LDSwiftEventSource in a Swift package, simply add it to the dependencies section of your `Package.swift` file. And add the desired product as a dependency for your targets. + ```swift dependencies: [ .package(url: "https://github.com/LaunchDarkly/swift-eventsource.git", .upToNextMajor(from: "3.1.1")) ] ``` + ## Contributing diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..e330f11 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,15 @@ +{ + "packages": { + ".": { + "release-type": "simple", + "bump-minor-pre-major": true, + "versioning": "default", + "include-v-in-tag": false, + "include-component-in-tag": false, + "extra-files": [ + "LDSwiftEventSource.podspec", + "README.md" + ] + } + } +}