macOSにaws_completerを導入してAWS CLIのコマンド補完を有効にする

macOSにaws_completerを導入し、AWS CLIのコマンド補完を有効にする手順を紹介します。

手順

(1) AWS CLIのインストール

まず、Homebrewを使用してAWS CLIをインストールします。

% brew install awscli

(2) aws_completerのパスを確認する

まず、aws_completerのパスを確認します。

% which aws_completer
/opt/homebrew/bin/aws_completer

(3) シェルを識別する

現在使用しているシェルを確認します。

% echo $SHELL
/bin/zsh

(4) コマンド補完を有効にする

次に、コマンド補完を有効にするために、~/.zshrcファイルに以下の設定を追加します。

autoload bashcompinit && bashcompinit
autoload -Uz compinit && compinit
complete -C '/opt/homebrew/bin/aws_completer' aws

(5) 変更を反映する

設定を反映するために、プロファイルを現在のセッションに再ロードします。

% source ~/.zshrc

(6) コマンド補完の確認する

最後に、AWS CLIのコマンド補完が有効になっていることを確認します。

% aws s<TAB>
s3                              sesv2
s3api                           shield
s3control                       signer

参考

MySQL 8.4ではmysql_native_password認証プラグインが無効になった

最近、DockerでMySQLが起動しなくなる問題が発生しました。
以下のエラーメッセージが表示されました。

[ERROR] [MY-000067] [Server] unknown variable 'default-authentication-plugin=mysql_native_password'.

このエラーは、efault-authentication-plugin=mysql_native_passwordという変数が不明であるために、MySQLサーバが起動に失敗していることを示しています。

原因は、DockerのMySQLバージョンがlatestになっていたため、自動的にMySQL 8.4に更新されたことです。

MySQL 8.4の変更点

MySQL 8.4では mysql_native_password 認証プラグインがデフォルトで無効になりました。

この変更により、古い認証方式を利用している場合には、MySQLサーバの起動に失敗する可能性があります。

解決策

MySQL 8.4では mysql_native_password 認証プラグインがデフォルトで無効になりましたが、削除されたわけではありません。

mysql_native_passwordを引き続き使用するためには、MySQLの設定ファイルに以下の設定を追加する必要があります。

[mysqld]
default_authentication_plugin=mysql_native_password

または、MySQLサーバを起動する際に以下のオプションを指定します。

--default-authentication-plugin=mysql_native_password

推奨される移行方法

長期的には、mysql_native_passwordから caching_sha2_password への移行が推奨されます。

caching_sha2_passwordはセキュリティが強化されており、MySQLの最新バージョンでデフォルトの認証プラグインとなっています。

Laravel(PHP)からSlackにメッセージを送信する

Laravel(PHP)からSlackにメッセージを送信する方法を紹介します。

以前にGuzzleを使った方法を紹介しました。
今回はLaravelのHTTPクライアントを使用した方法を紹介します。

まず、Slackにメッセージを送信するためのサービスクラスを作成します。以下はそのコード例です。

<?php
declare(strict_types=1);

namespace App\Utils;

use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class SlackService
{
    public static function post(string $message): bool
    {
        $webhookUrl = config('slack.webhook_url');
        $username = config('slack.username');
        $iconEmoji = config('slack.icon_emoji');

        try {
            Http::acceptJson()
                ->post($webhookUrl, [
                        'username' => $username,
                        'text' => $message,
                        'icon_emoji' => $iconEmoji,
                    ]
                )->throw();
            return true;
        } catch (RequestException $e) {
            Log::error('Slack Post Failed: '.$e->getMessage());
            Log::error('Failed message: '.$message);
            Log::error($e->getTraceAsString());
            return false;
        }
    }
}

このコードは、LaravelのHTTPクライアントを使用してSlackにメッセージを送信します。
設定ファイルからWebhook URL、ユーザー名、およびアイコンの絵文字を取得し、メッセージを送信します。

Laravelの設定ファイル

SlackのWebhook URLやその他の設定は、Laravelの設定ファイルに記載します。

例えば、config/slack.phpというファイルを作成し、以下のように設定します。

<?php

return [
    'webhook_url' => env('SLACK_WEBHOOK_URL'),
    'username' => env('SLACK_USERNAME', 'LaravelBot'),
    'icon_emoji' => env('SLACK_ICON_EMOJI', ':robot_face:'),
];

また、.envファイルに以下のように追加します。

SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook/url
SLACK_USERNAME=LaravelBot
SLACK_ICON_EMOJI=:robot_face:

以上で、LaravelからSlackにメッセージを送信する準備が整います。

必要に応じて、設定ファイルやコードを調整してください。

Compose for Desktopアプリでドラッグしてコントロールのサイズを変更する

Compose for Desktopでドラッグしてコントロールのサイズを変更するサンプルプログラムです。

2つのテキストエリアを分割線で分け、その分割線をドラッグしてテキストエリアの幅を調整します。

画面イメージ

サンプルプログラムの説明

App関数

アプリケーションのメインビューを定義します。

このビューには、Cyan色のテキスト、ドラッグ可能なDivider、そしてYellow色のテキストが含まれています。

テキストの幅は、ドラッグによって変更可能です。

@Composable
@Preview
fun App() {
    // 幅を保持するState
    val widthText = remember { mutableStateOf(200.dp) }
    MaterialTheme {
        Row(modifier = Modifier.fillMaxSize()) {
            DisplayText(widthText.value, Color.Cyan)
            DisplayDivider(widthText)
            DisplayText(200.dp, Color.Yellow)
        }
    }
}

DisplayText

指定された幅と色でテキストを表示します。

テキストは幅の値で、背景色は指定された色です。

@Composable
fun DisplayText(width: Dp, color: Color) {
    Text(
        "${width.value.toInt()}dp",
        modifier = Modifier.width(width).fillMaxHeight().background(color = color)
    )
}

DisplayDivider関数

ドラッグで幅を変更できるDivider(区切り線)を表示します。

このDividerは、ドラッグジェスチャーを検出し、ドラッグの量に基づいて幅を変更します。

また、マウスが上にホバーすると、カーソルがリサイズカーソルに変わります。

@Composable
fun DisplayDivider(widthText: MutableState<Dp>) {
    // 画面密度
    val density = LocalDensity.current

    Divider(
        color = Color.Red,
        modifier = Modifier
            .fillMaxHeight()
            // 幅を指定
            .width(8.dp)
            // ドラッグジェスチャーを検出するためのModifier
            .pointerInput(Unit) {
                // ドラッグジェスチャーを検出
                detectDragGestures { change, dragAmount ->
                    // ドラッグ量を取得し、幅を変更
                    with(density) {
                        val dragAmountDp = dragAmount.x.toDp()
                        val newWidth = (widthText.value + dragAmountDp).coerceIn(50.dp, 400.dp)
                        if (change.pressed) {
                            widthText.value = newWidth
                        }
                    }
                }
            }
            // マウスホバー時のカーソルを変更
            .pointerHoverIcon(PointerIcon(Cursor(Cursor.E_RESIZE_CURSOR)))
    )
}

サンプルプログラム全文

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.application
import java.awt.Cursor

/**
 * 指定された幅と色でテキストを表示します。
 * テキストは幅の値で、背景色は指定された色です。
 *
 * @param width 幅
 * @param color 背景色
 */
@Composable
fun DisplayText(width: Dp, color: Color) {
    Text(
        "${width.value.toInt()}dp",
        modifier = Modifier.width(width).fillMaxHeight().background(color = color)
    )
}

/**
 * ドラッグで幅を変更できるDividerを表示します。
 *
 * @param widthText 幅を保持するState
 */
@Composable
fun DisplayDivider(widthText: MutableState<Dp>) {
    // 画面密度
    val density = LocalDensity.current

    Divider(
        color = Color.Red,
        modifier = Modifier
            .fillMaxHeight()
            // 幅を指定
            .width(8.dp)
            // ドラッグジェスチャーを検出するためのModifier
            .pointerInput(Unit) {
                // ドラッグジェスチャーを検出
                detectDragGestures { change, dragAmount ->
                    // ドラッグ量を取得し、幅を変更
                    with(density) {
                        val dragAmountDp = dragAmount.x.toDp()
                        val newWidth = (widthText.value + dragAmountDp).coerceIn(50.dp, 400.dp)
                        if (change.pressed) {
                            widthText.value = newWidth
                        }
                    }
                }
            }
            // マウスホバー時のカーソルを変更
            .pointerHoverIcon(PointerIcon(Cursor(Cursor.E_RESIZE_CURSOR)))
    )
}

@Composable
@Preview
fun App() {
    // 幅を保持するState
    val widthText = remember { mutableStateOf(200.dp) }
    MaterialTheme {
        Row(modifier = Modifier.fillMaxSize()) {
            DisplayText(widthText.value, Color.Cyan)
            DisplayDivider(widthText)
            DisplayText(200.dp, Color.Yellow)
        }
    }
}

fun main() = application {
    Window(
        onCloseRequest = ::exitApplication,
        state = WindowState(size = DpSize(500.dp, 200.dp))
    ) {
        App()
    }
}

既知の問題

マルチディスプレイ環境において、異なるDPIのディスプレイにウィンドウを移動すると、ドラッグした距離を正しく計算できません。