极狐GitLab鸿蒙开发场景DevOps解决方案

极狐GitLab鸿蒙开发场景DevOps解决方案

August 21, 2024

参考资料

修订记录

日期 内容
2024.08.21 创建文档

重要说明

1. 环境依赖

  • 服务端
    • 已安装极狐GitLab专业版, 如未安装,可参考《极狐GitLab私有化部署指南》 安装部署。
    • 一台8C、32G、500G及以上配置的Linux服务器,x86架构,建议Ubuntu操作系统,用于执行Harmony项目的流水线,并根据项目大小与并发任务适当增加配置。
  • 开发机
    • 有Harmony项目,至少是OpenHarmony API 9版本,不支持更低版本。
    • 已安装git客户端。

2. 环境配置

注意:以下操作均在GitLab Runner所在服务器进行。

2.1 安装GitLab Runner

  • 参考文档 安装GitLab Runner,以Ubuntu为例:

    # 添加极狐GitLab仓库
    curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
    
    # 安装GitLab Runner
    sudo apt-get install gitlab-runner
  • 打开极狐GitLab,在全局(需GitLab管理员)或在指定的群组或项目中创建Runner。

  • 输入Runner标签,如harmony,创建Runner。

  • 根据页面提示,在GitLab Runner中执行命令:

    • 第一步直接按回车。
    • 第二步输入shell
  • 在GitLab页面查看Runner状态。

  • 修改/etc/gitlab-runner/config.toml中的concurrent=5,表示可以并发的任务数,可根据实际情况和资源配置修改。

2.2 安装环境依赖

  • 参考Harmony官方文档 ,分别安装并配置JDK、命令行工具、hdc、hvigor、npm、ohpm、libGL1,以下是Ubuntu操作系统下的简化命令,具体以官方为准:
    # 安装JDK17
    wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz
    mkdir /opt
    tar -xvf jdk-17_linux-x64_bin.tar.gz -C /opt/
    export JAVA_HOME=/opt/jdk-17.0.12
    export PATH=$PATH:$JAVA_HOME/bin
    java -version
    
    # 安装command line
    ## 访问 https://developer.huawei.com/consumer/cn/download/,下载 Command Line Tools For HarmonyOS for Linux,并上传到Runner所在服务器
    unzip -d /opt/ commandline-tools-linux-x64-5.0.3.700.zip
    chmod -R 777 /opt/command-line-tools
    
    # 配置hdc环境变量
    export HDC_HOME=/opt/command-line-tools/sdk/HarmonyOS-NEXT-DB5/openharmony/toolchains
    export PATH=$PATH:$HDC_HOME
    
    # 配置hvigor环境变量
    export PATH=/opt/command-line-tools/bin:$PATH
    
    # 配置npm镜像仓库
    apt install npm
    npm config set registry=https://repo.huaweicloud.com/repository/npm/
    npm config set @ohos:registry=https://repo.harmonyos.com/npm/
    
    # 安装ohpm
    export PATH=/opt/command-line-tools/bin:$PATH
    ohpm config set registry https://ohpm.openharmony.cn/ohpm/
    ohpm config set strict_ssl false
    
    # 安装libGL1
    apt install libgl1-mesa-dev

3. 项目配置

3.1 初始化项目

  • 在极狐GitLab中创建空白项目,如harmony demo,取消勾选“使用自述文件初始化仓库”。

  • 根据harmony demo的提示,在开发机本地初始化已存在的Harmony项目,将本地代码上传到GitLab。

3.2 申请HAP签名

从NEXT Developer Beta3版本开始,所有的HAP、APP文件需开启签名,否则将导致构建出的包无法安装到设备上。

  • 如果仅开发环境进行调试,DevEco提供了自动签名

  • 如果要发布应用,或在流水线中自动构建、打包,需参考以下步骤获取相关证书:

    • 参考官方文档 生成密钥和证书请求文件,分别是一个.p12文件和.csr文件。
    • 参考官方文档 生成发布证书,这是一个.cer文件。
    • 参考官方文档 生成发布Profile,这是一个.p7b文件。
  • 在当前项目harmony demo“设置——CI/CD——变量”中创建环境变量,类型为**“变量” ,取消勾选“保护变量”:**

    • keyPwd: 申请.p12文件时的password
    • keyAlias: 申请.csr文件时的keyAlias
    • keystorePwd:申请.csr文件时的password,正常与keyPwd一致
  • 在当前项目harmony demo“设置——CI/CD——Secure files”中上传以下文件:

    • 密钥.p12文件。注意请将文件直接改名为.p12,再上传。
    • 证书.cer文件。注意请将文件直接改名为.cer,再上传。
    • Profile.p7b文件。注意请将文件直接改名为.p7b,再上传。

3.3 流水线配置

在当前项目harmony demo创建流水线,内容如下,需修改:

  • PRODUCT_NAME:对应项目 build-profile.json5 中的productsname
  • OUTPUT_PATH:构建文件的输出路径,APP和HAP的输出路径不一样
  • UNSIGNED_FILE:未签名的文件路径
  • SIGNED_FILE:签名的文件路径
  • before_script中的环境变量需与2.2章节配置的环境变量一致
  • 根据实际情况修改build-job的构建命令,如构建Hap或构建Hsp
    # 仅在Tag为harmony的Runner中执行
    default:
      tags:
        - harmony
    
    variables:
      # 对应 build-profile.json5 中的products的name
      PRODUCT_NAME: default
      # 输出路径
      OUTPUT_PATH: build/outputs/$PRODUCT_NAME
      # 未签名文件名
      UNSIGNED_FILE: $CI_PROJECT_NAME-$PRODUCT_NAME-unsigned.app
      # 已签名文件名
      SIGNED_FILE: $CI_PROJECT_NAME-$PRODUCT_NAME-signed.app
    
    # 添加环境变量
    before_script:
      - export JAVA_HOME=/opt/jdk-17.0.12
      - export HDC_HOME=/opt/command-line-tools/sdk/HarmonyOS-NEXT-DB5/openharmony/toolchains
      - export HCT_HOME=/opt/command-line-tools/bin
      - export PATH=$PATH:$JAVA_HOME/bin:$HDC_HOME:$HCT_HOME
    
    stages:
      - build
      - test
      - upload
    
    # hvigorw编译打包
    build-job:     
      stage: build
      script:
        # 安装依赖
        - ohpm install
            # 构建Hsp, 生成产物:${PROJECT_PATH}/{moduleName}/build/{productName}/outputs/{targetName}/(xxx.har | xxx.hsp)
            # - hvigorw assembleHsp --mode module -p module=library@default -p product=default --no-daemon
            # 构建Har, 生成产物:${PROJECT_PATH}/{moduleName}/build/{productName}/outputs/{targetName}/outputs/xxx.har
            # - hvigorw assembleHar --mode module -p module=library1@default -p product=default --no-daemon
            # 构建Hap, 生成产物:${PROJECT_PATH}/{moduleName}/build/{productName}/outputs/{targetName}/xxx.hap
            # - hvigorw assembleHap --mode module -p product=$PRODUCT_NAME -p buildMode=debug --no-daemon
        # 构建App, 生成产物: ${PROJECT_PATH}/build/outputs/{productName}/xxx.app
        - hvigorw assembleApp --mode project -p product=$PRODUCT_NAME -p buildMode=debug --no-daemon
        # 下载security file
        - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
        # 签名
        - java -jar $HDC_HOME/lib/hap-sign-tool.jar sign-app -keyAlias "$keyAlias" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile ".secure_files/.cer" -profileFile ".secure_files/.p7b" -inFile "$OUTPUT_PATH/$UNSIGNED_FILE" -keystoreFile ".secure_files/.p12" -outFile "$OUTPUT_PATH/$SIGNED_FILE" -keyPwd "$keyPwd" -keystorePwd "$keystorePwd" -signCode "1"
      # 创建review环境
      environment:
        name: review/$CI_COMMIT_REF_SLUG
        url: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/jobs/artifacts/$CI_COMMIT_REF_NAME/download?job=build-job
      # 上传签名后的文件
      artifacts:
        paths:
          - $OUTPUT_PATH/$SIGNED_FILE
    
    # codelinter静态扫描
    lint-test-job: 
      stage: test 
      script:
        - codelinter -o codelinter.txt
        - |
            #!/bin/bash
            input_file="codelinter.txt"
            output_file="gl-code-quality-report.json"
    
            # 初始化变量
            declare -a issues
            current_path=""
    
            # 生成UUID的函数
            generate_uuid() {
                uuidgen
            }
    
            # 转义description中的特殊字符:双引号、正反斜杠
            escape_special_chars() {
                echo "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\//\\\//g'
            }
    
            # 读取文件逐行处理
            while IFS= read -r line
            do
                # 去除行尾的回车符
                line=$(echo "$line" | tr -d '\r')
    
                # 判断是否为path
                if [[ $line =~ ^/ ]]; then
                    # 去掉路径中的括号部分,并去除末尾的换行符
                    current_path=$(echo "$line" | sed 's/(.*)//' | tr -d '\n')
                    # 去掉开头的"$CI_PROJECT_DIR/"字符串,只取相对路径
                    current_path=$(echo "$current_path" | sed "s|^$CI_PROJECT_DIR/||")
                elif [[ $line =~ ^[0-9]+:[0-9]+ ]]; then
                    # 提取行号,错误类型,描述,和规则名称
                    line_info=$(echo "$line" | awk '{print $1}')
                    severity="minor"  # 固定为 minor
                    description=$(echo "$line" | awk '{$1=$2=""; print $0}' | awk '{$NF=""; print $0}' | tr -d '\n')
                    check_name=$(echo "$line" | awk '{print $NF}' | tr -d '\n')
                    fingerprint=$(generate_uuid)
    
                    begin_line=$(echo "$line_info" | cut -d: -f1)
                    end_line=$(echo "$line_info" | cut -d: -f2)
    
                    # 去掉description和check_name中的多余空格
                    description=$(echo "$description" | sed 's/^[ \t]*//;s/[ \t]*$//')
                    check_name=$(echo "$check_name" | sed 's/^[ \t]*//;s/[ \t]*$//')
    
                    # 转义description中的特殊字符
                    description=$(escape_special_chars "$description")
    
                    # 创建JSON对象并添加到数组中
                    issue=$(printf '{
                        "engine_name": "codelinter",
                        "fingerprint": "%s",
                        "type": "issue",
                        "check_name": "%s",
                        "description": "%s",
                        "severity": "%s",
                        "location": {
                            "path": "%s",
                            "lines": {
                                "begin": %d,
                                "end": %d
                            }
                        }
                    }' "$fingerprint" "$check_name" "$description" "$severity" "$current_path" "$begin_line" "$end_line")
    
                    issues+=("$issue")
                fi
            done < "$input_file"
    
            # 将数组内容格式化为JSON数组并写入输出文件
            printf "[\n%s\n]\n" "$(IFS=','; echo "${issues[*]}")" > "$output_file"
      artifacts:
        reports:
          codequality: gl-code-quality-report.json
    
    # 上传到制品库
    upload-job:
      stage: upload
      image: alpine/curl
      rules:
        # 如果是从tag触发,即生产版本,则执行上传到制品库任务
        - if: '$CI_COMMIT_TAG  =~ /^v?\d+\.\d+\.\d+$/'
      script:
        # 上传到软件包库
        - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $OUTPUT_PATH/$SIGNED_FILE "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/release/$CI_COMMIT_TAG/$SIGNED_FILE"'

3.4 分支策略

建议使用GitLab Workflow,可参考文档《快速开始极狐GitLab工作流——分支策略》

3.5 保护分支:防止越权提交

参考文档《快速开始极狐GitLab工作流——保护分支》 配置。

3.6 推送规则:规范提交信息

参考文档《快速开始极狐GitLab工作流——分支命名规则》 《快速开始极狐GitLab工作流——代码推送规则》 配置。

3.7 代码评审:提高代码质量

参考文档《快速开始极狐GitLab工作流——代码评审》 配置“合并请求批准”和“代码所有者CodeOwner”。

若未经过审核人审批,则无法合并代码。

3.8 代码扫描:Code Linter

Harmony官方提供了静态扫描工具Code Linter ,已在3.3流水线配置中进行了集成,用户可根据Harmony官方文档修改项目中的code-linter.json5文件,自定义规则或使用不同的规则集。

注意,由于当前Harmony的Code Linter对于Error、Warn区分的不是很好,也没有优先级的定义,所以在GitLab中,所有的质量问题均显示为“次要”,后续等Harmony完善后可再做调整。

扫描后的结果可在以下位置查询:

  • 流水线:显示流水线运行分支全量报告。

  • 合并请求:显示合并请求源分支相较于目标分支差异报告。

3.9 制品管理

在3.3流水线配置中,已将编译构建并签名后的软件包上传到GitLab,可在以下位置获取:

  • 合并请求:如从feature/my-new-feat合并到main分支,流水线会基于feature/my-new-feat分支进行打包。开发、测试人员可点击合并请求中的“查看最新应用”下载feature/my-new-feat分支的软件包。一般用于测试,并可将测试结果作为评审意见参与代码评审,提高产品、研发、测试协同效率。

  • 软件包库:如测试完成,准备正式发版,可基于main分支创建release分支,再基于release分支打上标签Tag。对于从Tag触发的流水线,会将软件包上传到GitLab软件包库,作为正式发版的制品。

最后更新于