のぴぴのメモ

クラウドやLinuxとかの技術メモ

gitでコミットする時のコミットメッセージのヘッダにつけるプレフィックスの例

gitでコミットする時のコミットメッセージのヘッダにプレフィックスをつける場合のプレフィックスの例。
AIが教えてくれたのでメモで記載。

主なコミットタイプ

プレフィックス 意味
feat: 新機能の追加 `feat: add user authentication`
fix: バグ修正 `fix: resolve login error`
chore: 雑務・環境設定 `chore: update dependencies`
docs: ドキュメント `docs: update README`
style: コードスタイル `style: format code with prettier`
refactor: リファクタリング `refactor: simplify validation logic`
test: テスト追加・修正 `test: add unit tests for API`

AWS Identity Center(旧AWS SSO)認証で、一時認証情報を環境変数にエクスポートするシェル

AWS Identity Centerの認証ではAWS CDKが動作しなかったので、やむなくSSO認証した後に一時認証情報を取得して環境変数にエクスポートする簡易シェルを作ってみた。

前提

  • AWS CLIがイントールされ、AWS Identity Centerの認証の設定が入っていること

ソースコード

!/bin/bash
###############################################################################
# aws-sso-export.sh
#
# AWS SSOで認証し、一時認証情報を環境変数にエクスポートする
#
# 使用方法:
#   source ./aws-sso-export.sh [プロファイル名]
#
# 例:
#   source ./aws-sso-export.sh
#   source ./aws-sso-export.sh my-profile
#
# 注意:
#   必ず source コマンドで実行してください
###############################################################################
PROFILE="${1:-default}"

# 既存の認証用環境変数を解除
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_REGION

 # SSO認証
echo "AWS SSO認証中 (プロファイル: ${PROFILE})..."
aws sso login --profile "${PROFILE}"

echo "認証情報を取得中..."
CREDS=$(aws configure export-credentials --profile "${PROFILE}" --format env-no-export)

export AWS_ACCESS_KEY_ID=$(echo "${CREDS}" | grep AWS_ACCESS_KEY_ID | cut -d'=' -f2)
export AWS_SECRET_ACCESS_KEY=$(echo "${CREDS}" | grep AWS_SECRET_ACCESS_KEY | cut -d'=' -f2)
export AWS_SESSION_TOKEN=$(echo "${CREDS}" | grep AWS_SESSION_TOKEN | cut -d'=' -f2)
export AWS_REGION=$(aws configure get region --profile "${PROFILE}" || echo "ap-northeast-1")

echo "認証情報を環境変数に設定しました"
echo "   Account: $(aws sts get-caller-identity --query Account --output text)"
echo "   Region: ${AWS_REGION}"

# 実行例

#実行例
source ./aws-sso-export.sh

#この書き方でも実行は同じ
.  ./aws-sso-export.sh

aws_signing_helperで取得したクレデンシャルを環境変数に反映するシェル

AWS CDKが対応していないのか、~/.aws/configのプロファイルに設定したaws_signing_helperからクレデンシャルを取得することができなかったので、aws_signing_helperで取得したクレデンシャルを、AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKENの3つの環境変数に反映するシェルを作りました。

使い方

source ./set_aws_credential_from_roles_anywhere.sh

または

. ./set_aws_credential_from_roles_anywhere.sh

コード

  • ./set_aws_credential_from_roles_anywhere.sh
#----
# Roles Anywhereで取得した一時クレデンシャルを環境変数に設定するシェル
# 使い方
#  source set_aws_credential_from_roles_anywhere.sh
# 前提
#  aws_signing_helperがインストールされていること
#  aws_signing_helperはパスが通る場所に実行権限が付与されて配置されていること
#  Roles Anywhereが設定済みであること

# Roles Anywhereの設定
CLIENT_CERT=<クライアント証明書のファイルパス>
PRIVATE_KEY=<クライアントの秘密鍵のファイルパス>
TRUST_ANCHOR_ARN=<IAM Roles Anywhereの信頼アンカーのARN>
PROFILE_ARN=<IAM Roles AnywhereのプロファイルのARN>
ROLE_ARN=<一時認証情報を取得したいIAM ロールのARN>

# 実行
credentials_json=$( aws_signing_helper credential-process \
  --certificate  ${CLIENT_CERT} \
  --private-key  ${PRIVATE_KEY} \
  --trust-anchor-arn ${TRUST_ANCHOR_ARN}\
  --profile-arn ${PROFILE_ARN} \
  --role-arn ${ROLE_ARN};
)

export AWS_ACCESS_KEY_ID=$(echo $credentials_json | jq -r .AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $credentials_json | jq -r .SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $credentials_json | jq -r .SessionToken)

npmのパッケージ管理メモ

自分用に、npmでのパッケージのインストール、削除、アップデート方法などをまとめたメモです。

パッケージインストール: npm install とnpm ci

  • npm install: 普通のインストール。
    • package.jsonに設定されたパッケージを一括インストールする。
    • または、パッケージを指定して、個別パッケージをインストールする
    • 実行時にpackage.jsonpackage-lock.jsonは必要に応じ更新される。

npm install

何もない状態から特定のパッケージをインストールする。
npm install 'package-name'
package.jsonに登録されたパッケージを一括インストールする。
npm install
特定のパッケージをグローバルインストールする

npmには、ローカルインストールとグローバルインストールがある。
ローカルインストールは、特定のプロジェクトに閉じたインストール。npmを実行したディレクトリ配下にpackage.jsonpackage-lock.jsonの設定ファイルやパッケージデータの格納先としてnode_modulesディレクトリが作成され保村される。
一方グローバルインストールは、実行者の環境のパスが通っている箇所にインストールされ、プロジェクトに関係なく実行できるようにインストールする方法である。

npm install -g 'package-name'

パッケージ削除

パッケージをアインインストールする

npm uninstall  'package-name'

インストールしたパッケージの依存情報を完全に再構築したい

  • プロジェクトのフォルダにあるnode_modulesディレクトリを削除する
  • 依存情報であるpackage-lock.jsonを削除する
  • npm installで再インストールする

参考情報

パッケージのアップデート

パッケージのアップデートはnpm-check-updateを利用するのが楽である。

  • npm-check-updateのインストール(グローバルインストール)
npm install -g npm-check-updates
  • アップデートできるパッケージを確認する
ncu
  • package.jsonを更新する
ncu -u
  • パッケージをインストールする
npm install

自己証明書でIAM Roles Anywhereを使いAWS CLIを実行してみた

  • はじめに
    • この記事は?
  • この手順で作られるもの
  • 能書き
    • IAM Roles Anywhereとは?
    • そもそも自己証明書とは?
  • 前提条件
  • 作業環境
  • 準備
    • AWS CLIをインストール
    • aws_signing_helperをインストール
      • aws_signing_helper の取得
      • aws_signing_helper を実行可能にする
  • 証明書を作成する
    • 自己認証局(CA)の秘密鍵と証明書を作成する
    • クライアント証明書発行のためのCSR(証明書署名要求)作成
      • クライアントの秘密鍵作成
      • CSR(証明書署名要求)の作成
    • CSR(証明書署名要求)を元に自己認証局(CA)でクライアント証明書を発行
  • AWS IAMの設定
    • IAM Roles Anywhere: 信頼アンカーの作成
    • ロールを作成する
    • ロールの信頼関係に信頼アンカーのCondition句を追加する
    • Roles Anywhereのプロファイルを作成する
  • aws_signing_helperでの設定
    • aws_signing_helperでの動作確認
    • クライアントの秘密鍵と証明書をmacOSのキーチェーンに保管して安全に利用する
      • クライアント証明書と秘密鍵をPFXファイル形式に変換する
      • カスタムキーチェーンを作成する
      • PFXファイルをインポートする
      • aws_signing_helperで実行確認する
  • AWS CLIから実行する
  • (アドバンスド)特定のクライアント証明書のみ許可する
  • IAMロールの信頼関係ポリシーのCondition句の変更
  • 参考

はじめに

この記事は?

通常IAM Roles Anywhereはセキュリティの観点から、外部認証局またはプライベート認証局を準備して運営する必要があります。
しかし外部認証局やプライベート認証局は金額が比較的高額になるや認証局の運用の手間が高いため、敷居が高いです。
そこでやむなくですが自己証明書を利用してIAM Roles Anywhereを実施しました。この記事では、その手順を説明しています。
またaws_signing_helperとmacOSのキーチェーンストアとの連携や、クライアント証明書の識別子での認証制限方法も説明しています。

この手順で作られるもの

この手順を実行すルト、以下の構成図のものが作られます。
特に証明書周りは複雑で、手順を追うだけで自分が何を作っているかわからなくなりやすいので、この図を参考にそれぞれのコマンドで何を作っているのかを確認しながら進めると良いかと思います。

続きを読む

(JavaScript)配列の文字列のいずれかを含むケースをチェックする方法

結論

pattern.some( pt => element.includes(pt) ) )でチェックする。

  • patternが検査に適用する特定のパターンの文字列を登録した配列
  • elementがチェックしたい文字列

サンプルコード

const pattern = ['cat','dog','mouse'];
const input = ['cat','house','dog','dog-dayo','do'];

input.forEach( (element) => {
    console.log(
        "input:", 
        element, 
        "pattern.some( pt => element.includes(pt) result:", 
        pattern.some( pt => element.includes(pt) ) 
    );
});

実行結果

完全一致するdog以外に、patternの文字列を含むdog-dayotrueで判定してくれる。

input: cat pattern.some( pt => element.includes(pt) result: true
input: house pattern.some( pt => element.includes(pt) result: false
input: dog pattern.some( pt => element.includes(pt) result: true
input: dog-dayo pattern.some( pt => element.includes(pt) result: true
input: do pattern.some( pt => element.includes(pt) result: false

検証詳細

Array.prototype.includes()を利用した場合。

Array.prototype.includes()は要素の文字列と完全一致した、catdogのみ、trueとなる。

サンプルコード
const pattern = ['cat','dog','mouse'];
const input = ['cat','house','dog','dog-dayo','do'];

input.forEach( (element) => {
    console.log(
        "input:",
        element,
        "pattern.includes() result:",
        pattern.includes(element)
    );
});
実行結果

完全一致するcatdogしか、trueにならない。
これは、Array.prototype.includes()はそれぞれの要素に対して、SameValueZeroアルゴリズムで評価しているかららしい。

$ node test1.js

input: cat pattern.includes() result: true
input: house pattern.includes() result: false
input: dog pattern.includes() result: true
input: dog-dayo pattern.includes() result: false
input: do pattern.includes() result: false

String.prototype.includes()を利用した場合。

Array.prototype.some()は、配列を順番に関数にかけてチェックしtrueが一つでも出たらtrueとするメソッド。 それにArray.prototype.includes()で対象の文字列が含まれるかをチェックすることで、やりたいことを実現する。

サンプルコード
const pattern = ['cat','dog','mouse'];
const input = ['cat','house','dog','dog-dayo','do'];

input.forEach( (element) => {
    console.log(
        "input:",
        element,
        "pattern.some( pt => element.includes(pt) result:",
        pattern.some( pt => element.includes(pt) )
    );
});
実行結果

Array.prototype.includes()のケースに加えて、dog-dayotrueと判断される。
これは、String.prototype.includes()が、1 つの文字列を別の文字列の中に見出すことができるかどうかを判断しているかららしい。(Array.prototype.includes())と挙動が異なる。

$ node test2.js

input: cat pattern.some( pt => element.includes(pt) result: true
input: house pattern.some( pt => element.includes(pt) result: false
input: dog pattern.some( pt => element.includes(pt) result: true
input: dog-dayo pattern.some( pt => element.includes(pt) result: true
input: do pattern.some( pt => element.includes(pt) result: false

TypeScriptを初めて触った時のメモ(その2: TypeScript言語編)

はじめに

この記事は、AWS CDKを覚えるにあたり、その前にAWS CDKを使ったIaCで最も使われるプログラミング言語であるTypeScriptの基礎を押さえないと行けないなと思い、触った時のメモである。

この記事では、TypeScriptとその前提となるJavaScritpの言語としてのメモを記載する。

TypeScriptを始めるまでの初期セットアップについては以下の記事にまとめている。
nopipi.hatenablog.com

おことわり

結局以下のページの解説を見ながらペコペコ叩いただけなので、このブログを見るよりこちらのページを見た方が有益かと思う。最初は分量が少ない「とほほ」から読んで、なんとなくわかったところから公式のドキュメントなどを読んだ。

あと以下のOREILLYの本も役に立った。
www.oreilly.co.jp

JavaScriptの仕様

JavaScriptの基本仕様

  • 大文字と小文字を区別する
  • 文はセミコロンで区切られる
  • 冒頭に"use strict";を入れると厳格な実行モードで安全でない仕様が無効化されるstrict modeで実行される
  • 実行ファイルの冒頭から実行される(main(){}のように必ずこの関数から実行するという仕様はない)
    • 一番最初に呼び出すファイル(エントリーポイント)は、index.jsにすることが多い

JavaScriptサンプル: index.js

"use strict";

// `name`という名前の変数を宣言
const name = "azu";

// `NAME`という名前の変数を宣言
const NAME = "azu";

console.log("name=",name)
console.log("NAME=",NAME)

(JavaScript仕様)コメント

  • 一行コメント
// 以降から行末までが一行コメント
    • 複数行コメント
/* ここに
コメントを
書きます
*/
let a: number = 1;

式と演算子

以下を参照
developer.mozilla.org

変数

(JavaScript仕様)変数・定数宣言

  • 変数の宣言
    • let : ES2015(ES6) から使える、varの後継。範囲が局所化できネストされたブロックで同一変数名で宣言しても上書きされない。
    • var: 以前からある変数宣言方式。ネストされたブロック内で同一変数名で宣言されると上書きされる。
var a = 3;
let b = 3;
{
  var a = 5;
  let b = 5;
}
console.log("a = "+a);    // 5 (ブロック内で上書きされてしまう)
console.log("b = "+b);    // 3 (ブロック内で上書きされない)
  • 定数の宣言
    • const : 定数を宣言する
const a = 1;

a = 2; <<= 定数には代入できないのでエラーになる(tscコンパイル時に"error TS2588: Cannot assign to 'aaa' because it is a constant."となる)

console.log("a = "+a); 

(TypeScript固有)変数の型指定

JavaScript動的型付け言語であることが特徴であるが、大規模開発を行う際は「動的型付け」であることが意図しない挙動に繋がりバグが見つけられにくいというデメリットがある。TypeScriptでは明示的に型宣言を行う(ゆるい)静的型付け宣言であることが特徴である。これにより潜在的なバグの存在を減らすことができる。

型の明示方法(型アノテーション)
  • 単一の型を指定する方法
let a: number = 1;
let b: string = "hello";
  • 複数の型を指定する方法 : | で複数の方を指定する
function add(a: number | undefined, b: number | undefined): number {
  if (a === undefined || b === undefined) {
    throw new Error("引数が不正です");
  }

  return a + b;
}

let bb: undefined;
console.log(add(1, 2));
console.log(add(1, bb));
型の種類

TypeScriptのデータ型は、結局のところJavaScriptで扱うデータ型に準ずる。
JavaScriptで扱うデータ型は大きく、プリミティブ型とオブジェクト型の2つに分類できる。
プリミティブ型には8種類のタイプがある。

  • プリミティブ型: 数値や文字列など単一のデータを格納するデータ型
    • よく使うデータ型
      • string : 文字列("Hello, world")
      • number : 数値(43)
      • boolean : 真偽(true, false)
    • 任意の型
      • any : どのような型の入力も許容する型(JavaScriptの型と一緒)
    • nullと未定義
      • null : ヌル(null pointer)
      • undefined : 初期化をしておらず未定義
    • あまり使わない型
      • bigint : ES2020で追加。 number型で扱えない非常に大きな整数に使用する型
      • symbol : よくわからない
let a: number = 1;
let b: string = "hello";
let c: boolean = true;
let d: any = 1;
let e: null = null;
let f: undefined = undefined;
// let g: bigint = 10n;
let h: symbol = Symbol("key");

console.log("type a:", typeof a);
console.log("type b:", typeof b);
console.log("type c:", typeof c);
console.log("type d:", typeof d);
console.log("type e:", typeof e);
console.log("type f:", typeof f);
// console.log("type g:", typeof g);
console.log("type h:", typeof h);
  • オブジェクト型: プリミティブ型以外のデータ型
    • Array型
    • Dictonary型
    • オブジェクト
    • などなど
let array: number[] = [1, 2, 3];
let dictionary: { [key: string]: string } = {
  "name": "Taro Yamada",
  "age": "20",
  "address": "Tokyo"
};

console.log("type array:", typeof array);
console.log("type dictionary:", typeof dictionary);
リテラル型(特定の文字列や数値などのデータのみ許可する型)

限られた文字や数値などのデータのみデータの投入を許可する特殊な型。
例えば、EC2インスタンスタイプの数値など、特定の文字列の入力のみ許可する変数を定義&宣言するのに利用する。

let StrInstanceSize: "m5.large" | "m5.xlarge" | "m5.2xlarge";

StrInstanceSize = "m5.large";
console.log(StrInstanceSize);

StrInstanceSize = "t2.xlarge"; // t2.xlargeは定義されてないためErrorになる

console.log(StrInstanceSize);

オブジェクト

(JavaScript&TypeScript)配列(Array)

Arrayの詳細な説明は以下を参照

基本的なArrayの操作
//配列の宣言
let year: number[] = [2019, 2020, 2021, 2022, 2023];
let word: string[] = ['hello', 'world'];
let arr: (number | string)[] = [1, 'a'];

// 配列の参照
console.log(year);     //全ての要素を表示
console.log(year[0]);  //1番目の要素を表示
console.log(year[1]);  //2番目の要素を表示

// 配列の操作
word.push('!');     //末尾に追加
word.unshift('Hi'); //先頭に追加

console.log(word.pop());   //末尾を削除
console.log(word.shift()); //先頭を削除
高度なArrayの操作
  • filter: 条件に合致する要素を一つ以上抽出し新しいArrayを作成する
const words: string[] = ["spray", "elite", "exuberant", "destruction", "present"];

const result: string[] = words.filter((word) => word.length > 6);

console.log(result);
// Expected output: Array ["exuberant", "destruction", "present"]
  • find: 先頭から検索し条件に合致する要素を一つ抽出する
const array1: number[] = [5, 12, 8, 130, 44];

const found: number | undefined = array1.find((element) => element > 10);

console.log(found);
// Expected output: 12
  • map: 全ての要素に対して定義したメソッドの処理を行い新しい配列を生成する
const array1: number[] = [1, 4, 9, 16];

// Pass a function to map
const map1: number[] = array1.map((x) => x * 2);

console.log(map1);
// Expected output: Array [2, 8, 18, 32]

(JavaScript&TypeScript)連想配列(=>はJavaScriptにない。オブジェクトで実現)

JavaScriptには連想配列の実装はない。
Objectを利用して、連想配列のような利用を実現する。
ということで、詳しくは次の「オブジェクト」を参照。

(JavaScript&TypeScript)オブジェクト

オブジェクトの基本的(定義&宣言/参照/更新)
// オブジェクトの定義と宣言
let poetletter: { [key: string]: string } = {
    name: "Taro Yamada",
    age: "20",
    address: "Tokyo"
};

// オブジェクトの参照
console.log(poetletter);
console.log(poetletter.name); // Taro Yamada
console.log(poetletter.age); // 20
console.log(poetletter["address"]); // <==こういう書き方もできる

// オブジェクトの操作
poetletter.name = "Hanako Yamada";
console.log(poetletter.name); // Hanako Yamada
  • 宣言時に複数の型タイプを指定することもできる
// オブジェクトの型ていぎ
let poetletter2: { [key: string]: string | number } = {
    name: "Taro Yamada",
    age: 20,
    address: "Tokyo"
};  // <==ageの型がnumberになっている
(TypeScript)オブジェクトの定義(type, interface)
// typeを使って型を事前に定義する
type StrPoetLetter = {
    name: string;
    age: number;
    address: string;
};

// オブジェクトの宣言
let poetletter: StrPoetLetter = {
    name: "Taro Yamada",
    age: 20,
    address: "Tokyo"
};

console.log(poetletter);
  • interfaceを使った定義
// interfaceを使って型を事前に定義する
interface StrPoetLetter {
    name: string;
    age: number;
    address: string;
}

// オブジェクトの宣言
let poetletter: StrPoetLetter = {
    name: "Taro Yamada",
    age: 20,
    address: "Tokyo"
};

console.log(poetletter);
(TypeScript)ネストする複雑なオブジェクトの定義
  • interfaceを使った定義
// 配列にするオブジェクトの型を宣言
interface StrInternal {
    fuga: string;
    nuga: string | undefined;
}

// オブジェクトの型を宣言
interface StrPoetLetter {
    name: string;
    age: number;
    address: string;
    hoge: StrInternal[];
}

// オブジェクトの宣言
let poetletter: StrPoetLetter = {
    name: "Taro Yamada",
    age: 20,
    address: "Tokyo",
    hoge: [
        {
            fuga: "fugafuga",
            nuga: undefined
        },
        {
            fuga: "fugafuga2",
            nuga: "nuganuga2"
        }
    ]
};

// オブジェクトの構成要素を表示
console.dir(poetletter);

typeの場合は、interfaceの部分を以下のように=を入れればOK

interface StrInternal = {
(TypeScript)型エイリアス(type)とinterfaceの違い
  • 継承同名の宣言時にマージされるなど、拡張性が高いのはinterface
  • 拡張性は低いがシンプルなのが型エイリアス(type)

この辺の記事が参考になる。

(Javascript)オブジェクト構造の表示方法
console.dir(オブジェクト);
継承

割愛

(JavaScript)JSON処理

JSONの処理にはJSONオブジェクトを利用する。

  • JSON to Object: JSON.parse()を利用
  • Object to: JSONJSON.stringify()を利用


オブジェクトの説明は以下を参照。

JSON to Object
const json = '{"result":true, "count":42, "data":[{"foo": "hoge1", "bar": "hage1},{"foo": "hoge2","bar": "hage2"}]}';

const obj = JSON.parse(json);

console.dir(obj);
Object to JSON
const obj = {
    result: true,
    count: 42,
    data: [
        {
            foo: "hoge1",
            bar: "hage1"
        },
        {
            foo: "hoge2",
            bar: "hage2"
        }
    ]
};

console.log(JSON.stringify(obj, null, 4));

関数

JavaScriptの関数仕様

JavaScriptの関数の基本

JavaScriptの関数は、

  • function XXX(){ }の形式
  • 関数呼び出し時に引数を省略した場合は、undefinedになる
function add(a, b) {
    console.log("type of a: "+typeof(a)+"   a="+a)
    console.log("type of b: "+typeof(b)+"   b="+b)
    return a + b;
}

// aとb両方指定した場合
console.log(add(1, 2));

// aだけ指定した場合 ->bはundefined,リターンはNaN(Not-A-Number)
console.log(add(1));

// bだけを明示的に指定した場合 ->aはundefined,リターンはNaN(Not-A-Number)
console.log(add(b=2));
JavaScriptの関数の引数の渡し方(参照渡しと値渡し)

javascriptには、参照渡しと値渡しの使い分けはない

  • プリミティブ型 : 値渡し(call by value)
  • オブジェクト型:参照渡し(call by reference)

(TypeScript)関数のパラメータ

関数パラメータの基本的な考え方

TypeScriptでは、

  • 関数を呼び出す時に、関数で定義した全てのパラメータを指定するのが基本
    • なので関数呼び出し時に、TypeScriptではJavaScriptでは許容されるadd(a=1, b=2)という書き方は許容されない
  • パラメータは、型アノテーションを行うのが基本(型アノテーションを省略した場合はanyになる)
  • 関数の戻り値の型アノテーションは、function xxx(): 型アノテーション {}と書く。
function add(a: number, b: number): number {
    console.log("type of a: " + typeof (a))
    console.log("type of b: " + typeof (b))

    return a + b;

}

console.log(add(1, 2));
console.log(add(1));  // エラーになる
console.log(add(a=1, b=2)); //エラーになる
オプションパラメーター

引数の :型アノテーションの前に?をつけると呼び出し時に引数を省略できるオプションパラメーターとなる
ただし以下の注意点がある。

  • 必須パラメーターの後ろにオプションパラメーターを指定する必要がある。
    • 必須パラメータの前にオプションパラメーターは指定できない
  • 指定を省略した場合、オプションパラメーターにはundefinedが設定される。
    • undefinedが設定されるのはjavascriptの仕様によるもの
  • オプションパラメーターを2つ以上定義することも可能であるが、基本はオプションパラメーターは1つにするのが望ましい
    • 呼び出し時に省略できるのは最後のパラメーターのみのため

注意点があるので、
次に説明するデフォルトパラメーターを基本的に利用する方が良いと思う。

function add(a: number, b?: number, c?: number): number {
    console.log("type of a: " + typeof (a))
    console.log("type of b: " + typeof (b))
    console.log("type of c: " + typeof (c))

    if (b && c) {
        return a + b + c;
    } if (b) {
        return a + b;
    } if (c) {
        return a + c;
    } else {
        return a;
    }
}

console.log(add(1, 2, 3));
console.log(add(1, 2)); // 3番目の引数cは省略可能(その場合、c=undefinedになる
console.log(add(1, undefined, 3)); // 2番目の引数を省略する場合はundefinedを渡す必要がある
console.log(add(1)); 
  • エラーになるパターン(必須パラメータの前にオプションパラメーターを指定)
function add2(a?: number, b: number): number { //エラーが発生する: error TS1016: A required parameter cannot follow an optional parameter.
    return a + b;
}
デフォルトパラメーター

パラメータ記載時にb = 9とイコールでデフォルト値を指定することで、パラメータ省略時のデフォルト値が自動的に入る。

デフォルトパラメータを利用した場合、デフォルト値で定義した型から類推しパラメーターの型になり、この関数ではその型固定となる。
そのため、パラメーター省略時を考慮した型チェック(undefinedかどうかの確認)を行う必要がないのがメリットとなる。

function add(a: number, b: number = 9): number {
    return a + b;
}

console.log(add(1, 2)); // 3
console.log(add(1));    // 10
console.log(add(1, undefined));    // 10
レストパラメーター

割愛

(TypeScript)関数の戻り値

関数戻り時の暗示的/明示的な型アノテーション

関数戻り時の型指定のポイント

  • TypeScriptは、関数内のreturnから関数戻り値の型を類推する。
  • そのため、通常は明示的な型アノテーションはしなくても良い(O.REILLYの本によると)
  • ただ再起呼出や大規模開発の場合は、明示的に指定するのが良い、らしい

所感として、安全のためには明示的に関数の戻り値を指定した方が良いような気がする。

function add(a: number, b: number) {
    console.log("type of a: " + typeof (a))
    console.log("type of b: " + typeof (b))

    return a + b;
}
function add(a: number, b: number): number {
    console.log("type of a: " + typeof (a))
    console.log("type of b: " + typeof (b))

    return a + b;
}

(JavaScript)関数宣言と関数式とアロー関数式

最初にJavaScriptの関数の仕様を整理する。

  • 関数宣言 : function 関数名{...}で定義する一般的な関数の宣言方法
  • 関数式: 一回しか利用しないような関数をより簡易的に書く方法
  • アロー関数式: 関数名を付けない無名関数を、より簡略化して書く方法。ES2015(ES6)でJavaScriptの仕様として採用されたもの。

関数宣言を簡略化してよりコードをシンプルにするために、関数式やアロー関数式があると理解している。
アロー関数は、Arrayの節でwords.filter((word) => word.length > 6);でちらっと書いているが、ここではアロー関数を使うことで何が便利になるかをarray.filter()を例として説明する。

関数宣言の利用例

文字列が6文字より大きかどうかを判定する関数checkLengthを作り、その関数をarray.filter()に渡している。
words.filter(checkLength)という書き方が一見不自然に見えるが、こちらのwords.filterの仕様を見ると、以下の通りである。

  • words.filter(checkLength)の構文仕様は以下の通り
    • filter(callbackFn, thisArg)
      • callbackFn : 真偽を返す関数を指定する。filter関数は、指定した関数に対して以下の引数を渡して実行する
        • (a)element:処理中の要素のデータ
        • (b)index:処理中要素のarrayの中のindex情報
        • (c)array: filter() が呼び出された配列の情報

つまり、真偽を判断する関数の関数ポインタ的なものをfilter関数に渡して、filter関数はその関数ポインタを利用し関数に所定の引数を渡して実行ということになる。

この書き方だと、(1)関数を外出しに書く必要があり冗長、(2)書き方が関数ポインタのような使い方と直感的に分かりずらい、というデメリットがある。(個人的な所感)

function checkLength(word) {
    return word.length > 6;
}

const words = ["spray", "elite", "exuberant", "destruction", "present"];
const result = words.filter(checkLength);

console.log(result);
関数式の利用例

先ほどの関数宣言での書き方を関数式で書き直した例。

コードの量自体は変わらないが、関数を変数的に扱うような書き方になったので、関数ポインタ的な利用が直感的にわかりやすい書き方に感じる。
可読性向上の目的が大きい書き方かと思う。

const checkLength = function(word) {
    return word.length > 6;
}

const words = ["spray", "elite", "exuberant", "destruction", "present"];
const result = words.filter(checkLength);

console.log(result);
アロー関数式の利用例

要は、上記のcheckLength関数をより簡略な書き方にして、filter()の中に押し込んだ書き方。

const words = ["spray", "elite", "exuberant", "destruction", "present"];
const result = words.filter(word => word.length > 6);

console.log(result);

アロー関数は正式には以下の書き方をする

(引数1, 引数N) => {}

以下のような簡略方式も可能である。

() => 式

引数 => 

(引数) => 

(引数1, 引数N) => 式

クラス(別途記載)

別途記載します。

モジュール(import、export、require)

JavaScriptのスコープ

JavaScriptで定義した変数・関数などは、そのままの状態では該当のJavaScriptファイル(.js)の中だけのスコープにとどまる。
別の.jsファイルからそれらを参照したい場合は、参照先のjsファイルの中で該当の変数なり関数をexportしたものを、利用したいjsファイルでrequireまたはimportで取り込む必要がある。

エラーになる例
  • util.js
function add(a, b) {
    return a + b;
}
  • index.js
console.log(add(3));  // add関数が見つからずエラーとなる
CommonJS形式でのexport, import
  • util.js
exports.add = add;
function add(a, b) {
    return a + b;
}
  • index.js
const lib_1 = require("./lib");
console.log((0, lib_1.add)(1, 2)); // 3

JavaScriptモジュールの記載方式

モジュール間のexport, importは、JavaScriptベースで、(a)JavaScriptの古い仕様、(b)node.jsの仕様(CommonJS)、(c) ES2015(ES6) の仕様、が混在していて正直よくわからない。
具体的内容をきちんと理解したい人は以下のリンク先を参照。

(JavaScript)CommonJSの書き方
  • util.js
exports.increment = (i) => i + 1;
  • index.js
const { increment } = require("./util");
 
console.log(util.increment(3));
(JavaScript)ES6の書き方
  • util.js
export const increment = (i) => i + 1;
  • index.js
import { increment } from "./util";
 
console.log(increment(3));

または以下の書き方

import * as util from "./util";
 
console.log(util.increment(3));

TypeScriptでの書き方

よくわからない&悩ましいですが、
私が使おうとしているのはAWS CDKでのTypeScript利用で、AWS CDKのサンプルではES6モードで書いているようなので、ES6で書こうと思う。
(TypeScriptからJavaScriptコンパイルする時に、tscコマンドがいい感じにcommonJSに書き直しているようにも見える)

TypeScriptを初めて触った時のメモ(その1: 初期セットアップ編)

はじめに

この記事は、AWS CDKを覚えるにあたり、その前にAWS CDKを使ったIaCで最も使われるプログラミング言語であるTypeScriptの基礎を押さえないと行けないなと思い、触った時のメモである。

TypeScriptを動かすまででも結構な量になったので、この記事ではTypeScriptを動かすまでの初期セットアップとして、node.js・npm/npx・tsc周りまでの話を記載する。TypeScriptの言語そのものは別途記載する。

TypeScriptとは?

wikipediaのTypeScriptの説明の冒頭から拝借

TypeScript はマイクロソフトによって開発され、メンテナンスされているフリーでオープンソースプログラミング言語である。TypeScriptはJavaScriptに対して、省略も可能な静的型付けとクラスベースオブジェクト指向を加えた厳密なスーパーセット(既存のものを全て含んだ上でより機能が拡張されている上位互換となるモノ)となっている。

TypeScriptとJavaScriptとnode.jsの関係性は?

最初はここから理解がふわっとしていたが、調べた内容を図にするとこんな感じ。
こちらのblog記事で別途まとめているのでよければ参照頂きたく。

メモ

以下、やったことと調べたことをつらつら書く。

TypeScript開発&実行環境のセットアップ

node.jsのセットアップ

まず、前提としてJavaScriptの実行環境としてnode.jsをセットアップする。
基本は、以下のnode.js公式ページの手順に従ってセットアップする。

インストール例(最新バージョンでの実行コマンドは上記リンク先を参照)

# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

# in lieu of restarting the shell
\. "$HOME/.nvm/nvm.sh"

# Download and install Node.js:
nvm install 22

# Verify the Node.js version:
node -v # Should print "v22.14.0".
nvm current # Should print "v22.14.0".

# Verify npm version:
npm -v # Should print "10.9.2".

ここで、node.jsnvmnpmの3つのツールがセットアップされる。この3つが何者かを一言でまとめると以下の通り。

  • node.js : JavaScriptの実行環境の一つ
  • nvm : node.jsのバージョン管理ツール
    • node.jsそのものの管理用のコマンド。最新バージョンや指定したバージョンのnode.jsをインストールしたり、複数のバージョンのnode.jsをインストールしている時に、使いたいバージョンのnode.jsを切り替えたりするのに使うコマンド。
  • npm : node.jsのパッケージ管理ツール
    • node.jsで動かすJavaScriptで作られたさまざまなパッケージを管理するためのツール。TypeScriptもnpmコマンドでこの後インストールする。

ちなみに、nvmnpmは最初に実行するシェルでインストールし、node.js は、3つ目のnvm install 22でインストールされる。

開発用のフォルダを準備する

任意のディレクトリを用意する。ここでは、TypeScriptPracticeというディレクトリを利用する前提とする。
GitHubなどで空のリポジトリを作成してgit coneしても良い。

mkdir TypeScriptPractice && cd TypeScriptPractice && pwd
npmパッケージ管理の初期化

開発するディレクトリでnpmの初期化を行う

npm init --yes

そもそもなぜnpmの初期化を行うのか?
npmのパッケージインストール方法は以下の2種類がある。そして通常は、プロジェクト間でnpmパッケージが干渉し合わないようにするために、ローカルモードでパッケージをインストールするのが推奨ということらしい。このローカルモードでインストールする際に、「このディレクトリから独立した領域ですよ」というふうにするためにnpmの初期化を行うようである。

npmのインストールモード

  1. ローカルモード :
    • npm initによる初期化を実行したディレクトリでのみ有効なパッケージとしてインストールされるモード
    • npm initを実行したディレクトリ直下に作成される、./node_modulesディレクトリにパッケージをインストールする。
    • npm installのデフォルトのモード。
  2. グローバルモード :
    • npm initによる初期化に関係なく、実行可能なglobalとしてインストールするモード
    • ~/.nvm/versions/node/vXX.XX.X/lib/node_modulesディレクトリにパッケージがインストールされる
    • 環境変数PATHにも追加されるため、ユーザーはディレクトリを意識することなくインストールしたパッケージを実行できる。
    • グローバルモードでインストールする場合は、npm install -g パッケージ名と、オプションを明示的に指定する必要がある。
TypeScriptのセットアップ
  • typescript, @types/node, ts-nodeの3つのモジュールのインストール
npm install --save-dev typescript @types/node@22 ts-node
  • TypeScriptの動作テスト

以下のコマンドを打って、tscコマンドのバージョンが表記されればTypeScriptのインストールは成功である。

npx tsc --version

インストールするパッケージの説明

パッケージ名 説明 備考
typescript Node.jsをTypeScriptで実装するための静的型付け言語の基本的なライブラリ
@types/node@node.jsバージョン TypeScriptのためのNode.jsの型情報を提供するライブラリ
ts-node TypeScriptファイルをリアルタイムでJavaScriptコンパイルし、利用者はコンパイルを意識せずnode.js上でTypeScriptを直接実行ように見せるためのツール 最初はts-nodeを使わず、tscでTypsScriptからJavaScriptコンパイルしてからnodeコマンドでJavaScriptを実行してみるとありがたみがわかる

npm installの--save-devオプションについて
開発だけでなく本番環境でも使うパッケージなのか、開発環境だけで良いパッケージなのかを指定する。違いはpackage.jsonDependenciesに登録されるかdevDependenciesに登録されるかが異なる。

オプション(short) オプション(long) 説明 インストール時
-D --save-dev 開発環境のみで利用するパッケージの場合に指定する。この場合package.jsonには、devDependenciesのブロックにパッケージが登録される npm installdevDependenciesDependenciesの両方がインストールされる
-P --save-prod 開発環境でも利用するパッケージの場合に指定する。この場合package.jsonには、Dependenciesのブロックにパッケージが登録される npm install --productionDependenciesのみインストールされる

xpnコマンドについて

xpnは、ローカルモードでインストールしたまたはリモートのパッケージを実行するためのコマンド。
例えばTypeScriptのコードをコンパイルするtscコマンドについて、グローバルモードでインストールした場合はPATHが設定されているためプロンプトでtscと打てば実行できるが、ローカルモードでインストールした場所にはPATHがないため、そのままでは実行できない。そのため、xpnコマンドを利用して、パッケージを実行する。 npm execと同様の動きになる。

tscの初期化

TypeScriptコンパイラtscの設定ファイルであるtsconfig.jsonを生成する。
この設定ファイルを編集することでコンパイラの動作をチューニングすることができるが、まずはデフォルトのままで利用する。

npx tsc --init

TypeScriptをとりあえず動かしてみた

前提のTypeScritp環境のセットアップ
# npmの初期化
npm init --yes

# パッケージのインストール
npm install --save-dev typescript @types/node@22

# tscの初期化
npx tsc --init
とりあえずTypeScriptを動かしてみる

こちらのblogの内容を参考に動かしてみた。

console.log("Hello! Node.js × TypeScript");
npx tsc --build
node ./ts_practice/sample1.js
  • 生成された ./ts_practice/sample1.jsファイルを削除する
npx tsc --build --clean
  • ts-nodeで実行してみる
npx ts-node ./ts_practice/sample1.ts
  • ポイント
    • tscコマンド、nodeコマンドを実行せず、ts-nodeコマンド一つでTypeScriptをnode.jsで実行可能
    • この場合はJavaScriptコンパイルされた.jsファイルの書き込みは発生しない
node.jsのhttpモジュールを使って簡易httpサーバを動かしてみる

引き続き、こちらのblogの内容を参考に動かしてみた。

import * as http from "http";

const port = 8989; // ポート番号

// httpサーバーを設定する
const server = http.createServer(
  (request, response) => {
    // サーバーにリクエストがあった時に実行される関数
    response.end("Hello! Node.js with TypeScript");
  }
);
// サーバーを起動してリクエストを待ち受け状態にする
server.listen(port);
// ログを出力する
console.log(`http://localhost:${port} へアクセスください`);
  • 実行してみる
npx ts-node ./ts_practice/sample2.ts
  • 別のターミナルを開いてアクセスしてみる
curl http://localhost:8989

npmでのTypeScriptプロジェクト管理

TypeScriptからJavaScriptへのコンパイル(ビルド)や、実行をnpmのpackage.jsonscriptsとして登録することで、npmコマンドを介して簡単に実行することができる。

  • package.jsonscriptsブロックに以下のように記載する
  "scripts": {
    "build": "tsc --build",
    "clean": "tsc --build --clean",
    "start": "node ./ts_practice/sample2.js"
  },
  • 実行してみる
# TypeScriptのビルド
npm run build

# sample2.jsの実行
npm run start

# ビルドしたファイルのクリア
npm run clean

どんなふうに記載すると良いのかは、node_module配下にインストールした有名なパッケージのpackage.jsonを眺めると参考になると思う。

TypeScript, JavaScript, node.jsの関係を図で整理してみた

今更ですが、TypeScript・JavaScript・node.jsの違いをよく分かってなかったので、自分の理解のために図にしてみました。

特徴

TypeScriptとは

JavaScriptとは

  • 言わずと知れた、WWWの中核要素の一つであるプログラミング言語仕様
  • WebブラウザNetscape Navigatorに起源を持ち、1997年に国際的な標準化団体であるEcmaインターナショナルがECMAScriptとして言語の仕様を標準化している
  • 言語の使用規定なので、実際に動作する環境はブラウザなどで仕様に準拠した実行エンジンを実装してJavaScriptを動作させることになる。

node.js

  • JavaScriptの実行環境の一つ
  • googleが開発・メンテナンスをしているV8 javascript engineをコアに非同期処理やファイルアクセスなどの機能拡張をした実行環境
  • 簡単なJavaScriptのプログラミングで、単独でhttpサーバーとして動作させることも可能
  • 公式ページ: node.js

GitHub ロール(Read,Triage,Write,Maintain,Admin)の権限一覧をスプレットシートで一覧化してみた

レポジトリのユーザー/チームに付与する権限設定(下記画面)には、Read/Triage/Write/MaintainAdminの5つのロールがあります。


それぞれのロールにどのような権限が付与されているかは、以下のGitHubのドキュメントに記載されてます。

ただ行が多すぎてドキュメント見ただけだとよくわからないので、スプレットシートに落としてフィルタリングしたりしやすくしました。
以下のリンクからEXCELファイル形式(GoogleスプレットのEXCEL形式での公開)で取得できます。


作り方

GitHubのマニュアルのページのhtmlから該当のtable部分を抜き出して新しいhtmlファイルを作成して、Googleスプレットシートにインポートしただけです。

  1. Organizationのリポジトリロール - GitHub Docsのページを開く
  2. ブラウザでソースコードを開きリポジトリアクションを検索する。4箇所ヒットするが、一つ目にチェックされたところが該当の表のはずである。
  3. 一行に<table> から </table>があるので、その行をまるっとコピーする。
  4. テキストエディタで新しいページを開き以下の内容を記載する
<html><body>

コピーした<table>から</table>までを貼り付け

</body></html>
  1. レ点とXはSVGになっているので、文字列に置換する。
    1. <svg version=1.1 から始まり</svg>>を探す。
    2. NoYesの2つがあるので、それぞれsvgの一連の文字列を、NoやYesに一気に置換する。
  2. htmlファイルを保存する
  3. Googleスプレットシートで保存したhtmlファイルをインポートする

結局どのロールを指定するか?

5名未満の小規模の開発チームでのプロジェクトで、あまりガチガチに役割を分離せず柔軟に運用するけど、締めるところは締めるという場合はこんな感じかと妄想。

ロール 割り当て先 ざっくりした役割
Admin そのプロジェクトの責任者&副責任者 レポジトリのSettingでの設定変更
Write チームメンバー 開発全般&PullReqの承認も含む
Read チーム以外のオブザーバー 監査やレビューなど

GitHub CLI - ghコマンド - を使ってみた

はじめに

この記事は、GitHub CLIをセットアップして使ってみたメモです。

ghコマンドとは?

ghコマンドは、GitHubの管理を行うためのCLIコマンドです。ghコマンドを使うことで、リポジトリの作成/変更/削除やユーザー管理などGitHubの各種管理をコマンドラインにて実行することが可能になります。
詳しくは、下記の公式ドキュメントを参照ください。
docs.github.com

セットアップ

こちらの [GitHub CLI リポジトリ](https://github.com/cli/cli#installation) のドキュメントに説明がありますが、ここではMacOSにセットアップした時のメモを記載します。

インストール

brewのセットアップ

ここでは、brewを利用してインストールしています。brewが入っていない場合は先にbrewをセットアップします。
brewのインストールは以下のコマンドで実行します。詳しくは[brewのページ](https://brew.sh/ja/)を参照してください。

  • brewのインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • 実行パスの設定のため、以下内容を~/.zshrcに追加
# For brew command
eval "$(/opt/homebrew/bin/brew shellenv)"
  • 実行確認
gh --version
ghコマンドのインストール
brew install gh

GitHubへのログイン認証

github.comに認証してログインします。

  • 下記コマンドでgithub.comへのブラウザを利用したweb認証を開始します。
gh auth login --git-protocol https --hostname github.com --web
  • 下記メッセージが表示されるのでyで進めます
? Authenticate Git with your GitHub credentials? Yes
  • ghコマンドでEnterキーを押すとブラウザ画面が出てくる
  • githubユーザーを確認して進め、ワンタイムコード入力画面が出たらghコマンドで表示されているXXXX-XXXXのコードを入力し進める
  • Passkeys設定が求められるのでクリックして進める
  • ghコマンドが進み、ログイン情報が表示されれば成功

ghコマンドの使い方

ここではいくつかの利用例を提示します。
詳しくは、下記のマニュアルを参照してください。
cli.github.com

リポジトリ関連操作

# 自分が所有するリポジトリの表示
gh repo list

# 指定したOrganizationが所有するリポジトリの表示(XXXXがOrganization名)
gh repo XXXX

# リポジトリの作成
gh repo create <Organization名>/<リポジトリ名>

Secrets/Variables関連操作

# secretsの一覧表示
gh secret list --env ENVIRONMENT_NAME

# secretsの設定
gh secret set NAME --body VALUE --env ENVIRONMENT_NAME

# secretsの削除
gh secret delete NAME  --env ENVIRONMENT_NAME

# variableの一覧表示
gh variable list --env ENVIRONMENT_NAME

# variableの設定
gh variable set NAME --body VALUE --env ENVIRONMENT_NAME

# variableの削除
gh variable delete NAME  --env ENVIRONMENT_NAME

Macの開発環境を整備する

はじめに

この記事は、自分自身のmacの開発環境をセットアップするための備忘録です。

セットアップ手順

前提

初期設定で以下の設定が完了していること。

  • appleアカウントにログインしデータの同期ができていること

ブラウザ

Macコマンドラインデベロッパーツール

appleが提供している、gitコマンドpipコマンドなど開発に必要なツールのパッケージをインストールする

  • command + spaceで検索ウィンドウを出し、terminalで検索しターミナルを起動する
  • xcode-select --installコマンドを実行する
  • 別ウィンドウでコマンドラインデベロッパーツールが必要です。ツールを今すぐインストールしますか?と聞かれるので、インストールでインストールする。

その他ツールのインストール

Visual Studio Codeセットアップの備忘録

はじめに

この記事は、自分自身のmac bookでVisual Studio Codeを利用した開発環境のセットアップのための備忘録です。
私は、主にpythonAWS CloudFormation、Terraformを利用しているので、その辺りの開発のためのセットアップ手順になります。

前提

このセットアップの前提です

VSCodeのインストール

  • 下記リンク先からパッケージ(私はMac)を選択してダウンロードする

code.visualstudio.com

  • ダウンロードしたパッケージのファイルをアプリケーションに移動する

VSCodeの日本語化

拡張機能 Japanese Language Pack for Visual Studio Codeをインストールし再起動して反映する

  • 左のExtensions Marketplaceを選択し、Japaneseで検索する
  • installボタンからインストールする
  • 拡張のインストール完了後に、VSCodeを再起動するとメニューが日本語表示に変更される


Settings Syncの有効化

説明

VSCodeの設定のバックアップと複数端末でのVSCode設定の共有を行う機能です。
同期のために、GitHubアカウントまたはMicrosoftのアカウントが必要です。
ここではGitHubアカウントを利用します。

手順

  • 左下の設定アイコン(歯車アイコン)のメニューからバックアップと同期の設定を選択
  • 上部中央のpannelに出ているサインインボタンからGitHubでサインインを選びサインインする

拡張機能

拡張機能のセットアップ

VSCodeで、以下の拡張機能をインストールする。

Category Extension Name 提供元 説明
common YAML Red Hat YAML支援機能
Code Spell Checker Street Side Software スペルチェッカー
Markdown Preview Enhanced Markdown Preview Enhanced ported to vscode Markdownのプレビュー。スタイルを編集したかも(要確認)
Markdown Preview Github Styling Matt Bierner MarkdownのプレビューをGitHubスタイルで表示。いらないのかも?
Draw.io Integration Henning Dieterichs VSC内でDraw.ioを編集する拡張
python Python Microsoft Python拡張機能
Pylance Microsoft Python拡張機能の言語サーバ
IaC HashiCorp Terraform HashiCorp TerraformのHLCのSyntaxハイライトとautocompletion機能
CloudFormation Linter kddejong CloudFormationテンプレートの解析ツール
Container Docker Microsoft Docker開発支援

CloudFormation Linterの前提コマンドセットアップ

CloudFormation Linterは、前提としてcfn-lintをインストールする必要があるため、以下の手順でインストールする。

pip3 install cfn-lint
  • 以下のコマンドで実行確認
cfn-lint --version
  • (オプション)もし実行できない場合は~/.zshrcに以下の内容を追記してpython3のパスを追加する
# Add Path
PATH="${PATH}:/Users/n/Library/Python/3.9/bin"

Terraform ExtensionのFormattingのための前提セットアップ

Terraform ExtensionでFormattingを行うためには、ローカルに`terraform`コマンドが必要そうです。そのため以下リンク先からterraformをインストールします。
developer.hashicorp.com

設定

自動フォーマット

保存時のフォーマッタの自動実行設定

  • 設定
    • 右下の設定(歯車アイコン)から設定を選び、以下のキーワードでフィルタリングしてチェック(true)にする。
  • 設定:Format On Save: true

terraformの自動フォーマット実行

terraform編集中のファイル保存時のフォーマッタ(terraform fmtコマンド)を実行する設定。

  • 設定
    • shift + command + pでコマンドパレットを開く
    • "open user setting"と入力し、>open user settings (JSON)を実行する
    • 開かれたsettings.jsonに以下のJSONブロックを追加する
"[terraform]": {
  "editor.defaultFormatter": "hashicorp.terraform",
  "editor.formatOnSave": true,
  "editor.formatOnSaveMode": "file"
},
"[terraform-vars]": {
  "editor.defaultFormatter": "hashicorp.terraform",
  "editor.formatOnSave": true,
  "editor.formatOnSaveMode": "file"
}

CloudFormationの構文チェック用のカスタム設定追加

下記設置を追加します

  1. CloudFormation仕様の追加
  2. CloudFormationのカスタムタグ定義の追加
設定(1) CloudFormation仕様の追加

VSCodeの設定ファイル(Macの場合: $HOME/Library/Application Support/Code/User/settings.json)*1に、CloudFormationのリソース仕様を追加します。この設定でをすると、拡張子がcf.yamlのファイル、cfn/、cloudformation配下のYAMLファイルがCloudFormation用のYAMLとして指定したソース仕様にしたがってチェックされます。

  1. コマンドパレット(⇧⌘P)を開いて >open user settings (JSON)を実行する
  2. 下記定義を追加する
    "yaml.schemas": {
        "https://d33vqc0rt9ld30.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json": [
            "*.cf.yaml",
            "*.cf.yml",
            "cfn/*.yaml",
            "cfn/*.yml",
            "cloudformation/*.yaml",
            "cloudformation/*.yml"
        ]
    },
設定(2) CloudFormationのカスタムタグ定義の追加

デフォルトのままだと!Sub, !RefなどのCloudFormation固有のスキーマYAMLの構文エラーとなるため、これら固有スキーマを追加します。内容はこちらのIssueAndyJPhillipsさんのコメント内容を流用しています。

    "yaml.customTags": [
        "!And sequence",
        "!Equals sequence",
        "!If sequence",
        "!Not sequence",
        "!Or sequence",
        "!Base64",
        "!Cidr sequence",
        "!FindInMap sequence",
        "!GetAtt",
        "!GetAZs",
        "!ImportValue",
        "!Join sequence",
        "!Select sequence",
        "!Split sequence",
        "!Sub",
        "!Ref"
    ]
(参考)setting.json全体の内容

設定(1)&(2)をすると、既存の設定に、下記設定が追加された形になります。

{
    <既存の設定>
    "yaml.schemas": {
        "https://d33vqc0rt9ld30.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json": [
            "*.cf.yaml",
            "*.cf.yml",
            "cfn/*.yaml",
            "cfn/*.yml",
            "cloudformation/*.yaml",
            "cloudformation/*.yml"
        ]
    },
    "yaml.customTags": [
        "!And sequence",
        "!Equals sequence",
        "!If sequence",
        "!Not sequence",
        "!Or sequence",
        "!Base64",
        "!Cidr sequence",
        "!FindInMap sequence",
        "!GetAtt",
        "!GetAZs",
        "!ImportValue",
        "!Join sequence",
        "!Select sequence",
        "!Split sequence",
        "!Sub",
        "!Ref"
    ]
}

MacOSでDocker Desktopの代替えとしてLima+docker環境を作ってみた

はじめに

MacOSでdockerを使おうとすると、第一候補は Docker Desktopになりますが、一定規模以上の企業で利用する場合は有料サブスクリプションが必要になります。*1

WindowsならWLS2でLinuxの仮想環境を作ってその上で、linuxのdocker(Docker Desktopでないので有料サブスクリプション不要)を動かすということができます。一方MacOSには標準でWLS2相当のものがないので、OSSLimaを使ってLinux仮想環境を作って、その上でlinuxのdockerを動かします。

ここではそのセットアップ時の手順を備忘録として記載します。

Limaとは

Mac上でLinuxの仮想環境のVMを作ることができるOSSです。
LimaはGitHubにホストされており、URLは以下になります。

以下のような特徴があります

  • brewコマンドでインストールが楽
  • intel on intelARM on intelARM on ARMintel on ARMのどれでも可能
  • Linuxとして、Alpine、Arch Linux、Debian、Fedora、Ubunts(default)などさまざまなイメージが利用可能

セットアップ手順

前提のセットアップ

すでにセットアップされている場合はスキップしてください。

brewコマンドのインストール

macOS用のパッケージマネージャーbrewをセットアップします。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

セットアップ手順やその他brew情報は、下記のbrewのページを参照ください。

Limaのインストールとdockerインスタンスの作成

Limaのインストール
brew install lima
dockerが入ったLinux仮想環境の起動

Limaには多数のVMテンプレートがあり、それらテンプレートを利用して簡単に目的の仮想環境をセットアップすることができます。dockerの環境もLimaのテンプレートにあるため、ここではそのテンプレートを利用してdocker環境を起動する手順をまとめました。

limactl start --name=default template://docker
(参考)dockerが入ったLinux仮想環境の停止
limactl stop
(参考)dockerが入ったLinux仮想環境の2回目以降の起動
limactl start

dockerコマンドのセットアップ

dockerコマンドのインストール
brew install docker
dockerコマンドからLimaの仮想環境上のdockerデーモンへの接続

ここで記載した手順は、Limaのdocker環境を起動した時のメッセージの最後に表示されています。
lima-defaultという名前のコンテキストを作成して、切り替える方法になります。

  • dockerコンテキストの作成
docker context create lima-default --docker "host=unix://${HOME}/.lima/default/sock/docker.sock"
  • dockerで作成したコンテキストへの切り替え
docker context use lima-default
  • テスト
docker run hello-world

下記メッセージが表示されればOKです。

Hello from Docker!
This message shows that your installation appears to be working correctly.

docker起動スクリプト

上記手順で作成した環境をベースに、Limaのdocker仮想環境を起動しdockerのコンテキストを変更する一括のシェルスクリプトです。

  • start-docker.sh
#!env bash

# start docker instance via Lima
limactl start

# change docker context
docker context use lima-default

補足

補足1 Limaの操作

Limaで稼働している仮想環境の一覧
limactl list
固有のインスタンス名をつけた仮想環境の新規作成と起動

fedora仮想環境をfedoravmというインスタンス名で起動する

limactl start --name=fedoravm template://fedora
Limaの仮想環境の停止

fedoravmというインスタンス名の環境を停止する。

limactl stop fedoravm
既存の仮想環境の起動

fedoravmという作成済みインスタンスを起動する。

limactl start fedoravm
起動中の仮想環境にインタラクティブなシェルで入る
limactl shell fedoravm
Limaの起動可能なテンプレート一覧参照
limactl start --list-templates

補足2 dockerのコンテキスト操作

コンテキスト一覧表示
docker context list
コンテキストの切り替え
docker context use <コンテキスト名>
コンテキストの削除
docker context rm <コンテキスト名>

*1:. Commercial use of Docker Desktop at a company of more than 250 employees OR more than $10 million in annual revenue requires a paid subscription (Pro, Team, or Business) to use Docker Desktop. Pricing | Docker