Compare commits

...

24 Commits

Author SHA1 Message Date
e003011b9a feat: Improved pipeline efficiency and adapted to new credentials template 2023-05-15 17:00:23 +02:00
b67a267ca5 fix: rollback native actions calls in helper 2023-05-02 09:41:34 +02:00
1c6b04e511 refactor: use other_action keyword to call native fastane actions 2023-05-02 09:30:21 +02:00
6e7befa067 fix: remove call keyword 2023-05-01 23:14:07 +02:00
9e685cae0e refactor: rollback in actions called in helper 2023-05-01 22:48:51 +02:00
04e06cf1a7 refactor: specify class for actions 2023-05-01 22:37:50 +02:00
fa9de2881f refactor: try to use method actions instead of classes 2023-05-01 22:31:03 +02:00
296d7181ca fix: change git_url label 2023-05-01 21:38:45 +02:00
a43966d6f8 feat: add logs and parameters to pilot 2023-05-01 21:34:17 +02:00
dbc8c0fed6 feat: add logs and parameters to match 2023-05-01 21:21:11 +02:00
24a09b44c4 feat: add logs and parameters to match 2023-05-01 21:16:35 +02:00
502c7b06e4 fix: add integrate args to cocoapods action args (#2) 2023-04-27 18:24:03 +02:00
74d3527b40 fix: add integrate args to cocoapods action args (#2) 2023-04-27 18:20:00 +02:00
d88276a0ec fix: add clean args to cocoapods action args (#2) 2023-04-27 18:17:58 +02:00
ec414c9864 fix: add necessary args (#2) 2023-04-27 18:05:53 +02:00
cc2cd8ea68 fix: update syntax (#2) 2023-04-27 16:52:22 +02:00
1759498e23 refactor: update params typo (#2) 2023-04-27 14:49:24 +02:00
f66ca731f8 refactor: upadte native fastlane calls (#1 #2) 2023-04-27 14:23:12 +02:00
89732f1f3f fix: add ios_directory (#2) 2023-04-26 17:20:22 +02:00
942f3afbf9 fix: fix export helper methode (#2) 2023-04-26 17:16:03 +02:00
87a5b0f809 fix: fix unexpected end-of-input error (#2) 2023-04-26 17:04:24 +02:00
0a004a595c refactor: rename action (#2) 2023-04-26 17:00:56 +02:00
155d048c18 feat: implement deployement pipeline (#2) 2023-04-26 16:54:43 +02:00
ee58f96126 feat: init ios plugin (#2) 2023-04-26 13:46:02 +02:00
22 changed files with 701 additions and 11 deletions

BIN
plugins/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -7,7 +7,7 @@ module Fastlane
def self.run(params)
# Check parameters
unless Helper::AndroidCdHelper.is_set(params[:beta_type])
UI.error("Parameters beta_type cannot be null")
UI.error("Parameters beta_type cannot be null")
puts("Error on beta type parameter")
end
@ -23,17 +23,17 @@ module Fastlane
build_number = Fastlane::Actions.number_of_commits
# Build the Android App Bundle
Fastlane::Actions.gradle(
Actions(GradleAction.run(
task: "bundle",
build_type: "Release",
print_command: true,
properties: {
"android.injected.version.code" => build_number
}
)
))
# Upload the Android App Bundle to the Play Store
Fastlane::Actions.upload_to_play_store(
Actions::UploadToPlayStoreAction.run(
track: params[:beta_type],
json_key: './service_account_key.json',
aab: '../build/app/outputs/bundle/release/app-release.aab',

View File

@ -24,7 +24,7 @@ module Fastlane
# Upload the Android App Bundle to the Play Store
Fastlane::Actions.upload_to_play_store(
track: params[:beta_type],
track: params[:from],
json_key: './service_account_key.json',
skip_upload_apk: true,
skip_upload_aab: true,

View File

@ -0,0 +1,29 @@
name: Test
on:
push:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile') }}
restore-keys: |
${{ runner.os }}-gem-
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.5
- name: Install dependencies
run: bundle check || bundle install --jobs=4 --retry=3 --path vendor/bundle
- name: Run tests
run: bundle exec rake
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: test-results
path: test-results

View File

@ -0,0 +1,12 @@
*.gem
Gemfile.lock
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
fastlane/README.md
fastlane/report.xml
coverage
test-results

View File

@ -0,0 +1,5 @@
--require spec_helper
--color
--format d
--format RspecJunitFormatter
--out test-results/rspec/rspec.xml

View File

@ -0,0 +1,188 @@
---
require:
- rubocop/require_tools
- rubocop-performance
AllCops:
TargetRubyVersion: 2.6
NewCops: enable
Include:
- "**/*.rb"
- "**/*file"
- "**/*.gemspec"
- "*/lib/assets/*Template"
- "*/lib/assets/*TemplateAndroid"
Exclude:
- "**/lib/assets/custom_action_template.rb"
- "./vendor/**/*"
- "**/lib/assets/DefaultFastfileTemplate"
- "**/lib/assets/MatchfileTemplate"
- "**/spec/fixtures/broken_files/broken_file.rb"
- "**/*.provisionprofile"
Lint/ErbNewArguments:
Enabled: false
Style/SlicingWithRange:
Enabled: false
Style/MultipleComparison:
Enabled: false
Style/PercentLiteralDelimiters:
Enabled: false
Style/ClassCheck:
EnforcedStyle: kind_of?
Style/FrozenStringLiteralComment:
Enabled: false
Style/SafeNavigation:
Enabled: false
Performance/RegexpMatch:
Enabled: false
Performance/StringReplacement:
Enabled: false
Style/NumericPredicate:
Enabled: false
Metrics/BlockLength:
Enabled: false
Metrics/ModuleLength:
Enabled: false
Naming/VariableNumber:
Enabled: false
Style/MissingRespondToMissing:
Enabled: false
Style/MultilineBlockChain:
Enabled: false
Style/NumericLiteralPrefix:
Enabled: false
Style/TernaryParentheses:
Enabled: false
Style/EmptyMethod:
Enabled: false
Lint/UselessAssignment:
Exclude:
- "**/spec/**/*"
Require/MissingRequireStatement:
Exclude:
- "**/spec/**/*.rb"
- "**/spec_helper.rb"
- spaceship/lib/spaceship/babosa_fix.rb
- fastlane_core/lib/fastlane_core/ui/disable_colors.rb
- "**/Fastfile"
- "**/*.gemspec"
- rakelib/**/*
- "**/*.rake"
- "**/Rakefile"
- fastlane/**/*
- supply/**/*
Layout/FirstHashElementIndentation:
Enabled: false
Layout/HashAlignment:
Enabled: false
Layout/DotPosition:
Enabled: false
Style/DoubleNegation:
Enabled: false
Style/SymbolArray:
Enabled: false
Layout/HeredocIndentation:
Enabled: false
Style/MixinGrouping:
Exclude:
- "**/spec/**/*"
Lint/SuppressedException:
Enabled: false
Lint/UnusedBlockArgument:
Enabled: false
Lint/AmbiguousBlockAssociation:
Enabled: false
Style/GlobalVars:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/SpecialGlobalVars:
Enabled: false
Metrics/AbcSize:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Style/WordArray:
MinSize: 19
Style/SignalException:
Enabled: false
Style/RedundantReturn:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/AndOr:
Enabled: true
EnforcedStyle: conditionals
Metrics/ClassLength:
Max: 320
Layout/LineLength:
Max: 370
Metrics/ParameterLists:
Max: 17
Style/GuardClause:
Enabled: false
Style/StringLiterals:
Enabled: false
Style/ConditionalAssignment:
Enabled: false
Style/RedundantSelf:
Enabled: false
Lint/UnusedMethodArgument:
Enabled: false
Lint/ParenthesesAsGroupedExpression:
Exclude:
- "**/spec/**/*"
Naming/PredicateName:
Enabled: false
Style/PerlBackrefs:
Enabled: false
Layout/SpaceAroundOperators:
Exclude:
- "**/spec/actions_specs/xcodebuild_spec.rb"
Naming/FileName:
Exclude:
- "**/Dangerfile"
- "**/Brewfile"
- "**/Gemfile"
- "**/Podfile"
- "**/Rakefile"
- "**/Fastfile"
- "**/Deliverfile"
- "**/Snapfile"
- "**/Pluginfile"
- "**/*.gemspec"
Style/Documentation:
Enabled: false
Style/MutableConstant:
Enabled: false
Style/ZeroLengthPredicate:
Enabled: false
Style/IfInsideElse:
Enabled: false
Style/CollectionMethods:
Enabled: false
Style/MethodCallWithArgsParentheses:
Enabled: true
IgnoredMethods:
- require
- require_relative
- fastlane_require
- gem
- program
- command
- raise
- attr_accessor
- attr_reader
- desc
- lane
- private_lane
- platform
- to
- not_to
- describe
- it
- be
- context
- before
- after

View File

@ -0,0 +1,4 @@
# os: osx # enable this if you need macOS support
language: ruby
rvm:
- 2.2.4

View File

@ -0,0 +1,9 @@
source('https://rubygems.org')
gemspec
gem 'json'
gem "cocoapods"
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2023 Malo Léon <malo.leon@wyatt-studio.fr>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,52 @@
# ios_cd plugin
[![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-ios_cd)
## Getting Started
This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-ios_cd`, add it to your project by running:
```bash
fastlane add_plugin ios_cd
```
## About ios_cd
Testflight and AppStore deployment plugin for Fastlane, simplifying the build and deployment porcess to internal, external, and production channels, and promoting builds for testing.
**Note to author:** Add a more detailed description about this plugin here. If your plugin contains multiple actions, make sure to mention them here.
## Example
Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
**Note to author:** Please set up a sample project to make it easy for users to explore what your plugin does. Provide everything that is necessary to try out the plugin in this project (including a sample Xcode/Android project if necessary)
## Run tests for this plugin
To run both the tests, and code style validation, run
```
rake
```
To automatically fix many of the styling issues, use
```
rubocop -a
```
## Issues and Feedback
For any other issues and feedback about this plugin, please submit it to this repository.
## Troubleshooting
If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
## Using _fastlane_ Plugins
For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
## About _fastlane_
_fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).

View File

@ -0,0 +1,9 @@
require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new
require 'rubocop/rake_task'
RuboCop::RakeTask.new(:rubocop)
task(default: [:spec, :rubocop])

View File

@ -0,0 +1,36 @@
lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'fastlane/plugin/ios_cd/version'
Gem::Specification.new do |spec|
spec.name = 'fastlane-plugin-ios_cd'
spec.version = Fastlane::IosCd::VERSION
spec.author = 'Malo Léon'
spec.email = 'malo.leon@wyatt-studio.fr'
spec.summary = 'Testflight and AppStore deployment plugin for Fastlane, simplifying the build and deployment porcess to internal, external, and production channels, and promoting builds for testing.'
# spec.homepage = "https://github.com/<GITHUB_USERNAME>/fastlane-plugin-ios_cd"
spec.license = "MIT"
spec.files = Dir["lib/**/*"] + %w(README.md LICENSE)
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
spec.required_ruby_version = '>= 2.6'
# Don't add a dependency to fastlane or fastlane_re
# since this would cause a circular dependency
# spec.add_dependency 'your-dependency', '~> 1.0.0'
spec.add_development_dependency('bundler')
spec.add_development_dependency('fastlane', '>= 2.212.2')
spec.add_development_dependency('pry')
spec.add_development_dependency('rake')
spec.add_development_dependency('rspec')
spec.add_development_dependency('rspec_junit_formatter')
spec.add_development_dependency('rubocop', '1.12.1')
spec.add_development_dependency('rubocop-performance')
spec.add_development_dependency('rubocop-require_tools')
spec.add_development_dependency('simplecov')
end

View File

@ -0,0 +1,3 @@
lane :test do
build_and_deploy(beta_type: "internal")
end

View File

@ -0,0 +1 @@
# Autogenerated by fastlane

View File

@ -0,0 +1,16 @@
require 'fastlane/plugin/ios_cd/version'
module Fastlane
module IosCd
# Return all .rb files inside the "actions" and "helper" directory
def self.all_classes
Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
end
end
end
# By default we want to import all available actions and helpers
# A plugin can contain any number of actions and plugins
Fastlane::IosCd.all_classes.each do |current|
require current
end

View File

@ -0,0 +1,158 @@
require 'fastlane/action'
require_relative '../helper/ios_cd_helper'
module Fastlane
module Actions
class BuildAndDeployAction < Action
def self.run(params)
# Check parameters
unless Helper::IosCdHelper.is_set(params[:beta_type])
UI.error("❌ Parameters beta_type cannot be null")
puts("Error on beta type parameter")
end
UI.message("⌛️ Building and deploying to Store in #{params[:beta_type]}..")
# Decrypt the keys archive and Extract the keys archive
Helper::IosCdHelper.decrypt_ios_keys('.')
UI.message("👉🏼 Credentials decrypted.")
# Retrieve credentials
creds = Helper::IosCdHelper.parseIosCredentials('.')
UI.message("👉🏼 Credentials parsed.")
# Delete keychain if existing
if File.exist?(File.expand_path("~/Library/Keychains/#{name}-db"))
other_action.delete_keychain(
name: creds['temp_keychain_user'].to_s
)
end
# Create keychain to store certifs
other_action.create_keychain(
name: creds['temp_keychain_user'].to_s,
password: creds['temp_keychain_password'].to_s,
unlock: false,
timeout: 0
)
UI.message("👉🏼 New keychain created")
# Obtain App Store Connect API key
api_key = other_action.app_store_connect_api_key(
key_id: creds['apple_key_id'].to_s,
issuer_id: creds['apple_issuer_id'].to_s,
key_content: creds['apple_key_content'].to_s,
duration: 1200,
in_house: false
)
UI.message("👉🏼 API Key formated")
last_testflight_build_number =
other_action.latest_testflight_build_number(
api_key: api_key,
team_id: creds['team_id'].to_s,
team_name: creds['team_name'].to_s,
platform: 'ios',
app_identifier: creds['developer_app_identifier'].to_s,
username: creds['username'].to_s
) + 1
# Increment build number for latest TestFlight build
other_action.increment_build_number(
build_number: last_testflight_build_number + 1,
xcodeproj: "Runner.xcodeproj"
)
UI.message("👉🏼 Build number incremented")
# Install Cocoapods
other_action.cocoapods(
clean_install: true,
clean: true,
integrate: true,
podfile: "./Podfile"
)
UI.message("👉🏼 Pod got")
# Set up code signing using match
# Configures and runs `match` which manages code signing certificates and provisioning profiles for the project.
# The function takes the app's bundle identifier, an authorization token for the project's Git repository, and the name and password for a temporary keychain used to store the signing certificate.
# It uses the App Store Connect API key to access the App Store and increment the build number.
# It then runs `gym` to build and sign the app using the selected provisioning profile, and finally, uses `pilot` to upload the app to TestFlight for beta testing.
other_action.match(
api_key: api_key,
type: 'appstore',
app_identifier: creds['app_identifier_extensions'],
git_basic_authorization: Base64.strict_encode64(ENV["GIT_AUTHORIZATION"]),
keychain_name: creds['temp_keychain_user'].to_s,
keychain_password: creds['temp_keychain_password'].to_s,
git_url: creds['git_url'].to_s,
username: creds['username'].to_s,
team_id: creds['team_id'].to_s,
team_name: creds['team_name'].to_s,
git_url: creds['git_url'].to_s,
storage_mode: "git"
)
UI.message("👉🏼 App signed")
# Build and export app using Gym
# Builds and packages an iOS app or framework for distribution to the App Store, TestFlight, or Enterprise distribution.
other_action.gym(
configuration: "Release",
workspace: "Runner.xcworkspace",
export_method: "app-store",
export_options: {
provisioningProfiles: creds['provisioning_profiles']
}
)
UI.message("👉🏼 App built")
# Upload build to App Store Connect using Pilot
other_action.pilot(
api_key: api_key,
apple_id: creds['developer_app_id'].to_s,
app_identifier: creds['developer_app_identifier'].to_s,
skip_waiting_for_build_processing: true,
skip_submission: true,
distribute_external: false,
notify_external_testers: false,
ipa: "./Runner.ipa"
)
# Delete keychain if existing
if File.exist?(File.expand_path("~/Library/Keychains/#{name}-db"))
other_action.delete_keychain(
name: creds['temp_keychain_user']
)
end
end
def self.description
"Testflight and AppStore deployment plugin for Fastlane, simplifying the build and deployment porcess to internal, external, and production channels, and promoting builds for testing."
end
def self.authors
["SAS Wyatt Studio"]
end
def self.return_value
end
def self.details
"The Fastlane Testflight / Appstore deloyment action streamlines the build and deployment to internal, external and production channels. Allow you to promote builds on testflight beta tests."
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :beta_type,
env_name: "IOS_CD_TEST_TYPE",
optional: false,
description: "Type of test (internal, external, production)")
]
end
def self.is_supported?(platform)
[:ios].include?(platform)
end
end
end
end

View File

@ -0,0 +1,71 @@
require 'fastlane/action'
require_relative '../helper/ios_cd_helper'
module Fastlane
module Actions
class PromoteAction < Action
def self.run(params)
UI.message("⌛️ Promoting to Store in external test..")
# Decrypt the keys archive and Extract the keys archive
Helper::IosCdHelper.decrypt_ios_keys('.')
UI.message("👉🏼 Credentials decrypted.")
# Retrieve credentials
creds = Helper::IosCdHelper.parseIosCredentials('.')
UI.message("👉🏼 Credentials parsed.")
# Obtain App Store Connect API key
api_key = other_action.app_store_connect_api_key(
key_id: creds['apple_key_id'].to_s,
issuer_id: creds['apple_issuer_id'].to_s,
key_content: creds['apple_key_content'].to_s,
duration: 1200,
in_house: false
)
UI.message("👉🏼 API Key formated")
# Upload build to App Store Connect using Pilot
other_action.pilot(
api_key: api_key,
apple_id: creds['developer_app_id'].to_s,
app_identifier: creds['developer_app_identifier'].to_s,
skip_waiting_for_build_processing: false,
distribute_only: true,
skip_submission: true,
distribute_external: true,
notify_external_testers: true,
groups: [
"Bêta externe",
],
app_platform:'ios',
)
end
def self.description
"Testflight and AppStore deployment plugin for Fastlane, simplifying the build and deployment porcess to internal, external, and production channels, and promoting builds for testing."
end
def self.authors
["SAS Wyatt Studio"]
end
def self.return_value
end
def self.details
"The Fastlane Testflight / Appstore deloyment action streamlines the build and deployment to internal, external and production channels. Allow you to promote builds on testflight beta tests."
end
def self.available_options
[]
end
def self.is_supported?(platform)
[:ios].include?(platform)
end
end
end
end

View File

@ -0,0 +1,47 @@
require 'fastlane/action'
require 'json'
require 'fastlane_core/ui/ui'
module Fastlane
module Helper
class IosCdHelper
# Check if a parameter is set or not
def self.is_set(variable)
str_variable = variable
str_variable = variable.strip if variable.class.to_s == "String"
variable && !(str_variable.nil? || str_variable.empty?)
end
# Decrypts ios credentials
def self.decrypt_ios_keys(ios_directory)
# Define the GPG command with options
gpg_command = "gpg --quiet --batch --yes --decrypt --passphrase=#{ENV['IOS_KEYS_SECRET_PASSPHRASE']} \
--output #{ios_directory}/ios_keys.zip #{ios_directory}/ios_keys.zip.gpg"
# Execute the GPG command using system
system(gpg_command)
# Check if the command executed successfully
if $?.success?
# Move the extracted files to the current directory
`jar xvf #{ios_directory}/ios_keys.zip && mv #{ios_directory}/ios_keys/* #{ios_directory}`
else
puts("Erreur lors de la décompression du fichier GPG")
end
end
# Parse credential file
def self.parseIosCredentials(ios_directory)
if File.exist?("#{ios_directory}/ios_crendentials.json")
# Read file and decrypt it
file = File.read("#{ios_directory}/ios_crendentials.json")
JSON.parse(file)
else
UI.error("❌ Ios credentials doesn't exist")
puts("json file doesn't exist")
end
end
end
end
end

View File

@ -0,0 +1,5 @@
module Fastlane
module IosCd
VERSION = "0.1.0"
end
end

View File

@ -0,0 +1,9 @@
describe Fastlane::Actions::BuildAndDeployAction do
describe '#run' do
it 'prints a message' do
expect(Fastlane::UI).to receive(:message).with("The ios_cd plugin is working!")
Fastlane::Actions::BuildAndDeployAction.run(nil)
end
end
end

View File

@ -0,0 +1,15 @@
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
require 'simplecov'
# SimpleCov.minimum_coverage 95
SimpleCov.start
# This module is only used to check the environment is currently a testing env
module SpecHelper
end
require 'fastlane' # to import the Action super class
require 'fastlane/plugin/ios_cd' # import the actual plugin
Fastlane.load_actions # load other actions (in case your plugin calls other actions or shared values)