2024年9月13日金曜日

Gitlab CI でセマンティクスバージョンを比較する

Gitlab CI でセマンティクスバージョンを比較する

概要

過去に前のバージョンを保存する方法を紹介しました
今回は取得したバージョン同士を比較するジョブを追加します

環境

  • Gitlab.com 17.4.0-pre
    * Runner (docker-mahcine executor ruby:3.1)

.gitlab-ci.yml

compare ジョブを追加しています
sort した結果現在のバージョンと違うバージョンが返ってきたらバージョンアップと判断しています

stages:
  - save_versions
  - fetch_versions
  - check_versions
  - compare_versions

save:
  stage: save_versions
  script:
    - |
      # バージョンファイルがすでにある場合は前回のバージョンファイルに移動する
      mkdir -p $CI_PROJECT_DIR/build/cache
      mkdir -p $CI_PROJECT_DIR/build/artifacts
      # ファイルが何もないと各種ディレクトリが破棄されるのでファイル作成
      touch $CI_PROJECT_DIR/build/cache/keep.txt
      touch $CI_PROJECT_DIR/build/artifacts/keep.txt
      if [ -f $CI_PROJECT_DIR/build/cache/prometheus.txt ]; then
        mv $CI_PROJECT_DIR/build/cache/prometheus.txt $CI_PROJECT_DIR/build/artifacts/pre_prometheus.txt
        echo "Prometheus pre version:"
        cat $CI_PROJECT_DIR/build/artifacts/pre_prometheus.txt
      fi
      if [ -f $CI_PROJECT_DIR/build/cache/alertmanager.txt ]; then
        mv $CI_PROJECT_DIR/build/cache/alertmanager.txt $CI_PROJECT_DIR/build/artifacts/pre_alertmanager.txt
        echo "Alertmanager pre version:"
        cat $CI_PROJECT_DIR/build/artifacts/pre_alertmanager.txt
      fi
      if [ -f $CI_PROJECT_DIR/build/cache/node_exporter.txt ]; then
        mv $CI_PROJECT_DIR/build/cache/node_exporter.txt $CI_PROJECT_DIR/build/artifacts/pre_node_exporter.txt
        echo "Node Exporter pre version:"
        cat $CI_PROJECT_DIR/build/artifacts/pre_node_exporter.txt
      fi
  # 前回のパイプラインを参照するために cache を使う
  cache:
    paths:
      - $CI_PROJECT_DIR/build/cache/*.txt
  # ジョブ間で結果を共有するために artifacts を使う
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt


# 任意のタグに基づいてバージョン情報を取得するジョブ
fetch:
  stage: fetch_versions
  script:
    - echo "Checking versions for the specified tag -> $TARGET_TAG"
    # GitLabソースコードをクローン
    - git clone https://gitlab.com/gitlab-org/omnibus-gitlab.git
    - cd omnibus-gitlab
    # ユーザーが指定したタグにチェックアウト
    - git checkout $TARGET_TAG
    # prometheusのバージョンを取得
    - grep 'Gitlab::Version.new' config/software/prometheus.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/prometheus.txt
    # Alertmanagerのバージョンを取得
    - grep 'Gitlab::Version.new' config/software/alertmanager.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/alertmanager.txt
    # Node Exporterのバージョンを取得
    - grep 'Gitlab::Version.new' config/software/node-exporter.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/node_exporter.txt
  # 前回のジョブの結果を使用するために artifacts を使う
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt
  # ジョブの実行にはTARGET_TAG変数が必須
  rules:
    - if: '$TARGET_TAG != null'

check:
  stage: check_versions
  script:
    - echo "Prometheus version:"
    - cat $CI_PROJECT_DIR/build/artifacts/prometheus.txt
    - echo "Alertmanager version:"
    - cat $CI_PROJECT_DIR/build/artifacts/alertmanager.txt
    - echo "Node Exporter version:"
    - cat $CI_PROJECT_DIR/build/artifacts/node_exporter.txt
    # 結果をキャッシュに保存する
    - cp $CI_PROJECT_DIR/build/artifacts/prometheus.txt $CI_PROJECT_DIR/build/cache/prometheus.txt
    - cp $CI_PROJECT_DIR/build/artifacts/alertmanager.txt $CI_PROJECT_DIR/build/cache/alertmanager.txt
    - cp $CI_PROJECT_DIR/build/artifacts/node_exporter.txt $CI_PROJECT_DIR/build/cache/node_exporter.txt
    - ls $CI_PROJECT_DIR/build/cache
    - ls $CI_PROJECT_DIR/build/artifacts
  # 次回のパイプラインに結果を残すために cache を使う
  cache:
    paths:
      - $CI_PROJECT_DIR/build/cache/*.txt
  # 前回のジョブの結果を使用するために artifacts を使う
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt

compare:
  stage: compare_versions
  script:
    - |
      # 前の Prometheus のバージョン情報を取得
      if [ -f $CI_PROJECT_DIR/build/artifacts/pre_prometheus.txt ]; then
        PREV_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/pre_prometheus.txt)
        # PREV_VERSION="0.0.0" # for test
      else
        exit 1
      fi
      # 現在の Prometheus のバージョン情報を取得
      if [ -f $CI_PROJECT_DIR/build/artifacts/prometheus.txt ]; then
        CURRENT_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/prometheus.txt)
      else
        exit 1;
      fi
      # バージョン比較のための関数
      version_gt() {
        [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" != "$1" ]
      }
      # バージョンを比較
      if version_gt "$CURRENT_VERSION" "$PREV_VERSION"; then
        echo "Version has increased from $PREV_VERSION to $CURRENT_VERSION"
      else
        echo "Version is unchanged."
        exit 0
      fi
  # 前回のジョブの結果を使用するために artifacts を使う
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt

最後に

今回はシェルスクリプトで実現していますが正しく比較したいのであれば Python などを使う方法を検討してください

2024年9月12日木曜日

Gitlab と dockerhub にある Prometheus のバージョンを比較する gitlab-ci.yml

Gitlab と dockerhub にある Prometheus のバージョンを比較する gitlab-ci.yml

概要

Gitlab 側で最新の Prometheus が使われているか定期的にチェックします

環境

  • Gitlab.com 17.4.0-pre
    * Runner (docker-mahcine executor ruby:3.1)

app.py

  • vim app.py
import re
import sys

import requests
from packaging.version import Version

image = sys.argv[1]
url = f"https://hub.docker.com/v2/repositories/prom/{image}/tags/"
response = requests.get(url)
data = response.json()
# セマンティックバージョンに一致するタグを正規表現でフィルタ
semver_pattern = re.compile(r"^v?(\d+\.\d+\.\d+)$")
# バージョンをリストに格納
versions = []
for tag in data["results"]:
    tag_name = tag["name"]
    if semver_pattern.match(tag_name):  # セマンティックバージョニングに一致
        versions.append(tag_name.lstrip("v"))
# バージョンを比較して最新を取得
latest_version = max(versions, key=lambda x: Version(x))
print(latest_version)

.gitlab-ci.yml

Gitlab のバージョンはソースコードから取得し dockerhub にあるタグのバージョンは API を使っています
取得した結果は artifacts を使ってファイルに保存して最後に比較しています

image: python:3.11.9-bullseye

stages:
  - fetch_versions
  - compare_versions

fetch_dockerhub:
  stage: fetch_versions
  before_script:
    - mkdir -p $CI_PROJECT_DIR/build/artifacts
    - pip install requests packaging
  script:
    - python app.py prometheus > $CI_PROJECT_DIR/build/artifacts/dockerhub_latest_prometheus.txt
    - python app.py alertmanager > $CI_PROJECT_DIR/build/artifacts/dockerhub_latest_alertmanager.txt
    - python app.py node-exporter > $CI_PROJECT_DIR/build/artifacts/dockerhub_latest_node_exporter.txt
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt

fetch_gitlab:
  stage: fetch_versions
  before_script:
    - mkdir -p $CI_PROJECT_DIR/build/artifacts
  script:
    - git clone https://gitlab.com/gitlab-org/omnibus-gitlab.git
    - cd omnibus-gitlab
    - git checkout $TARGET_TAG
    - grep 'Gitlab::Version.new' config/software/prometheus.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/gitlab_tagged_prometheus.txt
    - grep 'Gitlab::Version.new' config/software/alertmanager.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/gitlab_tagged_alertmanager.txt
    - grep 'Gitlab::Version.new' config/software/node-exporter.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/gitlab_tagged_node_exporter.txt
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt
  rules:
    - if: '$TARGET_TAG != null'

compare:
  stage: compare_versions
  script:
    - |
      # バージョンの比較
      DOCKERHUB_PROMETHEUS_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/dockerhub_latest_prometheus.txt)
      DOCKERHUB_ALERTMANAGER_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/dockerhub_latest_alertmanager.txt)
      DOCKERHUB_NODE_EXPORTER_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/dockerhub_latest_node_exporter.txt)
      GITLAB_TAGGED_PROMETHEUS_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/gitlab_tagged_prometheus.txt)
      GITLAB_TAGGED_ALERTMANAGER_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/gitlab_tagged_alertmanager.txt)
      GITLAB_TAGGED_NODE_EXPORTER_VERSION=$(cat $CI_PROJECT_DIR/build/artifacts/gitlab_tagged_node_exporter.txt)
      version_gt() {
        [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" != "$1" ]
      }
      if version_gt "$DOCKERHUB_PROMETHEUS_VERSION" "$GITLAB_TAGGED_PROMETHEUS_VERSION"; then
        echo "Version has increased from $GITLAB_TAGGED_PROMETHEUS_VERSION to $DOCKERHUB_PROMETHEUS_VERSION"
      else
        echo "Prometheus Version is unchanged."
      fi
      if version_gt "$DOCKERHUB_ALERTMANAGER_VERSION" "$GITLAB_TAGGED_ALERTMANAGER_VERSION"; then
        echo "Version has increased from $GITLAB_TAGGED_ALERTMANAGER_VERSION to $DOCKERHUB_ALERTMANAGER_VERSION"
      else
        echo "Alertmanager Version is unchanged."
      fi
      if version_gt "$DOCKERHUB_NODE_EXPORTER_VERSION" "$GITLAB_TAGGED_NODE_EXPORTER_VERSION"; then
        echo "Version has increased from $GITLAB_TAGGED_NODE_EXPORTER_VERSION to $DOCKERHUB_NODE_EXPORTER_VERSION"
      else
        echo "Node_Exporter Version is unchanged."
      fi
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt

最後に

Omnibus Gitlab の Prometheus のバージョンはボットが管理しているっぽいのでその仕組を使うのもありなのかもしれない

2024年9月11日水曜日

Gitlab CI で前回のパイプラインの結果を保存して次のパイプラインで使う方法

Gitlab CI で前回のパイプラインの結果を保存して次のパイプラインで使う方法

概要

cache を使います
gitlab-ci.yml の内容は前回のものを使います

環境

  • Gitlab.com 17.4.0-pre
    * Runner (docker-mahcine executor ruby:3.1)

.gitlab-ci.yml

流れとしては

  • 前回の結果があれば別ファイル名にして保存
  • 今回の結果を取得
  • 今回の結果を表示、キャッシュに保存

という感じです
ジョブ間でデータをやり取りするため artifacats も使います

stages:
  - save_versions
  - fetch_versions
  - check_versions

save:
  stage: save_versions
  script:
    - |
      # バージョンファイルがすでにある場合は前回のバージョンファイルに移動する
      mkdir -p $CI_PROJECT_DIR/build/cache
      mkdir -p $CI_PROJECT_DIR/build/artifacts
      # ファイルが何もないと各種ディレクトリが破棄されるのでファイル作成
      touch $CI_PROJECT_DIR/build/cache/keep.txt
      touch $CI_PROJECT_DIR/build/artifacts/keep.txt
      if [ -f $CI_PROJECT_DIR/build/cache/prometheus.txt ]; then
        mv $CI_PROJECT_DIR/build/cache/prometheus.txt $CI_PROJECT_DIR/build/artifacts/pre_prometheus.txt
        echo "Prometheus pre version:"
        cat $CI_PROJECT_DIR/build/artifacts/pre_prometheus.txt
      fi
      if [ -f $CI_PROJECT_DIR/build/cache/alertmanager.txt ]; then
        mv $CI_PROJECT_DIR/build/cache/alertmanager.txt $CI_PROJECT_DIR/build/artifacts/pre_alertmanager.txt
        echo "Alertmanager pre version:"
        cat $CI_PROJECT_DIR/build/artifacts/pre_alertmanager.txt
      fi
      if [ -f $CI_PROJECT_DIR/build/cache/node_exporter.txt ]; then
        mv $CI_PROJECT_DIR/build/cache/node_exporter.txt $CI_PROJECT_DIR/build/artifacts/pre_node_exporter.txt
        echo "Node Exporter pre version:"
        cat $CI_PROJECT_DIR/build/artifacts/pre_node_exporter.txt
      fi
  # 前回のパイプラインを参照するために cache を使う
  cache:
    paths:
      - $CI_PROJECT_DIR/build/cache/*.txt
  # ジョブ間で結果を共有するために artifacts を使う
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt


# 任意のタグに基づいてバージョン情報を取得するジョブ
fetch:
  stage: fetch_versions
  script:
    - echo "Checking versions for the specified tag -> $TARGET_TAG"
    # GitLabソースコードをクローン
    - git clone https://gitlab.com/gitlab-org/omnibus-gitlab.git
    - cd omnibus-gitlab
    # ユーザーが指定したタグにチェックアウト
    - git checkout $TARGET_TAG
    # prometheusのバージョンを取得
    - grep 'Gitlab::Version.new' config/software/prometheus.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/prometheus.txt
    # Alertmanagerのバージョンを取得
    - grep 'Gitlab::Version.new' config/software/alertmanager.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/alertmanager.txt
    # Node Exporterのバージョンを取得
    - grep 'Gitlab::Version.new' config/software/node-exporter.rb | sed -n "s/.*'\(.*\)'.*/\1/p" > $CI_PROJECT_DIR/build/artifacts/node_exporter.txt
  # 前回のジョブの結果を使用するために artifacts を使う
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt
  # ジョブの実行にはTARGET_TAG変数が必須
  rules:
    - if: '$TARGET_TAG != null'

check:
  stage: check_versions
  script:
    - echo "Prometheus version:"
    - cat $CI_PROJECT_DIR/build/artifacts/prometheus.txt
    - echo "Alertmanager version:"
    - cat $CI_PROJECT_DIR/build/artifacts/alertmanager.txt
    - echo "Node Exporter version:"
    - cat $CI_PROJECT_DIR/build/artifacts/node_exporter.txt
    # 結果をキャッシュに保存する
    - cp $CI_PROJECT_DIR/build/artifacts/prometheus.txt $CI_PROJECT_DIR/build/cache/prometheus.txt
    - cp $CI_PROJECT_DIR/build/artifacts/alertmanager.txt $CI_PROJECT_DIR/build/cache/alertmanager.txt
    - cp $CI_PROJECT_DIR/build/artifacts/node_exporter.txt $CI_PROJECT_DIR/build/cache/node_exporter.txt
    - ls $CI_PROJECT_DIR/build/cache
    - ls $CI_PROJECT_DIR/build/artifacts
  # 次回のパイプラインに結果を残すために cache を使う
  cache:
    paths:
      - $CI_PROJECT_DIR/build/cache/*.txt
  # 前回のジョブの結果を使用するために artifacts を使う
  artifacts:
    paths:
      - $CI_PROJECT_DIR/build/artifacts/*.txt

最後に

artifacts は結果を保存してダウンロードできますが次回のパイプラインでは使えません
cache は結果を保存してダウンロードはできませんが次回のパイプラインで使えます

どちらもサブディレクトリを使う場合は対象のファイルが一つでもないと次のジョブ実行時にサブディレクトリがないので注意しましょう

参考サイト

2024年9月10日火曜日

omnibus-gitlab に記載されている Promethues のバージョンを定期的に取得する gitlab-ci のサンプル

omnibus-gitlab に記載されている Promethues のバージョンを定期的に取得する gitlab-ci のサンプル

概要

clone して grep します
変数でタグを指定できるようにしているのでもう少し工夫すればタグを自動で取得したり複数のタグのバージョンを取得できたりできるはずです

環境

  • Gitlab.com 17.4.0-pre
    * Runner (docker-mahcine executor ruby:3.1)

.gitlab-ci.yml

stages:
  - fetch_versions

# 任意のタグに基づいてバージョン情報を取得するジョブ
get_versions:
  stage: fetch_versions
  script:
    - echo "Checking versions for the specified tag -> $TARGET_TAG"
    # GitLabソースコードをクローン
    - git clone https://gitlab.com/gitlab-org/omnibus-gitlab.git
    - cd omnibus-gitlab
    # ユーザーが指定したタグにチェックアウト
    - git checkout $TARGET_TAG
    # Prometheusのバージョンを取得
    - echo "Prometheus version:"
    - grep 'Gitlab::Version.new' config/software/prometheus.rb | sed -n "s/.*'\(.*\)'.*/\1/p"
    # Alertmanagerのバージョンを取得
    - echo "Alertmanager version:"
    - grep 'Gitlab::Version.new' config/software/alertmanager.rb | sed -n "s/.*'\(.*\)'.*/\1/p"
    # Node Exporterのバージョンを取得
    - echo "Node Exporter version:"
    - grep 'Gitlab::Version.new' config/software/node-exporter.rb | sed -n "s/.*'\(.*\)'.*/\1/p"
  # ジョブの実行にはTARGET_TAG変数が必須
  rules:
    - if: '$TARGET_TAG != null'

変数

注意事項

script 内でコロンを使う場合はコロンの後ろにスペースがあると「This GitLab CI configuration is invalid: jobs:get_versions:script config should be a string or a nested array of strings up to 10 levels deep.」と言われて怒られるので注意しましょう

最後に

シャローンクローンにしたりすればもっと速くなると思います
定期実行にするのも良いと思います

前回の実行からバージョンが上がったら通知するようにしたら便利かもしれません

Wordpress でショートコードを使ってデータベースの情報を取得する方法

Wordpress でショートコードを使ってデータベースの情報を取得する方法

概要

ショートコードを使えばサーバサイドにある functions.php を呼び出せるのでデータベースにある情報を表示することができます

環境

  • macOS 14.6.1
  • MySQL 9.0
  • Wordpress 6.6.1
  • docker 27.2.0

functions.php

function get_first_user_by_db() {
    global $wpdb;
    $result = $wpdb->get_results("SELECT user_login FROM wp_users LIMIT 1");
    foreach($result as $row) {
        return $row->user_login;
    }
}

add_shortcode ( 'first_user_name', 'get_first_user_by_db' );

ショートコード

[first_user_name]

動作確認

mysql> select user_login from wp_users limit 1;
+------------+
| user_login |
+------------+
| test       |
+------------+
1 row in set (0.00 sec)

おまけ: wordpress のテーブル一覧

mysql> show tables;
+-----------------------+
| Tables_in_wordpress   |
+-----------------------+
| wp_commentmeta        |
| wp_comments           |
| wp_links              |
| wp_options            |
| wp_postmeta           |
| wp_posts              |
| wp_term_relationships |
| wp_term_taxonomy      |
| wp_termmeta           |
| wp_terms              |
| wp_usermeta           |
| wp_users              |
+-----------------------+
12 rows in set (0.00 sec)

最後に

functions.php を使えばデータベースにある情報もサイトに表示することができます
他のデータベースにアクセスしたい場合は wordpress で使用しているユーザが他のデータベースにアクセスできるように権限設定する必要があります

functions.php は正直何でもできるのでこれだけ極めても Wordpress を使いこなせるようになると思います

参考サイト

2024年9月9日月曜日

Wordpress のショートコードに引数を設定する方法

Wordpress のショートコードに引数を設定する方法

概要

$atts を受取れるようにします

環境

  • macOS 14.6.1
  • MySQL 9.0
  • Wordpress 6.6.1
  • docker 27.2.0

functions.php

function say($atts) {
    /** デフォルト値の設定 
     * shortcode_atts を使うことでショートコードにkey,valueな属性を付与することができる
     * 以下では message という属性を定義している
     * ショートコード側の引数で message=hoge という引数が与えられるとそれが優先される
     * extract は与えられた連想配列から変数を生成するPHPの組み込み関数
     */
    extract(shortcode_atts(array(
      'message' => 'Hello'), $atts));
    return $message;
}

add_shortcode( 'say_something', 'say' );

動作確認

ショートコードを生成し message という引数の値をいろいろと変えてみましょう

最後に

Wordpress のショートコード用の関数で引数を受け取る方法を紹介しました
これで同じようなコンポーネントは functions.php で定義しデータだけを変数で与えることでコンポーネントの使い回しができるようになります

ショートコード用の関数には他にも content という引数が受け取れます
これは [sc]content[/sc] という感じでショートコードのタグで囲われたコンテンツの内容を受け取ることができます

参考サイト

2024年9月8日日曜日

Alembic で does not provide a MetaData object or sequence of objects to the context.

Alembic で does not provide a MetaData object or sequence of objects to the context.

概要

autogenerate オプションを付与すると発生するエラーになります
対策を紹介します

環境

  • Ubuntu 22.04
  • Python 3.10.2
  • alembic 1.13.2

env.py に metadata を追加する

DeclarativeBase or declarative_base で作成された Base クラスを import しその metadata を参照します
ポイントはちゃんとマイグレーションする際の context.configure にも metadata 情報を渡す点です

  • vim env.py
from app.models import Base
target_metadata = Base.metadata
def run_migrations_online() -> None:
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    configuration = config.get_section(config.config_ini_section)
    if configuration is None:
        raise ValueError()
    connectable = engine_from_config(
        configuration=configuration,
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=target_metadata,  # <= ここでちゃんと設定するのが重要
        )

        with context.begin_transaction():
            context.run_migrations()

最後に

あとは普通にマイグレーションできるか確認すれば OK です
almbic や sqlalchemy を最新にすると発生することがあるようです