無作為抽出 on GitHub Actions

PJ内で何名かランダムに選びたいとき用のアクションが欲しくなったので作った。
Shell Scriptレベルなのでローカルでも動かせるが、みんなが使えるようにGitHub Actionsのyamlで書いた。

ユースケース:

  • 司会を選ぶ
  • レビュワーを選ぶ
  • ランダム1 on 1

目次

つくったもの概要

作ったyamlが以下。
チーム名と選択数をInputとして指定することができる。

流れとしては、

  1. 対象メンバの件数取得
  2. shufコマンドを使って、ランダムに指定選択数分のindex値を取得
  3. 対象メンバリスト内からランダムindex値のデータを取得
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
name: Random sampling

on:
workflow_dispatch:
inputs:
team:
description: 'target team name: ALFA or BRAVO'
required: true
default: 'all'
number:
description: 'select number'
required: true
default: '1'

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: All
if: ${{ github.event.inputs.team == 'all' }}
env:
NUMBER: ${{github.event.inputs.number}}
run: |
max=$(($(yq e '.member | length' list.yaml)-1))
target=$(seq 0 $max | shuf -n ${NUMBER})
target_comma=$(echo $target| sed "s/ /,/g")
yq e ".member[${target_comma}]" list.yaml

- name: team
if: ${{ github.event.inputs.team != 'all' }}
env:
TEAM_NAME: ${{github.event.inputs.team}}
NUMBER: ${{github.event.inputs.number}}
run: |
max=$(($(yq e ".member[].team | select(.==\"""${TEAM_NAME}""\")" list.yaml | wc -l)-1))
target=$(seq 0 $max | shuf -n ${NUMBER})
target_comma=$(echo $target| sed "s/ /,/g")
yq e . list.yaml -j | jq ".member[] | select(.team==\"""${TEAM_NAME}""\")" | jq -s ".[${target_comma}]"

前提としてカレントディレクトリに以下のような、list.yamlファイルが存在する想定。
各チームメンバの名前と所属チームを記載したリスト。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
member:
- name: 太郎
team: ALFA
- name: 次郎
team: ALFA
- name: 三郎
team: ALFA
- name: 四郎
team: ALFA
- name: 花子
team: BRAVO
- name: 菊子
team: BRAVO
- name: 白子
team: BRAVO

詳細

対象メンバの件数取得

普通に全件取得なら件数はyqコマンドでlength指定すれば容易に取れる。
(欲しいのはindexの最大値なのでマイナス1している)

1
max=$(($(yq e '.member | length' list.yaml)-1))

teamで絞る場合は、valueの値でselectすればOK
配列に戻して、lengthで取るのが面倒だったので、wcで行数としてカウントした。

1
max=$(($(yq e ".member[].team | select(.==\"""${TEAM_NAME}""\")" list.yaml | wc -l)-1))

shufコマンドを使って、ランダムに指定選択数分のindexを取得

ランダム選択はshufコマンドを使う
-nオプションで先頭から指定個数分取得できる。

1
target=$(seq 0 $max | shuf -n ${NUMBER})

ちなみに、macの場合はgshufになる
インストールはbrew install coreutilsでOK

1
2
3
4
5
6
7
8
9
10
11
$ seq 0 5 | gshuf
0
3
5
4
1
2
$ seq 0 5 | gshuf -n 3
1
0
2

対象メンバリスト内からランダムindex値のデータを取得

取得したindex値が複数の場合、スペース区切りで変数に入っているので、カンマ区切りにしておく。

1
target_comma=$(echo $target| sed "s/ /,/g")

これはjqとかyqで、配列から複数index値指定で取得するときはカンマ区切りで指定するためこの変換を挟む。

全件取得ならyqでそのままindex値をカンマ区切り指定で取得すればOK

1
yq e ".member[${target_comma}]" list.yaml

team名で絞るなら、select条件を挟む必要がある。ただしそれをすると配列情報が失われて、index値で取得できなくなる。

1
2
3
4
5
6
7
8
9
$ yq e '.member[] | select(.team=="ALFA")' list.yaml 
name: 太郎
team: ALFA
name: 次郎
team: ALFA
name: 三郎
team: ALFA
name: 四郎
team: ALFA

なので、一旦jsonにしてからjqでフィルタ & 配列に戻した上で取得する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ yq e . list.yaml -j | jq '.member[] | select(.team=="ALFA")'
{
"name": "太郎",
"team": "ALFA"
}
{
"name": "次郎",
"team": "ALFA"
}
{
"name": "三郎",
"team": "ALFA"
}
{
"name": "四郎",
"team": "ALFA"
}

jq-sオプションで配列にすることができる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ yq e . list.yaml -j | jq '.member[] | select(.team=="ALFA")' | jq -s
[
{
"name": "太郎",
"team": "ALFA"
},
{
"name": "次郎",
"team": "ALFA"
},
{
"name": "三郎",
"team": "ALFA"
},
{
"name": "四郎",
"team": "ALFA"
}
]

参考

おわりに

GUI上で結果を見ることはできるが、最後に通知とかを入れれば完璧。
list.yamlは、GitHubのTeamから情報とってきて対象リストを用意する感じでもいい気がする。