深入淺出FE(十六)深入淺出YAML

YAML 並不是一種新奇的語言,YAML 首次發表於 2001 年,距離現在已經過去差不多 20 個年頭。YAML 雖然不如 JSON、XML 之類的語言流行,應用也沒有那麼廣泛,但是 YAML 也有它的優勢。

一、簡介

YAML 是一種較爲人性化的數據序列化語言,可以配合目前大多數編程語言使用。

YAML 的語法比較簡潔直觀,特點是使用空格來表達層次結構,其最大優勢在於數據結構方面的表達,所以 YAML 更多應用於編寫配置文件,其文件一般以 .yml 爲後綴。

YAML 目前的官方全稱爲 “ YAML Ain't Markup Language(YAML 不是標記語言)”,但有意思的是,其實 YAML 最初的含義是 “ Yet Another Markup Language(還是一種標記語言)”。
目前 YAML 的最新版本爲 1.2(第三個版本),本文將以  YAML 1.2 的標準進行講解。

二、基本語法

大小寫敏感

  • 就是字面上的意思
One: 1
one: 2

用縮進表示層級關係

  • 縮進只能使用空格,不能用 TAB 字符
  • 縮進的空格數量不重要,但是同一層級的元素左側必須對齊
# YAML
one:
  two: 2
  three:
    four: 4
    five: 5

// 以上的內容轉成 JSON 後
"one": {
  "two": 2,
  "three": {
    "four": 4,
    "five": 5 
  }
}

用 # 表示註釋

  • 只支持單行註釋
# 我是註釋
# 我也是註釋

一個文件中可以包含多個文件的內容

  • 用“ --- ”即三個破折號表示一份內容的開始
  • 用“ ... ”即三個小數點表示一份內容的結束(非必需)
---
# 這是第一份內容
one: 1
# 其他內容...
...

---
# 這是第二份內容
two: 2
# 其他內容...

數據結構與類型

對象(Mapping)

表示以鍵值對(key: value)形式出現的數據

  • 使用“冒號+空格”來分開
# YAML
key: value

// JSON
"key": "value"
  • 支持多層嵌套(用縮進表示層級關係
# YAML
key:
  child-key1: value1
  child-key2: value2

// JSON
"key": {
  "child-key1": "value1",
  "child-key2": "value2",
}
  • 支持流式風格( Flow style)的語法(用花括號包裹,用逗號加空格分隔,類似 JSON)
# YAML
key: { child-key1: value1, child-key2: value2 }

// JSON
"key": { "child-key1": "value1", "child-key2": "value2" }
  • 使用問號“?”聲明一個複雜對象,允許你使用多個詞彙(數組)來組成鍵
# YAML
?
  - keypart1
  - keypart2
:
  - value1
  - value2

數組(Sequence)

  • 一組以區塊格式(Block Format)(即“破折號+空格”)開頭的數據組成一個數組
# YAML
values:
  - value1
  - value2
  - value3

// JSON
"values": [ "value1", "value2", "value3" ]
  • 同時也支持內聯格式(Inline Format)來表達(用方括號包裹,逗號加空格分隔,類似 JSON)
# YAML
values: [value1, value2, value3]

// JSON
"values": [ "value1", "value2", "value3" ]
  • 支持多維數組(用縮進表示層級關係
# YAML
values:
  -
    - value1
    - value2
  -
    - value3
    - value4

// JSON
"values": [ [ "value1", "value2"], ["value3", "value4"] ]

標量(Scalars)

表示 YAML 中最基本的數據類型

字符串(String)

  • 字符串一般不需要用引號包裹,但是如果字符串中使用了反斜槓“\”開頭的轉義字符就必須使用引號包裹
# YAML
strings:
  - Hello without quote # 不用引號包裹
  - Hello
   world # 拆成多行後會自動在中間添加空格
  - 'Hello with single quotes' # 單引號包裹
  - "Hello with double quotes" # 雙引號包裹
  - "I am fine. \u263A" # 使用雙引號包裹時支持 Unicode 編碼
  - "\x0d\x0a is \r\n" # 使用雙引號包裹時還支持 Hex 編碼
  - 'He said: "Hello!"' # 單雙引號支持嵌套"

// JSON
"strings":
  [ "Hello without quote",
    "Hello world",
    "Hello with single quotes",
    "Hello with double quotes",
    "I am fine. ☺",
    "\r\n is \r\n",
    "He said: 'Hello!'" ]
  • 對於多行的文字,YAML 提供了兩種特殊的語法支持

保留換行(Newlines preserved)

使用 豎線符“ | ”來表示該語法,每行的縮進和行尾空白都會被去掉,而額外的縮進會被保留
# YAML
lines: |
  我是第一行
  我是第二行
    我是吳彥祖
      我是第四行
  我是第五行

// JSON
"lines": "我是第一行\n我是第二行\n  我是吳彥祖\n     我是第四行\n我是第五行"

摺疊換行(Newlines folded)

使用 右尖括號“ > ”來表示該語法,只有空白行纔會被識別爲換行,原來的換行符都會被轉換成空格
# YAML
lines: >
  我是第一行
  我也是第一行
  我仍是第一行
  我依舊是第一行

  我是第二行
  這麼巧我也是第二行

// JSON
"lines": "我是第一行 我也是第一行 我仍是第一行 我依舊是第一行\n我是第二行 這麼巧我也是第二行"

布爾值(Boolean)

  • “true”、“True”、“TRUE”、“yes”、“Yes”和“YES”皆爲
  • “false”、“False”、“FALSE”、“no”、“No”和“NO”皆爲
# YAML
boolean:
  - true # True、TRUE
  - yes # Yes、YES
  - false # False、FALSE
  - no # No、NO

// JSON
"boolean": [ true, true, false, false ]

整數(Integer)

  • 支持二進制表示
# YAML
int:
  - 666
  - 0001_0000 # 二進制表示

// JSON
"int": [ 666, 4096 ]

浮點數(Floating Point)

  • 支持科學計數法
# YAML
float:
  - 3.14
  - 6.8523015e+5 # 使用科學計數法

// JSON
"float": [ 3.14, 685230.15 ]

空(Null)

  • “null”、“Null”和“~”都是空,不指定值默認也是空
# YAML
nulls:
  - null
  - Null
  - ~
  -

// JSON
"nulls": [ null, null, null, null ]

時間戳(Timestamp)

  • YAML 也支持 ISO 8601 格式的時間數據
這裏使用 JavaScript 對象進行對比
# YAML
date1: 2020-05-26
date2: 2020-05-26T01:00:00+08:00
dete3: 2020-05-26T02:00:00.10+08:00
date4: 2020-05-26 03:00:00.10 +8

// JavaScript
date1: Tue May 26 2020 08:00:00 GMT+0800 (中國標準時間),
date2: Tue May 26 2020 01:00:00 GMT+0800 (中國標準時間),
dete3: Tue May 26 2020 02:00:00 GMT+0800 (中國標準時間),
date4: Tue May 26 2020 03:00:00 GMT+0800 (中國標準時間)

類型轉換

  • YAML 支持使用嚴格類型標籤“!!”(雙感嘆號+目標類型)來強制轉換類型
# YAML
a: !!float '666' # !! 爲嚴格類型標籤
b: '666' # 其實雙引號也算是類型轉換符
c: !!str 666 # 整數轉爲字符串
d: !!str 666.66 # 浮點數轉爲字符串
e: !!str true # 布爾值轉爲字符串
f: !!str yes # 布爾值轉爲字符串

// JSON
"a": 666,
"b": "666",
"c": "666",
"d": "666.66",
"e": "true"
"f": "yes"

其他高級類型

YAML 也可以使用一些更高級的類型,但是並不一定兼容所有解析器,包括集合(Sets)有序映射(Ordered Map)十六進制數據(Hexdecimal)二進制數據(Binary)。

本文將不會對這幾種類型進行講解,感興趣的讀者可以自行搜索研究。

數據重用與合併

  • 爲了保持內容的簡潔,避免過多重複的定義,YAML 提供了由錨點標籤“&”引用標籤“*”組成的語法,利用這套語法可以快速引用相同的一些數據...
# YAML
a: &anchor # 設置錨點
  one: 1
  two: 2
  three: 3
b: *anchor # 引用錨點

// JSON
"a": {
  "one": 1,
  "two": 2,
  "three": 3
},
"b": {
  "one": 1,
  "two": 2,
  "three": 3
}
  • 配合合併標籤“<<”使用可以與任意數據進行合併,你可以把這套操作想象成面嚮對象語言中的繼承...
# YAML
human: &base # 添加名爲 base 的錨點
    body: 1
    hair: 999
singer:
    <<: *base # 引用 base 錨點,實例化時會自動展開
    skill: sing # 添加額外的屬性
programer:
    <<: *base # 引用 base 錨點,實例化時會自動展開
    hair: 6 # 覆寫 base 中的屬性
    skill: code # 添加額外的屬性

// JSON
"human": { "body": 1, "hair": 999 },
"singer": { "body": 1, "hair": 999, "skill": "sing" },
"programer": { "body": 1, "hair": 6, "skill": "code" }

相關資料


三、YAML用法

  • stages
  • cache
  • only
  • when
  • before_script,script, after_script
  • artifacts
  • hidden_job && extends
  • reserved keywords - include

stages:

stages是用來定義一個pipeline的,一個pipeline就像一個流水線,由一系列job來構成。比如在發佈(publish)之前要做lint,test,build,那麼這四個job就構成一個pipeline,寫成下面的樣子:

stages
  - lint
  - test
  - build
  - publish

然後你在gitlab的pipeline下面就能看到下面的圖:

上面我們雖然定義了一個pipeline,和4個job名稱,但是具體每個job做什麼還是不清楚的,接下來我們學習怎麼定義一個job。

job

以上面的lint爲例,我們需要執行npm run lint命令來查看有沒有lint錯誤,那麼這個job可以寫成:

job-lint:
  stage: lint
  script: npm run lint

這裏job-lint是任務名稱,script是要在終端執行的命令,stage表示這個job屬於哪個stage(pipeline的某個節點)。job名稱這裏要注意一點是,不能使用保留字,比如:不能把一個job的名字稱爲stages或者image,就像變量名不能用if一樣。相關文檔可以看這裏

一個stage下可以有多個job,比如:打包的時候要打包到windows,mac,linux下,可以這樣:

job-build-mac:
  stage: build
job-build-win;
  stage: build
job-build-linux:
  stage: build

有時候,我們希望一些任務是在某些場景下執行的,比如:打tag的時候再build,這時候可以使用only/except。

variables

在跑一個job的過程中,我們可能會需要環境變量,比如development,production,一個url,又或者是一個不希望顯示在控制檯的token,都可以作爲一個環境變量,設置一個環境變量通常有3種方式:在項目設置中,在yml文件中,或者通過api。

通過項目設置界面來配置環境變量的入口如下:

在variables下可以配置變量名和值,通常是需要保密的變量

當然我們也可以設置在yml中:

variables:
  TEST: "HELLO WORLD"

然後就可以在腳本中引用:

  script:
    - echo $TEST 

only/except

以上面的場景爲例,我們可以這樣寫job-build:

job-build:
  stage: build
  script: npm run build
  only:
    - tags

這樣,上面的job就只有在我們push tags時纔會觸發。如果我們希望一個job只在某一類分支有提交的時候觸發,可以這樣:

job-bugfix-build:
  stage: build
  script: npm run build
  only:
    - /^bugfix-.+$/

上面這個例子只有在bugfix爲前綴的分支產生提交的時候,纔會觸發job-bugfix-build。

然而,這樣並不足以讓這個任務跑起來,因爲CI是跑在docker裏面的,在執行run lint之前,我們需要把node環境搭起來,這就需要image保留字了:

image: node:12.18

添加了image之後,在任務開始之前,還要安裝依賴,我們使用before_script來完成這件事:

# 使用node鏡像
image: node:12.18

# 安裝依賴
before_script:
 - npm install

有時候我們希望在某些場景下不執行某項任務,這時可以使用expect,比如不對hotfix進行lint:

job-lint-except-hotfix:
  script: 
    - npm run lint
  except: /^hotfix-.+$/
    

when

說了only,再說說when,when 是用來決定當前置任務失敗時,當前job是否執行,以及如何執行的問題。比如我們希望lint成功了再執行build:

build_job:
  when: on_success
  stage: build
  needs: lint_job

再比如我們在執行發佈的時候,希望手動點擊發布按鈕來執行發佈:

publish_job:
  when: manual
  stage: publish
  script: npm run deploy

artifacts

在前面提到的build job中,我們會使用webpack生成壓縮,混淆後的代碼,此時我們需要把它保存或者下載下來,這時就要用到artifacts了。用法如下:

build_job:
  script: npm run build
  artifacts:
    name: "$CI_COMMIT_REF_NAME"
    paths: dist/

artifacts最終會被打包成一個壓縮文件,這裏的path表示要添加到壓縮文件的文件或文件夾,name表示生成的壓縮文件的名字。然後在對應的任務詳情特面就可以下載:

include

正如通過程序通過模塊來實現代碼複用一樣,CI的yml配置可以通過include實現配置複用:

include:
  - remote: 'https://gitlab.com/awesome-project/raw/master/.before-script-template.yml'
  - local: '/templates/.after-script-template.yml'
  - template: Auto-DevOps.gitlab-ci.yml

這樣,我們可以把一些公用的環境變量或者job放到一個公共repo中,然後在其他項目中通過remote來引用。

最後,github actions與gitlab ci在概念上很相似,不過配置的寫法不太一樣,官方還貼心的出了遷移文檔:migrating-from-gitlab-cicd-to-github-actions

四、實例:

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_HOST: tcp://localhost:8888

.dind service: &dind_service
  - alias: docker
  name: ...
  command:
    - --insecure-registry=....

cache:
  key: node-modules
  paths:
    - ./node_modules

stages:
  - install
  - build
  - image

install dependency:
  stage: install
  image: ...
  script:
    - npm config set registry 倉庫地址
    - sudo npm install

npm build:
  stage: build
  image: ...
script:
  - sudo npm run build
  - sudo takill node
artifacts:
  name: "$CI_COMMIT_SHORT_SHA"
  paths: 
    - build

build images:
  build: image
  image: ...
  services: *dind_service 
  script:
    - cp /opt/frontend/Dockerfile
    - docker build ...
    - docker push ...
  dependencies:
    - npm build
  tags:
    - docker
  only:
    refs:
    - /^master/
    - /^qa/
    - /^dev/
    variables:
    - $CI_COMMIT_MESSAGE =~ /^build/

上面的YAML文件大致意思爲:一旦分支爲master 、 qa 、 dev開頭有提交信息爲build開頭的提交會觸發build這個job

參考:
https://gitlab.com/help/ci/yaml/README

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章