One Step Ahead

プログラミングやエンジニアリング全般について書いていきます

『CommandLineSelectableMenu』 v1.2.0 リリースノート

はじめに


CommandLineSelectableMenu』v1.2.0のリリースノートになります。

新しいVersionをインストールされたい方は、こちらからどうぞ。

PM> Install-Package CommandLineSelectableMenu -Version 1.2.0

Update内容


1. コンストラクタの追加

今回の対応元になったのは、『#7 Add a mechanism to allow the default options to be Hooked. 』のIssueです。
CommanLineSelectableMenuのインスタンス作成時、Optionを追加しようと思うと毎回Optionのインスタンスを作成するのが非常に面倒だったので、Actionを使ってHookできるようなコンストラクタを追加しました。

var selectableMenu = new SelectableMenu<Action>((opt) => { opt.IsClearAfterSelection = true; });

このように、わざわざ新しいSelectableMenuOptionsインスタンスを作成しなくても、与えられたOptionsに対して、Actionを介してプロパティを設定することができるようになります。

2. Wikiの追加

これは機能追加とは関係ありませんが、Wikiの内容を更新しました。
Usage』には簡単な実装方法をまとめさせていただきました。
About Options』にはSelectableMenuOptionsのプロパティ情報などを記載してあります。

お使いになられている方は役立てていただければと思います。

まとめ


  • v1.2.0に新規のコンストラクタを追加。
  • Wikiが更新され、簡単な実装方法とOptionの説明が追加。

CSSの :not疑似クラスの使い方

はじめに


個人開発をしていて、今まで使っていなかったCSSの疑似クラス:notを使う機会があったので、そのメモ書きです。

役割


:not疑似クラスは、かっこ内のセレクタリストと一致しない場合に、対象となっているCSSを適用する。という動作をします。 例えば以下の例。

p:not(.change-color) {
  color: red;
}

<p>タグで.change-colorクラスを持っていない要素は、文字色を赤に設定する。という具合になります。
この例では、クラスを指定していますが、クラス以外にもタグや、属性を指定できます。

/* 属性 dir="rtl" が設定されているdiv以外の背景色を変更する。 */
div:not([dir="rtl"]) {
  background-color: red;
}

注意点


1. ブランクの付け方

:not疑似クラスに限ったことではありませんが、疑似クラスの使い方には注意が必要です。
特にブランクの付け方によって意味が変わるので注意しましょう。

body :not(p) {
  color: gray;
}

上の例は、bodyの指定の後にブランクが存在しています。この場合は、適用されるのは、<body>タグの子要素の中で<p>タグ以外の文字色をグレーに設定する。』となります。
仮に,,,

body:not(p) {
  color: gray;
}

ブランクを除いて、bodyと:notを連結してしまうと、『<body>タグの属性<p>以外』というわけの分からない内容なってしまい、スタイルが適用されません。

2. 二重否定

:not疑似クラスは重ねて使用することができるため、二重否定の文法を記載することができます。
ただ、あくまで『出来る』というだけで、個人的には可読性が低いので積極的には使わない方が良いかなと思っています。

/* 二重否定なので、属性 dir="rtl" が設定されているdivの背景色を変更する。 */
div:not(:not([dir="rtl"])) {
  background-color: red;
}

まとめ


  • :not疑似クラスは、かっこ内のセレクタリストと一致しない場合に、対象となっているCSSを適用する。
  • 文法(特にブランク)によって、意味が異なるので注意して記載・確認しないと思ったようにスタイルが出来ようされない場合がある。

参考・引用


EntityFramework Coreを使って動的にテーブルへ追加、削除を行う。

はじめに


改修を依頼されたソースコードの中に不要な実装を見つけたので、同じような勘違いをされている方への注意喚起の記事です。

前提条件


  • .NET Core 2.2
  • EntityFramework Core 2.2

ソースコードとその目的


不要な処理が実装されていたのは、タイトル通り『EntityFramework Coreを使って動的にテーブルへ追加、削除を行う。』という部分です。
実際のソースコードは載せることが出来ないので、サンプルのソースコードを作成しました。

モデルクラス

using System.ComponentModel.DataAnnotations.Schema;

namespace SampleDao.Models
{
    /// <summary>
    /// DbEntity interface
    /// </summary>
    public interface IDbEntity
    {
        object[] Keys { get; }
    }

    /// <summary>
    /// Userテーブルモデル
    /// </summary>
    [Table("user")]
    public class User : IDbEntity
    {
        /// <summary>
        /// UserID
        /// </summary>
        [Column("id")]
        public long Id { get; set; }

        /// <summary>
        /// ユーザー名
        /// </summary>
        [Column("username")]
        public string UserName { get; set; }

        /// <summary>
        /// 自己紹介
        /// </summary>
        [Column("introduce")]
        public string Introduce { get; set; }

        public object[] Keys { get { return new[] { (object)Id }; } }
    }
}

コンテキストクラス

using Microsoft.EntityFrameworkCore;
using SampleDao.Models;

namespace SampleDao
{
    /// <summary>
    /// Sample用 DBコンテキスト
    /// </summary>
    public class MyDbContext : DbContext
    {
        /// <summary>
        /// コンストラクタ 新規インスタンスを生成します。
        /// </summary>
        public MyDbContext()
            : base(new DbContextOptionsBuilder().UseSqlServer("Data source=(localdb)\\SampleDbIns;Database=DB01SAMPLE;User ID=Test;Password=Password;", null).Options)
        { }

        /// <summary> Userテーブル </summary>
        public DbSet<User> User { get; set; }

        /// <summary>
        /// 引数に渡されたエンティティを該当するテーブルに対して、追加します。
        /// </summary>
        /// <typeparam name="TSource">対象Entity型</typeparam>
        /// <param name="entity">対象Entity</param>
        public void DynamicAdd<TSource>(TSource entity) where TSource : class, IDbEntity
        {
            var ctxProp = typeof(MyDbContext).GetProperty(typeof(TSource).Name);
            var dbSet = (DbSet<TSource>)ctxProp.GetValue(this);
            dbSet.Add(entity);
        }

        /// <summary>
        /// 引数に渡されたエンティティを該当するテーブルから削除します。
        /// </summary>
        /// <typeparam name="TSource">対象Entity型</typeparam>
        /// <param name="entity">対象Entity</param>
        public void DynamicRemove<TSource>(TSource entity) where TSource : class, IDbEntity
        {
            var ctxProp = typeof(MyDbContext).GetProperty(typeof(TSource).Name);
            var dbSet = (DbSet<TSource>)ctxProp.GetValue(this);
            dbSet.Remove(entity);
        }

        /// <summary>
        /// モデルの生成を行います。
        /// </summary>
        /// <param name="modelBuilder">モデルビルダー</param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region Usersテーブル

            modelBuilder.Entity<User>()
                .HasKey(e => new { e.Id })
                .HasName("user_tbl_pk");

            modelBuilder.Entity<User>()
                .Property(e => e.Id)
                .ValueGeneratedOnAdd();

            modelBuilder.Entity<User>()
                .Property(e => e.UserName)
                .IsRequired()
                .HasMaxLength(30);

            modelBuilder.Entity<User>()
                .Property(e => e.Introduce)
                .HasMaxLength(500);

            #endregion
        }
    }
}

処理クラス

using SampleDao.Models;
using System.Linq;

namespace SampleDao
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new MyDbContext())
            {
                // 動的 追加処理
                var user = new User() { UserName = "hogehoeg", Introduce = "sample" };
                context.DynamicAdd(user);
                context.SaveChanges();
            }


            using (var context = new MyDbContext())
            {
                // 動的 削除処理
                var user = context.User.Where(x => x.Id == 1).FirstOrDefault();
                context.DynamicRemove(user);
                context.SaveChanges();
            }
        }
    }
}

処理クラス、コンテキストクラスから類推するに、追加・削除処理に関しては、どのようなモデルクラスであってもcontext.DynamicAdd()メソッドを使用することで、明示的にcontext.User.Add(user)のような処理を書かなくても良い。という実装を目指していたと思われます。

問題点


このソースコードで問題の部分は、動的処理を行っているDynamicAddメソッドとDynamicRemoveメソッドそのものです。
Reflectionを使っているから遅い。とかそういった問題でなはく、動的な追加・削除メソッドはすでにDbContextに存在しています。

DbContext.Addメソッド、DbContext.Removeメソッドがそれに該当します。
DbContext.User.Add(user)のように、DbSetで設定したテーブルモデルのプロパティを明示的に指定しないと追加、削除が行えないと思っている方がいますが、そんなことはありません。
これらのメソッドを使用することでDbContextがDbSetのプロパティで抱えているテーブルモデルに対して、動的に追加・削除を行うことができます。

そのため、今回実装されているDynamicAddメソッドとDynamicRemoveメソッドという2つのメソッドは不要ということになります。

DbContextを使った動的追加・削除


実際に修正したソースコードは下記の通りです。
(修正と言っても、ただ削除しただけですが...)

修正後 コンテキストクラス

using Microsoft.EntityFrameworkCore;
using SampleDao.Models;

namespace SampleDao
{
    /// <summary>
    /// Sample用 DBコンテキスト
    /// </summary>
    public class MyDbContext : DbContext
    {
        /// <summary>
        /// コンストラクタ 新規インスタンスを生成します。
        /// </summary>
        public MyDbContext()
            : base(new DbContextOptionsBuilder().UseSqlServer("Data source=(localdb)\\SampleDbIns;Database=DB01SAMPLE;User ID=Test;Password=Password;", null).Options)
        { }

        /// <summary> Userテーブル </summary>
        public DbSet<User> User { get; set; }

        /// <summary>
        /// モデルの生成を行います。
        /// </summary>
        /// <param name="modelBuilder">モデルビルダー</param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region Usersテーブル

            modelBuilder.Entity<User>()
                .HasKey(e => new { e.Id })
                .HasName("user_tbl_pk");

            modelBuilder.Entity<User>()
                .Property(e => e.Id)
                .ValueGeneratedOnAdd();

            modelBuilder.Entity<User>()
                .Property(e => e.UserName)
                .IsRequired()
                .HasMaxLength(30);

            modelBuilder.Entity<User>()
                .Property(e => e.Introduce)
                .HasMaxLength(500);

            #endregion
        }
    }
}

修正後 処理クラス

using SampleDao.Models;
using System.Linq;

namespace SampleDao
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new MyDbContext())
            {
                // 動的 追加処理
                var user = new User() { UserName = "hogehoeg", Introduce = "sample" };
                context.Add(user);
                context.SaveChanges();
            }


            using (var context = new MyDbContext())
            {
                // 動的 削除処理
                var user = context.User.Where(x => x.Id == 1).FirstOrDefault();
                context.Remove(user);
                context.SaveChanges();
            }
        }
    }
}

作成したモデルクラスを直接、context.Add()context.Removeに渡すことで、追加・削除が実行されます。

まとめ


  • DbContextには動的追加・削除の機能が備わっている。

Github Actions で microsoft/setup-msbuild@v1.0.1が失敗する。

はじめに


開発で使用していたWorkflowが年明けくらいから失敗していました。
(自分が気づいたのが、年明けなので実際にいつから失敗していたのか不明です....お恥ずかしい...)   

今日はその修正方法のまとめです。

環境設定


  • OS : windows-latest
  • プロジェクト : .NET Framework

失敗していたworkflow


失敗していた実際のworkflowを簡素化したものは下記の通りです。

name: Dotnet Framework Build Actions

on:
  push:
    branches:
      - master
      - develop/**

jobs:
  build:
    runs-on: windows-latest
    env:
      BUILD_PATH: src\Example.sln

    steps:
      - uses: actions/checkout@v2

      - name: Setup Nuget
        uses: nuget/setup-nuget@v1

      - name: Restore package
        run: nuget restore $env:BUILD_PATH

      - name: Setup MSBuild
        uses: microsoft/setup-msbuild@v1.0.1

      - name: Run Build
        run: msbuild $env:BUILD_PATH -p:Configuration=Release

      - name: Run Test
        run: vstest.console.exe /Platform:x64 src\Tests\Test_**\bin\Release\Test_**.dll

失敗していたStepはSetup MSBuildで,,,

f:id:EaE:20210203003200p:plain

このようなErrorが出力されていて、Setupの段階でBuildが失敗していました。

Errorログの確認


まずはErrorログの確認です。

[原文]
Unable to process command '::add-path::C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin' successfully.

[和訳]
コマンド ::add-path::C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Binを正常に終了できません。

とりあえず、add-pathコマンドが正常終了していないことが分かりました。

[原文]
The add-path command is disabled. Please upgrade to using Environment Files or opt into unsecure command execution by setting the ACTIONS_ALLOW_UNSECURE_COMMANDS environment variable to true. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/

[和訳]
add-path コマンドは無効になっています。環境ファイルを使用するようにアップグレードするか、ACTIONS_ALLOW_UNSECURE_COMMANDS 環境変数に true を設定して、アンセーフコマンドを実行するようにしてください。詳細は https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ を参照してください。

なんかすでに答えが出ていますが、ACTIONS_ALLOW_UNSECURE_COMMANDS=TRUEの環境変数を設定すればいけそうです。

修正の経緯


GitHub Actions: Deprecating set-env and add-path commandsの内容を見るに,,,

[原文]
A moderate security vulnerability has been identified in the GitHub Actions runner that can allow environment variable and path injection in workflows that log untrusted data to STDOUT. This can result in environment variables being introduced or modified without the intention of the workflow author.

[和訳]
GitHub Actions ランナーに中程度のセキュリティ脆弱性が確認されており、信頼できないデータを STDOUT に記録するワークフローに環境変数やパスインジェクションを許す可能性があります。これにより、ワークフロー作成者の意図せずに環境変数が導入されたり変更されたりする可能性があります。

set-envadd-pathといったコマンドが該当するようです。

Workflowの修正


修正と言ってもやることは単純です。
エラーに示されている通り、環境変数ACTIONS_ALLOW_UNSECURE_COMMANDS: trueを設定するだけです。

name: Dotnet Framework Build Actions

on:
  push:
    branches:
      - master
      - develop/**

jobs:
  build:
    runs-on: windows-latest
    env:
      BUILD_PATH: src\Example.sln
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true

    steps:
      - uses: actions/checkout@v2

      - name: Setup Nuget
        uses: nuget/setup-nuget@v1

      - name: Restore package
        run: nuget restore $env:BUILD_PATH

      - name: Setup MSBuild
        uses: microsoft/setup-msbuild@v1.0.1

      - name: Run Build
        run: msbuild $env:BUILD_PATH -p:Configuration=Release

      - name: Run Test
        run: vstest.console.exe /Platform:x64 src\Tests\Test_**\bin\Release\Test_**.dll

まとめ


  • ACTIONS_ALLOW_UNSECURE_COMMANDS: trueの環境変数を追加する。
  • 信頼できないデータを STDOUT に記録するワークフローコマンドは、明示的に環境変数に設定を追加しないと利用できない。

Github Actionを活用してnugetへpackageの公開を行う。

はじめに


先日作成した『CommandLineSelectableMenu』はmasterブランチへのコミットに反応して、自動的に新しいPackageをUploadします。
今日は備忘録を兼ねて、そのまとめです。

前提


『CommandLineSelectableMenu』を前提にWorkflowを作成しているので、下記を前提としています。

  • OS : Ubuntu
  • プロジェクト: .NET Standard

プロジェクト構成は下記の通りです。

./
 |--- .github
 |     |--- workflows
 |           |--- upload.yml 
 |--- src
 |     |--- Example.sln
 |     |--- Example
 |           |--- Example.csproj
 |           `--- Program.cs
 `--- README.md

nugetのAPIアクセスキーを作成する。


nugetに自作のPackageを公開するためには、サイトから直接Uploadを行う方法とコマンドラインからUploadする方法があります。
今回はコマンドラインからdotnet nuget pushコマンドを使用してUploadを行っていきます。 コマンドライン上からUploadしていくためには、API キーの作成が必要なります。
作成方法については、nuget パッケージの公開方法 | Microsoft Docsを参考にしてください。

Actions secretにAPIアクセスキーを登録


取得したAPIアクセスキーは事前に『Actions secrets』に登録しておきましょう。
取得したAPIキーをworkflowに記載するのは絶対にやめましょう!!

まずは、Settings > Secrets を開きます。

f:id:EaE:20210202215206p:plain

すると、『Actions secrets』が表示されるので、『New repository secret』を選択します。

f:id:EaE:20210202215216p:plain

後はキー名を設定して、先ほど取得したAPIキーをValueに設定すればOKです。

f:id:EaE:20210202215222p:plain

Actionの実行をmasterブランチに限定する。


Packageの公開タイミングは、featureブランチやdevelopブランチ、bugfixといったトピックブランチをMergeするタイミングなど、masterブランチに変更が行われるタイミングに限定します。

name: Package publish eample - Publish to Nuget

on:
  push:
    branches:
      - master

これで、Action実行の対象がmasterブランチに限定されます。

Build & Packageステップの追加


次にBuildから Packageを作成するステップまでを追加します。
使用するOSはUbuntuを使用します。

name: Package publish eample - Publish to Nuget

on:
  push:
    branches:
      - master

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1

      - name: Setup .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 3.1

      - name: Build
        run: dotnet build --configuration Release
        working-directory: ./src

      - name: Unit tests
        run: dotnet test --configuration Release
        working-directory: ./src

      - name: Generate nuget package.
        run: dotnet pack --configuration Release -o nupkg
        working-directory: ./src
  1. ステップ1では、ブランチをチェックアウトします。
  2. ステップ2では、.NET Coreのセットアップを行います。
  3. ステップ3では、Buildの確認を行います。./src配下に.slnファイルがあるので、作業フォルダは./srcを指定します。作業フォルダやBuild設定は、実際のプロジェクトに合わせて変更します。
  4. ステップ4では、単体テストを実行します。
  5. ステップ5では、Packagingを行います。方法はほとんどBuild変わりませんが、ここでは出力フォルダを指定しています。./nupkgへ.nupkgファイルを出力します。

BuildからTestまでのステップに関しては、masterブランチにMergeするタイミングではなく、ブランチに関わらず、Pushに応じてActionが実行される方が望ましいです。
なので、BuildからTestまでは専用のWorkflowを作成しておくことをお勧めします。

Uploadステップの追加


最後にUploadステップを追加します。

name: Package publish eample - Publish to Nuget

on:
  push:
    branches:
      - master

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1

      - name: Setup .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 3.1

      - name: Build
        run: dotnet build --configuration Release
        working-directory: ./src

      - name: Unit tests
        run: dotnet test --configuration Release
        working-directory: ./src

      - name: Generate nuget package.
        run: dotnet pack --configuration Release -o nupkg
        working-directory: ./src

      - name: Publish to nuget
        run: find . -type f -name *.nupkg -print0 | xargs -0 -I pkg dotnet nuget push pkg -k $nuget_api_key -s "https://api.nuget.org/v3/index.json" --skip-duplicate
        env:
          nuget_api_key: ${{ secrets.NUGET_API_KEY }}
        working-directory: ./src/nupkg

find . -type f -name *.nupkg -print0でカレントディレクトリ内のファイルを対象に*.nupkgファイルを検索します。
-print0の部分は、後続のxargs-0オプションを指定して、NULL文字区切りにしているので、それに合わせて設定しています。
また、xarg -0 -I pkgの記載の通り、-Iオプションが指定されているので標準入出力から読み込んだ値はpkgに入っています。

後はnuget パッケージの公開方法 | Microsoft Docsに記載のある通り実行すればOKです。
ここでPointになるのは、nuget_api_keyの部分です。
nuget_api_key: ${{ secrets.NUGET_API_KEY }}この部分で、secrets.NUGET_API_KEYを環境変数nuget_api_keyに設定しています。

まとめ


  • nugetへPackageのUploadを行う際は、大まかに分けてサイトから行う方法とコマンドラインから行う方法の2通りがある。
  • APIアクセスキーをActionで使用する際は、事前に『Actions secrets』に登録しておく。
  • 意外と簡単。

こちらで実際に使用しているので気なる方はどうぞ。

参考・引用


『CommandLineSelectableMenu』をリリースしました。

はじめに


タイトル通り、先日『CommandLineSelectableMenu』をリリースしました。
今日はそのご紹介記事になります。
(リリースしたばかりで、versionがすでに『v1.1.1』になっていますが、そこはご愛嬌。)

紹介よりもまずは実物!! という方は、下記からどうぞ。

Githubはこちら
nugetはこちら

CommandLineSelectableMenuって何?


まさに読んで字のごとくで、そのままですが『CommandLine専用の選択可能メニュー』を作成するためのライブラリです。
対話形式の選択メニューを非常に簡単に実装することができます。

.NETStandard 2.0で作成されたプロジェクトですので、.NET Framework, .NET Coreどちらのプロジェクトでも使用することができます。

機能紹介


対話形式選択メニューの作成

非常に小さなライブラリなので、機能は本当にこれだけです。
Generatorの作成などに活用してもらえればという想いで作成しました。

実装方法Tips


最小限の実装

最小の実装は非常に簡単です。SelectableMenu<T>をインスタンス化し、選択対象の項目を追加するだけです。

using System;
using CommandLineSelectableMenu;

class Program
{
    static void Main(string[] args)
    {

        // インスタンスの生成
        var selectableMenu = new SelectableMenu<Action>();

        // メニュー項目の追加.
        selectableMenu.Add(title: "項目A ?", item: () => { Console.WriteLine("項目A が選択されました。"); });
        selectableMenu.Add(title: "項目B ?", item: () => { Console.WriteLine("項目B が選択されました。"); });
        selectableMenu.Add(title: "項目C ?", item: () => { Console.WriteLine("項目C が選択されました。"); });
        selectableMenu.Add(title: "項目D ?", item: () => { Console.WriteLine("項目D が選択されました。"); });

        // メニューの描画
        var item = selectableMenu.Draw();

        // 選択対象の実行
        item.Invoke();

        Console.ReadKey();
    }
}

描画メソッドにあたるDrawメソッドは、戻り値T型をとるため、関数を渡すことで即座に処理を実行することもできます。

f:id:EaE:20210201183347g:plain

サンプルからもわかる通り、Optionを設定しない状態では矢印が付くのみで選択対象の項目は色が変化しません。

カーソル付き選択メニューのカラー設定

今度はOptionの設定方法です。

  • 選択時カラーを赤色に設定
  • 矢印をアスタリスクに設定
  • 選択後にメニューをClearする。

上記の設定を追加していきます。

using System;
using CommandLineSelectableMenu;

class Program
using System;
using CommandLineSelectableMenu;

class Program
{
    static void Main(string[] args)
    {
        // オプションの設定
        var option = new SelectableMenuOptions()
        {
            // 選択タイプを選ぶことができます。
            // default は `ArrowSelectedType`が設定されています。
            SelectedType = new ArrowSelectedType()
            {
                SelectedColor = ConsoleColor.Red,
                ArrowType = ArrowType.Asterisk,
            },

            // defaultは `false`ですが、`true`を設定することで、項目選択後にメニューを消すことができます。
            IsClearAfterSelection = true
        };

        // インスタンスの生成
        var selectableMenu = new SelectableMenu<Action>(option);

        // メニュー項目の追加.
        selectableMenu.Add(title: "項目A ?", item: () => { Console.WriteLine("項目A が選択されました。"); });
        selectableMenu.Add(title: "項目B ?", item: () => { Console.WriteLine("項目B が選択されました。"); });
        selectableMenu.Add(title: "項目C ?", item: () => { Console.WriteLine("項目C が選択されました。"); });
        selectableMenu.Add(title: "項目D ?", item: () => { Console.WriteLine("項目D が選択されました。"); });

        // メニューの描画
        var item = selectableMenu.Draw();

        // 選択対象の実行
        item.Invoke();

        Console.ReadKey();
    }
}

SelectedTypeのDefaultはArrowSelectedTypeが設定されていますが、選択時カラーはConsole.ForegroundColorのままになっています。
ここにConsoleColor.Redを設定しています。

ArrowTypeは、Arrow(>), Asterisk(*), Plus(+) が選択可能です。

f:id:EaE:20210201185641g:plain

矢印が変更され、選択項目が赤くなっているのが確認できます。

カラー選択メニューの設定

最後は選択矢印なしのOption設定方法です。

  • 選択時カラーを緑色に設定
  • 選択矢印を描画しない。
  • 選択後にメニューをClearする。

上記の設定を追加していきます。

using System;
using CommandLineSelectableMenu;

class Program
{
    static void Main(string[] args)
    {
        // オプションの設定
        var option = new SelectableMenuOptions()
        {
            // 選択タイプを選ぶことができます。
            // ColorSelectedTypeは矢印が付きません。色のみが変更されます。
            // defaultで選択項目色は `Console.Yellow`に設定されています。
            SelectedType = new ColorSelectedType()
            {
                SelectedColor = ConsoleColor.Green,
            },

            // defaultは `false`ですが、`true`を設定することで、項目選択後にメニューを消すことができます。
            IsClearAfterSelection = true
        };

        // インスタンスの生成
        var selectableMenu = new SelectableMenu<Action>(option);

        // メニュー項目の追加.
        selectableMenu.Add(title: "項目A ?", item: () => { Console.WriteLine("項目A が選択されました。"); });
        selectableMenu.Add(title: "項目B ?", item: () => { Console.WriteLine("項目B が選択されました。"); });
        selectableMenu.Add(title: "項目C ?", item: () => { Console.WriteLine("項目C が選択されました。"); });
        selectableMenu.Add(title: "項目D ?", item: () => { Console.WriteLine("項目D が選択されました。"); });

        // メニューの描画
        var item = selectableMenu.Draw();

        // 選択対象の実行
        item.Invoke();

        Console.ReadKey();
    }
}

カラーのみの設定は非常に簡単で、ConsoleColor.Greenを設定するだけです。
ColorSelectedTypeをSelectedTypeに設定しているので、矢印は表示されません。

f:id:EaE:20210201191349g:plain

矢印が消え、選択中の項目が緑色になっているのが確認できました。

今後のUpdate予定


  • nugetに表示されるアイコンの設定
  • メニューデザインのオプション化

ここら辺が直近の目標です!!

まとめ


  • 対話形式の選択メニューを作成するためのライブラリです。
  • 矢印付きメニュー、矢印なしメニュー、カラーメニューを簡単に作成できます。

今後の情報はこの記事のUpdateと更新情報記事でやっていきます

ソースコード流出が色々と残念すぎる。

はじめに


あまり時事的なことを書いたことがなかったので、良い題材?として今話題の『SMBCなどのソースコードが流出』について書いていこうと思います。 この件について、知らない方はITMedia Newsの記事を参考にしてください。

業界を知らない人には難しいニュース


今回の騒動で最も被害を受けたのは、SMBCで間違いないでしょう。
本当に可哀想という他ありません。

そもそも、今回の件は一般の方やIT知識の少ない方には、明らかに情報過多のニュースだと思います。
おそらく大半の人にとって...

SMBCが何やら情報を流出させた。
そんなセキュリティ意識の低い金融は信用できない。

という事柄以外は何も残っていないのかなと思います。

『SE』,『Github』,『ソースコード』一般の方たち、ITに関する知識のない方からしたら、本当に何のことを言っているのか分かりません。
いくら『セキュリティ的に当該ソースコードは問題なかった。』と書いたところで、ソースコードが何のか知らなければ、結局のところ『情報漏洩させた会社』という認識しか残りません。
そして、IT業界の下請け構造を知らなければ『委託先の企業が云々』と書いたところで、何の説得にもならないでしょう。

SMBC側に全くの責任がないかと言われると、そうではありませんが本当に気の毒だなと思います。

(意外と下請け構造を知らないで、IT業界に来る人もいるので簡単に説明します。)

f:id:EaE:20210130223105p:plain

図のようにピラミッド構造になっていて、元請け企業が下請け企業に開発を依頼します。
下請け企業は、さらに孫請けと呼ばれる企業に開発を依頼します。

このような性質上、下に行くほど得られる利益は低くなっていきます。

  • 元請け:子会社が親会社から1億円で請け負う。利益を得るため、下請け会社4000万円で開発を依頼する。
  • 下請け:元請け企業から4000万円で請け負う。下請け企業も利益が欲しいが、下請けだけでは作り切れないため、孫請けに500万円で2社依頼する。
  • 孫請け:下請け企業から500万円で請け負う。頑張って機能を完成させる!

みたいな形になります。これはだいぶ極端な例ですが、利益を見ると明らかにピラミッドの上にいる方が儲かります。

  • 元請け:1億円 - 4000万円 = 6000万円の利益
  • 下請け:4000万 - (500万円 × 2社) = 3000万円の利益
  • 孫請け:500万円の利益

今回、ソースコードを流出させたと思われるのは、下請け以降の社員だと言われています。

現場のセキュリティ意識


さすがに自分の身の回りに、今回の件のような人はいませんが『Github』の扱いに限って言えば、Passwordやアクセスキーの取り扱いを理解していない人は意外と多いと感じています。

  • 設定ファイルにPassword情報を載せたままCommitしている。
  • Local推奨のファイルを.gitignoreで除外せず、Commitしている。
  • DeployスクリプトにPasswordやアクセスキーがそのまま記載されている。

などです。
『privateリポジトリ』だから良いとかそういう問題ではありません。
『本来秘匿され管理されるべき情報を公の場に出している。』ということのそのものが問題です。 Passwordなど限定的に公開されて然るべき情報が、『ソースコードをCloneしたら誰でも閲覧できる。』のはかなり危険です。

開発では知識レベルもバックグラウンドも異なる様々な人が参加して、機能を作り上げていきます。
そういった人たちすべてに、言ってみれば『本番環境を破壊できる鍵』を渡しているということです。
普通に考えて怖すぎます。

年収が云々の次元じゃない


報道にも書かれている通り、『Githubと連携を行うことでソースコードの内容から年収を判断できるサービスを使いたかったので、ソースコードをアップロードした。』というのが今回の件の発端だそうです。

これは完全に呆れているというか、IT人材の価値を下げないでくれよ。という愚痴なのですが...
人間誰しも、間違いはあります。
誤って、Passwordをコミットしてしまう云々も良くあります。
ただ、Githubも知らない、ソースコードの権利責任も分からないような人は、年収診断する前に勉強してください。っていうのが正直なところです。