diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c287c9f7..29b201af 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -60,13 +60,19 @@ variables: .setup_fastlane_android: extends: .build_rule before_script: + - export PUB_HOSTED_URL=https://pub.flutter-io.cn + - export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn - ls -li - export NEXT_VERSION="$(cat app_new.version)" -# - flutter pub get + # - flutter pub get - export PATH="/opt/homebrew/bin:$PATH" - eval "$(rbenv init -)" - - bundle config mirror.https://rubygems.org https://gems.ruby-china.com - - bundle install --gemfile android/Gemfile --quiet + - rbenv global 2.6.10 # 你实际用的 Ruby 版本 + - ruby -v + - which ruby + - gem env + - bundle config mirror.https://rubygems.org https://mirrors.aliyun.com/rubygems/ + - bundle install --gemfile android/Gemfile --path vendor/bundle_android --quiet cache: paths: - app_new.version @@ -74,13 +80,19 @@ variables: .setup_fastlane_ios: extends: .build_rule before_script: + - export PUB_HOSTED_URL=https://pub.flutter-io.cn + - export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn - ls -li - export NEXT_VERSION="$(cat app_new.version)" -# - flutter pub get + - flutter pub get - export PATH="/opt/homebrew/bin:$PATH" - eval "$(rbenv init -)" - - bundle config mirror.https://rubygems.org https://gems.ruby-china.com - - bundle install --gemfile ios/Gemfile --quiet + - rbenv global 2.6.10 # 你实际用的 Ruby 版本 + - ruby -v + - which ruby + - gem env + - bundle config mirror.https://rubygems.org https://mirrors.aliyun.com/rubygems/ + - bundle install --gemfile ios/Gemfile --path vendor/bundle_ios --quiet cache: paths: - app_new.version @@ -99,7 +111,6 @@ generate_git_tag: extends: .generate_tag_rule before_script: - bash pre_build.sh xhj - - bash pre_build.sh sky - project_url=$(echo $CI_PROJECT_URL | sed 's/http:\/\///') - echo "project_url:$project_url" - echo "CI_SERVER_FQDN:$CI_SERVER_FQDN/CI_PROJECT_ROOT_NAMESPACE:$CI_PROJECT_ROOT_NAMESPACE/CI_PROJECT_NAME:$CI_PROJECT_NAME" diff --git a/android/build.sh b/android/build.sh index 4b598959..c87b9420 100755 --- a/android/build.sh +++ b/android/build.sh @@ -13,20 +13,14 @@ if [[ "${ENV_BUILD_BRANCH}" == "canary_release" ]]; then echo "===build canary_release: ${NEXT_VERSION}" export ENV_BUILD_TAG=${NEXT_VERSION} bundle exec fastlane release_apk flavor:xhj --verbose - bundle exec fastlane release_apk flavor:sky --verbose elif [[ $ENV_BUILD_TAG =~ $regex ]]; then echo "===build release===$ENV_BUILD_TAG" bundle exec fastlane release_apk flavor:xhj --verbose - bundle exec fastlane release_apk flavor:sky --verbose - bundle exec fastlane release_bundle flavor:xhj_bundle --verbose - bundle exec fastlane release_bundle flavor:sky --verbose elif [[ "${ENV_BUILD_BRANCH}" == "develop" ]]; then echo "===build dev===${NEXT_VERSION}" bundle exec fastlane beta flavor:xhj env:dev --verbose - bundle exec fastlane beta flavor:sky env:dev --verbose elif [[ "${ENV_BUILD_BRANCH}" == "release" ]] || [[ "${ENV_BUILD_BRANCH}" == "feat_devops" ]] ; then echo "===build pre===${NEXT_VERSION}" bundle exec fastlane beta flavor:xhj env:pre --verbose - bundle exec fastlane beta flavor:sky env:pre --verbose fi exit 0 \ No newline at end of file diff --git a/ios/build.sh b/ios/build.sh index a8a977bf..4e2f126d 100755 --- a/ios/build.sh +++ b/ios/build.sh @@ -14,18 +14,14 @@ if [[ "${ENV_BUILD_BRANCH}" == "canary_release" ]]; then echo "===build canary_release: ${NEXT_VERSION}" export ENV_BUILD_TAG=${NEXT_VERSION} bundle exec fastlane release_ipa flavor:xhj --verbose - bundle exec fastlane release_ipa flavor:sky --verbose elif [[ $ENV_BUILD_TAG =~ $regex ]]; then echo "===build release===$ENV_BUILD_TAG" bundle exec fastlane release_ipa flavor:xhj --verbose - bundle exec fastlane release_ipa flavor:sky --verbose elif [[ "${ENV_BUILD_BRANCH}" == "develop" ]]; then echo "===build dev===${NEXT_VERSION}" bundle exec fastlane beta flavor:xhj env:Dev --verbose - bundle exec fastlane beta flavor:sky env:Dev --verbose elif [[ "${ENV_BUILD_BRANCH}" == "release" ]] || [[ "${ENV_BUILD_BRANCH}" == "feat_devops" ]] ; then echo "===build pre===${NEXT_VERSION}" bundle exec fastlane beta flavor:xhj env:Pre --verbose - bundle exec fastlane beta flavor:sky env:Pre --verbose fi exit 0 \ No newline at end of file diff --git a/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_logic.dart b/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_logic.dart index 5886160d..a9ca5ade 100755 --- a/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_logic.dart +++ b/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_logic.dart @@ -1,9 +1,9 @@ - import 'dart:async'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:get/get.dart'; import 'package:star_lock/main/lockDetail/lockSet/lockTime/getServerDatetime_entity.dart'; +import 'package:star_lock/main/lockDetail/passwordKey/passwordKey_perpetual/passwordKeyEntity.dart'; import '../../../../../blue/blue_manage.dart'; import '../../../../../blue/io_protocol/io_changeAdministratorPassword.dart'; @@ -18,14 +18,16 @@ import '../../../../../tools/eventBusEventManage.dart'; import '../../../../../tools/storage.dart'; import 'adminOpenLockPassword_state.dart'; -class AdminOpenLockPasswordLogic extends BaseGetXController{ +class AdminOpenLockPasswordLogic extends BaseGetXController { final AdminOpenLockPasswordState state = AdminOpenLockPasswordState(); // 获取解析后的数据 late StreamSubscription _replySubscription; void _initReplySubscription() { - _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) { - if (reply is ChangeAdministratorPasswordReply && state.ifCurrentScreen.value == true) { + _replySubscription = + EventBusManager().eventBus!.on().listen((Reply reply) { + if (reply is ChangeAdministratorPasswordReply && + state.ifCurrentScreen.value == true) { _replyChangeAdministratorPassword(reply); } @@ -49,11 +51,14 @@ class AdminOpenLockPasswordLogic extends BaseGetXController{ break; case 0x06: //无权限 - final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List? signKey = + await Storage.getStringList(saveBlueSignKey); final List signKeyDataList = changeStringListToIntList(signKey!); - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); + final List? privateKey = + await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = + changeStringListToIntList(privateKey!); final List token = reply.data.sublist(5, 9); final List saveStrList = changeIntListToStringList(token); @@ -63,7 +68,7 @@ class AdminOpenLockPasswordLogic extends BaseGetXController{ keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(), userID: await Storage.getUid(), pwdNo: state.lockBasicInfo.value.lockUserNo!, - pwd:state.changePwdController.text, + pwd: state.changePwdController.text, useCountLimit: 0xffff, startTime: 0, endTime: 0, @@ -179,23 +184,27 @@ class AdminOpenLockPasswordLogic extends BaseGetXController{ // 修改管理员密码(同添加自定义开锁密码,指纹/密码/卡片前5个是管理员) Future changeAdministratorPasswordCommand() async { - if(state.sureBtnState.value == 1){ + if (state.sureBtnState.value == 1) { return; } state.sureBtnState.value = 1; showEasyLoading(); - showBlueConnetctToastTimer(action: (){ + showBlueConnetctToastTimer(action: () { dismissEasyLoading(); state.sureBtnState.value = 0; }); - BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + BlueManage().blueSendData(BlueManage().connectDeviceName, + (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { - final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List? signKey = + await Storage.getStringList(saveBlueSignKey); final List signKeyDataList = changeStringListToIntList(signKey!); - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); + final List? privateKey = + await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = + changeStringListToIntList(privateKey!); final List? token = await Storage.getStringList(saveBlueToken); final List getTokenList = changeStringListToIntList(token!); @@ -204,7 +213,7 @@ class AdminOpenLockPasswordLogic extends BaseGetXController{ keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(), userID: await Storage.getUid(), pwdNo: state.lockBasicInfo.value.lockUserNo!, - pwd:state.changePwdController.text, + pwd: state.changePwdController.text, useCountLimit: 0xffff, startTime: 0, endTime: 0, @@ -213,11 +222,12 @@ class AdminOpenLockPasswordLogic extends BaseGetXController{ signKey: signKeyDataList, privateKey: getPrivateKeyList, token: getTokenList); - } else if (deviceConnectionState == BluetoothConnectionState.disconnected) { + } else if (deviceConnectionState == + BluetoothConnectionState.disconnected) { state.sureBtnState.value = 0; dismissEasyLoading(); cancelBlueConnetctToastTimer(); - if(state.ifCurrentScreen.value == true){ + if (state.ifCurrentScreen.value == true) { showBlueConnetctToast(); } } @@ -226,25 +236,62 @@ class AdminOpenLockPasswordLogic extends BaseGetXController{ // 更新管理员密码 Future addLockAdminPassword(bool isChange) async { - final GetServerDatetimeEntity entity = await ApiRepository.to.setAdminPasswordData( + final GetServerDatetimeEntity entity = + await ApiRepository.to.setAdminPasswordData( lockId: state.lockSetInfoData.value.lockBasicInfo!.lockId!, adminPwd: state.adminPwd.value, ); if (entity.errorCode!.codeIsSuccessful) { - if(isChange == true){ - showToast('修改成功'.tr, something: (){ + if (isChange == true) { + showToast('修改成功'.tr, something: () { state.lockBasicInfo.value.adminPwd = state.adminPwd.value; - eventBus.fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); + eventBus.fire( + PassCurrentLockInformationEvent(state.lockSetInfoData.value)); }); - }else{ - showToast('上传成功'.tr, something: (){ + } else { + showToast('上传成功'.tr, something: () { state.lockBasicInfo.value.adminPwd = state.adminPwd.value; - eventBus.fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); + eventBus.fire( + PassCurrentLockInformationEvent(state.lockSetInfoData.value)); }); } } } + /// 检查管理员密码是否重复 + /// + /// 返回值: + /// - true: 密码不重复,可以使用 + /// - false: 密码重复,不可使用 + Future checkPassword() async { + try { + if (state.lockSetInfoData.value.lockBasicInfo?.lockId == null) { + showToast('锁ID不能为空'.tr); + return false; + } + + if (state.adminPwd.value.isEmpty) { + showToast('密码不能为空'.tr); + return false; + } + + final PasswordKeyEntity entity = await ApiRepository.to.checkAdminPwd( + lockId: state.lockSetInfoData.value.lockBasicInfo!.lockId!, + adminPwd: state.changePwdController.text, + ); + + if (entity.errorCode!.codeIsSuccessful) { + return true; + } else { + showToast(entity.errorMsg ?? '已存在相同的密码,请更换。'); + return false; + } + } catch (e) { + showToast('检查密码失败'.tr); + return false; + } + } + @override void onReady() { super.onReady(); @@ -257,5 +304,4 @@ class AdminOpenLockPasswordLogic extends BaseGetXController{ super.onClose(); _replySubscription.cancel(); } - } diff --git a/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_page.dart b/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_page.dart index ffa2e162..03a6cc02 100755 --- a/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_page.dart +++ b/lib/main/lockDetail/lockSet/basicInformation/adminOpenLockPassword/adminOpenLockPassword_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; @@ -16,12 +15,16 @@ class AdminOpenLockPasswordPage extends StatefulWidget { const AdminOpenLockPasswordPage({Key? key}) : super(key: key); @override - State createState() => _AdminOpenLockPasswordPageState(); + State createState() => + _AdminOpenLockPasswordPageState(); } -class _AdminOpenLockPasswordPageState extends State with RouteAware { - final AdminOpenLockPasswordLogic logic = Get.put(AdminOpenLockPasswordLogic()); - final AdminOpenLockPasswordState state = Get.find().state; +class _AdminOpenLockPasswordPageState extends State + with RouteAware { + final AdminOpenLockPasswordLogic logic = + Get.put(AdminOpenLockPasswordLogic()); + final AdminOpenLockPasswordState state = + Get.find().state; @override Widget build(BuildContext context) { @@ -95,20 +98,29 @@ class _AdminOpenLockPasswordPageState extends State w LengthLimitingTextInputFormatter(9), ], controller: state.changePwdController, - sureClick: () { - if(state.changePwdController.text.length < 6){ + sureClick: () async { + if (state.changePwdController.text.length < 6) { logic.showToast('请输入6-9位管理员密码'.tr); return; } - if(state.changePwdController.text == state.lockBasicInfo.value.adminPwd!){ + if (state.changePwdController.text == + state.lockBasicInfo.value.adminPwd!) { logic.showToast('请输入新的管理员密码'.tr); return; } + // 检查密码是否重复 + final bool isPasswordAvailable = await logic.checkPassword(); + if (!isPasswordAvailable) { + state.changePwdController.clear(); + return; // 如果密码重复,直接返回 + } + Get.back(); logic.changeAdministratorPasswordCommand(); }, cancelClick: () { + state.changePwdController.clear(); Get.back(); }, ); diff --git a/lib/main/lockDetail/lockSet/basicInformation/basicInformation/basicInformation_page.dart b/lib/main/lockDetail/lockSet/basicInformation/basicInformation/basicInformation_page.dart index 58c5a9bb..69beabba 100755 --- a/lib/main/lockDetail/lockSet/basicInformation/basicInformation/basicInformation_page.dart +++ b/lib/main/lockDetail/lockSet/basicInformation/basicInformation/basicInformation_page.dart @@ -133,20 +133,20 @@ class _BasicInformationPageState extends State { } }); })), - Obx(() => Visibility( - visible: state.lockBasicInfo.value.isLockOwner == 1, - child: CommonItem( - leftTitel: '管理员开锁密码'.tr, - rightTitle: state.lockBasicInfo.value.adminPwd, - isHaveLine: true, - isHaveDirection: true, - action: () { - Get.toNamed(Routers.adminOpenLockPasswordPage, - arguments: { - 'lockSetInfoData': state.lockSetInfoData.value - }); - }), - )), + // Obx(() => Visibility( + // visible: state.lockBasicInfo.value.isLockOwner == 1, + // child: CommonItem( + // leftTitel: '管理员开锁密码'.tr, + // rightTitle: state.lockBasicInfo.value.adminPwd, + // isHaveLine: true, + // isHaveDirection: true, + // action: () { + // Get.toNamed(Routers.adminOpenLockPasswordPage, + // arguments: { + // 'lockSetInfoData': state.lockSetInfoData.value + // }); + // }), + // )), Obx(() => Visibility( visible: (state.lockBasicInfo.value.lockName ?? '') .contains('T9A'), diff --git a/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart b/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart index f11e9624..78f5a18a 100755 --- a/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart +++ b/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart @@ -99,7 +99,7 @@ class _LockSetPageState extends State CommonItem( leftTitel: '基本信息'.tr, rightTitle: '', - isHaveLine: false, + isHaveLine: state.lockBasicInfo.value.isLockOwner == 1, isHaveDirection: true, action: () { if (!state.lockSetInfoData.value.isValid()) { @@ -111,6 +111,25 @@ class _LockSetPageState extends State 'lockSetInfoData': state.lockSetInfoData.value }); }), + //管理员密码 需求变更:由基本信息子页面移至此处 + Obx(() => Visibility( + visible: state.lockBasicInfo.value.isLockOwner == 1, + child: CommonItem( + leftTitel: '管理员开锁密码'.tr, + rightTitle: state.lockBasicInfo.value.adminPwd, + isHaveLine: false, + isHaveDirection: true, + action: () { + if (!state.lockSetInfoData.value.isValid()) { + EasyLoading.showToast('网络访问失败,请检查网络是否正常'.tr); + return; + } + Get.toNamed(Routers.adminOpenLockPasswordPage, + arguments: { + 'lockSetInfoData': state.lockSetInfoData.value + }); + }), + )), SizedBox(height: 10.h), // 自动闭锁 Obx(() => Visibility( @@ -201,7 +220,7 @@ class _LockSetPageState extends State CommonItem( leftTitel: '基本信息'.tr, rightTitle: '', - isHaveLine: false, + isHaveLine: state.lockBasicInfo.value.isLockOwner == 1, isHaveDirection: true, action: () { Get.toNamed(Routers.basicInformationPage, @@ -209,6 +228,25 @@ class _LockSetPageState extends State 'lockSetInfoData': state.lockSetInfoData.value }); }), + //管理员密码 需求变更:由基本信息子页面移至此处 + Obx(() => Visibility( + visible: state.lockBasicInfo.value.isLockOwner == 1, + child: CommonItem( + leftTitel: '管理员开锁密码'.tr, + rightTitle: state.lockBasicInfo.value.adminPwd, + isHaveLine: true, + isHaveDirection: true, + action: () { + if (!state.lockSetInfoData.value.isValid()) { + EasyLoading.showToast('网络访问失败,请检查网络是否正常'.tr); + return; + } + Get.toNamed(Routers.adminOpenLockPasswordPage, + arguments: { + 'lockSetInfoData': state.lockSetInfoData.value + }); + }), + )), SizedBox(height: 10.h), //by DaisyWu 田总:移至锁详情配件区 // 门磁 diff --git a/lib/network/api.dart b/lib/network/api.dart index db9ccf33..8f80e752 100755 --- a/lib/network/api.dart +++ b/lib/network/api.dart @@ -49,6 +49,8 @@ abstract class Api { final String passwordKeyAddURL = '/keyboardPwd/add'; //自定义密码 final String passwordKeyCheckKeyboardpwdNameURL = '/keyboardPwd/checkKeyboardpwdName'; //自定义密码校验密码跟名字是否重复 + final String adminPwdCheckKeyboardPwdURL = + '/keyboardPwd/checkKeyboardPwd'; //检查密码是否重复包括管理员密码 final String updatePasswordKeyURL = '/keyboardPwd/update'; //修改密码详情 final String updatePWDNumberURL = '/keyboardPwd/updatePwdUserNo'; //更新锁用户需要 final String clearOperationRecordURL = '/lockRecords/clear'; //清空操作记录 diff --git a/lib/network/api_provider.dart b/lib/network/api_provider.dart index 4cad97cc..aa841e1a 100755 --- a/lib/network/api_provider.dart +++ b/lib/network/api_provider.dart @@ -562,6 +562,14 @@ class ApiProvider extends BaseProvider { 'keyboardPwd': keyboardPwd, })); + //检查密码是否重复包括管理员密码 + Future checkAdminPwd(int lockId, String keyboardPwd) => post( + adminPwdCheckKeyboardPwdURL.toUrl, + jsonEncode({ + 'lockId': lockId, + 'keyboardPwd': keyboardPwd, + })); + Future updateKeyboardPwd( int lockId, String keyboardPwdId, diff --git a/lib/network/api_repository.dart b/lib/network/api_repository.dart index b5e09c33..36ac710b 100755 --- a/lib/network/api_repository.dart +++ b/lib/network/api_repository.dart @@ -579,6 +579,13 @@ class ApiRepository { return PasswordKeyEntity.fromJson(res.body); } + //检查密码是否重复包括管理员密码 + Future checkAdminPwd( + {required int lockId, required String adminPwd}) async { + final res = await apiProvider.checkAdminPwd(lockId, adminPwd); + return PasswordKeyEntity.fromJson(res.body); + } + //修改密码 Future updatePasswordKey( {required int lockId, diff --git a/tag_generator.sh b/tag_generator.sh index 27e39182..f148c1e1 100755 --- a/tag_generator.sh +++ b/tag_generator.sh @@ -10,17 +10,21 @@ echo "PRIVATE-TOKEN: $TOKEN $URL/projects/$PROJECT_ID/repository/tags" tags_json=$(curl -H "Content-Type: application/json" -H "PRIVATE-TOKEN: $TOKEN" "$URL/projects/$PROJECT_ID/repository/tags") #echo "tags_json:$tags_json\n" tags=$(echo "$tags_json" | jq -r '.[].name') + +# 只保留不带 _sky 后缀的 tag 作为最新 tag +plain_tags=$(echo "$tags" | grep -v '_sky$') +latest_tag=$(echo "$plain_tags" | head -n 1) + tags_length=$(echo "$tags_json" | jq -r 'length') if [ "$tags_length" -lt 1 ]; then next_tag="v1.0.0" else - newest_tag=$(echo "$tags" | head -n 1) - IFS='.' read -r major minor patch <<< "$newest_tag" + IFS='.' read -r major minor patch <<< "$latest_tag" major="${major#v}" compare_json="" if [[ "$1" == "generate_tag" ]];then - echo "generate_tag:$newest_tag-to-master\n" - compare_json=$(curl -s --header "PRIVATE-TOKEN: $TOKEN" "$URL/projects/$PROJECT_ID/repository/compare?from=$newest_tag&to=master") + echo "generate_tag:$latest_tag-to-master\n" + compare_json=$(curl -s --header "PRIVATE-TOKEN: $TOKEN" "$URL/projects/$PROJECT_ID/repository/compare?from=$latest_tag&to=master") elif [[ "$1" == "generate_version" ]]; then echo "generate_version:master-to-$CI_COMMIT_BRANCH\n" compare_json=$(curl -s --header "PRIVATE-TOKEN: $TOKEN" "$URL/projects/$PROJECT_ID/repository/compare?from=master&to=$CI_COMMIT_BRANCH") @@ -45,9 +49,9 @@ else done < <(echo "$compare_json" | jq -c '.commits[] | {id: .id, message: .message}') next_tag="v$major.$new_minor.$new_patch" fi -echo "New Tag:$newest_tag;New version: $next_tag;command: $1" +echo "New Tag:$latest_tag;New version: $next_tag;command: $1" if [[ "$1" == "generate_tag" ]];then - if [ "$next_tag" == "$newest_tag" ]; then + if [ "$next_tag" == "$latest_tag" ]; then echo "no change from master,skip to generate tag" exit 0 fi