From Cocoapods to SPM: Automate Versioning with Fastlane's New Action
If you are a Cocapods framework maintainer or SPM library creator this article is for you! Switching from Cocoapods to Swift Package Manager is inevitable, more and more third-party solutions are making such a switch. Some of us use Fastlane to automate testing, building, and releasing our work.
When it comes to releasing, Cocoapods requires three actions:
- Changing version in
.podspec
file - Creating new tag matching
podspec
version - Registering a new version with Podspec Repo (public or private)
SPM simplifies the process, there is no version in Package.swift
, no repository of existing libraries, only git tag counts.
OK, so how does it affect the release process? For Cocoapods, we could rely on existing fastlane actions, like version_get_podspec
or version_bump_podspec
to get a new value for our release.
SPM on the other hand, doesn't have such easy-to-use actions. This is why I created new Fastlane Action, that can handle obtaining a new version using the last git tag!
To create new Fastlane Action, run [bundle exec] fastlane new_action
. Creator will ask for a name, we can use get_new_version
. The automated process will create a file structure to accommodate our new code and will return the path to the new action file.
Open ./fastlane/actions/get_new_version.rb
with your favorite code editor and replace its content with this implementation:
module Fastlane
module Actions
class GetNewVersionAction < Action
def self.run(params)
last_tag = other_action.last_git_tag
if last_tag.nil?
UI.message("Returning initial tag")
return '0.1.0'
end
major, minor, patch = last_tag.split('.').map(&:to_i)
if params[:bump_major]
new_major = major + 1
new_tag = "#{new_major}.0.0"
elsif params[:bump_minor]
new_minor = minor + 1
new_tag = "#{major}.#{new_minor}.0"
elsif params[:bump_patch]
new_patch = patch + 1
new_tag = "#{major}.#{minor}.#{new_patch}"
end
UI.message("New tag calculated: #{new_tag}")
return new_tag
end
def self.description
"Calculate the new tag by incrementing the last tag's major, minor, or patch version by one"
end
def self.authors
["Artur Gruchala"]
end
def self.is_supported?(platform)
true
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :bump_major,
description: "Bump the major version",
optional: true,
default_value: false,
is_string: false),
FastlaneCore::ConfigItem.new(key: :bump_minor,
description: "Bump the minor version",
optional: true,
default_value: false,
is_string: false),
FastlaneCore::ConfigItem.new(key: :bump_patch,
description: "Bump the patch version",
optional: true,
default_value: true,
is_string: false)
]
end
end
end
end
The Fastlane Action code has three parts:
- Implementation
- Description
- Action parameters definition
Code
We start our implementation by obtaining the last git tag, I use for this existing action: other_action.last_git_tag
.
After that, I make a simple check if the repository already has a tag, if not we can return it early with the default tag, I've chosen 0.1.0
.
The third step splits the tag into three parts, I assume the git repo has ONLY tags in the format x.y.z
. If your repo has other tag formats, you have to adjust the code to find the right pattern!
The last section checks what option was selected, and calculates the new version to be returned.
Description
We can specify instructions for potential users, authors, and platforms our Action supports.
Parameters
We are using three boolean options bump_major
, bump_minor
, and bump_patch
. All of them are optional, and all of them have default values. This way, we can use it in lanes without passing any options, and it will bump patch versions by one.
Conclusions
What we can do with it? You can use this action to calculate a new tag, use existing actions to create a new release, or even create a new release branch with a freshly obtained value. Fastlane can push changes for you, this way every release is automated, and there is no room for human error :)