少湖说 | 科技自媒体

互联网,科技,数码,鸿蒙

Flutter 开始支持App扩展

Flutter从3.16开始,支持在应用扩展中使用Flutter,具体操作可以参考官方文档。

注意事项

配置

  1. App extension 与原App是两个独立的项目/进程,无法直接相互调用,代码也不共享

  2. 如果需要共享资源,如存储等,需要配置 App Group,否则不需要

  3. 开发过程中使用的是debug/profile版的Flutter.xcframework,发布时需要手动更改导入的库,改为导入release版的Flutter.xcframework.
    profile/release版通过目录 /bin/cache/artifacts/engine/ios/extension_safe 逐级向上查找,可以找到profile或release相关字样的目录

  4. 不同类型的extension内存有限制,模拟器中没有限制。Flutter需要比较多的内存,所以文档中指出,只能在“内存限制不低于100M”的扩展类型中使用Flutter,例如 share extensions
    限制120M,可以使用Flutter;keyboard extension 内存限制为40M,故不可以使用Flutter。

参考资料

  1. https://flutter.cn/docs/platform-integration/ios/app-extensions

  2. [尝试Flutter开发custom keyboard](https://github.com/zacksleo/flutter-ios-custom-keyboard-extension) 仅能在模拟器运行,真机因内存限制无法运行

2008年,中本聪首次提出了比特币的设想,这打开了去中心化的大门。

比特币白皮书清晰的描述了去中心化支付的解决方案,并分别从以下几个方面阐述了他的理念:

一、由转账双方点对点的通讯,而不通过中心化的第三方,这彻底了解决了信任危机。通过用户签名,保证了信息的所有权,其他人无法更改。

  1. 互联网经过几十年的飞速发展,我们所使用的服务都倾向于集中化发展。这种中性化在给我们带来便利的同时,也带来了巨大的信任危机,这些巨头在尽其所能的采集用户数据和行为,而用户则表现出越来越抗拒,不信任。用户信息泄露,大数据杀熟,用户隐私被侵犯… 种种问题层出不穷,用户与大企业也在某种程度上站在了对立面。要解决这些问题,单单依靠反垄断或者政府司法机关的强力措施,是远不够的。
  2. 所以,我们需要一种从根本上,保证用户可以亲自掌控自己的数据,控制它如何被合理的使用、读取甚至被销毁的无上权利。
  3. 区块链去中心化和签名的思想,给了普通人这样的权利,我们不再依赖巨头也可以使用互联网服务,而这些服务由区块链上的节点提供,由每一个人提供。

二、作恶的代价

  1. 在区块链中,作恶的代价是巨大的。在工作量证明中,任何破坏者只能让 CPU白白浪费算力,而失去了获得奖励的机会,这会使得他们支付巨额的电费账单。
  2. 在共识机制中,大家总会承认最长的链,而忽略较短的链。破坏者如果想要篡改账本,需要进行大量的哈希计算,以求得符合条件的 nonce值,在这个漫长过程中,新的合法区块早已产生,最终他篡改的内容会被大家拒绝。
  3. 与其浪费时间篡改,不如按游戏规则挖矿,这样反而带来可观的收益。而这正是比特币先进独到的地方。

三、双重支付

  1. 比特币通过回溯未花费交易(UTXO)来验证合法性,保证用户花费的财产拥有合法的依据
  2. 同时,在等到第6个区块后再进行链的确认,双重支付的概率也降到了最低

四、互联网向左,区块链向右

  1. 作为区块链的开山鼻祖,比特币未我们带来了一个崭新的世界。一个点对点的世界, Web 3.0的世界。这个世界更加平等和民主,彼此的联系更加紧密,社区自治理念更加深入人心。
  2. 区块链使得去中心化的理念更加深入人心,互联网过度中化的治理方式也得以矫正,我们迎来一个更加美好的未来…

参考资料

问题缘由

在微信小程序开发中,先要在本地使用微信开发者工具进行调试,如果需要在线测试,则需要将编译好的代码上传。
目前,只能通过微信开发者工具手动点击上传,而该过程无法与持续集成/持续部署结合在一起,本文就是为了解决能够实现自动化和持续部署的难题

实现原理

微信 miniprogram-ci 库,提供了命令行调用方式,其中就包括上传的命令,我们可以通过脚本实现自动化集成

步骤

安装并配置 GitLab Runner

这部分文档在 Install GitLab Runner on macOS

安装

首先需要在本机上安装 GitLab Runner, 由于微信开发者工具只提供了 mac 和 windows 版本,所以目前只能在这两种系统上实现持续集成,本文讲述在 mac 的具体实现, windows 上的实现与此类似,只是相关命令和路径需要做些变更

注册 GitLab Runner

1
2
gitlab-runner register

编写 .gitlab-ci.yml

在此之前,你需要对GitLab-CI有一定的掌握,这部分资料参考下方的相关文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
image: node:alpine

before_script:
- export APP_ENV=testing
- yarn config set registry 'https://registry.npm.taobao.org'
stages:
- build
- deploy

variables:
NPM_CONFIG_CACHE: "/cache/npm"
YARN_CACHE_FOLDER: "/cache/yarn"
DOCKER_DRIVER: overlay2
build-package:
stage: build
dependencies: []
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- node_modules
script:
- if [ ! -d "node_modules" ]; then
- yarn install --cache-folder /cache/yarn
- fi
- yarn build
- cp deploy/project.config.json ./dist/project.config.json
artifacts:
name: "wxpkg-dlkhgl-$CI_COMMIT_TAG"
untracked: false
paths:
- dist
only:
- tags
tags:
- docker
release:
stage: deploy
before_script: []
dependencies:
- build-package
variables:
GIT_STRATEGY: none
script:
- yarn global add miniprogram-ci
- if [[ -z "$CI_COMMIT_TAG" ]];then
#- CI_COMMIT_TAG=$CI_COMMIT_REF_NAME
- CI_COMMIT_TAG="测试版:$(date '+%Y-%m-%d')"
- fi
- COMMIT_MESSAGE=`cat ./dist/release.txt`
- rm -f ./dist/release.txt
# 将单行格式转为多行格式
- LF=$'\\\x0A'
- echo $UPLOAD_PRIVATE_KEY | sed -e "s/-----BEGIN RSA PRIVATE KEY-----/&${LF}/" -e "s/-----END RSA PRIVATE KEY-----/${LF}&${LF}/" | sed -e "s/[^[:blank:]]\{64\}/&${LF}/g" > private.key
- miniprogram-ci upload --project-path=$PWD/dist --appid=$APP_ID --upload-version=$CI_COMMIT_TAG --private-key-path=$PWD/private.key --upload-description="$COMMIT_MESSAGE"
environment:
name: production
url: https://mp.weixin.qq.com/wxamp/wacodepage/getcodepage
only:
- tags
- master

注意,在设置-CI/CD-变量中,需要添加两个变量:

APP_ID 小程序 appid

UPLOAD_PRIVATE_KEY 小程序代码上传密钥, 需要在小程序后台获取

由于配置的密钥,在运行时过去到是单行文本形式,需要转换为多行原始格式,并输出到文件,以便上传使用

于是使用一下命令- LF=$'\\\x0A' - echo $UPLOAD_PRIVATE_KEY | sed -e "s/-----BEGIN RSA PRIVATE KEY-----/&${LF}/" -e "s/-----END RSA PRIVATE KEY-----/${LF}&${LF}/" | sed -e "s/[^[:blank:]]\{64\}/&${LF}/g" > private.key

相关文档

概述

在之前的文章Golang持续集成与自动化测试和部署
简要介绍了如何快速搭建Api 微服务, 并进行DevOps集成。本文将进一步深入介绍,如果使用Gin实现Restful规范并进行自动化测试。

Restful

RESTful接口规范一文中,介绍了 Restful 及其规范。

Gin 是一个性能优异的微服务框架,对于Restful有着良好的支持。因为基于此实现Restful并不困难。

Restful 中的核心在于对于资源的定义和使用,和对 HTTP 动词(GET, PUT, DELETE, POST, DELETE, OPTIONS)的充分使用,通过 HTTP 动词,免去了繁冗的接口方法定义。
例如,传统方式如果实现删除文章功能,则需要实现一个类似 post/delete?id=6 的方法, 如果使用 Restful, 则使用 DELETE post/1 形式,其中 DELETE是动词,代表删除操作,
post/1 代表单个文章资源。

Restful 中的另一大规法是对于状态码的充分使用,不再使用响应内容表明状态。如200代表成功,404代表资源不存在,204代表删除成功,201代表创建成功。
有了状态码,响应内容中不再需要使用单独的字段来标记结果的状态(如 {result: “success”} )。

这样的一大好处是实现方式更规范、统一和直观。只要知道资源定义(posts), 就可以猜测出该如何进行增删改查的操作,不再需要知道方法名。
如此一来,可以很轻松的封装出一套同意的方法和操作。

路由定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

package router

import (
"os"
"gitlab.com/zacksleo/project/controllers"
"github.com/gin-gonic/gin"
)

api.GET("/customers", controllers.GetCustomers)
api.GET("/customers/:id", controllers.GetCustomer)
api.POST("/customers", controllers.CreateCustomer)
api.PUT("/customers/:id", controllers.UpdateCustomer)
api.DELETE("/customers/:id", controllers.DeleteCustomer)

下面一个较为完整的案例 controllers/customer.go

自动化测试

当实现了接口发后,为了验证接口运行正常,确保接口可靠和可用性,建议你对接口进行HTTP测试。
这里使用 httpexpect 对api进行测试,该库对响应的数据类型和结构验证有着较好的支持。

概述

前面分别介绍了 Golang 持续集成与自动化测试和部署使用Gin开发Restful接口并进行自动化测试
那么Restful接口搭建好了以后,如何进行接口授权验证,本文将讲述这些内容。

go-oauth2

Oauth2 是一种验证授权机制,通过下发令牌,来实现接口的认证。

go-oauth2

路由

1
2
3
4
auth := r.Group("/api/v1/oauth2")
{
auth.POST("/tokens", controllers.CreateToken)
}

控制器实现

获取Token的测试

这里为了方便在其他接口测试案例中调用获取token的方法,特意进行了拆分

其他接口的测试

概述

Golang是一门性能优异的静态类型语言,但因其奇快的编译速度,结合DevOps, 使得它也非常适合快速开发和迭代。

本文讲述如何使用Golang, 进行持续集成与自动化测试和部署。主要使用了以下相关技术:

  • dep: 进行包的依赖管理
  • gin: 搭建 api 服务
  • gorm:ORM, 数据CRUD
  • mysql: 存储数据
  • testfixtures: 测试夹具,在自动化测试时,自动向数据库填充用于测试的数据
  • httpexpect: HTTP 测试包,用于API测试
  • GoDotEnv: 环境变量处理
  • go test: 使用test命令进行单元测试, 基准测试和 HTTP 测试
  • GitLabCI: DevOps 工具
  • golint: Golang 静态检查工具
  • migrate: 数据库迁移工具
  • Docker: 使用 zacksleo/golang 镜像, 该镜像默认安装了 curl,git,build-base,dep 和 golint
  • db2struct: 将数据库表结构一键生成为 struct(gorm的model)
  • apig: 基于 gorm 和 gin 一键生成 CRUD API

开发流程

  • 使用 apig 脚手架工具初始化项目结构和目录
  • 使用 dep 安装相关依赖
  • 使用 migrate 编写数据库迁移方法,并执行迁移创建数据表
  • 使用 db2struct 生成 models
  • 使用 apig 生成 crud 代码
  • 使用 httpexpect 编写 api 测试代码,并通过 testfixtures 实现数据的自动填充
  • 编写 GitLabCI 脚本进行持续集成

在上述过程中,如需连接数据库时,可通过 GoDotEnv 来实现环境变量的使用

相关CI脚本

集成测试

注意事项

在测试中,如果需要区分单元测试和集成测试,可以使用 build tags 实现,如在文件头部中添加 // +build integration, 运行测试使用 - go test -tags=integration $(go list ./tests/... | grep -v /vendor/) -v 可以只执行集成测试

参考文档

REST接口文档遵循 API Blueprint 编码规范

中文说明需要遵循 中文文案排版指北

按照该格式书写的文档, 可以被 GitLab 及 Gollum 完美解析

主要内容

  1. 使用 Markdown 编写
  2. 请求和响应主体, 使用8个字符缩进
  3. 不使用` 包裹json代码

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

## 用户密码 [/users/password]

### 重置密码 [PATCH]


+ Attributes
+ phone (number, required) - 手机号码
+ password (string, required) - 密码
+ verify_code (string, required) - 短信验证码

+ Header
+ Authorization Bearer: {clientToken} - 访问令牌, 通过客户端模式获得
+ Content-Type: application/json

+ Request (application/json)

{
"phone":"17083300514",
"password":"pa66w0rd",
"verify_code":"312338"
}

+ Response 201 (application/json)

{
"phone":"17083300514",
"password":"pa66w0rd",
"verify_code":"312338"
}

+ Response 422 (application/json)

[
{
"field": "verify_code",
"message": "短信验证码错误"
},
{
"field": "phone",
"message": "手机号不存在"
},
{
"field": "password",
"message": "密码至少包含一位数字"
}
]



## 扫描二维码 [/qrcode-tokens/{ticket}]

### 更新状态 [PUT]

+ Attributes
+ action (string, required) - 动作, 值为SCANNED或LOGGED_IN
+ ticket (string, required) - ticket 二维码中链接中后面的字符串

+ Header
+ Authorization: Bearer {clientToken}
+ Content-Type: application/json

+ Request (application/json)

{
"action":"SCANNED"
}

+ Response 201 (application/json)

{
"action":"SCANNED"
}


参考链接

流程概要

持续集成和持续交付是 DevOps 最核心的两个部分。

持续集成通过即时将最新的代码,集成到主干分支,并进行相关的测试(单元测试、集成测试等)和静态检查(代码格式,代码质量等),以期提早发现问题。

持续交付,在持续集成完成之后,即时生成生产环境可用的产物(如二进制文件、包、或者 Docker镜像),并准备随时部署,如果伴随着部署过程,则称为持续部署。

开发流程

  • 系统分析与设计:需求分析,架构设计,数据库设计等

  • 相关文档编写, 文档应与代码仓库一起

  • 系统开发

    • 持续集成 (gitlab-ci)
    • 格式检查和静态检查 (vscode, linter)
    • 数据库迁移 (migration)
    • 使用 Docker 搭建开发、测试和生产环境,docker-compose
    • Git开发流程 (git cz, master, branch )
    • 自动化测试:单元测试,HTTP测试(api测试),功能测试,基准测试 (ab)
    • 自动化部署(主备)(ngixn back)
    • 更新日志 (angular changelog)
    • crontab supervisord
  • 测试与验收

参考文档

  1. 删除旧版
1
sudo yum remove docker docker-common  docker-selinux  docker-engine
  1. 安装库
1
sudo yum install -y yum-utils  device-mapper-persistent-data  lvm2
  1. 配置stable repo
1
sudo yum-config-manager --add-repo  https://download.docker.com/linux/centos/docker-ce.repo
  1. 安装
1
sudo yum install docker-ce
  1. 启动
1
sudo systemctl start docker
  1. 开机启动
1
sudo systemctl enable docker

7 . hello world

1
sudo docker run hello-world

简述

本文讲述了如何使用 GitLab-CI 对 ReactNative 项目,自动打包和部署,由于安卓环境更易于在Docker上搭建环境,所以先实现了安卓应用的打包和部署。

环境依赖

  1. Docker环境使用 registry.gitlab.com/wldhx/gitlab-ci-react-native-android:master 镜像, 该镜像包含了 ReactNative 打包安卓应用所需要的所有依赖
  2. 注册一个 fir.im 账号
  3. Python3。 部署应用采用的 fir.im 平台发布,因此实现了一段用于自动上传 apk 的 Python 脚本
  4. Node。 用于安卓依赖和格式化检查

编写GitLab-CI 脚本

$ cat .gitlab-ci.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
image: node

stages:
- test
- build
- deploy

cache:
key: ${CI_PROJECT_ID}
paths:
- android/.gradle/

variables:
NPM_CONFIG_CACHE: "/cache/npm"
YARN_CACHE_FOLDER: "/cache/yarn"
DOCKER_DRIVER: overlay2
lint:
stage: test
dependencies: []
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- node_modules
script:
- if [ ! -d "node_modules" ]; then
- yarn install --cache-folder /cache/yarn
- fi
- yarn run lint
except:
- master
- tags
tags:
- docker

build-package:
stage: test
dependencies: []
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- node_modules
script:
- yarn install
artifacts:
name: "node_modules"
untracked: false
expire_in: 60 mins
paths:
- node_modules
only:
- master
- tags
tags:
- docker

build-android:
image: registry.gitlab.com/wldhx/gitlab-ci-react-native-android:master
stage: build
dependencies:
- build-package
script:
- cd android && ./gradlew assembleRelease --no-daemon
artifacts:
expire_in: 7 days
paths:
- android/app/build/outputs/apk/
only:
- master
- tags

release-android:
image: python:3
stage: deploy
cache: {}
dependencies:
- build-android
script:
- pip install -r requirements.txt
- if [[ -z "$CI_COMMIT_TAG" ]];then
- echo $(git log -3 --pretty=%s | tail -1) > ./release.txt
- else
- echo $(git tag -l --format='%(contents)' $CI_COMMIT_TAG) > ./release.txt
- fi
- python ./upload.py
only:
- master
- tags

该脚本分为三个阶段,第一阶段进行格式化检查,和安装 npm 依赖,第二阶段用于构建 apk, 第三阶段将apk上传到 fir.im 平台上,
其中 release.txt 用于存储最近的更新日志,便于在上传 apk 时,加上对应信息。

requirement.txt 文件中则使用了request库:

$ cat requirement.txt

1
requests

upload.py 文件则调用fir.im的上传API, 则 apk 上传到改平台,需要注意的时,api_token 需要在 fir.im 平台的右上角个人信息处获取

$ cat upload.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

import requests
import os


def file_get_contents(filename):
with open(filename) as f:
return f.read()


r = requests.post(
'https://api.fir.im/apps',
data={'type': 'android',
'bundle_id': 'com.company.package',
'api_token': '个人的API TOKEN'
}
)

data = r.json()

if os.environ.get('CI_COMMIT_TAG') is None:
tag = 'latest'
else:
tag = os.environ['CI_COMMIT_TAG']

changelog = file_get_contents('./release.txt')

files = {'file': open(
'./android/app/build/outputs/apk/release/app-release.apk', 'rb')}
r2 = requests.post(
data['cert']['binary']['upload_url'],
data={
'key': data['cert']['binary']['key'],
'token': data['cert']['binary']['token'],
'x:name': '应用名称',
'x:version': tag,
'x:build': 'com.company.package',
'x:changelog': changelog
},
files=files
)

print(r2.json())

参考内容

0%