分类 技术 下的文章

随着像 APT 和 Yum 等传统包管理解决方案渐渐老去,并且不适用于嵌入式和 IoT 等 Linux 的大量新兴领域,新一代的基于原子化的 Linux 软件升级方案应运而生。Konsulko Group 的 Matt Porter 在本周的 2016 年欧洲嵌入式 Linux 峰会(LCTT 译注:于 2016 年 10 月 11-13 日在德国柏林召开)为大家对比了这些新技术的不同点。

目前已有多个 Linux 软件商使用增量原子更新方式来传递更可靠的发行版更新,通过二进制差异实现更小体积的更新,假如出现意外状况也运行回退。这些新的发行版升级机制包含了 SWUpdate、Mender、OSTree 和 swupd。但有趣的是,幻灯片之中并没有提及 Ubuntu 的 Snappy。

SWUpdate 一种单/双镜像的模块化升级框架,支持镜像签名、可以使用 Kconfig 来进行配置、能够处理本地或者远程升级等。SWUpdate 简直就是为嵌入式系统设计的。

Mender 则是以无线传输进行升级位目标的升级方案。它是用 Go 编程语言编写的双镜像升级框架。

OSTree 是此次增量原子升级方案演示中最有名气的,它类似于 Git。Fedora 和 RedHat 都有它的身影,甚至 Gnome 的 Flatpak 容器系统也使用了 OSTree。

Swupd 是最后展示的一个升级系统,是由 Intel 的 Clear Linux 发行版率先使用的升级方案。它的代码放在 GitHub,而它的客户端和服务端则由 Clear Linux 托管。Swupd 与 OSTree 相似,但它不必重启就可以启用更新。

而那些在本次柏林召开的欧洲嵌入式 Linux 峰会中没有提及的,你也可以访问 这些 PDF 讲演稿 来了解这些专注于嵌入式 Linux 的软件更新机制。


via: http://phoronix.com/scan.php?page=news_item&px=ELC2016-Software-Updates

作者:Michael Larabel 译者:GHLandy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用过 GitHub 的人大多知道它上面有个“Fork”的功能,用来将某个仓库克隆到你的账户之下,从而可以对其进行修改、衍生,也可以比较方便的将你的修改推回到原来的仓库(所谓的上游)。

随着 GitHub 的流行,我们经常能在各种文章中见到,“fork 某某项目”等说法,是的,“fork”这个一直没有一个正式的译名。

其实这个问题不独出现在 GitHub 中,fork 这个词更早的出现在 Unix/Linux 中的 C 语言编程之中。在 Unix/Linux 的进程模型中,fork 是指进程创建自身副本的操作,它通常是一个在内核中实现的系统调用。fork 是 Unix 类系统中进程创建的主要方式(历史上曾经是唯一的方式)。从那个时候起,fork 就一直没有一个确定的译名。

不过,我们认为,对于这样的一个经常使用的专业名词,有一个明确的译名比较适合,虽然大家都习惯了直接用 fork 一词。

fork 一词在英文中的原意是“叉子”, 虽然翻译成“分叉”、“分支”似乎也可以,但是前者较少用做动词,后者则和 Branch 的译名重复混淆。

据 Linux 中国翻译组(LCTT)的译者 dongfengweixiao 提议,可译作“复刻”,我们认为这是一个可取的译法,一方面照顾到了音译,另一方面其释义也形象直观。

补充 1,“复刻”这样的译法,在网络上已存在,包括中文维基)中也采用该译法,只是并未得到公认和流行。

补充 2,据 @爱开源魅影 称,git 软件包里面,蒋新将“fork”译为“派生”。似也可取。

既然说到这里,顺便我们对 复刻 fork 做一个技术方面的简介吧。

复刻 fork GitHub 仓库

在 GitHub 上评价一个项目(仓库)是否流行,其中一个重要指标就是其 复刻 fork 数。

在 GitHub 上参与一个开源项目的程度有三个阶段:

  • star(点赞),如果你觉得某个项目不错,可以为其点赞(star)
  • watch(关注),如果你希望进一步关注这个项目的进展,可以关注(watch)它
  • fork(复刻),如果你还想进一步为这个项目做一些贡献,可以复刻(fork)它到你自己的账户下,做出了修改之后通过 Pull-Request(PR)方式将你的改动推回给原仓库(上游),如果被接纳就会进入到原仓库之中

显然,一个项目的复刻数越高,代表着贡献者越多。

通过 复刻 fork + PR 的方式对开源项目进行贡献的流程类似下图:

我们知道 GitHub 是运行在 Git 之上的,GitHub 中的 复刻 fork 其本质上是 Git 中的 克隆 clone 。关于 GitHub 中的复刻的进一步介绍,可以参考“在 Github 和 Git 上 fork 之简单指南”一文。

顺便说一句,我们的 LCTT 翻译组就是通过 复刻 fork + PR 的方式运作的,这也是 GitHub 上绝大多数开源项目的运作方式。

复刻 fork 子进程

在 Unix 下的 C 语言编程中,通过 fork() 系统调用来对进程本身进行复制,然后被复制出来的子进程就可以执行不同于父进程的操作,或通过 exec() 运行其它进程。典型的 C 代码如下:

fpid = fork();   

if (fpid < 0)   
    printf("error in fork!");   
else if (fpid == 0) {  
    printf("i am the child process, my process id is %d/n",getpid());   
} else {  
    printf("i am the parent process, my process id is %d/n",getpid());   
}

所有的服务器守护进程,包括你所见到的 Web 服务、MySQL 数据库服务等,都是通过这种方式来产生子进程来提供服务的。甚至,整个 Linux/Unix 中的进程,除了 init 进程本身之外,都是由 init 进程 复刻 fork 出来的。关于服务器编程方面的 复刻 fork 的使用,可以进一步参阅“搭个 Web 服务器(三)”一文。

复刻 fork 炸弹

其实,不只是 C 语言里面有 复刻 fork 的功能,在 shell 里面也有,想必大家可能都听说过 “fork 炸弹”,这就是利用函数的迭代执行,无限 复刻 fork 出许多子进程,从而耗尽系统资源,导致系统崩溃的一个恶意(玩笑)用法。

复刻炸弹有很多种形式,不过最简洁的可能就是如上图的这个了,关于这个炸弹的具体解释,可以参阅“经典的 Fork 炸弹解析”,在此就不赘述了。

如果你对 fork 的翻译有不同的意见,欢迎留言评论。

在 Linux 和其他类 Uniux 系统中,init(初始化)进程是系统启动时由内核执行的第一个进程,其进程 ID(PID)为 1,并静默运行在后台,直到系统关闭。

init 进程负责启动其他所有的进程,比如守护进程、服务和其他后台进程,因此,它是系统中其它所有进程之母(偏偏叫做“父进程”)。某个进程可以启动许多个子进程,但在这个过程中,某个子进程的父进程结束之后,该子进程的父进程会变成 init 进程。

这么多年过去了,许多的初始化系统在主流 Linux 脱颖而出,和本文中,我将你来看看在 Linux 操作系统最好的初始化系统。

1. System V Init

System V (SysV) 是一个在类 Unix 系统中最为成熟而且大受欢迎的初始化方案,是 Unix/Linux 系统中所有进程的父进程。SysV 是第一个商业 Unix 系统设计的初始化方案。

除了 Gentoo 使用自主的初始化系统、Slackware 使用 BSD 风格的初始化方案外,几乎所有的 Linux 发行版都率先使用 SysV 作为初始化方案。

随着时间的推移,由于一些设计上的缺陷,有几个 SysV 初始化替换方案已经开发出来,用以为 Linux 创建更加高效和完美的初始化系统。

尽管这些替代方案都超越了 SysV 并提供了更多新特性,但它们仍然和原始 SysV 初始化脚本保持兼容。

2. SystemD

SystemD 是一个 Linux 平台中相对较新的初始化方案。它由 Fedora 15 引入,集成了各类工具以便更好的管理系统。主要目的是:系统初始化、管理和跟踪引导进程中和系统运行时所有的系统进程。

Systemd 全面有别于其他传统的 Unix 初始化系统,特别是在启动系统和服务管理方面。它同样兼容 SysV 和 LBS 初始化脚本。

其中较为突出的特性如下:

  • 纯粹、简单、高效的设计
  • 启动时的并发和并行处理
  • 更好的 API
  • 开启可选进程的移除功能
  • 使用 journald 来支持事件日志
  • 使用 systemd calender timers 来支持任务计划
  • 以二进制文件存储日志
  • 保存 systemd 的状态以待今后查看
  • 与 GNOME 更好整合实现等

查看 Systemd 初始化系统简介:https://fedoraproject.org/wiki/Systemd

3. Upstart

Upstart 是一个基于事件的初始化系统,由 Ubuntu 的制作团队开发的,用以替代 SysV。它可以启动不同的系统任务和进程、在系统运行时校验进程并在系统关闭时结束进程。

它是一个使用 SysV 和 Systemd 启动脚本的混合初始化系统,Upstart 中值得一提的特性如下:

  • Ubuntu 的原生初始化系统,但可以运行在其他所有的发行版中
  • 基于事件启动/结束的任务和服务
  • 启动/结束任务和服务时生成事件
  • 可以由其他系统进程发送事件
  • 使用 D-Bus 和 init 进程通信
  • 用户可以启动/结束其各自的进程
  • 可以再现崩溃的进程等

访问 Upstart 主页:http://upstart.ubuntu.com/index.html

4. OpenRC

OpenRC 是一个基于依赖关系的类 Unix 系统初始化方案,兼容 SysV。基本可以说是 SysV 的升级版,但必须要清楚记住的是:OpenRC 并非只是完全替代 /sbin/init 文件。

它所提供的出色特性如下:

  • 可运行在包括 Gentoo 和 BSD 在内的多数 Linux 系统之中
  • 支持硬件触发的初始化脚本(LCTT 译注:如硬件热插拔所触发的)
  • 支持单个配置文件
  • 不支持单个服务配置文件
  • 以守护进程的方式运行
  • 并行服务启动等

访问 OpenRC 主页:https://wiki.gentoo.org/wiki/OpenRC

5. runit

runit 同样是一个跨平台初始化系统,可以运行在 GNU/Linux、Solaris、BSD 和 Mac OS X 中,用替代 SysV,同时提供服务监控。

相比于 SysV 和其他 Linux 初始化系统,它提供了一些好用和卓越的组件,如下:

  • 服务监控:每个服务都关联一个服务目录
  • 清理进程状态,以保证每个进程处于干净状态
  • 可靠的日志机制
  • 快速的系统启动和关闭
  • 可移植
  • 打包方便
  • 代码体积小等

访问 runit 主页:http://smarden.org/runit/

正如我之前所说的,Linux 中的初始化系统负责启动和管理所有的进程。此外,SysV 是 Linux 系统中主要的初始化系统,但由于一些性能缺陷,系统开发者已经开发出几个替代品。

在这里,我已经介绍了几个可用的替代方案,但你可能觉得有一些其他的初始化系统值得在此提及。请在下方的评论区将你的想法告诉我们。


via: http://www.tecmint.com/best-linux-init-systems/

作者:Aaron Kili 译者:GHLandy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

介绍

众所周知,Arch Linux 是最好的 Linux 发行版之一,我们可以随心所欲的进行定制。我们可以滚动式更新最新的软件,但它的安装和配置的过程对于初学者则相对困难。现在就让我来为你展示如何安装 Antergos,这个基于 Arch 的、给新手的最好发行版,要尝试 Arch 的用户可以试一试,我保证绝对值得一试。

Antergos 是一个滚动式更新的发行版,基于 Arch Linux 提供桌面环境,是集现代化、高雅和高效率于一体的操作系统。想要挑战一下安装 Arch Linux 的用户,都可以先试一试 Antergos。

由于是滚动式更新,当上游发布更新,你的整个系统 (包括系统组件和你已安装的应用) 就会升级到最新的滚动版本,仅仅稍微延期一点时间以确保系统的稳定。

Antergos 有多个语言版本,包括英语、西班牙语等版本,在安装时你可以在语言栏选择预置语言。默认的配置确保了开箱即用,不需要安装额外的软件就可以直接浏览网页、听音乐、看视频以及做任何你想做的事情。

Antergos 项目最初名字是 Cinnarch,并且只提供一个桌面环境。现在它有多个桌面环境(包括 Cinnamon、Gnome、KDE、MATE、Openbox 和 Xfce)以供用户在安装之时进行选择。同时,它还默认包含了著名的 Numix GTK 主题和 Icon 主题。

安装

下载 Antergos Linux

访问 Antergos 下载页面,然后下载最新的 Live ISO 镜像。任何人都可以免费获取 Antergos 镜像,烧录到 CD/DVD 或者写到 USB 设备,开始 Antergos 之旅。

创建可启动 USB

下载好最新的 Antergos Live ISO 镜像之后,你需要使用命令行或者图形界面的方法来制作一个可启动介质。

使用 Antergos 安装介质来启动电脑

使用 Antergos 安装介质来启动电脑。成功启动到 Live ISO 之后,你会看到与下图相似的界面。

点击 Install It 按钮开始 Angergos 的安装过程。

选择语系

Antergos 的易于使用的图形安装器非常不错。此处选择你需要选用的语系并点击 Next 按钮继续下一步。

系统检测

请确保你有足够的磁盘空间,连接好电源以及连接到网络,点击 Next 按钮继续下一步。

选择地点

选择地点可以帮助决定系统所使用的本地化信息(locale)。通常是选择你所属的国家即可。这里有一个基于你选择的语言的位置的简短列表。点击 Next 按钮继续下一步。

选择时区

选择使用的时区,然后点击 Next 按钮继续下一步。

选择键盘布局

选择你选用的键盘布局,然后点击 Next 按钮继续下一步。

选择桌面环境

Antergos 提供了多个选择(包括 Cinnamon、Gnome、KDE、MATE、Openbox 和 Xfce),根据你的喜好进行选择即可。然后点击 Next 按钮继续下一步。

Gnome – 特性选择

这里我选择了默认的 Gnome 桌面环境,此处你可以选择需要额外安装的软件以及正确的驱动程序。然后点击 Next 按钮继续下一步。

安装类型

初次安装可以选择第一个选项( Erase disk and install Antergos )。然后点击 Next 按钮继续下一步。(LCTT 译注,但是要注意这个选项会擦除你的整个硬盘数据,如果你是全新安装,则可以使用该选项。)

自动安装模式

这里会有一个檫除硬盘的警告,并选择安装 Grub 启动器,然后点击 Next 按钮继续下一步。

WARNING ! This will overwrite everything currently on your drive !

安装设置综述

确认最后的安装设置概览,如地点、时区、键盘布局、桌面环境和特性,然后点击 Next 按钮继续下一步。

创建你的用户名

此时需要为你的系统创建一个新用户名和对应的密码,然后点击 Next 按钮继续下一步。

  • 你的昵称
  • 电脑名称
  • 用户名
  • 设置密码
  • 密码二次确认
  • 选择“登录系统需要密码”

安装进程

此时就是等待 Antergos 安装完成。安装好之后,移除安装介质在点击“立刻重启”按钮。

输入用户名和密码

这是欢迎界面,输入密码即可登录:

发行信息截图:

我们还会提供了一些列循序渐进的 Linux 系统管理的相关文章。如果本文对你有用,请花费几分钟分享你得想法到评论区。

请于我们保持联系,祝君好运!


via: http://www.2daygeek.com/how-to-install-antergos-linux/

作者:MAGESH MARUTHAMUTHU 译者:GHLandy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

序言

这篇教程将会教你怎么制作你的第一个 Atom 文本编辑器的插件。我们将会制作一个山寨版的 Sourcerer,这是一个从 StackOverflow 查询并使用代码片段的插件。到教程结束时,你将会制作好一个将编程问题(用英语描述的)转换成获取自 StackOverflow 的代码片段的插件,像这样:

教程须知

Atom 文本编辑器是用 web 技术创造出来的。我们将完全使用 JavaScript 的 EcmaScript 6 规范来制作插件。你需要熟悉以下内容:

教程的仓库

你可以跟着教程一步一步走,或者看看 放在 GitHub 上的仓库,这里有插件的源代码。这个仓库的历史提交记录包含了这里每一个标题。

开始

安装 Atom

根据 Atom 官网 的说明来下载 Atom。我们同时还要安装上 apm(Atom 包管理器的命令行工具)。你可以打开 Atom 并在应用菜单中导航到 Atom > Install Shell Commands 来安装。打开你的命令行终端,运行 apm -v 来检查 apm 是否已经正确安装好,安装成功的话打印出来的工具版本和相关环境信息应该是像这样的:

apm -v
> apm  1.9.2
> npm  2.13.3
> node 0.10.40
> python 2.7.10
> git 2.7.4

生成骨架代码

让我们使用 Atom 提供的一个实用工具创建一个新的 package(软件包)来开始这篇教程。

  • 启动编辑器,按下 Cmd+Shift+P(MacOS)或者 Ctrl+Shift+P(Windows/Linux)来打开 命令面板 Command Palette
  • 搜索“Package Generator: Generate Package”并点击列表中正确的条目,你会看到一个输入提示,输入软件包的名称:“sourcefetch”。
  • 按下回车键来生成这个骨架代码包,它会自动在 Atom 中打开。

如果你在侧边栏没有看到软件包的文件,依次按下 Cmd+K Cmd+B(MacOS)或者 Ctrl+K Ctrl+B(Windows/Linux)。

命令面板 Command Palette 可以让你通过模糊搜索来找到并运行软件包。这是一个执行命令比较方便的途径,你不用去找导航菜单,也不用刻意去记快捷键。我们将会在整篇教程中使用这个方法。

运行骨架代码包

在开始编程前让我们来试用一下这个骨架代码包。我们首先需要重启 Atom,这样它才可以识别我们新增的软件包。再次打开命令面板,执行 Window: Reload 命令。

重新加载当前窗口以确保 Atom 执行的是我们最新的源代码。每当需要测试我们对软件包的改动的时候,就需要运行这条命令。

通过导航到编辑器菜单的 Packages > sourcefetch > Toggle 或者在命令面板执行 sourcefetch:toggle 来运行软件包的 toggle 命令。你应该会看到屏幕的顶部出现了一个小黑窗。再次运行这条命令就可以隐藏它。

“toggle”命令

打开 lib/sourcefetch.js,这个文件包含有软件包的逻辑和 toggle 命令的定义。

toggle() {
 console.log('Sourcefetch was toggled!');
 return (
   this.modalPanel.isVisible() ?
   this.modalPanel.hide() :
   this.modalPanel.show()
 );
}

toggle 是这个模块导出的一个函数。根据模态面板的可见性,它通过一个三目运算符 来调用 showhide 方法。modalPanelPanel(一个由 Atom API 提供的 UI 元素) 的一个实例。我们需要在 export default 内部声明 modalPanel 才可以让我们通过一个实例变量 this 来访问它。

this.subscriptions.add(atom.commands.add('atom-workspace', {
  'sourcefetch:toggle': () => this.toggle()
}));

上面的语句让 Atom 在用户运行 sourcefetch:toggle 的时候执行 toggle 方法。我们指定了一个 匿名函数 () => this.toggle(),每次执行这条命令的时候都会执行这个函数。这是事件驱动编程(一种常用的 JavaScript 模式)的一个范例。

Atom 命令

命令只是用户触发事件时使用的一些字符串标识符,它定义在软件包的命名空间内。我们已经用过的命令有:

  • package-generator:generate-package
  • Window:reload
  • sourcefetch:toggle

软件包对应到命令,以执行代码来响应事件。

进行你的第一次代码更改

让我们来进行第一次代码更改——我们将通过改变 toggle 函数来实现逆转用户选中文本的功能。

改变 “toggle” 函数

如下更改 toggle 函数。

toggle() {
  let editor
  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    let reversed = selection.split('').reverse().join('')
    editor.insertText(reversed)
  }
}

测试你的改动

  • 通过在命令面板运行 Window: Reload 来重新加载 Atom。
  • 通过导航到 File > New 来创建一个新文件,随便写点什么并通过光标选中它。
  • 通过命令面板、Atom 菜单或者右击文本然后选中 Toggle sourcefetch 来运行 sourcefetch:toggle 命令。

更新后的命令将会改变选中文本的顺序:

sourcefetch 教程仓库 查看这一步的全部代码更改。

Atom 编辑器 API

我们添加的代码通过用 TextEditor API 来访问编辑器内的文本并进行操作。让我们来仔细看看。

let editor
if (editor = atom.workspace.getActiveTextEditor()) { /* ... */ }

头两行代码获取了 TextEditor 实例的一个引用。变量的赋值和后面的代码被包在一个条件结构里,这是为了处理没有可用的编辑器实例的情况,例如,当用户在设置菜单中运行该命令时。

let selection = editor.getSelectedText()

调用 getSelectedText 方法可以让我们访问到用户选中的文本。如果当前没有文本被选中,函数将返回一个空字符串。

let reversed = selection.split('').reverse().join('')
editor.insertText(reversed)

我们选中的文本通过一个 JavaScript 字符串方法 来逆转。最后,我们调用 insertText 方法来将选中的文本替换为逆转后的文本副本。通过阅读 Atom API 文档,你可以学到更多关于 TextEditor 的不同的方法。

浏览骨架代码

现在我们已经完成第一次代码更改了,让我们浏览骨架代码包的代码来深入了解一下 Atom 的软件包是怎样构成的。

主文件

主文件是 Atom 软件包的入口文件。Atom 通过 package.json 里的条目设置来找到主文件的位置:

"main": "./lib/sourcefetch",

这个文件导出一个带有生命周期函数(Atom 在特定的事件发生时调用的处理函数)的对象。

  • activate 会在 Atom 初次加载软件包的时候调用。这个函数用来初始化一些诸如软件包所需的用户界面元素的对象,以及订阅软件包命令的处理函数。
  • deactivate 会在软件包停用的时候调用,例如,当用户关闭或者刷新编辑器的时候。
  • serialize Atom 调用它在使用软件包的过程中保存软件包的当前状态。它的返回值会在 Atom 下一次加载软件包的时候作为一个参数传递给 activate

我们将会重命名我们的软件包命令为 fetch,并移除一些我们不再需要的用户界面元素。按照如下更改主文件:

'use babel';

import { CompositeDisposable } from 'atom'

export default {

  subscriptions: null,

  activate() {
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'sourcefetch:fetch': () => this.fetch()
    }))
  },

  deactivate() {
    this.subscriptions.dispose()
  },

  fetch() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      let selection = editor.getSelectedText()
      selection = selection.split('').reverse().join('')
      editor.insertText(selection)
    }
  }
};

“启用”命令

为了提升性能,Atom 软件包可以用时加载。我们可以让 Atom 在用户执行特定的命令的时候才加载我们的软件包。这些命令被称为 启用命令,它们在 package.json 中定义:

"activationCommands": {
  "atom-workspace": "sourcefetch:toggle"
},

更新一下这个条目设置,让 fetch 成为一个启用命令。

"activationCommands": {
  "atom-workspace": "sourcefetch:fetch"
},

有一些软件包需要在 Atom 启动的时候被加载,例如那些改变 Atom 外观的软件包。在那样的情况下,activationCommands 会被完全忽略。

“触发”命令

菜单项

menus 目录下的 JSON 文件指定了哪些菜单项是为我们的软件包而建的。让我们看看 menus/sourcefetch.json

"context-menu": {
  "atom-text-editor": [
    {
      "label": "Toggle sourcefetch",
      "command": "sourcefetch:toggle"
    }
  ]
},

这个 context-menu 对象可以让我们定义右击菜单的一些新条目。每一个条目都是通过一个显示在菜单的 label 属性和一个点击后执行的命令的 command 属性来定义的。

"context-menu": {
  "atom-text-editor": [
    {
      "label": "Fetch code",
      "command": "sourcefetch:fetch"
    }
  ]
},

同一个文件中的这个 menu 对象用来定义插件的自定义应用菜单。我们如下重命名它的条目:

"menu": [
  {
    "label": "Packages",
    "submenu": [
      {
        "label": "sourcefetch",
        "submenu": [
          {
            "label": "Fetch code",
            "command": "sourcefetch:fetch"
          }
        ]
      }
    ]
  }
]

键盘快捷键

命令还可以通过键盘快捷键来触发。快捷键通过 keymaps 目录的 JSON 文件来定义:

{
  "atom-workspace": {
    "ctrl-alt-o": "sourcefetch:toggle"
  }
}

以上代码可以让用户通过 Ctrl+Alt+O(Windows/Linux) 或 Cmd+Alt+O(MacOS) 来触发 toggle 命令。

重命名引用的命令为 fetch

"ctrl-alt-o": "sourcefetch:fetch"

通过执行 Window: Reload 命令来重启 Atom。你应该会看到 Atom 的右击菜单更新了,并且逆转文本的功能应该还可以像之前一样使用。

sourcefetch 教程仓库 查看这一步所有的代码更改。

使用 NodeJS 模块

现在我们已经完成了第一次代码更改并且了解了 Atom 软件包的结构,让我们介绍一下 Node 包管理器(npm) 中的第一个依赖项模块。我们将使用 request 模块发起 HTTP 请求来下载网站的 HTML 文件。稍后将会用到这个功能来扒 StackOverflow 的页面。

安装依赖

打开你的命令行工具,切换到你的软件包的根目录并运行:

npm install --save [email protected]
apm install

这两条命令将 request 模块添加到我们软件包的依赖列表并将模块安装到 node_modules 目录。你应该会在 package.json 看到一个新条目。@ 符号的作用是让 npm 安装我们这篇教程需要用到的特定版本的模块。运行 apm install 是为了让 Atom 知道使用我们新安装的模块。

"dependencies": {
  "request": "^2.73.0"
}

下载 HTML 并将记录打印在开发者控制台

通过在 lib/sourcefetch.js 的顶部添加一条引用语句引入 request 模块到我们的主文件:

import { CompositeDisposable } from 'atom'
import request from 'request'

现在,在 fetch 函数下面添加一个新函数 download 作为模块的导出项:

export default {  

  /* subscriptions, activate(), deactivate() */

  fetch() {
    ...
  },

  download(url) {
    request(url, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        console.log(body)
      }
    })
  }
}

这个函数用 request 模块来下载一个页面的内容并将记录输出到控制台。当 HTTP 请求完成之后,我们的回调函数会将响应体作为参数来被调用。

最后一步是更新 fetch 函数以调用 download 函数:

fetch() {
  let editor
  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    this.download(selection)
  }
},

fetch 函数现在的功能是将 selection 当作一个 URL 传递给 download 函数,而不再是逆转选中的文本了。让我们来看看这次的更改:

  • 通过执行 Window: Reload 命令来重新加载 Atom。
  • 打开开发者工具。为此,导航到菜单中的 View > Developer > Toggle Developer Tools
  • 新建一个文件,导航到 File > New
  • 输入一个 URL 并选中它,例如:http://www.atom.io
  • 用上述的任意一种方法执行我们软件包的命令:

开发者工具让 Atom 软件包的调试更轻松。每个 console.log 语句都可以将信息打印到交互控制台,你还可以使用 Elements 选项卡来浏览整个应用的可视化结构——即 HTML 的文本对象模型(DOM)

sourcefetch 教程仓库 查看这一步所有的代码更改。

用 Promises 来将下载好的 HTML 插入到编辑器中

理想情况下,我们希望 download 函数可以将 HTML 作为一个字符串来返回,而不仅仅是将页面的内容打印到控制台。然而,返回文本内容是无法实现的,因为我们要在回调函数里面访问内容而不是在 download 函数那里。

我们会通过返回一个 Promise 来解决这个问题,而不再是返回一个值。让我们改动 download 函数来返回一个 Promise:

download(url) {
  return new Promise((resolve, reject) => {
    request(url, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        resolve(body)
      } else {
        reject({
          reason: 'Unable to download page'
        })
      }
    })
  })
}

Promises 允许我们通过将异步逻辑封装在一个提供两个回调方法的函数里来返回获得的值(resolve 用来处理请求成功的返回值,reject 用来向使用者报错)。如果请求返回了错误我们就调用 reject,否则就用 resolve 来处理 HTML。

让我们更改 fetch 函数来使用 download 返回的 Promise:

fetch() {
  let editor
  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    this.download(selection).then((html) => {
      editor.insertText(html)
    }).catch((error) => {
      atom.notifications.addWarning(error.reason)
    })
  }
},

在我们新版的 fetch 函数里,我们通过在 download 返回的 Promise 调用 then 方法来对 HTML 进行操作。这会将 HTML 插入到编辑器中。我们同样会通过调用 catch 方法来接收并处理所有的错误。我们通过用 Atom Notification API 来显示警告的形式来处理错误。

看看发生了什么变化。重新加载 Atom 并在一个选中的 URL 上执行软件包命令:

如果这个 URL 是无效的,一个警告通知将会弹出来:

sourcefetch 教程仓库 查看这一步所有的代码更改。

编写一个爬虫来提取 StackOverflow 页面的代码片段

下一步涉及用我们前面扒到的 StackOverflow 的页面的 HTML 来提取代码片段。我们尤其关注那些来自采纳答案(提问者选择的一个正确答案)的代码。我们可以在假设这类答案都是相关且正确的前提下大大简化我们这个软件包的实现。

使用 jQuery 和 Chrome 开发者工具来构建查询

这一部分假设你使用的是 Chrome 浏览器。你接下来可以使用其它浏览器,但是提示可能会不一样。

让我们先看看一张典型的包含采纳答案和代码片段的 StackOverflow 页面。我们将会使用 Chrome 开发者工具来浏览 HTML:

  • 打开 Chrome 并跳到任意一个带有采纳答案和代码的 StackOverflow 页面,比如像这个用 Python 写的 hello world 的例子或者这个关于 C 来读取文本内容的问题
  • 滚动窗口到采纳答案的位置并选中一部分代码。
  • 右击选中文本并选择 检查
  • 使用元素侦察器来检查代码片段在 HTML 中的位置。

注意文本结构应该是这样的:

<div class="accepted-answer">
  ...
    ...
      <pre>
        <code>
          ...snippet elements...
        </code>
      </pre>
    ...
  ...
</div>
  • 采纳的答案通过一个 class 为 accepted-answerdiv 来表示
  • 代码块位于 pre 元素的内部
  • 呈现代码片段的元素就是里面那一对 code 标签

现在让我们写一些 jQuery 代码来提取代码片段:

  • 在开发者工具那里点击 Console 选项卡来访问 Javascript 控制台。
  • 在控制台中输入 $('div.accepted-answer pre code').text() 并按下回车键。

你应该会看到控制台中打印出采纳答案的代码片段。我们刚刚运行的代码使用了一个 jQuery 提供的特别的 $ 函数。$ 接收要选择的查询字符串并返回网站中的某些 HTML 元素。让我们通过思考几个查询案例看看这段代码的工作原理:

$('div.accepted-answer')
> [<div id="answer-1077349" class="answer accepted-answer" ... ></div>]

上面的查询会匹配所有 class 为 accepted-answer<div> 元素,在我们的案例中只有一个 div。

$('div.accepted-answer pre code')
> [<code>...</code>]

在前面的基础上改造了一下,这个查询会匹配所有在之前匹配的 <div> 内部的 <pre> 元素内部的 <code> 元素。

$('div.accepted-answer pre code').text()
> "print("Hello World!")"

text 函数提取并连接原本将由上一个查询返回的元素列表中的所有文本。这也从代码中去除了用来使语法高亮的元素。

介绍 Cheerio

我们的下一步涉及使用我们创建好的查询结合 Cheerio(一个服务器端实现的 jQuery)来实现扒页面的功能。

安装 Cheerio

打开你的命令行工具,切换到你的软件包的根目录并执行:

npm install --save [email protected]
apm install

实现扒页面的功能

lib/sourcefetch.jscheerio 添加一条引用语句:

import { CompositeDisposable } from 'atom'
import request from 'request'
import cheerio from 'cheerio'

现在创建一个新函数 scrape,它用来提取 StackOverflow HTML 里面的代码片段:

fetch() {
  ...
},

scrape(html) {
  $ = cheerio.load(html)
  return $('div.accepted-answer pre code').text()
},

download(url) {
  ...
}

最后,让我们更改 fetch 函数以传递下载好的 HTML 给 scrape 而不是将其插入到编辑器:

fetch() {
  let editor
  let self = this

  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    this.download(selection).then((html) => {
      let answer = self.scrape(html)
      if (answer === '') {
        atom.notifications.addWarning('No answer found :(')
      } else {
        editor.insertText(answer)
      }
    }).catch((error) => {
      console.log(error)
      atom.notifications.addWarning(error.reason)
    })
  }
},

我们扒取页面的功能仅仅用两行代码就实现了,因为 cheerio 已经替我们做好了所有的工作!我们通过调用 load 方法加载 HTML 字符串来创建一个 $ 函数,然后用这个函数来执行 jQuery 语句并返回结果。你可以在官方 开发者文档 查看完整的 Cheerio API

测试更新后的软件包

重新加载 Atom 并在一个选中的 StackOverflow URL 上运行 soucefetch:fetch 以查看到目前为止的进度。

如果我们在一个有采纳答案的页面上运行这条命令,代码片段将会被插入到编辑器中:

如果我们在一个没有采纳答案的页面上运行这条命令,将会弹出一个警告通知:

我们最新的 fetch 函数给我们提供了一个 StackOverflow 页面的代码片段而不再是整个 HTML 内容。要注意我们更新的 fetch 函数会检查有没有答案并显示通知以提醒用户。

sourcefetch 教程仓库 查看这一步所有的代码更改。

实现用来查找相关的 StackOverflow URL 的谷歌搜索功能

现在我们已经将 StackOverflow 的 URL 转化为代码片段了,让我们来实现最后一个函数——search,它应该要返回一个相关的 URL 并附加一些像“hello world”或者“快速排序”这样的描述。我们会通过一个非官方的 google npm 模块来使用谷歌搜索功能,这样可以让我们以编程的方式来搜索。

安装这个 Google npm 模块

通过在软件包的根目录打开命令行工具并执行命令来安装 google 模块:

npm install --save [email protected]
apm install

引入并配置模块

lib/sourcefetch.js 的顶部为 google 模块添加一条引用语句:

import google from "google"

我们将配置一下 google 以限制搜索期间返回的结果数。将下面这行代码添加到引用语句下面以限制搜索返回最热门的那个结果。

google.resultsPerPage = 1

实现 search 函数

接下来让我们来实现我们的 search 函数:

fetch() {
  ...
},

search(query, language) {
  return new Promise((resolve, reject) => {
    let searchString = `${query} in ${language} site:stackoverflow.com`

    google(searchString, (err, res) => {
      if (err) {
        reject({
          reason: 'A search error has occured :('
        })
      } else if (res.links.length === 0) {
        reject({
          reason: 'No results found :('
        })
      } else {
        resolve(res.links[0].href)
      }
    })
  })
},

scrape() {
  ...
}

以上代码通过谷歌来搜索一个和指定的关键词以及编程语言相关的 StackOverflow 页面,并返回一个最热门的 URL。让我们看看这是怎样来实现的:

let searchString = `${query} in ${language} site:stackoverflow.com`

我们使用用户输入的查询和当前所选的语言来构造搜索字符串。比方说,当用户在写 Python 的时候输入“hello world”,查询语句就会变成 hello world in python site:stackoverflow.com。字符串的最后一部分是谷歌搜索提供的一个过滤器,它让我们可以将搜索结果的来源限制为 StackOverflow。

google(searchString, (err, res) => {
  if (err) {
    reject({
      reason: 'A search error has occured :('
    })
  } else if (res.links.length === 0) {
    reject({
      reason: 'No results found :('
    })
  } else {
    resolve(res.links[0].href)
  }
})

我们将 google 方法放在一个 Promise 里面,这样我们可以异步地返回我们的 URL。我们会传递由 google 返回的所有错误并且会在没有可用的搜索结果的时候返回一个错误。否则我们将通过 resolve 来解析最热门结果的 URL。

更新 fetch 来使用 search

我们的最后一步是更新 fetch 函数来使用 search 函数:

fetch() {
  let editor
  let self = this

  if (editor = atom.workspace.getActiveTextEditor()) {
    let query = editor.getSelectedText()
    let language = editor.getGrammar().name

    self.search(query, language).then((url) => {
      atom.notifications.addSuccess('Found google results!')
      return self.download(url)
    }).then((html) => {
      let answer = self.scrape(html)
      if (answer === '') {
        atom.notifications.addWarning('No answer found :(')
      } else {
        atom.notifications.addSuccess('Found snippet!')
        editor.insertText(answer)
      }
    }).catch((error) => {
      atom.notifications.addWarning(error.reason)
    })
  }
}

让我们看看发生了什么变化:

  • 我们选中的文本现在变成了用户输入的 query
  • 我们使用 TextEditor API 来获取当前编辑器选项卡使用的 language
  • 我们调用 search 方法来获取一个 URL,然后通过在得到的 Promise 上调用 then 方法来访问这个 URL

我们不在 download 返回的 Promise 上调用 then 方法,而是在前面 search 方法本身链式调用的另一个 then 方法返回的 Promise 上面接着调用 then 方法。这样可以帮助我们避免回调地狱

sourcefetch 教程仓库 查看这一步所有的代码更改。

测试最终的插件

大功告成了!重新加载 Atom,对一个“问题描述”运行软件包的命令来看看我们最终的插件是否工作,不要忘了在编辑器右下角选择一种语言。

下一步

现在你知道怎么去 “hack” Atom 的基本原理了,通过 分叉 sourcefetch 这个仓库并添加你的特性 来随心所欲地实践你所学到的知识。


via: https://github.com/blog/2231-building-your-first-atom-plugin

作者:NickTikhonov 译者:OneNewLife 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我在红帽工作的期间,每天在 Fedora Atomic host 上使用 Docker 容器。 来自 原子项目 Project Atomic 原子主机 Atomic Host 是一个轻量级容器操作系统,可以以 Docker 格式运行 Linux 容器。它专门为提高效率而定制,使其成为用于云环境的 Docker 运行时系统的理想选择。

幸运的是,我发现一个很好的方式来管理在主机上运行的容器:Cockpit。 它是一个具有漂亮的 Web 界面的 GNU/Linux 服务器远程管理工具。它可以帮我管理在主机上运行的服务器和容器。你可以在从之前发布在这里的这篇概述中了解 Cockpit 的更多信息。不过,我也希望在主机上可以自动运行容器,我可以使用 Ansible 来完成这个工作。

请注意,我们不能在原子主机上使用 dnf 命令。原子主机并没有设计为通用操作系统,而是更适合容器和其他用途。但在原子主机上设置应用程序和服务仍然非常容易。这篇文章向您展示了如何自动化和简化这个过程。

设置组件

开始之前,请确保你的系统上安装了 Ansible。

sudo dnf -y install ansible

首先,我们需要在原子主机上运行 cockpit 容器。在你的机器上从 https://github.com/trishnaguha/fedora-cloud-ansible 下载它的源代码。

$ git clone https://github.com/trishnaguha/fedora-cloud-ansible.git

现在切换到 cockpit 的目录,并如下编辑 inventory 文件:

$ cd fedora-cloud-ansible
$ cd cockpit
$ vim inventory

完成如下修改:

  1. 使用你的原子主机的 IP 替换掉 IP_ADDRESS_OF_HOST
  2. 用您的 SSH 私钥文件的路径替换 ansible_ssh_private_key_file ='PRIVATE_KEY_FILE' 行中的PRIVATE_KEY_FILE

然后保存并退出 inventory 文件编辑。

接下来,编辑 ansible 配置文件:

$ vim ansible.cfg

替换 remote_user=UserUser 为你的原子主机上的远程用户名。然后保存并退出文件编辑。

结合起来

现在是运行 Ansible 的 PlayBook 的时候了。此命令开始运行原子主机上的 Cockpit 容器:

$ ansible-playbook cockpit.yml

Cockpit 现在运行在原子主机上了。使用浏览器去访问你的实例的公网 IP 的 9090 端口——这是 Cockpit 的默认端口。举个例子,如果该实例的 IP 地址是 192.168.1.4,就去访问 192.168.1.4:9090,你将会看到如下的 Web 界面:

管理你的容器

使用原子主机的登录信息或以 root 用户身份登录。然后访问 Cockpit 管理器上的 Containers 部分来查看原子主机上运行的容器。在下面的示例中,您会看到我还设置了其他容器,如 httpdredis

注意,该界面允许您直接在 Cockpit 管理器中使用 Run 和 Stop 按钮启动和停止容器。您还可以使用 Cockpit 管理器管理您的原子主机。转到 Tools -> Terminals,在这里里你可以使用原子主机的终端:

如果您打算在原子主机上部署容器化的应用程序,则可以简单地为其编写一个 PlayBook。然后,您可以使用 ansible-playbook 命令进行部署,并使用 Cockpit 来管理容器。

欢迎你对这个仓库进行分支或添加容器的 PlayBook。


via: https://fedoramagazine.org/deploy-containers-atomic-host-ansible-cockpit/

作者:trishnag 译者:Bestony 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出