美文网首页
Flutter混合工程CI/CD最佳实践

Flutter混合工程CI/CD最佳实践

作者: jackyshan | 来源:发表于2021-12-17 10:09 被阅读0次

    背景

    项目处于混合开发状态,native开发的同学没有装flutter环境,无法编译flutter的代码,工程无法跑起来。

    官方推荐方案

    将 Flutter module 集成到 iOS 项目

    源码依赖

    flutter_application_path = './my_flutter'
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    
    target 'MyApp' do
      install_all_flutter_pods(flutter_application_path)
    end
    

    Flutter工程以submodule方式引入native工程

    优点

    源码依赖,方便调试,分支管理方便。

    缺点

    没有flutter环境的同学无法编译运行工程,对native侵入性比较强。

    产物依赖

    流程图

    iOS工程持续集成产物依赖

    优点

    侵入性低,产物依赖提升打包速度

    缺点

    开发迭代比较麻烦,打包产物步骤麻烦,生成产物需要托管管理,产物体积比较大

    我的方案

    APP.xcframework和Flutter.xcframework是以产物依赖,其他的插件是以源码形式依赖

    产物和源码混合依赖

    流程图

    iOS持续集成依赖

    优点

    只把dart代码编译成产物,其他使用源码方式依赖,产物体积很小,侵入性低,打包速度快。

    缺点

    开发调试比较麻烦,需要托管产物,要花时间实现一套全网没有参考的新方案

    比较

    方案实现

    制作

    打包产物脚本

    打包app.xcframework产物,然后把flutter.podspec、FlutterPluginRegistrant、plugins复制到binary目录,压缩binary.zip目录

    #环境变量
    function exportFlutterEnv() {
            export PUB_HOSTED_URL=https://pub.flutter-io.cn
            export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
    }
    
    #打包app.xcframework
    function buildiOSFramework() {
            if [ $BUILD_MODE == "Debug" ]
            then
                    $FLUTTER_PATH/bin/flutter build ios-framework --no-release --no-profile --output=./ios-framework --verbose --cocoapods --no-tree-shake-icons
            else
              $FLUTTER_PATH/bin/flutter build ios-framework --no-debug --no-profile --output=./ios-framework --verbose --cocoapods --no-tree-shake-icons
            fi
    
    }
    
    #创建目录binary
    function createBinaryDir() {
            mkdir -v binary
    }
    
    #移动plugins
    function movePluginsDir() {
            cp -r -v .ios/.symlinks/plugins/. binary/plugins
    }
    
    #移动 app.xcframework、flutter.podspec、FlutterPluginRegistrant
    function moveFlutterDir() {
            mkdir -p binary/flutter/FlutterPluginRegistrant
            cp -r -v .ios/Flutter/FlutterPluginRegistrant binary/flutter
            cp -v ios-framework/$BUILD_MODE/Flutter.podspec binary/flutter/Flutter.podspec
            cp -r -v ios-framework/$BUILD_MODE/App.xcframework/. binary/flutter/App.xcframework
    }
    
    #创建App.podspec
    function createAppPodspec() {
            touch binary/flutter/App.podspec
            echo """Pod::Spec.new do |s|
      s.name                  = 'App'
      s.version               = '1.0.0'
      s.summary               = 'fast apps.'
      s.description           = <<-DESC
    Business Code
    DESC
      s.homepage              = 'https://flutter.cn'
      s.license               = { :type => 'BSD' }
      s.author                = { 'Jacky' => 'shanhaoqiang@lizhi.fm' }
      s.source                = { :path => '.' }
      s.documentation_url     = 'https://flutter.cn/docs'
      s.platform              = :ios, '9.0'
      s.vendored_frameworks   = 'App.xcframework'
    end
            """>binary/flutter/App.podspec
    }
    
    #打zip包
    function zipBinaryDir() {
            zip -r binary-$BUILD_MODE.zip binary
    }
    
    #执行
    exportFlutterEnv
    buildiOSFramework
    createBinaryDir
    movePluginsDir
    moveFlutterDir
    createAppPodspec
    zipBinaryDir
    

    jenkins任务

    拉取flutter工程代码,执行上面的shell脚本,生成binary.zip,归档zip到jenkins的artifacts目录中,获取zip链接env.BUILD_URL + "artifact/binary-${BUILD_MODE}.zip"

        stage('\u261D 使用分支名称作为任务名称') {
            currentBuild.displayName = "#${BUILD_NUMBER}_${BRANCH_NAME}"
        }
    
        stage('\u262D 拉取代码') {
            checkout([$class: 'GitSCM', branches: [[name: '*/' + BRANCH_NAME]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CleanBeforeCheckout']], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '25b61d91-a240-4eaa-8390-9ae2655fb969', refspec: '+refs/heads/' + BRANCH_NAME + ':refs/remotes/origin/' + BRANCH_NAME, url: GIT_URL]]])
            withCredentials([sshUserPrivateKey(credentialsId: '25b61d91-a240-4eaa-8390-9ae2655fb969', keyFileVariable: 'SSH_KEY')]) {
                sh """
                    git checkout ${BRANCH_NAME}
                    git pull origin ${BRANCH_NAME}
                """
            }
    
        }
        stage('\u261D 拉取脚本') {
            sh 'rm -rf ./' + 'flutterbinary'
            sh 'git clone ' + 'git@gitlab.xx.fm:ocean/xx.git' + ' -b master --depth=1 ./' + 'flutterbinary'
        }
    
        stage('\u2615 编译产物') {
            sh """
            cp flutterbinary/build.sh build.sh
            sh build.sh ${BUILD_MODE} ${BUILD_NUMBER} ${BUILD_MODEL} ${FLUTTER_PATH} ${JDK_PATH}
            """
        }
    
        stage('\u26B0 保存成品') {
                archiveArtifacts artifacts: "binary-${BUILD_MODE}.zip", fingerprint: true
        }
    
        stage('\u2709 发送通知') {
            //iOS产物地址
            url = env.BUILD_URL + "artifact/binary-${BUILD_MODE}.zip"
    
            wrap([$class: 'BuildUser']) {
                USER_ID = BUILD_USER_ID
                USER_NAME = BUILD_USER
            }
    
            def updateLog = "${env.UPDATELOG}".trim()
    
            String content = "请相关同事知悉。本次Flutter产物发布信息如下:\\n 操作人:${USER_NAME}" + "\\n 打包类型:${BUILD_MODE}" + "\\n 任务名:${env.JOB_NAME}" + "\\nFlutter iOS产物地址:${url}"+ "\\nFlutter Android aar包地址:${aarUrl}"+"\\n对应分支:${BRANCH_NAME}\\nFlutter地址:${GIT_URL}\\n更新内容:${updateLog.replace("\n", "\\n")}\\n"
    
            def contentall = """
        {"content":{"text": "${content}"},"msg_type":"text"}
    """
            println("contentall:" + contentall)
            def command = """
        curl -X POST -H "Content-Type: application/json"\
          -d '${contentall}' \
          "https://open.feishu.cn/open-apis/bot/v2/hook/${env.NOTIFY_KEY}"
      """
            sh(script: command)
        }
    

    集成

    一行代码集成flutter,Podfile中填写jenkins打包的flutter项目的zip链接

    def pod_flutter
      puts "=== 集成flutter sdk ==="
      install_remote_flutter_binary('https://jksclient.xx.fm/job/%E8%8D%94%E6%9E%9D-flutter/106/artifact/binary-Release.zip')
    end
    

    编写Podfile插件

    • 收到传进来的url,对url做md5,创建Flutter缓存目录,下载zip包,解压
    • 安装flutter引擎,flutter指向podspec,podspec的source zip是官方地址
    • 安装plugins,各个plugin包含FlutterPluginRegistrant,指向解压后端path地址
    • 安装App.xcframework,指向App所在的path路径
    ## author:Jacky
    ## desc:install binary pods in Podfile
    #!/usr/bin/env ruby
    
    require 'digest'
    require 'fileutils'
    require 'uri'
    require 'net/http'
    require 'net/https'
    
    module Pod
      class Podfile
        module DSL
    
          #下载
          def install_remote_flutter_binary(url = nil)
            #md5
            md5 = Digest::MD5.new               # =>#<Digest::MD5>
            md5 << url
            md5value = md5.hexdigest                        # => "78e73102..."
            flutter_f_home = Dir.home+'/Library/Caches/CocoaPods/Flutter/'
            flutter_binary_home = flutter_f_home+md5value
            flutter_binary_path = flutter_binary_home+'/binary'
            
            #创建目录
            FileUtils.mkdir_p(flutter_binary_home)
    
            #清除超过30天的缓存
            xxxx
            
            #判断缓存
            if File::directory?(flutter_binary_path) == false
              #下载
              puts "开始下载 "+url
    
            #集成
            install_all_lzflutter_pods(flutter_binary_path)
          end
    
          #安装
          def install_all_lzflutter_pods(flutter_binary_path)
            install_lzflutter_engine_pod(flutter_binary_path)
            install_lzflutter_plugin_pods(flutter_binary_path)
            install_lzflutter_application_pod(flutter_binary_path)
          end
    
          # 安装flutter引擎
          def install_lzflutter_engine_pod(flutter_binary_path)
              xxx
          end
    
          # Install Flutter plugin pods.
          def install_lzflutter_plugin_pods(flutter_binary_path)
            # Keep pod path relative so it can be checked into Podfile.lock.
            # Process will be run from project directory.
    
            #FlutterPluginRegistrant
            xxx
            
            #插件目录
            xxx
    
            #plugins遍历
            xxx
          end
    
          # Install Flutter application pod.
          def install_lzflutter_application_pod(flutter_binary_path)
              xxx
          end
        end
      end
    end
    

    相关文章

      网友评论

          本文标题:Flutter混合工程CI/CD最佳实践

          本文链接:https://www.haomeiwen.com/subject/rqxifrtx.html