分类目录归档:技术

如何找出小程序项目中体积最大的文件?

如何找出小程序项目中体积最大的文件?

为什么要找到小程序项目中体积最大的文件?

微信小程序由于有「用完即走」的愿景,在小程序的大小上做了一些限制,单个小程序的大小需要在 2M 以内,如果小程序大于2M,则需要通过分包来实现。

在不使用分包的情况下,想要确保小程序的大小符合要求,就需要对项目中的文件进行优化。通过找到大文件,对项目的大文件进行优化是一个好办法。

如何找到项目中的大文件?

在 macOS 系统中,你可以借助命令行工具,来快速找到项目中的大文件。

你可以打开命令行,输入以下命令,获取到项目中最大的 10 个文件。

find . -type f -not -path '**/.git/**'  -exec du -h {} + | sort -r -h | head -n 10

执行效果如下

命令行执行效果

代码解读

上面这行命令采用了 Linux 下的通道来进行数据的传递,可以分为以下三个子命令

  1. find . -type f -not -path '**/.git/**' -exec du -h {} + 找到当前目录下的所有文件,并执行 du -h 获取到文件尺寸
  2. sort -r -h 对输入的内容,降序排列
  3. head -n 10 提取前 10 条数据,进行展示

通过对于上述的三个子命令的理解,你可以根据自己的实际需求进行调整,适配你自己的项目情况。

平台型 Serverless 产品的必杀技:免鉴权调用API

平台型 Serverless 产品的必杀技:免鉴权调用API

我在2019年的文章中(别找了,被我删了)曾经介绍过,国内的 Serverless 产品可以分为两个大类:

  1. 大公司产品:包括腾讯云云开发、字节跳动微服务、阿里云的云开发产品、Google 的 Firebase
  2. 小公司产品:比如 LeanCloud、Bmob 等等

而前者的最佳发展路线我也曾经提到过,大公司的 Serverless 想要赢得时间的最佳方案,是通过内部资源的整合,将开发者彻底绑架在自己的平台战车之上

而这样的方案,现在已经在各家的方案中有所体现,比如腾讯云云开发和微信合作推出的「小程序·云开发」产品中的「云调用」能力、轻服务推出的免鉴权调用小程序 API 的能力。

在微信小程序中,你可以通过 cloud.openapi.templateMessage.send 来实现免鉴权调用公众号的模板消息 API ,将过去数百行的代码简化为一行代码实现,可以有效的提升开发者的开发效率。

如今,轻服务提供了类似的功能,你可以通过 inspirecloud.openapi.tt.sendTemplateMessage实现免鉴权调用推送模板消息。

我一直觉得,Serverless 这样的产品,是为小团队、创意者而生的。而这些人的特点是,对于技术的深度可能不那么在意,毕竟他们关注的是我要实现一个创意,解决一个问题,至于 Scale 的问题,是在后续才需要注意的。而对于大公司的产品来说,想要干掉小公司的产品,一个利器就是免鉴权调用平台 API

免鉴权调用平台 API 对于开发者而言是一杯毒酒,不喝,你可能会被竞争对手超越,最终失去用户而死;喝了,你就会因为 Serverless 这样的产品模式,最终被绑死在战车之上。随之而来的,便是伴随着产品用户规模的不断提升的,是这些 Serverless 基础设施的成本就会逐渐超过你使用传统的服务器模式的成本。

但回过头来说,你真的能拒绝这样的平台优势么?很难,即使作为我这样的前后端一把梭的开发者,也很难抵挡 Serverless 产品前期的低成本和快速迭代,对于大量无法独立完成大型后端开发的工程师来说,这是一个必须喝下去的毒酒。

希望你在选择技术方案的同时,不给自己挖坑。

用 Docker 调试 Nginx

用 Docker 调试 Nginx

容器技术被广泛应用在各种场景,在实际的应用过程中,我们也可以根据自己的需要,进行各种配置。我最近因为在调试 Nginx ,因此,也使用 Docker 来调试 Nginx。

Requirements

  • 已经安装 Docker
  • 安装了 docker-compose

实现思路

docker-compose 可以帮助我们把一些 Docker 启动的配置给简化,通过编写配置文件,简化启动容器的命令。

具体操作

创建一个项目,并在项目的根目录中放置 docker-compose.yml 以及 nginx.conf。其中,nginx.conf 是你需要测试的 nginx 文件,docker-compose.yml 则是用来记录你的 Docker 容器启动配置。

文件结构

启动容器并测试效果

将你需要测试的配置文件内容放置在 nginx.conf 中,并在dockcer-compose.yml 中添加如下内容

web:
  image: nginx
  ports:
    - "8080:80"
  volumes:
    - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
  command: [nginx-debug, '-g', 'daemon off;']

随后,执行 docker-compose up 就可以启动容器,并展示出容器的运行效果

执行效果

此时,你就可以访问 localhost:8080 来查看你自己的配置了。

平台型 Serverless 如何赋能中小开发者?

平台型 Serverless 如何赋能中小开发者?

1 月 7 日的推文中,我有提到,「大公司的 Serverless 想要赢得时间的最佳方案,是通过内部资源的整合,将开发者彻底绑架在自己的平台战车之上」,对于很多人来说,如何理解这种内部资源的整合和它带来的赋能开发者?今天就用小程序 · 云开发最近开放的一个新能力 —— 短信跳小程序为例来聊一聊。

短信跳小程序这个能力其实背后是另外三个能力的组合:

  1. 微信最近开放的 URL Scheme ,我已经在 1 月 9 日的推文中写到了。
  2. 云开发的网站托管能力
  3. 腾讯云自有的短信推送能力

云开发基于上述的两个能力,辅以一些很低的开发工作量,实现了短信跳小程序这个能力。

其中,云开发的网站托管能力和短信推送能力,便是平台型 Serverless 产品内部资源整合而来的。

同样是一个 Serverless 团队要做这样的功能,作为小公司的 Serverless 就会面临:

  1. 短信服务供应商的选择和沟通
  2. 短信签名的审核和提交
  3. 底层代码拉通

但大公司的平台内往往已经自有短信推送的渠道,平台型 Serverless 只需要将底层代码拉通,就可以完成产品能力的对接。

这一点相比于小团队的重新选择供应商等,流程和耗时要少的多,要踩的坑也少的多

平台型 Serverless 团队借助于这样的很多内部能力,就可以轻松实现各种各样的能力,让开发者可以以最低的成本在自己的业务系统中加入产品能力。实际发布给开发者使用的产品,也就变得更加的简单易用。

就短信触达用户而言,云开发将这个能力做到了极简,你需要做的仅仅是将模板代码上传到静态网站托管中,并部署一个配套的云函数,就可以用cloud.openapi.cloudbase.sendSms触达你的用户,剩下的无需你操心,就自动完成了相关的功能。

const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
  try {
    const result = await cloud.openapi.cloudbase.sendSms({
        env: 'online-12345678910',
        content: '发布了新的能力',
        path: '/index.html',
        phoneNumberList: [
          "+8612345678910"
        ]
      })
    return result
  } catch (err) {
    return err
  }
}

作为一个云开发的用户,我很期待有更多的同类型功能出现。

用 KOA 做 API Mock

用 KOA 做 API Mock

在测试一些服务的时候,会需要用到一些第三方 API, 但如果你在测试的时候需要调用这个 API 的同时,又不太关注这个 API 具体的返回值的时候(比如你要测一个功能,但这个功能依赖了一个第三方服务,这个服务的返回值并不是你所关注的)。你需要一个比较好用的 Mock 方案,来解决这个问题。

写一个 Mock 服务并不是很复杂,想要写好的话,又不是一个很简单的事情。因此,各种语言提供的一些轻框架就是一个不错的选择。比如说我比较常用的是 KOA。

Koa 是 Node.js 下的一个轻量级框架,你可以通过简单的几行代码,构建一个简单的 HTTP 服务。

比如说我测试的服务依赖了两个第三方服务,这个时候我就可以借助于 Koa 提供的基础能力,快速编写出两个 API,从而在本地构建一个高性能的 Mock 服务。

比如说,这是我的一个 Mock 服务的代码案例。

其中我只使用了一个中间件,在中间件中使用 startsWith 来匹配请求路径,从而接管该路径下的所有请求,实现针对同一个 API 的所有请求返回一个特定的结果。

const Koa = require('koa');
const app = new Koa();
// response
app.use(async ctx => {
    ctx.body = "ok"
    if(ctx.url.startsWith("/api/xxx/")){
        ctx.body = 'need return2';
    }
    if(ctx.url.startsWith("/v2/xxx/")){
        ctx.body = 'need return 1'
    }
});
app.listen(3000);

如果你希望这个方案进一步优化,则可以考虑在返回结果时,给出一组数据,使用 Random 来在每一次返回时给出一个随机值,确保返回的数据不是唯一的。

@action/checkout 如何抓取所有的历史记录

@action/checkout 如何抓取所有的历史记录

GitHub 的 Action Template 中默认带了一个 checkout 插件,这个插件可以实现将你的项目 Clone 到 CI 的运行环境中,从而执行各项操作。

为了提升速度,Github 在实际上实现的时候,默认会限制 depth=1,这就导致在 clone 的时候,仅 clone 一个 commit ,如果你需要依赖 git 进行操作,则需要更多的 commit 。

在具体的实现过程中,你需要做的仅仅是在配置 github action 中的 fetch-depth 选项,设置为你需要的 commit 数量。如果你需要的是所有的 commit, 则将该选项设置为 0。具体配置如下

- uses: actions/checkout@v2
        with:
          fetch-depth: 0

这样你就可以在 CI 中访问到所有的数据。

不过,这样你并不能直接执行诸如 git merge-base 这样的命令,因为你虽然将所有的数据抓取到了本地,但并没有建立相应的分支,则需要你先建立相应的分支,才能进行处理。

这个时候你可以在 action 当中新增一行,用来切换分支。

- run: git checkout x

这里的 x 是你需要操作的分支,不过你需要注意,切换过来以后,还需要重新切换回去,不然你的代码就版本不对了。

物联网设备如何链接到你的小程序?

物联网设备如何链接到你的小程序?

物联网项目在涉及到传统行业的数字化改造时,是一个非常常见的选择。通过对于传统的物联网设备进行改造,就可以和云计算设备链接起来,并辅以大数据设施,完成对于产业的优化和迭代。

而到了具体物联网设备和小程序开发时,主要有以下几种链接方式:

直接链接 · 蓝牙

蓝牙链接物联网设备算是最为常见的方式。小程序提供了蓝牙的接口,你只需要调用蓝牙接口,链接到物联网设备上,并读取数据,就可以实现和物联网设备的链接。

比如你可以使用小程序来链接小米手环(如果你知道他的蓝牙配置)。

直接链接 · NFC

小程序提供了 NFC 接口,有了 NFC 接口,你就可以调用机身自带的 NFC Reader 和 NFC Writer 对 NFC 卡片进行读取和写入,来完成和外部设备的交互。

对于 Android 用户来说,使用 NFC 交互可以快速完成近场通讯的数据同步问题。

直接链接 · WiFi

小程序提供了 WiFi 接口,如果你的物联网设备可以对外提供 WiFi 服务,则可以通过调用 WiFi 接口,连接到指定 WiFi,并通过标准的 HTTP 接口来获取数据。

间接链接 · WiFi

WiFi 除了可以用作直接链接,还可以用作间接链接,你可以让设备通过 WiFi 连接到互联网上,并通过 HTTP 协议与服务器进行沟通,从而与服务器之间进行交互,交互完成后,你就可以在小程序端直接读取数据了。

间接链接 · NBIoT

NBIoT 你可以简单理解为手机卡链接,只不过使用的是物联网卡和2G/3G模组。如果你的设备上链接了 NBIoT 设备,就可以直接通过 NBIoT 设备访问到你自己的服务器,并提交信息。

总结

物联网的链接方式随着基础设施的迭代,会有不断的更新。如果你知道有别的方法, 欢迎留言告诉我。

如何将 HTTP 请求中的 UA 转化为可读的 UA 信息

如何将 HTTP 请求中的 UA 转化为可读的 UA 信息

什么是 UA (User Agent)

UA 是 HTTP 在发送请求的时候,带上的请求方的各项基本信息。就我自己为例,我的 UA 是 Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.50

这个 UA 当中包含了大量的我的基本信息,包括我的电脑信息、我的操作系统、浏览器的版本等。

在具体的 UA 信息中,具体的结构为

User-Agent: <product> / <product-version> <comment>

如果有多组 UA 信息,则语法如下

User-Agent: Mozilla/5.0 (<system-information>) <platform> (<platform-details>) <extensions>

UA 能够帮助我们理解什么?

UA 可以提供设备的基本信息,比如浏览器、比如设备的版本号等,有了这些信息,我们就可以分析用户的使用习惯、使用场景等信息。

比如,当 UA 中有大量的访问是来自微信浏览器,那么你就可以考虑针对微信的浏览器提供一些优化,比如使用微信自带的第三方 SDK 等信息。

如何将 UA 转换为可读的 UA 信息?

各种语言都有自己的 SDK 来实现从 UA 的 String 提取出操作系统的信息,一般来说,都是通过正则表达式来识别的,你可以在 Github 上找到你所使用的语言提供的 SDK。

不过,除了使用开源的 SDK 以外,你还可以考虑使用一些服务来进行解析,比如我今天发现的 User Stack

UserStack 是一个 UA 识别服务,如果你接入该服务,就无需自己维护 User Agent 的识别规则,由服务帮你维护,你只需要调用该 API 即可实现对设备的识别。

总结

UA 转换为人类可读的信息需要通过第三方 API 或开源的 SDK 来实现,你可以根据自己的需要来选择不同的方案。SDK 免费可用,但可能更新不够频繁,无法很好的兼容一些重要的设备。

如何修复 Beego 运行测试时的报错 undefined: web.Trace

如何修复 Beego 运行测试时的报错 undefined: web.Trace

Beego 近来已经更新了 2.X,因此,你现在可以使用更新版本的 Beego 来完成你的快速开发。

不过,beego 2.x 在使用 bee 工具初始化一个新的项目时,默认的项目中有一个 bug,当你在项目根目录下执行 go test ./... 时,会提示 tests 目录中有一个错误。而你并没有修改任何的源文件。

如果单独执行 tests 目录,就会报错如下内容

tests/default_test.go:28:2: undefined: web.Trace

你会发现这段代码指向了 default_test.go 中的这行代码。

beego.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String())

这段代码中的 Trace 找不到导致无法正常使用。

我通过一些搜索,找到了答案。在 GitHub 中,beego 的 repo 下有一个 issue 和一个 pull request 与这个 bug 相关。结论是这个方法已经被移动到 log 文件夹,因此需要调整一下输出的来源。

在代码中引入 "github.com/beego/beego/v2/core/logs" 并将 beego.Trace 替换为 logs.Trace ,即可解决这个问题。

修改后的代码如下:

package test
import (
    "net/http"
    "net/http/httptest"
    "testing"
    "runtime"
    "path/filepath"
    _ "backend-server/routers"
    "github.com/beego/beego/v2/core/logs"
    beego "github.com/beego/beego/v2/server/web"
    . "github.com/smartystreets/goconvey/convey"
)
func init() {
    _, file, _, _ := runtime.Caller(0)
    apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator))))
    beego.TestBeegoInit(apppath)
}
// TestBeego is a sample to run an endpoint test
func TestBeego(t *testing.T) {
    r, _ := http.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()
    beego.BeeApp.Handlers.ServeHTTP(w, r)
    logs.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String())
    Convey("Subject: Test Station Endpoint\n", t, func() {
            Convey("Status Code Should Be 200", func() {
                    So(w.Code, ShouldEqual, 200)
            })
            Convey("The Result Should Not Be Empty", func() {
                    So(w.Body.Len(), ShouldBeGreaterThan, 0)
            })
    })
}

参考阅读

如何使用 GitHub Action 自动部署 MKDocs

如何使用 GitHub Action 自动部署 MKDocs

MKDocs 是一个基于 Python 写成的文档生成工具,在实际的使用过程中,我们可以借助 GitHub Action 来实现自动部署到 GitHub Pages 。

操作流程

首先,你需要先在本地生成一个 MKDocs 项目

生成后, 你可以在项目的根目录创建 GitHub Action 所需目录。

mkdir -p .github/workflows/

并创建一个配置文件 publish_docs.yml

name: Publish docs via GitHub Pages
on:
  push:
    branches:
      - main
jobs:
  build:
    name: Deploy docs
    runs-on: ubuntu-latest
    steps:
      - name: Checkout main
        uses: actions/checkout@v2
      - name: Deploy docs
        uses: mhausenblas/mkdocs-deploy-gh-pages@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CONFIG_FILE: mkdocs.yml
          EXTRA_PACKAGES: build-base

创建完成后,将配置文件添加到版本控制中,并提交到 GitHub 上,就可以在你提交代码的时候,自动推送到 GitHub Pages 中。

需要注意的是,我使用的是 MKDocs 的 Material Design 主题,如果你使用的是其他主题(非自带的主题),则需要修改 EXTRA_PACKAGES 的配置。

代码解读

这个配置文件中主要是 on 和 steps 比较有学习的价值。

on:
  push:
    branches:
      - main

这段代码限制了,只有在 main 分支上,出现了 commit 的时候,才会触发这个 action 的执行,如果是其他的分支,则不会触发 action 的构建。

    steps:
      - name: Checkout main
        uses: actions/checkout@v2
      - name: Deploy docs
        uses: mhausenblas/mkdocs-deploy-gh-pages@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CONFIG_FILE: mkdocs.yml
          EXTRA_PACKAGES: build-base

这里的两个 step, 第一个是 clone 出所有的代码, 第二个 step 就是我使用一个已经做好的 action mhausenblas/mkdocs-deploy-gh-pages@master,这个 action 的作用就是将 mkdocs 的文档生成并部署到 GitHub Pages 中。

这个 action 自带了 material design 的主题,执行后,会构建并推送到 GitHub Pages。如果你使用的也是这个主题就可以直接按照我的样式进行配置。如果你使用的不是这个主题,则需要将你的主题配置在 EXTRA_PACKAGES,从而让 action 在执行的时候安装相应的 package。

总结

GitHub Action 的 Marketplace 中有大量的现成 Action 可以供你使用,借助现成的 Action ,你可以非常简单的完成自己的 CI/CD 流程,非常方便。

LCTT 从Travis CI迁移到 GitHub Action 实践

LCTT 从Travis CI迁移到 GitHub Action 实践

LCTT 的 CI 已经在 Travis CI 上运转了多年,一致保持着良好的使用体验。自 2019 年 Github 推出了自家的 CI 工具 Github Action 后,我们就在考虑将 CI 从 Travis-CI 迁移到 Github,以降低维护和沟通的成本,并借助于 GitHub Action Marketplace 实现更强的功能。

项目首页
项目首页

最近,因为 TravisCI 屡屡部署出错,而我们的账户因为使用的较多,已经超出了免费使用的限制,以此为契机,将 CI 从 Travis CI 迁移到 GitHub Action。

Travis CI 的提醒
Travis CI 的提醒

项目介绍

Translate Project 是 LCTT 翻译组的主要协作项目,几百位译者通过 GitHub 进行围绕开源、Linux、软件工程等领域的文章翻译,从 2013 年来,累计了大量的 Commit,致使项目下有非常多的文件。

Transalte Project 借助于 CI 帮助译者进行基本的文章格式进行检查;并定时执行命令,以进行所有的文章检查,对于超时未完成翻译的工作进行回收;对于文章的状态进行标记,生成相应的 Badge。

生成 badge
生成 badge

迁移思路

Travis CI 和 Github Action 在使用方面,其实总体差异不会太大,都是基于 YAML 文件格式来编写配置文件。不过,和 Travis CI 不同的是,Github Action 支持多个不同的配置文件,因此,你可以根据不同的场景,设定不同的配置文件,降低单个配置的文件的复杂度。

此外,由于我们的脚本中依赖了一些 Travis CI 的环境变量,也需要将其替换为 Github Action 中的相应环境变量,从而确保脚本可以运转。

改造实践

1. 分析之前的 CI 流程

我们在 TravisCI 上的 CI 配置文件如图

配置文件
配置文件

总体可以分为三块:

  1. 命令区:说明了安装阶段和执行阶段的操作有哪些
  2. 条件区:指定了这个配置文件在哪些条件下会生效
  3. 部署区:写明了构建产物如何进行部署

在命令区中,有预置的安装过程和后续的执行过程。在安装过程中,安装了一些依赖,并将当前的 pages 资源克隆到本地,以继承上一次构建生成的资料。

在条件区则指明了仅作用于 master 分支

在部署区便是将前面命令区的执行结果进行部署。

基本流程
基本流程

在实际的执行过程中,还会根据环境变量不同,决定是否要执行特定的命令,这部分在后续的改造过程中,就可以调整部署,拆分到不同的文件中。

构建流程
构建流程

2. 直译配置文件

在完成了基本的分析后,就可以建立新的 Action 配置文件了。由于基本的语法很类似,对于其中的不少内容可以进行直译。

比如,我们的配置文件在直译后,结果如下

直译后的结果
直译后的结果

直译的文件已经可以直接运行,不过,这里有很多不满足需要的地方,所以需要做一些修改。

3. 恢复 Travis CI 的环境变量

由于我们使用的 Badge 等生成脚本并非我所编写,所以在这一次的迁移中,并不打算对齐进行调整,以避免出现故障。而脚本中依赖了一些变量,需要将其重新设置出来。

Github Action 提供了一些方法,可以让你手动设置环境变量。你可以在你的构建的 step 中,加入如下代码,从而在构建环境中设定 TRAVIS_BRANCH 和 TRAVIS_EVENT_TYPE 环境变量,确保你可以使用这个环境变量。

 - name: Set ENV variables
        run: |
          echo "::set-env name=TRAVIS_BRANCH::${TRAVIS_BRANCH:-$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')}"
          echo "::set-env name=TRAVIS_EVENT_TYPE::$(if [ "schedule" == "$" ]; then echo "cron"; else echo "$"; fi)"

此外,由于 set-env 这个方法相对比较危险,你还需要在环境变量中,开启危险函数的执行。

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true

4. 拆分配置文件

Github Action 和 TravisCI 不同的一点是你可以将你的 CI文件拆分成多个文件,从而降低每一个单独的配置文件的复杂度。

根据前面对于流程的分析,可以将我们的 CI 流程拆分成三个部分:

  1. 生成 badge 文件,应当跟随每一次 commit 进行
  2. 生成 status 文件,执行时间较长,可以定期执行
  3. 根据 Pull Request 内容进行整理,做核验

则将我们的配置文件拆分成三个不同的文件

也得益于拆分开,则在 checker 中就可以免于安装一些必要的依赖,从而精简 CI 流程,提升 CI 的执行时间。

5. 测试 CI 的运行情况

在完成了配置文件的编写和拆分后,就可以进行本地的执行测试。Github Action 写完了,难免要执行一下,确保整个流程是正常的。

这个时候你可以安装工具(https://github.com/nektos/act),来在本地执行 action ,从而确认你的代码执行是正确的。

如果你是 macOS ,只需要执行 brew install act 就可以安装 act 工具,来完成 act 的安装。

安装完成 act ,就可以通过执行 act 命令来在本地执行 action ,比如,执行 act pull_request来触发 GitHub 的 Pull Request 事件

通过本地测试后,再将你的配置文件推送到 GitHub 上,进行生产环境的测试即可。

6. 移除 Travis-CI

通过上述的一些步骤,我们完成了从 Travis CI 到 GitHub Action 的迁移,此时,就可以移除项目根目录中的 .travis.yml 文件,彻底关闭 travis-ci。

7. 修改 GitHub 的 Branch 保护条件

为了确保修改符合标准,我们限制了新的 Pull Request 必须通过 CI 检查,才能合并进入 master,因此,也需要修改相应的分支保护规则,确保设定相应的保护。

一些注意事项

1. 环境变量不同

如果你依赖了环境变量,则需要进行相应的修改。或者可以像我一样,先通过 set-env 来让本地拥有临时的环境变量,后续再逐步进行迁移。

2. Action 运行依赖要注意安全

Action 执行过程中会有两个部分。action 本身流程依赖于 master,但执行过程中调用的脚本是根据 source 决定的,因此,从安全角度来看,你应当尽可能将所有的流程放在 Action 中,而不是放在你的源码目录中。

总结

通过对 TravisCI 的流程整理、代码修改等流程,我们将之前的 Travis-CI 迁移至速度更快、性能更好的 GitHub Action ,一方面可以优化我们的工作流,另一方面,也让我们的代码更加简洁明了。

对于还在使用 Travis CI 的项目来说,也可以考虑迁移到 GitHub Action 中,来优化自己的工作流。

参考阅读

  • https://mauricius.dev/run-and-debug-github-actions-locally/
  • https://jeffrafter.com/working-with-github-actions/
  • https://developer.okta.com/blog/2020/05/18/travis-ci-to-github-actions
拯救老旧 Discuz 的 Flash 上传

拯救老旧 Discuz 的 Flash 上传

Discuz 目前依旧是一个非常重要的建站程序,不少老的站点依旧会使用 Discuz 上运转。
由于 Discuz 是一个多年的程序,所以在系统的设计上,有很多地方充满了对老旧系统的兼容。

比如,Discuz 的上传使用的是 Flash 上传,但在 2021 年,Flash 已经彻底停止使用了,这个时候你就需要使用自己的方式来进行上传。

这里我以 Linux 中国进行的相关改造为例来介绍。

Linux 中国的文章发布系统就是基于 Discuz 实现的,编辑器层面使用的是 Discuz 提供的 TinyMCE,

Discuz 提供了一个基于 Flash 的上传组件,但在2021 年,现在这个 Flash 按钮已经完全显示不出来了。

上传组件

这个时候,你就需要自己实现一套上传系统。

由于 Linux 中国的很多底层实现都是基于 Discuz 进行的,因此,此次改造希望继续沿用 Discuz 的系统。

实现路线

在对 Discuz 默认的 Flash 插件进行抓包后,很轻松的就找到了 Discuz 的 Flash 上传组件的接口:/misc.php?mod=swfupload&action=swfupload&operation=album

这个接口接受 Form 表单作为参数,并在表单中接受一个 Hash 作为身份校验,用来判断请求的合法性。

因此,只需要在 TinyMCE 自带的上传功能中加入相应的 Hash 和表单提交的能力,就可以将 Discuz 实现的上传功能改为用 TinyMCE 的原生组件实现。

而 Hash 可以通过分析得出,其算法为 md5(substr(md5($_G['config']['security']['authkey']), 8).$_G['uid'])

因此,需要做的便是,在页面中注入 Hash ,并在 TinyMCE 中调用此值,实现上传功能即可。

样例代码

html/template/default/portal/portalcp_article.htm 文件顶部加入如下代码,从而在文章发布页面注入 hash

<!--{eval$swf_hash =  md5(substr(md5($_G['config']['security']['authkey']), 8).$_G['uid']); }--><script type="text/javascript">var FORMHASH = "{$swf_hash}"</script>

html/static/js/editor_tinymce.js 中的 tinyMCE 配置中添加如下配置项目,即可调用 HASH 来上传文件。从而实现文件的上传。

        images_upload_url: "/misc.php?mod=swfupload&action=swfupload&operation=album",
        images_upload_credentials: true,
        images_upload_handler:function(blobInfo, success, failure, progress) {
            var xhr, formData;
            xhr = new XMLHttpRequest();
            xhr.withCredentials = true;
            xhr.open('POST', '/misc.php?mod=swfupload&action=swfupload&operation=album');
            xhr.upload.onprogress = function(e) {
                progress(e.loaded / e.total * 100);
            };
            xhr.onload = function() {
                var json;
                if (xhr.status === 403) {
                    failure('HTTP Error: ' + xhr.status, {
                        remove: true
                    });
                    return;
                }
                if (xhr.status < 200 || xhr.status >= 300) {
                    failure('HTTP Error: ' + xhr.status);
                    return;
                }
                json = JSON.parse(xhr.responseText);
                if (!json || typeof json.bigimg != 'string') {
                    failure('Invalid JSON: ' + xhr.responseText);
                    return;
                }
                success(json.bigimg);
            };
            xhr.onerror = function() {
                failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
            };
            filenameArray = blobInfo.filename().split(".");
            formData = new FormData();
            formData.append('Filedata', blobInfo.blob(), blobInfo.filename());
            formData.append('Filename',blobInfo.filename() );
            formData.append('type','image');
            formData.append('Upload','Submit Query');
            formData.append('uid',discuz_uid);
            formData.append('filetype',"." +filenameArray[filenameArray.length-1]);
            formData.append('hash',FORMHASH);
            xhr.send(formData);
        },
        file_picker_callback: function(cb, value, meta) {
            var input = document.createElement('input');
            input.setAttribute('type', 'file');
            input.setAttribute('accept', 'image/*');
            input.onchange = function() {
                var file = this.files[0];
                var reader = new FileReader();
                reader.onload = function() {
                    var id = 'blobid' + (new Date()).getTime();
                    var blobCache = tinymce.activeEditor.editorUpload.blobCache;
                    var base64 = reader.result.split(',')[1];
                    var blobInfo = blobCache.create(id, file, base64);
                    blobCache.add(blobInfo);
                    cb(blobInfo.blobUri(), {
                        title: file.name
                    });
                };
                reader.readAsDataURL(file);
            };
            input.click();
        },

总结

老旧系统的升级改造不可怕,只要你用心,从业务的底层抽丝剥茧,就能找到要解决的问题。剩下,不过是代码层间的问题罢了。