Skip to content

Commit

Permalink
Tag Releasing: automated open-source releases (#55)
Browse files Browse the repository at this point in the history
* Use the new build system

* Add codeowners

* Filter submodule results

* Disable the orphaned_doc_comment rule

* Filter submodules warnings based on message

* Use the branch for now

* Use temp branch for xcode summary

* Start implementing tag releasing

* Added more stages to tag releasing

* Full release code is now in place

* Update Bitrise workflows

* Fix tag fetching

* Install mint and changelog producer on tag release

* Unhide dev dependencies for danger and local testing

* Fetching the organization and repo name now works

* Fix escaping chars

* Fix latest tag fetching

* Add cocoapods gem

* Use SSH and temporary disable pod_trunk_push

* Add extra documentation

* Fix CI runs

* Fix changelog generation

* Update the readme

* Enable pod_push again

* Fix changelog generation by adding extra linebreak and title hash

* Only run cocoapods stuff if there's a podspec

* Remove package.resolved

* Update README.md
  • Loading branch information
AvdLee committed Jan 28, 2020
1 parent 02e160f commit 3ddce93
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 43 deletions.
Binary file added Assets/bitrise_env_vars.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions Bitrise/tag_releasing_bitrise.yml
@@ -0,0 +1,40 @@
---
format_version: '8'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: ios
trigger_map:
- tag: "*"
workflow: wetransfer_tag_releasing
workflows:
wetransfer_tag_releasing:
steps:
- cache-pull: {}
- bitrise-step-install-bundler: {}
- script:
inputs:
- content: |-
envman add --key BREW_MINT --value "$(brew --cellar)/mint"
envman add --key BREW_OPT_MINT --value "/usr/local/opt/mint"
brew install mint
brew link mint
mint install WeTransfer/ChangelogProducer
title: Brew install
- script:
title: Run Fastlane
inputs:
- content: |-
#!/usr/bin/env bash
bundle config set path 'vendor/bundle'
bundle install
bundle exec fastlane release_from_tag
- cache-push:
run_if: true
inputs:
- is_debug_mode: 'true'
- cache_paths: |
$BITRISE_CACHE_DIR
$BREW_MINT
$BREW_OPT_MINT
.build
./vendor-> ./Gemfile.lock #gem installation directory
5 changes: 3 additions & 2 deletions Bitrise/bitrise.yml → Bitrise/testing_bitrise.yml
Expand Up @@ -4,9 +4,9 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: ios
trigger_map:
- pull_request_source_branch: "*"
workflow: open_source_projects
workflow: wetransfer_pr_testing
workflows:
open_source_projects:
wetransfer_pr_testing:
steps:
- cache-pull: {}
- bitrise-step-install-bundler: {}
Expand All @@ -20,6 +20,7 @@ workflows:
# Use the environment FASTLANE_LANE if available. Otherwise, fallback to "test"
lane=${FASTLANE_LANE:=test}
bundle exec fastlane $lane
bundle exec fastlane unhide_spm_package_dev_dependencies
- script:
inputs:
- content: |-
Expand Down
82 changes: 74 additions & 8 deletions Fastlane/Fastfile
Expand Up @@ -48,14 +48,80 @@ lane :test_pr_linter do |options|
destination: "platform=macOS")
end

desc 'Create a release from a tag triggered CI run'
lane :release_from_tag do |options|
# Get the latest tag, which is the new release that triggered this lane.
sh "git fetch --tags origin master --no-recurse-submodules"

released_tag = sh("git describe --abbrev=0").strip
previous_tag = sh("git describe --abbrev=0 --tags `git rev-list --tags --skip=1 --max-count=1`").strip

# Get the changelog
changelog = current_changelog(since_tag: previous_tag)

# Stop if there's no changelog
next unless !changelog.strip!.empty?

# Create a release on Github with the changelog
title = "#{released_tag}"
release_url = create_github_release(title: title, tag_name: released_tag, changelog: changelog)

puts "Created release: #{release_url}"

# Create a release branch
sh "git branch release/#{released_tag} origin/master"
sh "git checkout release/#{released_tag}"
sh "git merge -X theirs #{released_tag}"

# Update the changelog
create_changelog_release(release_title: title, changelog: changelog)

# Run only if there's a podspec to update
if Dir['../*.podspec'].any?
# Update the podspec. It finds the .podspec automatically in the current folder.
version_bump_podspec(version_number: released_tag)

# Push the podspec to trunk
pod_push
end

# Push the changes to the branch
sh('git commit -a -m "Created a new release"')
sh("git push origin release/#{released_tag}")

# Create a PR
# Create a pull request for master to include the updated Changelog.md and podspec
create_pull_request(
api_token: ENV["DANGER_GITHUB_API_TOKEN"],
repo: git_repository_name,
title: "Merge release #{released_tag} into master",
base: "master", # The branch to merge the changes into.
body: "Containing all the changes for our [**#{released_tag} Release**](#{release_url})."
)
end

desc "Unhide dev dependencies for danger"
lane :unhide_spm_package_dev_dependencies do
text = File.read('../Package.swift')
new_contents = text.gsub('// dev ', "")

# To write changes to the file, use:
File.open('../Package.swift', "w") {|file| file.puts new_contents }
end

desc "Returns the repository name. E.g: WeTransfer/Mocker"
lane :git_repository_name do
sh("git remote show origin -n | ruby -ne 'puts /^\s*Fetch.*(:|\\/){1}([^\\/]+\\/[^\\/]+).git/.match($_)[2] rescue nil'").strip
end

desc "Create a new release on Github"
desc ""
desc "- Uses the changelog as description of the new release"
lane :create_github_release do |options|
title = options[:title]
tag_name = options[:tag_name]
commitish = sh "git rev-parse HEAD"
repository_name = sh("git remote show origin -n | ruby -ne 'puts /^\s*Fetch.*:(.*).git/.match($_)[1] rescue nil'").strip
repository_name = git_repository_name

puts "Creating Github Release '#{title} for repository #{repository_name}"

Expand All @@ -80,11 +146,11 @@ lane :create_changelog_release do |options|

changelog_path = options[:changelog_path] || '../Changelog.md'
existing_changelog = File.read(changelog_path)
new_changelog = current_changelog
new_changelog = options[:changelog] || current_changelog

File.open(changelog_path, 'w') { |file|
file << "# #{release_title}\n" + "\n"
file << new_changelog + "\n"
file << "### #{release_title}\n" + "\n"
file << new_changelog + "\n\n"
file << existing_changelog
}
new_changelog
Expand All @@ -93,16 +159,16 @@ end
desc "Get the current changes since the last non-draft release"
lane :current_changelog do |options|
puts "Fetching changelog for base branch #{options[:base_branch]}"
latest_release_tag = latest_github_release
latest_release_tag = options[:since_tag] || latest_github_release
base_branch = options[:base_branch] || "master"
changelog = sh("changelogproducer -b #{base_branch} -s #{latest_release_tag}")
end

desc "Get latest Coyote release from Github. Draft releases and prereleases are not returned by this endpoint. See: https://developer.github.com/v3/repos/releases/#get-the-latest-release"
lane :latest_github_release do
origin_name = sh("git remote show origin -n | ruby -ne 'puts /^\s*Fetch.*:(.*).git/.match($_)[1] rescue nil'").split('/')
organisation = origin_name[0].strip
repository = origin_name[1].strip
origin_name = git_repository_name.split('/')
organisation = origin_name[0]
repository = origin_name[1]

result = github_api(
server_url: "https://api.github.com",
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -5,3 +5,4 @@ source 'https://rubygems.org'
gem 'fastlane'
gem 'xcpretty'
gem 'xcpretty-json-formatter'
gem 'cocoapods'
123 changes: 90 additions & 33 deletions README.md
Expand Up @@ -12,6 +12,7 @@ What's in it for me? Well, quite a lot! With low effort to add it to your projec
- Integrate [SwiftLint](https://github.com/realm/SwiftLint) to lint source code and tests
- Integrate [Fastlane](https://github.com/fastlane/fastlane) to run tests for PRs
- Integrate [Danger](http://danger.systems/) to automatically improve PR reviews
- Easily add automated releases based on tag-triggers

## Danger features
Following is a list of features which are posted in a comment on PRs based on the submitted files.
Expand All @@ -36,76 +37,132 @@ These warnings are posted inline inside the PR, helping you to solve them easily
![](Assets/danger_comment.png)
_This is an example comment. Note that `WeTransferBot` will be replaced by your own bot. More info can be found here: [Getting started with Danger](http://danger.systems/guides/getting_started.html)._

# Shared Bitrise.yml
The shared Bitrise.yml makes it really easy to integrate CI into open-source projects. It's been optimized using [this](https://blog.bitrise.io/tune-your-bitrise-workflows-using-cache-in-steps) blog post for caching and triggers like:
# How to integrate?

### 1: Add submodule
Add this repository as a submodule with the correct path `Submodules/WeTransfer-iOS-CI`:

```
[submodule "Submodules/WeTransfer-iOS-CI"]
path = Submodules/WeTransfer-iOS-CI
url = https://github.com/WeTransfer/WeTransfer-iOS-CI.git
```

### 2: Create a fastlane file

Create a fastlane file which executes testing with code coverage enabled. Import the Fastfile from this repo and trigger the `validate_changes` lane.

```ruby
import "./../Submodules/WeTransfer-iOS-CI/Fastlane/Fastfile"

desc "Run the tests and prepare for Danger"
lane :test do |options|
test_project(
project_path: "YOUR_PROJECT_PATH/",
project_name: "YOUR_PROJECT_NAME",
scheme: "YOUR_PROJECT_SCHEME")
end

```

### 3: Integrate SwiftLint in your project
Add a run script and use the common used [SwiftLint](https://github.com/WeTransfer/WeTransfer-iOS-CI/blob/master/SwiftLint/swiftlint.sh) script:

```shell
./Submodules/WeTransfer-iOS-CI/SwiftLint/swiftlint.sh
```

### 4: Make use of the shared Bitrise.yml workflows
The shared Bitrise.yml files make it really easy to integrate CI into open-source projects. It's been optimized using [this](https://blog.bitrise.io/tune-your-bitrise-workflows-using-cache-in-steps) blog post for caching and triggers like:

- Manage gems & brews
- Cache pulling
- Run fastlane for testing
- Run Danger from this repo
- Cache pushing

### How to use this in your Bitrise configuration?
#### How to use this in your Bitrise configuration?
For Danger, you need to set the `DANGER_GITHUB_API_TOKEN` in your Bitrise secrets.

Make sure your Bitrise.yml looks like this:

```yml
trigger_map:
- pull_request_source_branch: "*"
workflow: open_source_projects
workflow: wetransfer_pr_testing
workflows:
open_source_projects:
wetransfer_pr_testing:
steps:
- activate-ssh-key@4.0.3:
- activate-ssh-key:
run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
- git-clone@4.0.17: {}
- git-clone: {}
- script:
title: Continue from WeTransfer-iOS-CI repo
inputs:
- content: |-
#!/bin/bash
set -ex
bitrise run --config ./Bitrise/bitrise.yml "${BITRISE_TRIGGERED_WORKFLOW_ID}"
bitrise run --config ./Bitrise/testing_bitrise.yml "${BITRISE_TRIGGERED_WORKFLOW_ID}"
```

_Note: Don't change `open_source_projects` as this needs to match the Bitrise.yml file workflow._
_Note: Don't change `wetransfer_pr_testing` as this needs to match the Bitrise.yml file workflow._

# How to integrate?
### 5: Add automated releases based on tags
By making use of the Bitrise tag triggered builds we can automate the releases of open-source projects. The automation currently performs the following steps:

### 1: Add submodule
Add this repository as a submodule with the correct path `Submodules/WeTransfer-iOS-CI`:
- Automatically fetch the changelog using the [ChangelogProducer](https://github.com/WeTransfer/ChangelogProducer)
- Create a GitHub release containing the changelog
- Update and push the podspec
- Update the `Changelog.md` with the new changes
- Create a release branch and open a PR for those changes

```
[submodule "Submodules/WeTransfer-iOS-CI"]
path = Submodules/WeTransfer-iOS-CI
url = https://github.com/WeTransfer/WeTransfer-iOS-CI.git
```
#### How to use this in your Bitrise configuration?
As open-source projects are making use of HTTPS by default we need to force Bitrise to use SSH instead. Therefore, we need to add the SSH key manually to the secret environment variables with the key `SSH_RSA_PRIVATE_KEY`. You can can read more about this here: [How can I generate an SSH key pair?](https://devcenter.bitrise.io/faq/how-to-generate-ssh-keypair/).

### 2: Create a fastlane file
We also need to create a environment secret for CocoaPods trunk pushes with the key `COCOAPODS_TRUNK_TOKEN`. How to do that is explained here: [Automated CocoaPod releases with CI](https://fuller.li/posts/automated-cocoapods-releases-with-ci/).

Create a fastlane file which executes testing with code coverage enabled. Import the Fastfile from this repo and trigger the `validate_changes` lane.
After all, you're secrets should look as follows:

```ruby
import "./../Submodules/WeTransfer-iOS-CI/Fastlane/Fastfile"
![](Assets/bitrise_env_vars.png)

desc "Run the tests and prepare for Danger"
lane :test do |options|
test_project(
project_path: "YOUR_PROJECT_PATH/",
project_name: "YOUR_PROJECT_NAME",
scheme: "YOUR_PROJECT_SCHEME")
end
After that, we need to add a new trigger for tags:

```yaml
trigger_map:
- pull_request_source_branch: "*"
workflow: wetransfer_pr_testing
- tag: "*"
workflow: wetransfer_tag_releasing
```

### 3: Integrate SwiftLint in your project
Add a run script and use the common used [SwiftLint](https://github.com/WeTransfer/WeTransfer-iOS-CI/blob/master/SwiftLint/swiftlint.sh) script:

```shell
./Submodules/WeTransfer-iOS-CI/SwiftLint/swiftlint.sh
And we need to add the new workflow:

```yaml
wetransfer_tag_releasing:
steps:
- activate-ssh-key:
run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
- script:
title: Force SSH
inputs:
- content: |-
#!/usr/bin/env bash
# As we work with submodules, make sure we use SSH for this config so we can push our PR later on.
# See for more info: https://discuss.bitrise.io/t/git-force-to-use-ssh-url-instead-of-https-for-github-com/4384
git config --global url."git@github.com:".insteadOf "https://github.com/"
- git-clone: {}
- script:
title: Continue from WeTransfer-iOS-CI repo
inputs:
- content: |-
#!/bin/bash
set -ex
bitrise run --config ./Submodules/WeTransfer-iOS-CI/Bitrise/tag_releasing_bitrise.yml "${BITRISE_TRIGGERED_WORKFLOW_ID}"
```

After that, you can simply create a new tag and the whole release process will be triggered! 🚀


## License

WeTransfer-iOS-CI is available under the MIT license. See the LICENSE file for more info.
Expand Down

0 comments on commit 3ddce93

Please sign in to comment.