官网文档:GitLab CI/CD 之 Pipeline Configuration Reference
GitLab CI/CD is configured by a file called .gitlab-ci.yml placed at the repository’s root. This file creates a pipeline, which runs for changes to the code in the repository. Pipelines consist of one or more stages that run in order and can each contain one or more jobs that run in parallel. These jobs (or scripts) get executed by the GitLab Runner agent.
Concepts | Description |
---|---|
Pipelines | 通过pipeline来构建我们的CI/CD流程 |
Environment variables | 全局变量,定义一些重复使用的变量 |
Environments | 将我们的项目部署到不同的环境当中(开发、测试、正式环境等) |
Job artifacts | 我们build完项目的jar包、跑完UT得到的report等,都可以放到这里面。以供我们后续的使用 |
Cache dependencies | 将我们下载的资源进行缓存 |
GitLab Runner | 定义我们自己的Gitlab Runner来执行我们的脚本/流程 |
GitLab CI/CD pipelines是通过每个项目中一个叫做:.gitlab-ci.yml 的文件进行配置的。
Pipeline configuration begins with jobs. Jobs are the most fundamental element of a .gitlab-ci.yml file.
job1:
script: "execute-script-for-job1"
job2:
script: "execute-script-for-job2"
每一个job必须有唯一的名字,同时一些保留字是不能够用作jobs的名字的
一个Job定义了一系列的参数来定义job的行为(job里面的参数,而不是job的名字),如下:
Keyword | Description |
---|---|
script | 用于在Runner中执行的Shell脚本 |
image | 使用docker的镜像,支持的参数:image:name, image:entrypoint |
services | 使用docker的服务镜像,支持的参数:services:name, services:alias, services:entrypoint, services:commond |
before_script | 定义一些命令,使得它们在每次执行job之前会执行一遍 |
after_script | 定义一些命令,使得它们在每次执行job之后会执行一遍 |
stage | Defines a job stage (default: test). (不知道应该怎么翻译,看例子可能会更好理解一点) |
only | jobs执行的限制条件,支持的参数:only:refs, only:kubernetes, only:variables, only:changes |
except | jobs创建前的限制条件,支持的参数:except:refs, except:kubernetes, except:variables, except:changes |
rules | 设定一系列的规则,以此来判断是否能够创建/执行这个job,不能够与only / except共用 |
tags | 用来选定使用的是哪一个Runner(一个GitLab中,可以创建多个Runner,有私有的和公共的) |
allow_failure | 允许这个job执行失败,且不会影响正常的流程 |
when | 什么时候去执行这个job,支持的参数:when:manual, when:delayed |
environment | 这个job具体部署的环境的名称,支持的参数:environment:name, environment:url, environment:on_stop, environment:auto_stop_in, environment:action |
cache | 需要被缓存的文件,能够被后续的jobs所引用,支持的参数:environment:name, environment:url, environment:on_stop, environment:auto_stop_in, environment:action |
artifacts | 当这个job执行成功后,其结果所保存的地方,支持的参数:artifacts:paths, artifacts:expose_as, artifacts:name, artifacts:untracked, artifacts:when, artifacts:expire_in, artifacts:reports, artifacts:reports:junit, artifacts:reports:cobertura, and artifacts:reports:terraform. |
dependencies | 定义这个job所依赖的job是哪一个,例如:我们先build项目,然后再deploy项目。那么deploy就依赖于build,如果build失败了,那么deploy也不会执行 |
coverage | Code coverage settings for a given job/允许您配置如何从作业输出中提取代码覆盖率(看了具体的说明,也没有很懂具体的作用是什么 (TODO)) |
retry | 设定job失败后,可以重新执行的次数 |
timeout | 定义job执行的超时时间,此优先级比项目中设定的pipelines timeout要高(项目中,pipelines默认的超时时间是1小时。修改路径:Settings > CI/CD > General pipelines settings) |
parallel | 定义这个job会被多少个线程并行执行 |
trigger | Defines a downstream pipeline trigger(不懂具体的作用(TODO)) |
include | 允许这个job包含其他的YAML文件,支持的参数:include:local, include:file, include:template, and include:remote. |
extends | 将会继承整个对象job的配置 |
pages | 将job执行的结果,上传到执行的位置 |
variables | 定义job level的变量 |
interruptible | Defines if a job can be canceled when made redundant by a newer run(不懂具体的作用(TODO)) |
resource_group | 限制job的并发执行。(官方进一步解释:在.gitlab-ci中为作业定义resource_group键时。yml中,作业执行在同一项目的不同管道中是互斥的。如果属于同一资源组的多个作业同时进入队列,则运行程序只会选择其中一个作业,而其他作业将一直等待,直到resource_group空闲。) |
一些参数可以用于全局定义,以达到影响所有jobs的执行
可以使用 default: 关键字将某些参数全局设置为所有jobs的默认值。这种设置,可以在特定的job中被重新定义。
相关的参数有:
如:
ruby:2.5只会作用于rspec job中,rspec将会使用ruby:2.6
default:
image: ruby:2.5
rspec:
script: bundle exec rspec
rspec 2.6:
image: ruby:2.6
script: bundle exec rspec
我们可以使用inherit:参数,在某个job中决定使用/不使用全局定义的variables: & default: 参数
全盘肯定/否定的写法
inherit:
default: true
variables: true
inherit:
default: false
variables: false
部分肯定/否定的写法:
inherit:
default: [parameter1, parameter2]
variables: [VARIABLE1, VARIABLE2]
或者
inherit:
default:
- parameter1
- parameter2
variables:
- VARIABLE1
- VARIABLE2
例子:
# default 定义了image & before_script
default:
image: 'ruby:2.4'
before_script:
- echo Hello World
# variables 定义了DOMAIN & WEBHOOK_URL两个变量
variables:
DOMAIN: example.com
WEBHOOK_URL: https://my-webhook.example.com
# rubocop的inherit中,default & variables 都为false,所以将不会用到上面全局定义的变量
rubocop:
inherit:
default: false
variables: false
script: bundle exec rubocop
# rspec 继承default:image & variables:WEBHOOK_URL两个变量
rspec:
inherit:
default: [image]
variables: [WEBHOOK_URL]
script: bundle exec rspec
# capybara 继承default定义的所有变量,不会继承variables定义的变量
capybara:
inherit:
variables: false
script: bundle exec capybara
# capybara 继承default定义的所有变量,以及variables的DOMAIN变量,不会继承variables的WEBHOOK_URL变量
karma:
inherit:
default: true
variables: [DOMAIN]
script: karma
stages中定义了哪些stages可以在jobs中使用
需要注意的是:
例如:
stages:
- build
- test
- deploy
build_project:
stage: build
script: build_project
unit_test:
stage: test
script: run unit test
int_test:
stage: test
script: run int test
deploy_project:
stage: deploy
script: deploy project
定义自己的规则,以此来判断这个job是否执行。
rules 支持定义一些列的判断条件,运行时将会根据顺序做判断,直到找到符合条件的那个。
rules 不能同时和 only/except 一起使用。如果一起使用的话,将会抛出异常:key may not be used with rules
目前支持的配置选项
例子:
job:
script: "echo Hello, Rules!"
rules:
# 假如第一个 if 匹配上的话,这个stage就always执行
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
when: always
# 如果第一个 if 条件没有匹配上,则会判断第二个 if 条件。
# 如果第二个 if 条件匹配上的话,这个stage将不会自动结束,直到我们手动触发且成功
- if: '$VAR =~ /pattern/'
when: manual
# 最后,如果上面的两个 if 条件都没有匹配上的话,则会自动执行这个stage,因为没有if条件了
- when: on_success
rules:if 与 only:variables 的区别仅在于,rules:if只支持单个表达式判断,后者支持数组
rules:if 只能通过 && 或者 ** || ** 进行结合判断
例子
job:
script: "echo Hello, Rules!"
rules:
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' # 这个rules将会被匹配上
when: always
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/' # 主要当目标分支不是master时,才会匹配此规则
when: manual
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' # 如果前两个判断条件都不匹配,且这个 if 条件只有简单的变量,那么它将默默认为:on_success
如果所有条件都不匹配,则默认为:when:never
rules:change 与 only:changes, except: changes的用法是一样的,都能够接受数组路径
例子
docker build:
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
rules:
- changes: # 当Dockerfile发现改变时,stage将会转成手动触发
- Dockerfile
when: manual
- when: on_success
rules:exists 接受数组路径,任意一个文件匹配上即可
例子
job:
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
rules:
- exists:
- Dockerfile
# 也可以使用模糊匹配路径,看匹配此路径下的所有*.rb文件
job:
script: bundle exec rspec
rules:
- exists:
- spec/**.rb
为了考虑效率问题,使用 exist 将会默认匹配10000次。超过10000次后,都将算是匹配成功
我们可以使用 rules:allow_failure 命令,它允许我们的job执行失败,或者让一个需要手动触发的job进行等待,而无需停止整个pipeline的执行。
如果没有定义这个配置的话,所有的job默认 allow_failure 为 false
例子
job:
script: "echo Hello, Rules!"
rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
when: manual
allow_failure: true
docker build:
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
rules:
- if: '$VAR == "string value"'
changes: # Will include the job and set to when:manual if any of the follow paths match a modified file.
- Dockerfile
- docker/scripts/*
when: manual
# - when: never would be redundant here, this is implied any time rules are listed.
# 上面这个例子,当 $VAR == "string value 且 (Dockerfile 文件 或者 docker/scripts/* 匹配到的文件,有改动时),此stage将会转成手动触发。否则的话,这个stage将不会被执行
官网给出的解释是:A very important difference between rules and only/except, is that jobs defined with rules trigger merge request pipelines by default, but only/except jobs do not((⊙o⊙)…不太懂,官网给的例子网页也打不开…)
使用include配置,可以引入外部的YAML文件。这么做有两个好处:
要求:
include:需要外部的YAML文件是以:.yml 或者 .yaml 结尾的,否则将不会被引入。
支持的参数
Method | Description |
---|---|
local | 引入同一个项目中的文件 |
file | 引入不同项目的文件 |
remote | 引入一个可访问的远程文件 |
template | 引入GitLab提供的模板文件 |
例子:
# include:local
include:
- local: '/templates/.gitlab-ci-template.yml'
# 或者,更简便的写法
include: '.gitlab-ci-production.yml'
# include:file
include:
- project: 'my-group/my-project'
file: '/templates/.gitlab-ci-template.yml'
# 也可以使用 ref 来指定branch
include:
- project: 'my-group/my-project'
ref: master
file: '/templates/.gitlab-ci-template.yml'
# include:remote
include:
- remote: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
# include:template
# File sourced from GitLab's template collection
include:
- template: Auto-DevOps.gitlab-ci.yml
include:
- template: Android-Fastlane.gitlab-ci.yml
- template: Auto-DevOps.gitlab-ci.yml
指定docker的image,用于job的执行
script 是一个job必须要有的参数,它里面定义的是可以在Runner中执行的 shell脚本
例子
job:
script: "bundle exec rspec"
# 一个job执行多个脚本时
job:
script:
- uname -a
- bundle exec rspec
当任何脚本命令返回的结果不为0时,这个job将会失败且影响到接下来的其他job的运行
官网:可以定义一个为0的全局变量来避免这种情况(我:没有特殊情况的话,我使用allow_failure是不是也可以达到同样的效果?)
例子
job:
script:
- false || exit_code=$?
- if [ $exit_code -ne 0 ]; then echo "Previous command failed"; fi;
before_script: 定义执行命令,在每个job执行前执行
after_script:定义执行命令,在每个job执行后执行(包括跑失败的job)。
after_script:是在一个新的shell中执行,与 before_script 和 script 相隔离开来
例子
default:
before_script:
- global before script
job:
before_script:
- execute this instead of global before script
script:
- my command
after_script:
- execute this after my script
每个job都会定义 stage,stage 所使用的变量是 stages 全局定义的变量。这让Runner可以执行不同 stage 的脚本,以及能够让相同 stage 的job并行执行。
例子
stages:
- build
- test
- deploy
# job1 & job2会并行执行
job 1:
stage: build
script: make build dependencies
job 2:
stage: build
script: make build artifacts
job 3:
stage: test
script: make test
job 4:
stage: deploy
script: make deploy
Runner有分私有的和公共的,当你使用私有的Runner时,它每次只会执行一个job。
Jobs只有在一下的情况会并行执行:
.pre 和 .post被允许执行于所有的pipeline
只要定义了 .pre 和 .post,它们在stages中定义的顺序,并不会影响它们在首尾执行的顺序
例子
stages:
- .pre
- a
- b
- .post
# 和
stages:
- a
- .pre
- b
- .post
# 所执行的顺序是一样的:.pre > a > b > .post
如果一个pipeline只有 .pre 和 .post,则这个pipeline将不会创建/执行
extends 可以将其他的job继承到自身job内
例子
# 定义了一个.tests的stage
.tests:
script: rake test
stage: test
only:
refs:
- branches
# 这里用extends: .tests,继承了.tests里面的配置
rspec:
extends: .tests
script: rake rspec
only:
variables:
- $RSPEC
# 最终的结果为:
rspec:
script: rake rspec
stage: test
only:
refs:
- branches
variables:
- $RSPEC
# 可以看到,script执行的是:rake rspec,这是因为被script: rake rspec给重写了。(感觉就像Java的抽象类,子类继承父类,相同的方法名、参数将会被重写)
extends支持嵌套继承,官方不建议超过3层嵌套。嵌套最多可支持10层。
例子
.tests:
only:
- pushes
# 这里继承了.tests
.rspec:
extends: .tests
script: rake rspec
# 这里继承了.rspec
rspec 1:
variables:
RSPEC_SUITE: '1'
extends: .rspec
# 结果是:
rspec 1:
variables:
RSPEC_SUITE: '1'
script: rake rspec
only:
- pushes
extends也支持同时继承多个stage
.only-important:
only:
- master
- stable
tags:
- production
.in-docker:
tags:
- docker
image: alpine
rspec:
extends:
- .only-important
- .in-docker
script:
- rake rspec
# 结果为:
rspec:
only:
- master
- stable
tags:
- docker
image: alpine
script:
- rake rspec
extends的例子中,官网使用了 .xxx 开头的stage名字。官网也是有对其做解释的:
如果你想要暂时的停掉某个stage的运行,那么有两种方式:
# 整段注释掉(简单粗暴)
#hidden_job:
# script:
# - run test
# 使用.xxx的命名方式(优雅)
.hidden_job:
script:
- run test
上面提到:
include可以引入同项目、不同项目、远程、GitLab官方提供的YAML模板
extends可以继承其他stage的配置
例子
# 假设这是一个待被引入的yaml文件,称之为:include.yml
# 同时这里定义的是“被注释”的stage
.template:
script:
- echo Hello!
# 在我们本地的.gitlab-ci.yml文件中
# 先将同项目中的yml文件引入
include: included.yml
# 使用继承
useTemplate:
image: alpine
extends: .template
# 结果为:
useTemplate:
image: alpine
script:
- echo Hello!
官方推荐:rules 能够更好、更强大的定义何时执行一个job。尽可能地使用 rules 来代替 only/except
only 和 except 是两个用来定义是否创建 job 的参数
关于 only 与 except 的一些使用规则
only 和 except 允许使用的关键字
Value | Description |
---|---|
branches | 当管道的Git引用是一个分支时 |
tags | 当管道的Git引用是一个tag时,即一个被打了tag的分支 |
api | 当这个pipeline被其他pipeline所触发时 |
external | 当使用GitLab以外的CI服务时 |
pipelines | 对于多项目触发器,使用带有CI_JOB_TOKEN的API创建 |
pushes | 当一个pipeline被用户用 git push 提交时 |
schedules | 用于特定的某个时间段触发的pipeline |
triggers | 作用于使用trigger token创建的pipeline |
web | 在GitLab UI中使用Run pipeline按钮创建的管道 |
merge_requests | 当创建/修改一个merge request时 |
external_pull_requests | 当在GitHub上创建或更新外部pull request时 |
chat | 当jobs是被 GitLab ChatOps 命令 所创建时 |
例子
# Case#1
job:
# 使用了正则表达式匹配,匹配以:issue- 开头的branchs, tags..
only:
- /^issue-.*$/
# Case#2
# 正则表达式匹配默认是区分大小写的,使用 i 可以使得忽略大小写而进行匹配
job:
only:
- /^issue-.*$/i
# Case#3
# 这个例子只能作用于主项目,而不能作用于fork的项目,以及主项目的master分支
job:
only:
- branches@gitlab-org/gitlab
except:
- master@gitlab-org/gitlab
- /^release/.*$/@gitlab-org/gitlab
如果一个job没有设置任何的 only 规则,那么默认会设置为:only: [‘branches’, ‘tags’]
如果没有 except 规则,那么将什么都不会默认设置。
例子
job:
script: echo 'test'
# 将会转化成:
job:
script: echo 'test'
only: ['branches', 'tags']
因为 @ 被用来表示项目的根目录,所以使用正则表达式中包含 @ 字符的路径,则需要使用 \x40 代替。
只有 tag 和 branch 能够使用正则表达式匹配。
使用正则表达式时,需要用 / 包裹着整个正则表达式。像:issue-/.*/ 没有被包裹住,所以它并不会起到作用。
Warning: 这是测试版本的功能,官方随时会进行修改!
GitLab supports both simple and complex strategies, so it’s possible to use an array and a hash configuration scheme.
有4个可用参数
当 only 和 except 下有多个条件时,表现形式为:
例子
test:
script: npm run test
only:
refs:
- master
- schedules
variables:
- $CI_COMMIT_MESSAGE =~ /run-end-to-end-tests/
kubernetes: active
# only
# (any of refs) AND (any of variables) AND (any of changes) AND (if Kubernetes is active)
test:
script: npm run test
except:
refs:
- master
- schedules
variables:
- $CI_COMMIT_MESSAGE =~ /run-end-to-end-tests/
kubernetes: active
# except
# NOT((any of refs) AND (any of variables) AND (any of changes) AND (if Kubernetes is active))
在GitLab 10.0 版本引进 refs
例子
deploy:
only:
refs:
- master
- schedules
# 这个job只有在,branch = master 或者 这个job由 scheduled 触发时,才会执行
在GitLab 10.0 版本引进 kubernetes
kubernetes 仅支持 active 参数
例子
deploy:
only:
kubernetes: active
# 这个job只有在,kubernetes服务在运作时,才会执行
在GitLab 10.7 版本引进 variables
variables 关键字,是用于定义变量的。我们可以通过定义一些全局变量,搭配上 only:variables/except:variables,来最终决定是否创建和执行这个job。
例子
deploy:
script: cap staging deploy
only:
refs:
- branches
variables:
- $RELEASE == "staging"
- $STAGING
在GitLab 11.4 版本引进 changes
在 only 和 except 上定义 changes,可以通过每一次Git push引起的某个文件改变,来触发job的创建以及执行。
例子
docker build:
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
only:
changes:
- Dockerfile
- docker/scripts/*
- dockerfiles/**/*
- more_scripts/*.{rb,py,sh}
# 当Dockerfile发生改变时
# 当任何在docker/scripts/ 路径下的文件发生改变时
# 当任何在dockerfiles路径下、子目录下的文件发生改变时
# 当任何在more_scripts路径下,以rb, py, sh结尾的文件发生改变时
我们也可以使用全局定义的方式,但是需要使用双引号(" ")包裹住匹配路径
例子
# only example
test:
script: npm run test
only:
changes:
- "*.json"
- "**/*.sql"
# except example
build:
script: npm run build
except:
changes:
- "*.md"
例子
# 当我们提交的merge_requests中,包含了对:Dockerfile 或者 service-one目录下的文件 的改动
# 那么将会执行script中的脚本
docker build service one:
script: docker build -t my-service-one-image:$CI_COMMIT_REF_SLUG .
only:
refs:
- merge_requests
changes:
- Dockerfile
- service-one/**/*
needs 允许无序的执行jobs。
needs 可以让我们忽视jobs的执行顺序,以达到多个步骤可以并发的执行。
例子
linux:build:
stage: build
mac:build:
stage: build
lint:
stage: test
needs: []
linux:rspec:
stage: test
needs: ["linux:build"]
linux:rubocop:
stage: test
needs: ["linux:build"]
mac:rspec:
stage: test
needs: ["mac:build"]
mac:rubocop:
stage: test
needs: ["mac:build"]
production:
stage: deploy
For Omnibus installations
sudo gitlab-rails console
# 设置为50个
Feature::disable(:ci_dag_limit_needs)
# 设置为10个
Feature::enable(:ci_dag_limit_needs)
For installations from source
sudo -u git -H bundle exec rails console -e production
# 设置为50个
Feature::disable(:ci_dag_limit_needs)
# 设置为10个
Feature::enable(:ci_dag_limit_needs)
当某个job使用了needs,是否下载它所依赖job的Artifact将由 artifacts: true || artifacts: false 控制,默认是:true
例子
build_job:
stage: build
artifacts:
paths:
- binaries/
# rspec将会下载build_job中的artifact
rspec:
stage: test
needs:
- job: build_job
artifacts: true
# rubocop将不会下载build_job中的artifact
rubocop:
stage: test
needs:
- job: build_job
artifacts: false
# rspec2将会下载build_job的artifact,因为其中的artifacts是默认为true的
rspec2:
needs:
- job: build_job_1
用needs依赖于不同项目的jobs
build_job:
stage: build
script:
- ls -lhR
needs:
- project: group/project-name
job: build-1
ref: master
artifacts: true
使用tags
选择项目可使用的Runner
。
当我们注册Runner过程中,会要求我们输入Runner对应的tags
。具体注册Runner操作,可参考Gitlab runner的注册 & 使用
在下面的例子中,job
这个步骤将会在被定义了ruby & postgres
两个tags
下的Runner
中执行。
job:
tags:
- ruby
- postgres
我们也可以使用tags
来实现让不同步骤在不同的平台上执行。例子如下:
windows job:
stage:
- build
tags:
- windows
script:
- echo Hello, %USERNAME%!
osx job:
stage:
- build
tags:
- osx
script:
- echo "Hello, $USER!"
当执行CI/CD过程中,我们允许某一个步骤失败时,可以使用allow_failure
。这样,即使这个步骤执行失败了,也不会影响接下来的其他步骤继续执行。allow_failure
默认值为:false
。
正如下面的例子:job1
和 job2
并行执行时,如果job1
执行失败了,它不会影响到接下来的其他步骤。
job1:
stage: test
script:
- execute_script_that_will_fail
allow_failure: true
job2:
stage: test
script:
- execute_script_that_will_succeed
job3:
stage: deploy
script:
- deploy_to_staging
when
用于实现在失败时或即使失败也要运行的作业。
when
可以用于设置以下的变量
on_success
:(默认值) - 只有当之前的步骤都执行成功了,或者是之前的步骤设置了allow_failure: true
时。会执行此步骤。on_failure
:当之前的步骤至少有某一步失败了,才会执行此步骤。always
:不管前面的步骤成功与否,都会执行此步骤。manual
:手动执行此步骤。delayed
:延迟执行此步骤。never
:
例子:
stages:
- build
- cleanup_build
- test
- deploy
- cleanup
build_job:
stage: build
script:
- make build
cleanup_build_job:
stage: cleanup_build
script:
- cleanup build when failed
when: on_failure
test_job:
stage: test
script:
- make test
deploy_job:
stage: deploy
script:
- make deploy
when: manual
cleanup_job:
stage: cleanup
script:
- cleanup after jobs
when: always
上面的步骤:
build_job
执行失败时,cleanup_build_job
会执行。cleanup_job
无论如何都会执行deploy_job
只有当我们在Gitlab UI上手动触发时会执行。当使用了rules:
,allow_failure
将默认为:false
,包括manual jobs
。
只有当用户拥有merge
的权限,才能够触发manual job
。我们可以使用protected branches
功能来防止未授权的用户随意触发manual job
。
使用Protected Environments可以定义一组有权限操作manual job
的用户。
使用when:delayed
让步骤等待一段时间再执行。
可以通过start_in
的方式,设置等待时间。默认是:秒
,或者主动提供时间单位
。start_in
设置的时间必须小于等于一个星期
。
可以设置的值的例子:
看官网文档进行学习、实践的过程。尽可能地记下自己觉得有用的内容,及翻译。
尽量地强迫自己看官方/英文文档,学习一手资料。