欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由angel_郁发表于云+社区专栏

介绍

Buildbot是一个基于Python的持续集成系统,用于自动化软件构建,测试和发布过程。

在本教程中,我们将演示如何设置持续集成系统以自动测试对存储库的新更改。我们将使用一个简单的Node.js应用程序来演示测试过程和必要的配置。为了将我们的测试环境与Buildbot主机隔离,我们将创建一个Docker镜像作为Buildbot worker运行。然后,我们将配置Buildbot主服务器以观察GitHub存储库的更改,每次检测到新的更改时自动进行测试。

准备

要学习本教程,您需要:

  • 一个具有至少1 GB的RAM的Ubuntu16.04服务器,并包括一个可以使用sudo命令的非root 用户。

另外,在开始本教程之前需要完成以下内容:

  • 如何在Ubuntu上安装Buildbot

  • 安装Nginx

  • 在Ubuntu上使用SSL来保护Nginx

  • 给Buildbot加上SSL,使用Nginx做反向代理

  • 搭建Docker环境

在GitHub中使用fork示例存储库

在我们开始配置Buildbot之前,我们将看一下我们将用于本教程的示例存储库。

在您的Web浏览器中,访问我们将用于演示的GitHub上的hello hapi应用程序。这个应用程序是一个简单的“hello world”程序,带有一些单元和集成测试,用hapi编写的一个Node.js Web框架。

由于此示例用于演示各种持续集成系统,您可能会注意到一些文件用于为其他系统定义管道。对于Buildbot,我们将在服务器上而不是在存储库中定义构建步骤。

稍后,我们将在我们的存储库中为Buildbot设置webhook,以便更改将自动触发新测试。现在,我们需要创建自己的存储库分支。

单击屏幕右上角的Fork按钮:

GitHub fork 按钮

如果您是GitHub组织的成员,可能会询问您在哪里使用fork存储库:

哪里使用fork存储库

选择帐户或组织后,存储库的副本将添加到您的帐户中:

存储库的副本将添加到您的帐户

您将在Buildbot配置中使用fork的URL。现在我们有了一个存储库URL,我们可以开始配置Buildbot了。

为Buildbot设置Docker

我们将从设置Docker开始,以便Buildbot使用它来执行构建。首先,我们需要配置Docker和Buildbot之间的访问。之后,我们需要创建一个Docker镜像以用于我们的容器。

为Buildbot配置Docker访问

我们需要允许Buildbot和Docker在几个不同的级别进行通信。

首先,我们需要确保Buildbot进程可以访问Docker守护程序。我们可以通过将做到这一点buildbot用户的docker组:

$ sudo usermod -aG docker buildbot

下次重新启动Buildbot主服务器时,这个新组将可用于Buildbot,我们稍后会这样做。

我们还需要确保Buildbot知道如何与Docker通信。由于Buildbot是用Python编写的,它利用docker-py Python包而不是直接发出Docker命令。

您可以输入以下命令安装docker-py

$ sudo -H pip install docker-py

最后,我们需要打开从容器到主机系统和外部世界的网络访问。我们可以通过在防火墙中允许docker0的接口例外来实现此目的。

通过输入以下内容允许从docker0界面访问流量:

$ sudo ufw allow in on docker0

Buildbot和Docker现在应该能够有效地相互通信。

创建一个Docker镜像以用作Buildbot Worker

接下来,我们将创建一个Docker容器,用作Buildbot worker来运行我们的测试。Buildbot可以动态启动Docker容器以用作worker,但首先需要使用包含的一些Buildbot worker组件构建容器。

幸运的是,Buildbot项目提供了一个基本的Buildbot worker映像,该镜像已经配置了所有特定于Buildbot的需求。我们只需要将此镜像用作基础并安装我们项目所需的其他依赖项。

在我们的例子中,我们将使用的示例应用程序是Node.js应用程序,因此我们需要确保Node.js在镜像上可用。

要定义我们的镜像,请创建并打开Dockerfile在主目录中调用的文件:

$ nano ~/Dockerfile

在这个文件中,我们使用了基于Buildbot worker的镜像FROM buildbot/buildbot-worker:master。之后,我们可以切换到root用户安装Node.js,然后切换回buildbot用户运行实际命令:

〜/ Dockerfile

FROM buildbot/buildbot-worker:masterUSER root
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -
RUN apt-get install -y nodejs
USER buildbot

完成后保存并关闭文件。

一旦我们拥有了Dockerfile,我们就可以从中构建我们的镜像。我们将调用镜像npm-worker来明确我们安装的额外依赖项:

$ docker build -t npm-worker - < ~/Dockerfile

Docker将根据我们在其中列出的命令开始构建您的镜像Dockerfile。它将下拉基本镜像及其依赖层,安装Node.js,然后将生成的环境保存到一个名为npm-worker的镜像中。

配置Buildbot主服务器

现在我们有了Docker镜像,我们可以配置Buildbot主机来使用它。

因为我们正在定义一个全新的构建过程,并且因为到目前为止我们对主配置的定制很少,所以我们将从头开始配置。为避免丢失当前信息,我们将原始文件移动到备份文件:

$ sudo mv /home/buildbot/master/master.cfg /home/buildbot/master/master.cfg.bak

显示备份文件的配置,以便我们可以复制一些重要值以在新配置中使用:

$ sudo cat /home/buildbot/master/master.cfg.bak

我们要转移到新配置的重要部分是用户凭据和权限。在输出中查找c['www']['authz']c['www']['auth']开头的配置部分:

. . .
c['www']['authz'] = util.Authz(allowRules = [util.AnyEndpointMatcher(role="admins")],roleMatchers = [util.RolesFromUsername(roles=['admins'], usernames=['Sammy'])]
)
c['www']['auth'] = util.UserPasswordAuth({'Sammy': 'Password'})
. . .

将这些行复制并保存在某处,以便以后可以引用它们。我们将这些详细信息添加到我们的新Buildbot主配置中,以保留我们的用户和身份验证设置。

现在,创建一个新master.cfg文件,我们可以在其中重新定义Buildbot实例的行为:

$ sudo nano /home/buildbot/master/master.cfg

我们将在此文件中定义新的Buildbot主配置。

设置基本项目配置

Buildbot配置文件实际上是一个Python模块,它以牺牲一些复杂性为代价提供了极大的灵活性。

我们将从一些基本配置开始。将以下行粘贴到您的文件中:

/home/buildbot/master/master.cfg

# -*- python -*-
# ex: set filetype=python:
from buildbot.plugins import *c = BuildmasterConfig = {}# Basic config
c['buildbotNetUsageData'] = None
c['title'] = "Hello Hapi"
c['titleURL'] = "https://github.com/your_github_name/hello_hapi"
c['buildbotURL'] = "https://buildmaster_domain_name/"
c['protocols'] = {'pb': {'port': 9989}}

该文件的顶部包含一些注释,许多文本编辑器能够解释这些注释以正确应用语法高亮显示。然后,我们从buildbot.plugins包中导入所有内容,以便我们可以使用工具来构建配置。

Buildbot配置全部由名为BuildmasterConfig的字典定义,因此我们将此变量设置为空字典以启动。

我们创建一个名为c的速记变量,设置为同一个字,以减少整个文件中所需的输入量。

下面的配置中需要注意的一些事项:

  • buildbotNetUsageData设置为None。如果要向开发人员报告使用情况数据,请将其更改为字符串"basic"

  • titletitleURL反映项目的名称和GitHub的仓库。使用指向您自己的fork的链接。

  • buildbotURL设置为Buildbot主服务器的SSL安全域名。记得从https://开始并以尾部斜杠/结束。

  • 与我们的上一个配置不同,该protocol定义并不会和本地主机绑定。我们需要通过Docker桥接网络docker0允许来自Docker容器的连接。

配置Docker Worker

接下来,我们需要定义我们的Docker worker。Buildbot将根据需要使用Docker来配置工作人员。为此,它需要知道如何连接到Docker以及使用哪个映象。

将以下内容粘贴到文件的底部:

/home/buildbot/master/master.cfg

. . .# Workers
c['workers'] = []
c['workers'].append(worker.DockerLatentWorker("npm-docker-worker", None,docker_host='unix://var/run/docker.sock',image='npm-worker',masterFQDN='buildmaster_domain_name'))

c['workers'] =[]行演示了我们在完成配置时将使用的基本约定。我们将配置字典中的键设置为空列表。然后,我们将元素附加到列表以实现实际配置。这使我们可以在以后添加其他元素。

为了定义我们的worker,我们创建一个worker.DockerLatentWorker实例并将其追加到worker列表中。我们将此工作者命名为npm-docker-worker,以便稍后在配置中引用它。然后我们将docker_host设置为Docker的套接字位置,并提供我们创建的Docker镜像的名称(在我们的例子中为npm-worker)。我们将masterFQDN设置为Buildbot master的域名,以确保容器可以到达主服务器,而不管服务器的内部主机名设置如何。

配置调度程序

接下来,我们将定义一个调度程序。Buildbot使用调度程序根据从变更源或更改挂钩收到的更改来决定何时以及如何运行构建(稍后我们将配置更改挂钩)。

将以下配置粘贴到文件的底部:

/home/buildbot/master/master.cfg

. . .# Schedulers
c['schedulers'] = []
c['schedulers'].append(schedulers.SingleBranchScheduler(name="hello_hapi",change_filter=util.ChangeFilter(project='your_github_name/hello_hapi', branch='master'),treeStableTimer=3,builderNames=["npm"]))

我们使用相同的方法将配置附加到空列表中。在这种情况下,我们附加一个schedulers.SingleBranchScheduler实例。这允许我们在存储库中观察单个分支,并简化了配置。

我们将调度程序命名为“hello_hapi”以正确识别它。然后我们定义一个更改过滤器。来自不同来源的许多不同变更集可以交给调度程序。更改过滤器定义一组标准,用于确定此特定调度程序是否应处理相关更改。在我们的例子中,我们根据项目名称进行过滤,这将由GitHub webhook和我们希望观看的分支报告。

接下来,我们将treeStableTimer设置为3秒,该treeStableTimer确定等待其他更改的时间量。这有助于防止Buildbot为与密切相关的更改排队许多小型构建。最后,我们定义当更改符合我们的条件时应该使用的构建器的名称(我们将暂时定义此为构建器)。

为Node.js项目配置构建工厂

接下来,我们将配置一个用于处理Node.js项目的构建工厂。构建工厂负责定义构建或在我们的案例测试中应该采取的步骤。它通过定义util.BuildFactory实例然后添加应执行的顺序步骤来完成此操作。

将以下内容粘贴到文件的底部:

/home/buildbot/master/master.cfg

. . .# Build Factories
npm_f = util.BuildFactory()
npm_f.addStep(steps.GitHub(repourl='git://github.com/your_github_name/hello_hapi.git', mode='full', method='clobber'))
npm_f.addStep(steps.ShellCommand(command=["npm", "install"]))
npm_f.addStep(steps.ShellCommand(command=["npm", "test"]))

首先,我们定义一个名为npm_f的构建工厂。我们添加的第一步是steps.GitHub实例。在这里,我们设置应该下拉到构建器中的存储库。我们设置mode“full”method“clobber”以在每次提取新代码时完全清理我们的存储库。

我们添加的第二个和第三个步骤是steps.ShellCommand对象,它们定义在构建期间在存储库中运行的shell命令。在我们的例子中,我们需要运行npm install以收集项目的依赖项。之后,我们需要运行npm test以运行我们的测试套件。在大多数情况下,建议将命令定义为一个list (["npm","install"]),以防止shell对命令中的元素应用不需要的扩展。

配置构建器

一旦我们有一个添加了步骤的构建工厂,我们就可以设置一个构建器。构建器将我们已定义的许多元素绑定在一起,以确定构建的执行方式。

将以下配置粘贴到文件的底部:

/home/buildbot/master/master.cfg

. . .# Builders
c['builders'] = []
c['builders'].append(util.BuilderConfig(name="npm",workernames=["npm-docker-worker"],factory=npm_f))

我们将一个util.BuilderConfig对象附加到builders列表中。请记住,我们的构建工厂名为npm_f,我们的Docker工作者称为npm-docker-worker,我们定义的调度程序将把任务传递给名为npm的worker。。我们的构建器定义了这些元素之间的关系,以便我们的调度程序的更改将导致构建工厂步骤在Docker worker中执行。

配置数据库和Web界面

最后,我们可以配置数据库和Web界面设置。与之前的许多项目不同,这两个设置被定义为字典而不是列表。该db字典只指向/home/buildbot/master目录中已有的state.sqlite文件。www词典包含大量额外配置。

将以下内容粘贴到文件的底部。将您从原始Buildbot主配置中复制的身份验证信息替换为以下身份验证块:

/home/buildbot/master/master.cfg

. . .# Database
c['db'] = { 'db_url': "sqlite:///state.sqlite",}# Web Interface
c['www'] = dict(port=8010, plugins=dict(waterfall_view={}, console_view={}))# Auth info copied from the original configuration
c['www']['authz'] = util.Authz(allowRules = [util.AnyEndpointMatcher(role="admins")],roleMatchers = [util.RolesFromUsername(roles=['admins'], usernames=['Sammy'])]
)
c['www']['auth'] = util.UserPasswordAuth({'Sammy': 'Password'})
# End of auth info copied from the original configuration# GitHub webhook receiver
c['www']['change_hook_dialects'] = {'github': {'secret': 'your_secret_value','strict': True,}
}

在定义数据库设置之后,我们创建一个www字典,该字典首先定义要侦听的端口以及要包含在Web UI中的一些视图。接下来,我们添加从先前的Buildbot配置文件中提取的身份验证要求。

最后,我们在www字典中定义了一个名为change_hook_dialects的字典。我们使用它来定义一个GitHub更改挂钩,它将侦听来自GitHub的webhook消息。为您的机密选择一个安全密码,GitHub将使用该密码来验证它将发送的消息。

完成后,保存并关闭文件。

重新启动Buildbot Master以应用新配置

此时,我们已经完全重新配置了Buildbot主进程。我们需要重新启动Buildbot主进程来实现更改。

在我们这样做之前,检查我们的文件是否有重要的语法错误。由于我们从头开始重建配置,因此我们很可能会引入一些错误。

输入以下命令检查文件的语法:

$ sudo buildbot checkconfig /home/buildbot/master

该命令将报告它找到的任何问题。如果未找到任何错误,您将收到如下消息:

Config file is good!

如果报告了任何错误,请仔细阅读错误消息,以便更好地了解错误。再次打开配置文件以尝试解决任何问题。

如果不再出现任何错误,请输入以下命令重新启动Buildbot主服务:

$ sudo systemctl restart buildbot-master

输入以下命令检查操作是否成功:

$ sudo systemctl status buildbot-master
● buildbot-master.service - BuildBot master serviceLoaded: loaded (/etc/systemd/system/buildbot-master.service; enabled; vendor preset: enabled)Active: active (running) since Tue 2017-06-27 19:24:07 UTC; 2s agoMain PID: 8298 (buildbot)Tasks: 2Memory: 51.7MCPU: 1.782sCGroup: /system.slice/buildbot-master.service└─8298 /usr/bin/python /usr/local/bin/buildbot start --nodaemonJun 27 19:24:07 bb5 systemd[1]: Started BuildBot master service

如果服务能够成功重新启动,则会将其标记为活动状态。

在示例存储库中创建GitHub Webhook

现在Buildbot配置了一个Web端点来接受GitHub webhook帖子,我们可以为我们的fork配置一个webhook。

在Web浏览器中,导航到示例项目存储库的fork:

https://github.com/your_github_user/hello_hapi

单击“设置”选项卡以查看项目设置。在设置页面的左侧菜单中,单击Webhooks(GitHub可能会提示您在此过程中重新输入密码以确认您的身份):

项目设置

单击右侧的“ 添加webhook”按钮以添加新的webhook。

下面的页面将包含一个用于定义webhook的表单。在Payload URL字段中,添加项目的GitHub更改的URL。这是通过指定https://协议,然后是Buildbot master的域名,然后是/change_hook/github构建的。

将内容类型设置为application/x-www-form-urlencoded。在“密码”字段中,输入您在Buildbot主配置文件中选择的秘密密码。您可以选中“Just push”事件触发器,勾选“Active”复选框:

添加新的webhook

完成后,单击“ 添加webhook”按钮。

您将返回到项目的webhooks索引,在该索引中将显示您的新webhook。如果刷新几次,则应在webhook旁边显示绿色复选标记图标,表示邮件已成功传输:

webhooks索引

如果您看到红色的X,请再次单击webhook,然后向下滚动到Recent Deliveries部分。如果您单击failed delivery,可以获得有关出现问题的更多信息。

测试Webhook

现在我们已经有了webhook,我们可以测试以确保当我们对存储库进行更改时,Buildbot会被警告,触发Docker中的构建,并且能够成功执行测试套件。

在GitHub fork的主页面中,单击绿色“克隆或下载”按钮左侧的“ 创建新文件 ”按钮:

创建新文件

在随后的屏幕上,创建dummy_file并填写一些文本:

dummy_file

完成后,单击页面底部的“ 提交新文件”按钮。

接下来,访问您的Buildbot Web界面,如果您尚未通过身份验证,请登录。

根据您提交dummy_file到存储库后的时间长度,您可能会看到正在进行的构建,如下所示:

Buildbot 正在构建

如果构建已经完成,则它将位于“最近构建”部分中:

构建完成

我们定义的构建器名称“npm”用于标记构建。在该示例中,我们还可以从先前的主配置中看到较早的样本构建器运行。

无论进度如何,单击构建器名称和内部版本号链接以访问构建详细信息页面。此视图包含有关所执行的构建的信息。我们添加到构建工厂的每个步骤都将显示在其自己的部分中:

构建详细信息

如果单击某个步骤,将显示该命令的输出。如果出现问题,这可以帮助调试:

调试输出

在上面的输出中,我们可以验证Buildbot是否在我们的测试套件中成功运行了三个测试。

如果构建未成功完成,您可能希望检查的其他一些区域是构建详细信息页面上的其他选项卡以及/home/buildbot/master/twistd.log文件。

调整Buildbot服务

在我们完成之前,我们应该对我们的Buildbot服务进行一些调整。

目前,我们为不再使用的工作人员定义了buildbot-worker服务(我们的Docker工作程序在需要时自动启动)。

我们应该停止并禁用old worker。

要停止正在运行的服务并禁止它在引导时启动,请输入:

$ sudo systemctl stop buildbot-worker
$ sudo systemctl disable buildbot-worker
Removed symlink /etc/systemd/system/buildbot-master.service.wants/buildbot-worker.service.

上面的输出结果表明工作人员下次启动时不会启动。要验证服务是否不再运行,请输入:

$ sudo systemctl status buildbot-worker
● buildbot-worker.service - BuildBot worker serviceLoaded: loaded (/etc/systemd/system/buildbot-worker.service; disabled; vendor preset: enabled)Active: inactive (dead)Jun 27 21:12:48 bb6 systemd[1]: Started BuildBot worker service.
Jun 27 21:55:51 bb6 systemd[1]: Stopping BuildBot worker service...
Jun 27 21:55:51 bb6 systemd[1]: Stopped BuildBot worker service.

我们应该做的最后一件事是在Buildbot主服务和Docker守护进程之间建立一个soft依赖项。由于Buildbot主服务无法在没有Docker的情况下配置new workers,因此我们应该定义此要求。

打开/ etc / systemd / system目录中的buildbot-master.service文件以调整服务文件:

$ sudo nano /etc/systemd/system/buildbot-master.service

[Unit]部分中,将docker.service添加到network.target项之后的After指令中。添加另一个名为docker.serviceWants指令。Wants建立了一个软依赖,而After指令建立了起始顺序:

/etc/systemd/system/buildbot-master.service

[Unit]
Description=BuildBot master service
After=network.target docker.service
Wants=docker.service[Service]
User=buildbot
Group=buildbot
WorkingDirectory=/home/buildbot/master
ExecStart=/usr/local/bin/buildbot start --nodaemon[Install]
WantedBy=multi-user.target

完成后保存并关闭文件。

重新加载systemd守护程序和服务以立即应用配置:

$ sudo systemctl daemon-reload
$ sudo systemctl restart buildbot-master

现在应该在Docker可用之后启动Buildbot主进程。

结论

在本教程中,我们将Buildbot配置为使用webhooks监听对GitHub存储库的更改。收到更改后,Buildbot会根据自定义Docker镜像启动容器以测试新提交。Docker镜像包含一个Buildbot工作器实例以及测试项目代码所需的依赖项。这允许Buildbot在对存储库进行更改时根据需要动态启动Buildbot worker。


参考文献:《How To Set Up Continuous Integration with Buildbot on Ubuntu 16.04》

问答

腾讯云服务器?

相关阅读

教你从0到1搭建小程序音视频

教你快速搭建一场发布会直播方案

移形换影 - 短视频色彩特效背后的故事

此文已由作者授权腾讯云+社区发布,原文链接:https://cloud.tencent.com/developer/article/1178239?fromSource=waitui

欢迎大家前往腾讯云+社区或关注云加社区微信公众号(QcloudCommunity),第一时间获取更多海量技术实践干货哦~

海量技术实践经验,尽在云加社区! https://cloud.tencent.com/developer?fromSource=waitui

入门系列之在Ubuntu 16.04使用Buildbot建立持续集成系统相关推荐

  1. 基于Ubuntu 16.04的HUSTOJ在线判题系统的搭建

    打开Ubuntu终端键入 sudo apt-get update 此命令为了升级Ubuntu软件库 接下来键入 sudo apt-get install subversion 安装subversion ...

  2. 安装win 7 + ubuntu 16.04 双系统安装

    安装win 7 + ubuntu 16.04 双系统安装 安装windows 7 1. 下载windows 7镜像文件 2. 制作u盘启动盘 3. 激活win 7系统 4. 安装无线网卡驱动 5. 安 ...

  3. 关于ubuntu 16.04 无法从挂起唤醒及无法关机、卡在关机界面的解决办法

    二话不说,直接上代码 1. sudo vim /etc/modules 在文件末尾添加 apm power_off=1 sudo vim /etc/default/grub 在文件中将 GRUB_CM ...

  4. Ubuntu 16.04如何安装Cinnamon 3.0

    Linux Mint 的桌面环境系统 Cinnamon 今天发布了一个重要版本, Cinnamon 3.0 已经提前于 Linux Mint 18 发布.Linux Mint 18 基于 Ubuntu ...

  5. Ubuntu 16.04中为Chromium、Chrome、Firefox安装Flash播放器插件

    Ubuntu 16.04 LTS正式发布有段时间了,许多Linux迷喜欢在 Ubuntu 平台上使用开源的 Chromium 浏览器(也就是Google Chrome 浏览器的开源版本),但是在 Ch ...

  6. Ubuntu 16.04 桌面版使用体验报告

    Ubuntu 16.04 Desktop Linux 操作系统 非双系统.非虚拟机 主要用途 办公.影音娱乐.上网 编程学习开发 -放弃Windows,Ubuntu体验之旅 – 正式开始 ▼ –01- ...

  7. 入门系列之使用Sysdig监视您的Ubuntu 16.04系统

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由乌鸦 发表于云+社区专栏 介绍 Sysdig是一个全面的开源系统活动监控,捕获和分析应用程序.它具有强大的过滤语言和可自定义的输出,以 ...

  8. 入门系列之使用Sysdig监视您的Ubuntu 16.04系统 1

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由乌鸦 发表于云+社区专栏 介绍 Sysdig是一个全面的开源系统活动监控,捕获和分析应用程序.它具有强大的过滤语言和可自定义的输出,以 ...

  9. linux nginx 配置端口访问,Linux入门教程:ubuntu 16.04配置nginx服务器实现一个IP一个端口多个站点,ubuntunginxNginx 使用异步...

    Linux入门教程:ubuntu 16.04配置nginx服务器实现一个IP一个端口多个站点,ubuntunginxNginx 使用异步 特点: Nginx 可以部署在网络上使用 FastCGI,脚本 ...

最新文章

  1. linux c warning: implicit declaration of function 'strtok_r'解决方案
  2. gevent -1073740791
  3. 【实验】配置DHCP和NAT访问Internet公网案例
  4. 大剑无锋之Zookeeper面试题
  5. OpenSSL常见用法
  6. 4. PDO 事务处理
  7. 实对称矩阵的特征值一定为实数证明
  8. openGL绘制地球
  9. java 打印 线程_java线程状态和获取线程基本信息
  10. 查看dll文件的两种办法
  11. ubuntu下点云库pcl错误问题集
  12. 外部表报错:ORA-29913: 执行 ODCIEXTTABLEOPEN 调出时出错 ORA-29400: 数据插件错误KUP-04043: table column not found in ext
  13. 图书馆和档案馆的职能
  14. P1309 [NOIP2011 普及组] 瑞士轮
  15. 常见的地理坐标系与投影坐标系
  16. uefi装完系统后无法引导_uefi gpt安装win7启动不了如何解决_uefi gpt装win7后无法启动怎么办...
  17. a16z:推翻互联网的偶然君主制,如何设计Web3平台治理?
  18. Google和facebook登录
  19. LeetCode每日一题(2022/5/31)剑指 Offer II 114. 外星文字典(困难)
  20. 【Code】ASCII码表

热门文章

  1. jq之$(“p.test“)
  2. crontab shell 每5秒执行_视频 |全球最快全自动播种分拣机器人,每5秒处理一件货物...
  3. php 运行外部程序_PHP在linux上执行外部命令的方法
  4. 语音识别插件_AnsweringMachine XS: 越狱理由之二,iPhone 电话语音答录机
  5. golang 数组组合成最小的整数_golang数组-----寻找数组中缺失的整数方法
  6. 计算机如何制作U盘启动盘,电脑如何制作U盘启动盘
  7. C语言、C++学习路线
  8. 七、Python第七课——有关列表的二三事(切片、切片的遍历和复制)
  9. 使用未初始化的内存是什么意思_他们都说JVM能实际使用的内存比-Xmx指定的少?这是为什么呢...
  10. 实验报告: 线性表的基本操作及应用