老李

~路漫漫其修远兮~


  • 首页

  • 关于

  • 标签

  • 归档

Gitlab CI/CD之.gitlab-ci.yml复用类配置

发表于 2021-09-10 | 更新于 2021-09-28 | 评论数:

复用类配置:

锚点 Anchors

YAML 有一项名为“锚点”的功能,您可以使用它在整个文档中复制内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cache: &global_cache # 定义一个全局缓存的锚点:global_cache
untracked: true
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
policy: pull-push

install:
cache:
<<: *global_cache # 将global_cache合并到当前配置中
policy: push

lint:
cache:
<<: *global_cache # 将global_cache合并到当前配置中

&设置锚点的名称 (global_cache),<<意思是“将给定的哈希对象合并到当前哈希对象中”,*包含命名锚点(如:global_cache)。

这个例子的结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cache: &global_cache # 定义一个全局缓存的锚点:global_cache
untracked: true
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
policy: pull-push

install:
cache:
untracked: true
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
policy: push

lint:
cache:
untracked: true
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
policy: pull-push

你还可以使用锚点来定义两个服务集合。例如以下示例(test:postgres和test:mysql共享了.job_template的script,但是却有这不同的services,分别定义在了.postgres_services和.mysql_services中):。

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
.job_template: &job_configuration
script:
- test project
tags:
- dev

.postgres_services:
services: &postgres_configuration
- postgres
- ruby

.mysql_services:
services: &mysql_configuration
- mysql
- ruby

test:postgres:
<<: *job_configuration
services: *postgres_configuration
tags:
- postgres

test:mysql:
<<: *job_configuration
services: *mysql_configuration

得到的结果将是这样的:

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
.job_template:
script:
- test project
tags:
- dev

.postgres_services:
services:
- postgres
- ruby

.mysql_services:
services:
- mysql
- ruby

test:postgres:
script:
- test project
services:
- postgres
- ruby
tags:
- postgres

test:mysql:
script:
- test project
services:
- mysql
- ruby
tags:
- dev

我们可以看到隐藏的作业被方便地用作模板,并且tags: [postgres] 覆盖了tags: [dev]。

更多锚点的使用示例:

  1. YAML 锚点用于脚本(script)中:

您可以将 YAML 锚点与 script、before_script 和 after_script 结合使用,以便在多个作业中使用预定义的命令:

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
.some-script-before: &some-script-before
- echo "Execute this script first"

.some-script: &some-script
- echo "Execute this script second"
- echo "Execute this script too"

.some-script-after: &some-script-after
- echo "Execute this script last"

job1:
before_script:
- *some-script-before
script:
- *some-script
- echo "Execute something, for this job only"
after_script:
- *some-script-after

job2:
script:
- *some-script-before
- *some-script
- echo "Execute something else, for this job only"
- *some-script-after
  1. YAML 锚点用于用于变量(variables)中:

使用带有变量的 YAML 锚点在多个作业中重复分配变量。当作业需要特定的变量块时,您还可以使用 YAML 锚点,否则会覆盖全局变量。以下示例显示如何覆盖 GIT_STRATEGY 变量而不影响 SAMPLE_VARIABLE 变量的使用:

1
2
3
4
5
6
7
8
9
10
11
12
# global variables
variables: &global-variables
SAMPLE_VARIABLE: sample_variable_value
ANOTHER_SAMPLE_VARIABLE: another_sample_variable_value

# a job that must set the GIT_STRATEGY variable, yet depend on global variables
job_no_git_strategy:
stage: cleanup
variables:
<<: *global-variables
GIT_STRATEGY: none
script: echo $SAMPLE_VARIABLE

引用 !reference

使用 !reference 自定义 YAML 标签从其他作业部分选择关键字配置,并在当前部分中复用它。与锚点不同,您可以使用 !reference 标签来复用通过include引用的配置文件中的配置。

在以下示例中,test作业中重复使用了来自两个不同位置的script和after_script:

  • setup.yml:
1
2
3
.setup:
script:
- echo creating environment
  • .gitlab-ci.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
include:
- local: setup.yml

.teardown:
after_script:
- echo deleting environment

test:
script:
- !reference [.setup, script]
- echo running my own command
after_script:
- !reference [.teardown, after_script]

在以下示例中,test-vars-1 复用 .vars 中的所有变量,而 test-vars-2 选择特定变量并将其复用为新的 MY_VAR 变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.vars:
variables:
URL: 'http://my-url.internal'
IMPORTANT_VAR: 'the details'

test-vars-1:
variables: !reference [.vars, variables]
script:
- printenv

test-vars-2:
variables:
MY_VAR: !reference [.vars, variables, IMPORTANT_VAR]
script:
- printenv

您不能重复使用已经包含 !reference 标签的部分。仅支持一层嵌套。

继承 extend

使用extends来复用配置块。它是锚点(Anchor)的替代品,并且更加灵活和可读。您可以使用extends来复用通过include引用的配置文件中的配置。

在以下示例中,rspec 作业使用 .tests 模板作业中的配置。GitLab:

  • 根据键执行反向深度合并。
  • 将 .tests 内容与 rspec 作业合并。
  • 不合并键的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
.tests:
script: rake test
stage: test
only:
refs:
- branches

rspec:
extends: .tests
script: rake rspec
only:
variables:
- $RSPEC

rspec作业的结果是:

1
2
3
4
5
6
7
8
rspec:
script: rake rspec
stage: test
only:
refs:
- branches
variables:
- $RSPEC

本例中的 .tests 是一个隐藏作业,但也可以从常规作业中extends配置。

extends 支持多级继承。 虽然您可以使用多达 11 个级别,但是您应该避免使用 3 个以上的级别。以下示例具有两个继承级别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.tests:
rules:
- if: $CI_PIPELINE_SOURCE == "push"

.rspec:
extends: .tests
script: rake rspec

rspec 1:
variables:
RSPEC_SUITE: '1'
extends: .rspec

rspec 2:
variables:
RSPEC_SUITE: '2'
extends: .rspec

spinach:
extends: .tests
script: rake spinach

在 GitLab 12.0 及更高版本中,也可以使用多个父项进行扩展(extends)。

您可以用extends来合并哈希对象但是不能用来合并数组。用于合并的算法是“就近优先原则(closest scope wins)”,因此来自最后一个成员的键总是覆盖其他级别上定义的任何内容。例如:

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
.only-important:
variables:
URL: 'http://my-url.internal'
IMPORTANT_VAR: 'the details'
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH == "stable"
tags:
- production
script:
- echo "Hello world!"

.in-docker:
variables:
URL: 'http://docker-url.internal'
tags:
- docker
image: alpine

rspec:
variables:
GITLAB: 'is-awesome'
extends:
- .only-important
- .in-docker
script:
- rake rspec

结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
rspec:
variables:
URL: 'http://docker-url.internal'
IMPORTANT_VAR: 'the details'
GITLAB: 'is-awesome'
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH == "stable"
tags:
- docker
image: alpine
script:
- rake rspec

在上面示例中:

  • variables合并,但 URL:"http://docker-url.internal" 覆盖 URL:"http://my-url.internal"。
  • tags:['docker'] 覆盖tags:['production']。
  • script不会合并,但 script: ['rake rspec'] 会覆盖 script: ['echo "Hello world!"']。 您可以使用锚点来合并数组。

extends 和 include一起使用:要复用来自不同配置文件的配置,请结合extends 和 include。

在下面的示例中,在 included.yml 文件中定义了一个脚本script。然后,在 .gitlab-ci.yml 文件中,使用extends继承了那部分脚本内容:

  • included.yml:
1
2
3
.template:
script:
- echo Hello!
  • .gitlab-ci.yml:
1
2
3
4
5
include: included.yml

useTemplate:
image: alpine
extends: .template

包含 include

TODO…

总结&注意

使用include关键字时,您不能跨多个文件使用 YAML 锚点。锚点只在定义它们的文件中有效。要复用来自不同 YAML 文件的配置,请使用引用(!reference)或继承(extends)。

英文原文:
锚点 Anchors:https://docs.gitlab.com/ee/ci/yaml/#anchors
引用 !reference:https://docs.gitlab.com/ee/ci/yaml/#reference-tags
继承 extend:https://docs.gitlab.com/ee/ci/yaml/#extends
包含 include:https://docs.gitlab.com/ee/ci/yaml/#include

Gitlab CI/CD之.gitlab-ci.yml常用配置说明

发表于 2021-07-23 | 更新于 2021-10-09 | 评论数:

这里记录着我在配置.gitlab-ci.yml过程中碰到的常用的几个配置,对其进行分类解释说明,方便后续理解与查阅。

我的一个完整的.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# default stages: .pre build test deploy .post
# my stages: pre test build deploy post
stages:
- pre # install
- test # lint, sonar_check, unit_test
- build # build, build_with_sentry
- deploy # deploy_testing, deploy_production (upload to cdn or server)
- post # clean if needed

variables:
BUILD_DIR_FOR_TESTING: pre
BUILD_DIR_FOR_PRODUCTION: dist
URL_FOR_TESTING: https://test-demo.example.com/
URL_FOR_PRODUCTION: https://demo.example.com/

cache: &global_cache
untracked: true
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
policy: pull-push

.common:
tags:
- gr

############ stage: pre ############

install:
extends: .common
stage: pre
script:
- npm install
cache:
<<: *global_cache
policy: push
rules:
- if: $CI_COMMIT_REF_NAME =~ /^dev$|^master$/
changes:
- package.json
- package-lock.json
- yarn.lock
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^dev$|^master$/
changes:
- package.json
- package-lock.json
- yarn.lock

############ stage: test ############

lint:
extends: .common
stage: test
cache:
<<: *global_cache
script:
- echo "==== 代码校验 ===="
- npm run lint
rules:
- if: $CI_COMMIT_REF_NAME =~ /^dev$|^master$/
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^dev$|^master$/

sonar_check:
extends: .common
stage: test
script:
- echo "==== SONAR ANALYSIS ===="
allow_failure: true
when: manual
only:
- dev
- master

############ stage: build ############

build_testing:
extends: .common
stage: build
cache:
<<: *global_cache
policy: pull
script:
- echo "==== 测试构建 ===="
- npm run pre
artifacts:
name: '$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA-testing'
expire_in: 1 week
paths:
- $BUILD_DIR_FOR_TESTING/
only:
- dev

build_production:
extends: .common
stage: build
cache:
<<: *global_cache
policy: pull
script:
- echo "==== 生产构建 ===="
- npm run build
artifacts:
name: '$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA-production'
expire_in: 1 week
paths:
- $BUILD_DIR_FOR_PRODUCTION/
only:
- master

############ stage: deploy ############

deploy_testing:
extends: .common
stage: deploy
script:
- echo "测试环境代码发布"
after_script:
- echo "发布成功:"$URL_FOR_TESTING
environment:
name: testing
url: $URL_FOR_TESTING
when: manual
only:
- dev

deploy_production:
extends: .common
stage: deploy
script:
- echo "生产环境代码发布"
after_script:
- echo "发布成功:"$URL_FOR_PRODUCTION
environment:
name: production
url: $URL_FOR_PRODUCTION
when: manual
only:
- master

Gitlab CI/CD之.gitlab-ci.yml复用类配置:

  • Anchors
  • !reference
  • extend
  • include

条件类配置:

  • only
  • except
  • rules
  • when

脚本类配置:

  • before_script
  • script
  • after_script

缓存类配置:

  • cache
  • artifacts

Git工作流之派生工作流

发表于 2021-05-18 | 更新于 2021-09-23 | 评论数:

派生(forking)工作流与其他流行的 Git 工作流根本不同。它没有使用单个服务器端仓库作为“中央”代码库,而是为每个开发人员提供了自己的服务器端仓库。这意味着每个贡献者都是两个 Git 仓库:一个私有本地仓库和一个公共服务器端仓库。派生工作流最常见于公共开源项目中。

派生工作流的主要优点是可以集成贡献,而无需每个人都将其推送到单个中央仓库。开发人员将推送到他们自己的服务器端仓库,只有项目维护者才能推送到官方仓库。这使维护者可以接受任何开发人员的提交,而无需授予他们对官方代码库的写访问权限。

派生工作流通常遵循基于《Git工作流之GitFlow工作流》的分支模型。这意味着将使用完整的功能分支来合并到原始项目维护者的仓库中。结果是一个分布式工作流,为大型组织团队(包括不受信任的第三方)提供灵活的方式来安全地进行协作。这也使其成为开源项目的理想工作流程。

运作原理

与其他 Git 工作流程一样,派生工作流从存储在服务器上的官方公共仓库开始。但是,当新的开发人员希望开始从事该项目时,他们不会直接克隆官方仓库。

相反,他们派生(fork)了官方仓库以在服务器上创建它的副本。此新副本将用作其个人公共存储库-不允许其他开发人员将其推送到其中,但他们可以从中进行更改(我们稍后将解释为什么这很重要)。在创建服务器端副本之后,开发人员执行,git clone将其副本复制到本地计算机上。就像其他工作流程一样,这是他们的私有开发环境。

准备发布本地提交时,他们会将提交推送到自己的公共仓库中,而不是正式的仓库中。然后,他们向主仓库提交合并请求(pull request),这使项目维护者知道已准备好要集成更新。如果所贡献的代码存在问题,则合并请求还可以用作方便的讨论线程。以下是此工作流程的分步示例。

  1. 开发人员派生(fork)一个“官方”服务器端存储库(这将创建自己的服务器端副本);
  2. 新的服务器端副本将克隆(git clone)到其本地;
  3. “官方”存储库的 Git 远程路径添加到本地(git remote add origin xxx);
  4. 创建一个新的本地功能分支(feature branch);
  5. 开发人员在新功能分支上进行更改;
  6. 将为更改创建新的提交(git add && git commit);
  7. 分支被推送到开发人员自己的服务器端(副本);
  8. 开发人员打开从新分支到“官方”存储库的合并请求(pull request);
  9. 合并请求被批准用于合并,并被合并到原始服务器端存储库中。

为了将完成的功能集成到官方代码库中,维护人员将贡献者的更改拉入他们的本地存储库,检查以确保它不会破坏项目,将其合并到其本地 master 分支中,然后将其推送到服务器上的官方存储库中。贡献现在是项目的一部分,其他开发人员应从官方存储库中提取信息以同步其本地存储库。

重要的是要了解,Forking 工作流中“正式”存储库的概念仅仅是一个约定。实际上,使正式存储库如此正式的唯一原因是它是项目维护者的公共存储库。

派生与克隆(Forking vs cloning)

需要注意:“派生”不是特殊操作。派生的存储库是使用标准的git clone命令创建的。派生的存储库通常是“服务器端克隆”,通常由诸如Bitbucket、Gitlab 之类的第三方 Git 服务进行管理和托管。

在派生工作流中分支

就像功能分支工作流(Feature Branch Workflow)和 Gitflow 工作流一样,开发者仍然应该使用分支来隔离各个功能。唯一的区别是这些分支如何共享。在派生(Forking)工作流中,中央存储库被拉到另一个开发人员的本地存储库中,而在功能分支工作流和 Gitflow 工作流中,它们被推到官方存储库中。

派生存储库(Fork a repository)

派生(Forking)工作流项目的所有新开发人员都需要派生正式存储库。如前所述,派生只是一种标准git clone操作。可以通过 SSH进入服务器并运行git clone将其复制到服务器上的另一个位置来实现。流行的 Git 托管服务提供了自动执行此步骤的派生功能。

克隆派生仓库(Clone your fork)

1
git clone https://user@bitbucket.org/user/repo.git

添加远程跟踪分支(Adding a remote)

其他 Git 工作流程使用指向中央存储库的单个原始远程服务器,而 Forking 工作流则需要两个远程服务器-一个用于官方存储库,一个用于开发人员的个人服务器端存储库。虽然您可以随意调用这些远程服务器,但通常的约定是使用origin作为派生存储库的远程服务器(运行git clone时将自动创建该远程服务器),并使用上游作为正式存储库的远程服务器。

1
2
3
git remote add upstream https://bitbucket.org/maintainer/repo
# 身份验证
# git remote add upstream https://user@bitbucket.org/maintainer/repo.git

在分支中工作:开发和推送更改(making & pushing changes)

就像其他 Git 工作流程中一样,他们可以在开发人员的本地化分支存储库副本中编辑代码,提交更改并创建分支:

1
2
git checkout -b some-feature # Edit some code
git commit -a -m "Add first draft of some feature"

他们所做的所有更改都将完全是私有的,直到将其推送到其公共存储库中为止。而且,如果正式项目向前发展,他们可以使用git pull访问新的提交:

1
git pull upstream master

由于开发人员应在专用功能分支中工作,因此通常应导致快速合并。

发起合并请求(Making a Pull Request)

pull request

一旦开发人员准备好共享他们的新功能,他们需要做两件事。首先,他们必须将其贡献推送到其公共存储库中,以使其他开发人员可以访问他们的贡献。他们的源远程跟踪库应该已经设置好了,所以他们要做的就是以下操作:

1
git push origin feature-branch

其次,他们需要通知项目维护者他们想将其功能合并到官方代码库中。Git 托管服务(如 Bitbucket、Gitlab)提供了一个“合并请求(pull request)”按钮,该按钮会提交一个表单,要求您指定要合并到官方存储库中的分支。通常,您需要将功能分支集成到上游远程的主分支中。

总结

回顾一下,派生(Forking)工作流通常在公共开源项目中使用。派生是在项目存储库的服务器副本上执行的git clone操作。派生工作流通常与 Bitbucket 之类的 Git 托管服务结合使用。

派生(forking)工作流的高级示例是:

  1. 您想为托管在bitbucket.org/userA/open-project上的开源库做出贡献;
  2. 使用 Bitbucket,您可以创建一个仓库的分支到bitbucket.org/YourName/open-project;
  3. 在您的本地,您可以运行git clone以获取仓库的本地副本;
  4. 您在本地仓库中创建一个新的功能分支;
  5. 完成新功能的工作已完成,并运行git commit执行了保存更改的工作;
  6. 然后,您将新功能分支推送到您的远程派生仓库中;
  7. 使用 Bitbucket 您可以在bitbucket.org/userA/open-project上针对原始仓库对新分支发起一个合并请求(pull request)。

派生(forking)工作流可帮助项目的维护者打开存储库以接受任何开发人员的贡献,而不必手动管理每个贡献者的授权设置。这为维护人员提供了更多的“拉”式工作流程。派生(forking)工作流最常用于开放源代码项目,也可以应用于私有业务工作流,以从对合并到发行进行更权威的控制,这对于具有发布部署管理或严格发布周期的团队很有用。

不确定哪种工作流程适合您?查看我们全面的Git 工作流比较页面。

英文原文

Git工作流之GitFlow工作流

发表于 2021-05-17 | 更新于 2021-09-23 | 评论数:

GitFlow 工作流(GitFlow Workflow)是一个 Git 工作流程,可帮助持续进行软件开发和实施 DevOps 实践。它最初由 Vincent Driessen 在 nive 上发行并广受欢迎。Gitflow 工作流定义了围绕项目发行版设计的严格分支模型。这为管理较大的项目提供了一个强大的框架。

Gitflow 非常适合具有预定发布周期的项目以及DevOps 连续交付的最佳实践。此工作流程未添加《Git工作流之功能分支工作流》所需的任何新概念或命令。而是将非常具体的角色分配给不同的分支,并定义它们应如何以及何时进行交互。除了功能分支外,它还使用单独的分支来准备、维护和记录版本。当然,您还可以利用功能分支工作流(Feature Branch Workflow)的所有好处:合并请求、功能试验和更有效的协作。

入门

Gitflow 实际上只是 Git 工作流的抽象概念。这意味着它决定了要建立哪种分支以及如何将它们合并在一起。我们将触及以下分支的目的。 git-flow工具集是具有安装过程的实际命令行工具,安装过程很简单,git-flow 的软件包可在多个操作系统上使用。

  • Mac OS: brew install git-flow
  • Windows: 下载并安装 git-flow

安装 git-flow 后,您可以通过执行git flow init在项目中使用它。Git-flow 是基于 Git 的包装,git flow init命令是git init命令的扩展,除了为您创建分支外,不会更改仓库中的任何内容。

运作原理

how it works

develop 和 master 分支

该工作流使用两个分支而不是单个主(master)分支来记录项目的历史记录。master 分支存储正式的发行历史,而 develop 分支则充当功能的集成分支。使用版本号标记 master 分支中的所有提交也很方便。

第一步是用一个develop分支来补充默认的 master。一种简单的方法是让一个开发人员在本地创建一个空的 develop 分支并将其推送到服务器:

1
2
git branch develop
git push -u origin develop

该分支将包含项目的完整历史记录,而 master 将包含简化版本。现在,其他开发人员应该克隆中央仓库,并为develop创建跟踪分支。

使用 git-flow 扩展库时,在现有存储库上执行git flow init将创建develop分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ git flow init


Initialized empty Git repository in ~/project/.git/
No branches exist yet. Base branches must be created now.
Branch name for production releases: [master]
Branch name for "next release" development: [develop]


How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []


$ git branch
* develop
master

功能分支(feature branches)

每个新功能应驻留在其自己的分支中,可以将其推送到中央仓库以进行备份与协作。但是,功能分支不是基于master分支,而是将develop分支用作其父分支。当功能开发完成后,它会重新合并到develop分支中。功能绝不能直接与master分支进行交互。

feature branches

请注意,出于所有意图和目的,将功能分支(feature branch)与开发分支(develop)结合使用是功能分支工作流的处理方式。但是,Gitflow 工作流并不止于此。

通常创建功能分支是基于最新的开发分支(develop)。

创建一个功能分支

1
2
3
4
5
6
# 不使用git-flow 扩展工具方式:
git checkout develop
git checkout -b feature_branch

# 使用git-flow 扩展工具方式:
git flow feature start feature_branch

完成一个功能分支

完成功能开发工作后,下一步就是将功能分支(feature branch)合并到开发分支(develop)中。

1
2
3
4
5
6
# 不使用git-flow 扩展工具方式:
git checkout develop
git merge feature_branch

# 使用git-flow 扩展工具方式:
git flow feature finish feature_branch

发布分支(release branches)

release branches

一旦开发分支(develop)获得了足够的功能以进行发布(或临近预定的发布日期),就可以从开发分支(develop)中派生(fork)一个发布分支(release)。创建此分支将开始下一个发行周期,因此此刻之后不能添加任何新功能-该分支中仅应包含错误修复、文档生成以及其他面向发行版的任务。一旦准备好发布,发布分支将合并到master并用版本号标记。此外,应该将其重新合并到开发分支(develop)中。

使用专门的分支来准备发布,使一个团队可以完善 ​​ 当前版本,而另一个团队可以继续开发下一个版本的功能。

创建发布分支是另一种直接的分支操作。像功能分支一样,发布分支也基于开发分支。可以使用以下方法创建新的发布分支。

1
2
3
4
5
6
7
# 不使用git-flow 扩展工具方式:
git checkout develop
git checkout -b release/0.1.0

# 使用git-flow 扩展工具方式:
$ git flow release start 0.1.0
Switched to a new branch 'release/0.1.0'

一旦release分支准备好发布,它将被合并到 master 中并进行开发,然后发布分支将被删除。重新合并到develop分支中很重要,因为关键更新可能已添加到 release 分支,并且新功能需要访问这些更新。如果您的团队要求代码评审,那么这将是提出发起合并请求的理想场所。

要完成release分支,请使用以下方法:

1
2
3
4
5
6
# 不使用git-flow 扩展工具方式:
git checkout master
git merge release/0.1.0

# 使用git-flow 扩展工具方式:
git flow release finish '0.1.0'

热修分支(hotfix branches)

hotfix branches

维护或热修分支(hotfix)用于快速修补生产版本。热修分支(hotfix)与发布分支(release)以及功能(feature)分支很像,只是它们基于master而不是develop分支。这是唯一应直接基于master而创建的分支。修复完成后,应将其合并到master和develop(或当前 release 分支)中,并应使用更新的版本号标记 master。

拥有专用的错误修复开发线(hotfix 分支),您的团队可以在不中断其余工作流程或不等待下一个发布周期的情况下解决问题。您可以将热修分支(hotfix)视为直接与 master 一起使用的临时发布分支。可以使用以下方法创建热修分支:

1
2
3
4
5
6
# 不使用git-flow 扩展工具方式:
git checkout master
git checkout -b hotfix_branch

# 使用git-flow 扩展工具方式:
git flow hotfix start hotfix_branch

与完成发布分支相似,热修分支也合并到master和develop中。

1
2
3
4
5
6
7
8
9
# 不使用git-flow 扩展工具方式:
git checkout master
git merge hotfix_branch
git checkout develop
git merge hotfix_branch
git branch -D hotfix_branch

# 使用git-flow 扩展工具方式:
git flow hotfix finish hotfix_branch

示例

完整示例如下,假设我们有一个带有 master 分支的仓库。

1
2
3
4
5
6
7
8
9
git checkout master
git checkout -b develop
git checkout -b feature_branch
# work happens on feature branch 发生在功能分支的工作
git checkout develop
git merge feature_branch
git checkout master
git merge develop
git branch -d feature_branch

除了功能开发和发布流程外,热修(hotfix)示例如下:

1
2
3
4
5
6
7
git checkout master
git checkout -b hotfix_branch
# work is done commits are added to the hotfix_branch
git checkout develop
git merge hotfix_branch
git checkout master
git merge hotfix_branch

总结

在这里,我们讨论了 Gitflow 工作流。 Gitflow 是您和您的团队可以利用的多种《Git工作流》之一。

了解 Gitflow 的一些关键点:

  • 该工作流程非常适合基于发行版的软件工作流程。
  • Gitflow 为生产修复程序提供了专用渠道(热修分支)。

Gitflow 的总体流程为:

  1. 一个基于主分支(master)创建的开发分支(develop);
  2. 一个基于开发分支创建的发布分支(release);
  3. 功能分支(feature branch)基于开发分支创建;
  4. 功能完成后,将其合并到开发分支中;
  5. 完成发布分支后,将它合并到master和develop中;
  6. 如果检测到主分支(master)出现问题,则会从主分支中创建一个热修分支(hotfix);
  7. 热修分支完成后,将它合并到master和develop中。

接下来,了解分《Git工作流之派生工作流》

英文原文

Git工作流之功能分支工作流

发表于 2021-05-15 | 更新于 2021-09-23 | 评论数:

Git 功能分支工作流(Feature Branch Workflow)背后的核心思想是所有功能开发应在专用分支而不是master分支中进行。这种封装使多个开发人员可以轻松地使用特定功能,而又不会干扰主代码库。这也意味着master分支将永远不会包含残破的代码,这对于持续集成环境是一个巨大的优势。

功能分支开发还可以利用合并请求(pull request),这是围绕分支发起讨论的一种方式。这为其他开发人员提供了在功能集成到正式项目中之前对其进行审查的机会。甚至,如果您在开发功能过程中卡住了,则可以开启一个合并请求(pull request),以征询同事的建议。更关键是,合并请求操作使您的团队可以非常轻松地就彼此的工作发表讨论。

Git 功能分支工作流是可组合的工作流程,其他高级 Git 工作流程也可以利用。Git 功能分支工作流是针对分支模型的,这意味着它是用于管理和创建分支的指导框架,其他工作流则更以仓库为中心。 Git 功能分支工作流可以合并到其他工作流程中。实际上,GitFlow 工作流(GitFlow Workflow)和Git 派生工作流(Forking Workflow)在其分支模型方面使用了Git 功能分支工作流(Feature Branch Workflow)。

运作原理

Git 功能分支工作流(Feature Branch Workflow)假设有一个中央仓库,而master则代表正式的项目历史记录,开发人员在每次开始使用新功能时都创建一个新分支,而不是直接在其本地master分支上提交。功能分支应具有描述性名称,例如animated-menu-items、issue-#1061或feature_xxxx,这样命名的目的是明确每一个分支的功能和作用。 Git 在主分支和功能分支之间没有技术上的区别,因此开发人员可以编辑、暂存和提交对功能分支的更改。

另外,可以(并且应该)将功能分支推送到中央仓库,这样就可以与其他开发人员共享功能,而无需更改任何正式代码。由于master是唯一的“特殊”分支,因此在中央仓库中存储多个功能分支不会造成任何问题。当然,这也是备份每个人的本地提交的便捷方法。以下是功能分支的生命周期。

从主(master)分支开始

所有功能分支都是根据项目的最新代码状态创建的。这里假设 master 分支即为我们项目的主分支。

1
2
3
git checkout master # 切换到 master 分支
git fetch origin # 提取最新的提交
git reset --hard origin/master # 重置仓库的master本地副本

这会将仓库切换到 master 分支,提取最新的提交,并重置仓库的master本地副本以匹配最新版本。

创建新分支

针对每一个新功能(或新 bug )创建一个单独的分支。创建分支后,请在本地将其检出(checkout),以便您所做的任何更改都将在该分支上。

1
git checkout -b new-feature

这会检出一个基于master的名为new-feature的分支,并且-b标志告诉 Git 创建该分支(如果该分支不存在)。

更新

在该分支上,以正常的方式编辑、暂存和提交更改,并根据需要构建多次提交。

1
2
3
git status  # 查看状态
git add <some-file> # 暂存文件
git commit # 提交更改

将功能分支推送到远程(中央仓库)

最好将功能分支推送到中央仓库。与其他开发人员协作时,这既可以用作备份,也能使其他人查看到新分支的提交。

1
git push -u origin new-feature

这样便将新功能推送到中央仓库(origin),并且加上-u标志将其添加为(上游)远程跟踪分支。设置跟踪分支后,后面提交代码时,无需任何其他参数即可通过git push命令来自动将更新操作推送到远程中央仓库。

接着,到 git 仓库管理平台(如 Bitbucket、Gitlab)去创建合并请求,将功能分支合并到master主分支。可以在提合并请求时指定审批人以确保顺利合并。(其间,如果有冲突,需要先解决冲突再提交)

合并请求

除了隔离功能开发之外,Git 分支还可以通过合并请求来讨论更改。某人完成一项功能后,不会立即将其合并到主功能中,相反,他们将功能分支推送到中央仓库,并提出合并请求,要求将其更改合并到主分支中。这使其他开发人员有机会在其更改成为主代码的一部分之前对其进行代码审查(code review)。

代码审查是合并请求的主要好处,但实际上,它们是被设计用来讨论代码的通用方法。您可以将合并请求视为用于专门讨论特定分支的讨论地。这意味着它们也可以在开发过程中更早地被使用。例如,如果开发人员需要特定功能的帮助,则他们要做的就是提交合并请求。感兴趣的各方将被自动通知,他们将能够在相关提交旁边看到问题。

一旦合并请求被接受后,发布一个功能的实际操作与《Git工作流之集中式工作流》中的操作几乎相同。(关于合并请求,首先,您需要确保本地主机与上游主机同步。然后,将功能分支合并到 master 中,并将更新后的 master 推回中央仓库。仓库管理平台<如 Bitbucket、Gitlab>可以更好的简化该操作)。

示例

以下是使用功能分支工作流的示例,该场景是一个团队围绕新功能合并请求(pull request)进行代码审查(code review)的场景:

小丽开始一个新功能开发

model

在开始开发功能之前,小丽需要一个新的分支来进行工作,她可以使用下面命令创建一个新功能分支:

1
git checkout -b xl-feature master

这样便基于master分支创建了一个叫xl-feature的分支,-b标记是告诉 git 如果分支不存在就创建分支。

小丽为了完成新功能,在分支上进行开发、修改以及提交操作(可能重复多次):

1
2
3
git status
git add <some-file>
git commit

小丽中途吃午饭

到了午饭时间,当小丽有事要停下功能开发时,在她离开前最好将代码提交到中央仓库,一是方便代码备份;二是有利于其他开发人员看到她的初始提交。

1
git push -u origin xl-feature

此命令将xl-feature推送到中央仓库,并且使用参数-u标志将其添加为远程跟踪分支。设置跟踪分支后,小丽后面可以调用git push而无需添加任何其他参数来推送其功能。

小丽完成新功能

finish_xl

当小丽吃完午饭回来,完成了剩余功能开发。在将其合并为 master 之前,她需要提交合并请求,以让团队的其他成员知道她已经完成了。但是首先,她应该确保中央仓库中有她的最新提交:

1
git push

然后,她通过仓库管理平台(如 Bitbucket、Gitlab)发起合并请求将xl-feature合并到master,团队其他人便可自动收到通知。合并请求的好处在于,它们在相关的提交旁边会显示注释,因此很容易提出有关特定变更的问题。

小王收到合并请求

pr_xw

小王收到合并请求,然后查看一下xl-feature,他决定在将其集成到正式项目之前要进行一些更改,并且他和小丽通过合并请求来回进行了一些操作。

小丽做出更改

change_xl

为了进行更改,小丽使用了该功能的第一次迭代完全相同的过程,她编辑、暂存、提交并将更新推送到中央仓库。她的所有操作行为都显示在合并请求中,并且小王在此过程中仍然可以发表评论。

如果他愿意,小王可以将xl-feature添加到他的本地仓库中,然后自己进行处理。他添加的所有提交也将显示在合并请求中。

小丽发布(已完成)功能

pf_xl

一旦小王准备好接受合并请求,就需要有人将功能合并到稳定的项目中(这可以由小王或小丽来完成):

1
2
3
4
git checkout master
git pull
git pull origin xl-feature
git push

某些 GUI (仓库管理平台,如 Bitbucket、Gitlab)只需单击“接受”按钮,即可通过运行所有这些命令来自动执行合并请求接受过程。

同时,小明也在做完全相同的事情(进行新功能开发)。

小王和小丽正在研究小丽开发的功能,并在其合并请求中对其进行讨论时,小明则在自己的功能分支中做着完全相同的事情。我们可以看到,通过将功能隔离到单独的分支中,每个人都可以独立工作,在必要时再与其他开发人员共享更改。

总结

在本文中,我们讨论了Git 功能分支工作流(Feature Branch Workflow)。此工作流有助于组织和跟踪专注于业务域功能集的分支。其他 Git 工作流(如派生工作流(Forking Workflow)和GitFlow 工作流(GitFlow Workflow))是针对仓库的,可以利用 Git 功能分支工作流(Feature Branch Workflow)来管理其分支模型。本文档演示了用于实现Git 功能分支工作流(Feature Branch Workflow)的高级代码示例和虚构示例。与功能分支工作流相关的一些关键:

  • 专注于分支模式
  • 可以被其他面向仓库的工作流程所利用
  • 通过合并请求和合并审查促进与团队成员的协作

在功能分支的审阅和合并阶段使用 git rebase 将强制创建功能合并的 Git 历史记录。功能分支模型是在团队环境中促进协作的出色工具。

接下来,我们看看《Git工作流之GitFlow工作流》来更深入的了解 Git 工作流。

英文原文

Git工作流之集中式工作流

发表于 2021-05-14 | 更新于 2021-09-23 | 评论数:

对于从 SVN 过渡的团队来说,集中式工作流(Centralized Workflow)是一个很棒的 Git 工作流。与Subversion一样,集中式工作流使用中央仓库作为项目所有更改的单个入口点。类比 svn 的 trunk,它 使用master作为默认的开发分支且所有提交操作都在该分支上。 这种工作流除了master分支外,不需要任何其他分支。

集中式工作流在使用远程服务器端托管仓库方面与其他工作流类似,开发人员可以通过这种仓库推送(push)和拉取(pull)资源。

与其他工作流程相比,集中式工作流程没有定义的合并请求(pull request)或派生(fork)模式。集中式工作流通常更适合从 SVN 迁移到 Git 的团队以及规模较小的团队。

运作原理

初始化中央仓库

初始化中央仓库

1
ssh user@host git init --bare /path/to/repo.git

一般我们将仓库托管到 git 仓库托管平台(如 Bitbucket、Gitlab),在托管平台上面初始化中央仓库。

克隆中央仓库

接下来,每个开发人员都将创建整个项目的本地副本。通过以下git clone命令完成的:

1
git clone ssh://user@host/path/to/repo.git

当 clone 一个仓库时,假设您要在以后与之进行进一步的交互,Git 会自动添加一个叫origin的快捷方式,并指向“父”仓库。

修改与提交

一旦在本地克隆了仓库,开发人员就可以使用标准的 Git 提交过程进行更改:编辑(edit),暂存(stage)和提交(commit)。

暂存:实际上就是我改了 3 个文件,但是我只想提交 2 个文件,就可以先git add将指定的 2 个文件暂存,然后使用git commit就只会提交暂存的那 2 个文件,这样便避免了提交非必要文件。

1
2
3
git status # 查看仓库状态
git add # 暂存
git commit # 提交

请记住,由于这些命令创建了本地提交,小王可以根据需要多次重复此过程,而不必担心中央仓库中的情况。这对于需要分解为更简单,更原子的块的大型功能非常有用。

推送新提交到中央仓库

将本地更新推送到远程中央仓库中。

1
git push origin master

该命令会将本地新提交的更改推送到远程中央仓库。在此推送过程中可能出现一种特殊情况就是小丽(另一开发者)在小王之前推送过代码,此时 Git 将输出一条消息,指出此冲突。

冲突情况下,应该先执行 Git 拉取命令:

1
git pull

下一节将对此冲突情形进行扩展。

冲突管理

中央仓库代表官方项目,因此其提交历史应视为神圣且不可改变。如果开发人员的本地提交偏离中央仓库,则 Git 将拒绝推送其更改,因为这将覆盖官方提交。

冲突管理

在开发人员发布功能之前,他们需要获取更新的中央提交并在其基础上重新进行更改。这就像在说:“我想将自己的更改添加到其他所有人已经完成的操作中。”与传统的 SVN 工作流程一样,结果是完美的线性历史记录。

如果本地更改直接与上游提交冲突,则 Git 将暂停变基过程(the rebasing process),并为您提供手动解决冲突的机会。关于 Git 的好处是,它使用相同的git status和git add命令来生成提交和解决合并冲突。这使新开发人员可以轻松管理自己的合并。另外,如果他们遇到麻烦,Git 可以很轻松地中止整个重新部署并重试(或寻求帮助)。

举例说明

让我们举一个例子,说明典型的小型团队如何使用此 Git 工作流进行协作。我们将看到小王和小丽这两个开发人员如何使用单独的功能并通过集中式仓库共享他们的贡献。

小王

scfp_xw

在他的本地仓库中,小王可以使用标准的 Git 提交过程(编辑、暂存和提交)来开发功能。

1
2
3
git status # 查看仓库状态
git add # 暂存
git commit # 提交

请记住,由于这些命令创建了本地提交,因此小王可以根据需要多次重复此过程,而不必担心中央仓库中的情况。

小丽

scfp_xl

同时,小丽使用相同的(编辑、暂存和提交)过程在自己的本地仓库中开发自己的功能。像小王一样,她不在乎中央仓库中发生的事情,她也不用在乎小王在其本地仓库中正在做什么,因为所有本地仓库都是私有的。

小王发布他完成的功能

pf_xw

小王完成其功能后,应将其本地提交发布到中央仓库,以便其他团队成员可以访问它。他可以使用git push命令执行此操作,如下所示:

1
git push origin master

请记住,源(origin)是与小王克隆 Git 仓库时创建的中央仓库的远程连接。master参数告诉 Git 尝试使源(origin)的master分支对照本地的master分支。由于小王克隆以来中央仓库尚未更新,因此不会造成任何冲突,并且推送将按预期进行。

小丽尝试发布她完成的功能

pce_xl

让我们看看如果小王成功将其更改发布到中央仓库后小丽尝试发布其功能,会发生什么情况。她可以使用完全相同的push命令:

1
git push origin master

但是,由于她的本地历史与中央仓库有所不同,因此 Git 会以相当冗长的错误消息拒绝该请求:

1
2
3
4
5
error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

这样可以防止小丽覆盖正式提交。她需要将小王的更新放入仓库中,并将其与本地更改集成,然后重试。

小丽重新以小王的提交为基准

小丽可以使用git pull将上游更改合并到她的仓库中。这个命令有点像svn update-将整个上游提交历史记录拉到小丽的本地仓库中,并尝试将其与她的本地提交集成:

1
git pull --rebase origin master

--rebase选项告诉 Git 在将其与中央仓库中的更改同步后,将所有小丽的提交都移至 master 分支的末端,如下所示:

rebase

如果您忘记了此选项--rebase,pull仍然有效,但是每次有人需要与中央仓库同步时,您都会得到多余的“合并提交”。对于此工作流程,最好重新设置基准,而不是生成合并提交。

小丽解决合并冲突

rc_xl

变基(rebase)工作是通过将每个本地提交一次一个的转移到最新的(master)主分支。这意味着您可以逐个提交地解决合并冲突,而不是在一个大规模合并提交中解决所有冲突。这样可以使您的提交尽可能集中,并保证项目历史记录的整洁。反过来,这使得找出错误的位置变得容易得多,并且在必要时以对项目的影响最小的方式回滚更改。

如果小丽和小王正在开发不相关的功能,则重新定基过程不太可能产生冲突。但如果产生了冲突,Git 将在当前提交时暂停定基(rebase),并输出以下消息以及一些相关说明:

1
CONFLICT (content): Merge conflict in xxx

conflict_resolution

Git 的伟大之处在于,任何人都可以解决自己的合并冲突。在我们的示例中,小丽只需运行git status即可查看问题出在哪里。冲突的文件将显示在“未合并的路径”部分中:

1
2
3
4
5
# Unmerged paths:
# (use "git reset HEAD ..." to unstage)
# (use "git add/rm ..." as appropriate to mark resolution)
#
# both modified:

然后,她将根据自己的喜好编辑文件。对结果感到满意后,她可以按照常用的方式暂存文件(git add),然后让git rebase完成其余工作:

1
2
git add
git rebase --continue # 合并冲突,结合 git add 命令一起用于修复冲突(fix conflicts and then run "git rebase --continue")

以上就是一个提交的冲突处理的整个流程,Git 将继续进行下一个提交,并针对产生冲突的任何其他提交重复该过程。

当您(在冲突处理过程中)意识到感觉不对、不知道发生了什么时,不要惊慌,只需执行以下命令,您便会回到开始的位置:

1
git rebase --abort # 放弃合并,回到rebase操作之前的状态,之前的提交不会丢弃;

小丽成功发布已完成的功能

scr_xl

与中央仓库同步后,小丽将能够成功发布其更改:

1
git push origin master

总结

如您所见,仅使用少量 Git 命令即可复制传统的 Subversion 开发环境。这对于将团队从 SVN 过渡过来非常有用,但它没有利用 Git 的分布式特性。

集中式工作流程非常适合小型团队。当您的团队规模扩大时,上面详述的冲突解决过程可能会成为瓶颈。如果您的团队对集中式工作流程感到满意,但想简化其协作工作,那么绝对值得探索Git工作流之功能分支工作流。通过为每个功能指定一个独立的分支,可以在将新功能集成到正式项目中之前就新功能进行深入的讨论。

英文原文

Git工作流

发表于 2021-05-13 | 更新于 2021-09-23 | 评论数:

Git 工作流程是有关如何使用 Git 以一致且富有成效的方式完成工作的秘诀或建议。Git 工作流程鼓励用户有效,一致地利用 Git。

Git 在用户管理变更方面提供了很大的灵活性,鉴于 Git 对灵活性的关注,没有关于如何与 Git 交互的标准化流程。所以 与团队合作进行 Git 管理的项目时,重要的是要确保团队就如何应用变更流程达成一致。 为确保团队在同一页面上,应开发或选择一个已达成共识的 Git 工作流程。

什么是成功的 Git 工作流?

在为团队评估工作流时,最重要的是要考虑团队的文化。您希望工作流能够提高团队的效率,而不是限制生产力的负担。评估 Git 工作流程时应考虑以下几点:

  • 此工作流程是否随团队规模扩展?
  • 使用此工作流程是否容易减少犯错和错误?
  • 此工作流程是否会给团队带来新的不必要的认知开销?

软件团队常见 Git 工作流

  • 集中式工作流(Centralized Workflow)
  • 功能分支工作流(Feature Branch Workflow)
  • GitFlow 工作流(GitFlow Workflow)
  • 派生工作流(Forking Workflow)

Centralized Workflow
Feature Branch Workflow
GitFlow Workflow
Forking Workflow

至于团队应该使用哪种工作流,这要取决于团队具体情况来定。最好的不一定好,最适合的才最好,不同团队有不同的工作流。像我们团队采用的工作流并非上面 4 选 1,而是结合(功能分支、派生工作流)起来形成了适合自己的工作流。

英文原文

npm peerDependencies

发表于 2021-05-13 | 更新于 2021-09-23 | 评论数:

希望通过此文阐明当你要使用 npm 的时候知道什么是npm peerDependencies,弄明白什么时候以及为什么使用它。
peerDependencies(翻译:“同行依赖”或“对等依赖”)是package.json文件中的peerDependencies属性对象(列表)。
为了更好的理解此文,你至少要先了解npm

本文内容:

  1. 首先,我们将准确比较dependencies与peerDependencies的工作方式。
  2. 其次,我们将看到一些dependencies与peerDependencies的示例。
  3. 然后,我们将研究 npm 如何处理版本冲突。
  4. 最后,通过上面的基础铺垫,我们将看看如何合理的使用peerDependencies。

设想

为了保持真实性,假设您正在创建一个 Angular 库,甚至只是一个简单的 JavaScript 文件,其中导出了一些功能(函数)。

您的项目依赖于npm Registry中的软件包,这些软件包是您项目的dependencies依赖项。

你想从你的项目中创建你自己的 npm 包,所以你使用npm pack生成了一个 npm 包,你甚至想将它发布到npm Registry中。

团队中其他人想在他们项目中使用你的 npm 包作为他们项目的依赖。你在package.json使用dependencies和peerDependencies来告诉使用你的 npm 包的其他项目,要使你的 npm 包正常工作,则dependencies和peerDependencies中列出的包也需要安装。

因此,在最基本的层次上,这是dependencies与peerDependencies的工作方式:

Dependencies

Dependencies是package.json文件中的dependencies对象,当你添加一个 npm 包到dependencies对象中时,仿佛在说:

  • 我的代码需要这个包才能运行。
  • 如果该 npm 包在我的node_modules目录中尚不存在,请自动添加。
  • 此外,添加dependencies中列出的 npm 包,这些 npm 包称为依赖项(transitive dependencies)。

peerDependencies

peerDependencies是package.json文件中的peerDependencies对象,当你添加一个 npm 包到peerDependencies对象中时,仿佛在说:

  • 我的代码与此版本的 npm 包兼容。
  • 如果此 npm 包已存在于node_modules中,则什么也不用做。
  • 如果该软件包在node_modules目录中尚不存在或版本错误,请不要添加它。但是,需要向用户提示未找到该 npm 包的警告。

添加依赖

添加 dependencies

依赖包是我们使我们项目能够正常运行的一个 npm 包,通常作为dependencies添加的一些受欢迎的程序包有lodash、request和moment等等。

我们添加一个常规依赖项,如下所示:

1
npm install lodash

npm 将包名称和版本添加到我们项目的 package.json 文件中的dependencies对象中:

1
2
3
"dependencies": {
"lodash": "^4.17.11"
}

你们中的有些人可能还记得过去,我们不得不使用--save标志来更新 package.json 中的依赖项。值得庆幸的是,我们不再需要这样做了。

添加 peerDependencies

同行依赖包(peerDependencies)用于指定我们的程序包与 npm 程序包的特定版本兼容,Angular和React就是很好的例子。

要添加peerDependencies,您实际上需要手动修改package.json文件。例如,对于 Angular 组件库项目,我建议添加angular/core作为peerDependencies。因此,如果您想指定您的程序包是为 Angular 7 构建的,则可以包含以下内容:

1
2
3
"peerDependencies": {
"@angular/core": "^7.0.0"
}

关于冲突

我对某个 npm 包应该进入dependencies还是进入peerDependencies提出了很多疑问,做出此决定的关键在于了解 npm 如何处理版本冲突。

冲突测试

首先,我们创建一个简单的测试项目:conflict-test

我是这样创建的:

1
2
3
md conflict-test
cd conflict-test
npm init -y

然后,我手动编辑package.json文件并添加了两个依赖项:

1
2
3
4
"dependencies": {
"todd-a": "^1.0.0",
"todd-b": "^1.0.0"
}

这两个todd-a和todd-b软件包也具有它们自己的依赖项:

1
2
3
4
5
6
7
8
9
10
11
// todd-a:
"dependencies": {
"lodash": "^4.17.11",
"todd-child": "^1.0.0"
}

// todd-b:
"dependencies": {
"lodash": "^4.17.11",
"todd-child": "^2.0.0"
}

我想让您注意的是,todd-a和todd-b使用的是lodash的相同版本。但是,它们对于todd-child有版本冲突:

  • todd-a使用todd-child版本1.0.0
  • todd-b使用todd-child版本2.0.0

现在我知道,像我一样,您非常感兴趣,看看 npm 如何处理此版本冲突。在我的项目conflict-test中运行npm install,就像我们期望的那样,npm 在我们的node_modules文件夹中神奇地安装了todd-a和todd-b软件包。它还添加了它们所依赖的程序包(transitive dependencies)。因此,在运行npm install之后,我们来看一下node_modules文件夹。看起来像这样:

1
2
3
4
5
6
7
node_modules
├── lodash 4.17.11
├── todd-a 1.0.0
├── todd-b 1.0.0
│ └── node_modules
│ └── todd-child 2.0.0
└── todd-child 1.0.0

有趣的是,我们的项目有一个lodash副本。但是,它有两个todd-child副本。请注意,todd-b获得了其自己的todd-child 2.0.0的专用副本。

因此:

npm 通过添加有冲突的程序包的重复私有版本来处理版本冲突。

peerDependencies

从我们对 npm 版本冲突的实验中可以看出,如果将软件包添加到依赖项中,则有可能最终将其复制到node_modules中。

有时,具有相同软件包的两个版本也可以。但是,在同一代码库中有两个不同版本的某些程序包会导致冲突。

例如,假设我们的组件库是使用Angular 5创建的。当有人将其添加为Angular 6应用的依赖项时,我们不希望我们的程序包添加另一个完全不同的angular/core版本。

关键是:当该程序包可能与现有版本冲突并导致问题时,我们不希望我们的库添加另一个版本的程序包到node-modules。

选择peerDependencies还是dependencies?

当我的程序包依赖于另一个程序包时,应该将其放在dependencies还是peerDependencies中?

好吧,就像大多数技术问题一样:看情况。

peerDependencies表示兼容性。例如,您将希望具体说明您的库与哪个版本的Angular兼容。

指导方案

当满足以下条件之一时,建议使用peerDependencies:

  • 多个依赖包可能会导致冲突
  • 依赖关系清晰明了
  • 您希望开发人员决定要安装哪个版本

让我们以angular/core为例。显然,如果您正在创建 Angular Library,则angular/core将成为您的库界面中非常明显的一部分。因此,它属于peerDependencies。

但是,也许您的库在内部使用Moment.js来处理一些与时间相关的输入,Moment.js很可能不会在 Angular Services 或组件的界面中公开。因此,它属于dependencies。

Angular 作为依赖

假设您要在文档中指定您的库是一组 Angular 组件和服务,您可能会问以下问题:

我是否甚至需要将angular/core指定为依赖项?如果有人在使用我的库,他们将已经有一个现有的 Angular 项目。

好问题!

是的,我们通常可以假设对于我们特定于 Angular 的库,Workspace 将已经有可用的 Angular 软件包。因此,从技术上讲,我们无需费心将它们添加到我们的依赖项列表中。

但是,我们确实想告诉开发人员我们的库与哪些 Angular 版本兼容。因此,我建议采用以下方法:

至少为peerDependencies添加兼容的 Angular 版本的angular/core。

这样,如果开发人员尝试在其 Angular 6 项目中使用 Angular 7 库,则会看到警告。不必添加其他 Angular 软件包。您可以假定它们是否具有angular/core,是否具有其他 Angular 库。

总结

如有疑问,您可能应该倾向于使用peerDependencies。这样,您的软件包的用户就可以选择添加哪些软件包。

英文原文:https://indepth.dev/posts/1187/npm-peer-dependencies

Linux(CentOS 7)安装redis过程记录

发表于 2020-12-10 | 评论数:

前两步非必须,因为我的系统是新的,所以需要做一些配置和更新。

一、查看系统版本信息

1
2
3
4
5
6
# 查看系统信息
cat /proc/version
# Linux version 3.10.0-229.20.1.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.3 20140911 (Red Hat 4.8.3-9) (GCC) )

# 查看版本
cat /etc/redhat-release # CentOS Linux release 7.9.2009 (Core)

二、修改 yum 安装源

使用国内源,安装更新包更快。我直接按照阿里云 CentOS 镜像的操作。

三、安装 redis

可以到这里找到最新下载地址:
redis下载

1
2
3
4
5
6
7
8
# 下载
wget https://download.redis.io/releases/redis-6.0.9.tar.gz
# 解压
tar xzf redis-6.0.9.tar.gz
# 进入redis项目
cd redis-6.0.9
# 编译
make

错误 1:zmalloc.h:50:10: 致命错误:jemalloc/jemalloc.h:没有那个文件或目录

解决方法:

1
2
3
4
5
6
7
# 在`make`之前,先编译依赖
make persist-settings

# 然后再运行make
make install
# 默认安装到目录“/usr/local”,如果需要安装到其它目录,可指定 make 的参数“PREFIX”,如:
make install PREFIX=/usr/local/redis

错误 2:server.c:1032:31: 错误:‘struct redisServer’没有名为‘logfile’的成员

解决方法:升级依赖

1
2
3
4
5
6
yum -y install centos-release-scl devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils

scl enable devtoolset-9 bash

# 以上为临时启用,如果要长期使用gcc 9.1的话:
echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile

三、运行

我的 redis 安装在/opt/目录下:

1
2
# 启动redis
/opt/redis-6.0.9/src/redis-server /opt/redis-6.0.9/redis.conf

四、修改为后台启动

编辑配置文件 redis.conf

1
vim /opt/redis-6.0.9/redis.conf

将daemonize no修改为daemonize yes,保存之后,重新启动。

五、参考

  • https://developer.aliyun.com/mirror/centos?spm=a2c6h.13651102.0.0.3e221b11ClmKNM
  • https://redis.io/download
  • https://www.cnblogs.com/aquester/p/13581147.html
  • https://www.cnblogs.com/shook/p/12883742.html

渐进增强和优雅降级

发表于 2020-12-10 | 更新于 2021-09-23 | 评论数:

概念

拿浏览器兼容举例:

  • 渐进增强(progressive enhancement):针对低版本浏览器进行构建页面,保证最基本的功 能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
  • 优雅降级(graceful degradation):一开始就构建完整的功能,然后再针对低版本浏览器 进行兼容。

区别

优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的、能够起作用的版本开始,并不断扩充,以适应未来环境的需要。优雅降级(功能衰减)意味着往回看;而渐进增强则意味着朝前看,同时保证其根基处于安全地带。

“优雅降级”观点

“优雅降级”观点认为应该针对那些最高级、最完善的浏览器来设计网站。而将那些被认为“过时”或有功能缺失的浏览器下的测试工作安排在开发周期的最后阶段,并把测试对象限定为主流浏览器(如 IE、Mozilla 等)的前一个版本。 在这种设计范例下,旧版的浏览器被认为仅能提供“简陋却无妨(poor, but passable)”的浏览体验。你可以做一些小的调整来适应某个特定的浏览器。但由于它们并非我们所关注的焦点,因此除了修复较大的错误之外,其它的差异将被直接忽略。

“渐进增强”观点

“渐进增强”观点则认为应关注于内容本身,内容是我们建立网站的诱因。有的网站展示它,有的则收集它,有的寻求,有的操作,还有的网站甚至会包含以上的种种,但相同点是它们全都涉及到内容。这使得“渐进增强”成为一种更为合理的设计范例。这也是它立即被 Yahoo! 所采纳并用以构建其“分级式浏览器支持(Graded Browser Support)”策略的原因所在。

那么问题来了。现在产品经理看到 IE6,7,8 网页效果相对高版本现代浏览器少了很多圆角, 阴影(CSS3),要求兼容(使用图片背景,放弃 CSS3),你会如何说服他?

资料

  • Graceful degradation versus progressive enhancement
  • Progressive Enhancement vs Graceful Degradation

内容整理来源于网络。

123
Cleam Lee

Cleam Lee

前端生涯记录总结

30 日志
18 标签
RSS
GitHub Twitter 微博
© 2021 Cleam Lee