ノーコード(?)入門した & 令和の情報ジャンキー

あけました、おめでとうございました。
気候の変化が激しい今日このごろ、皆さまお元気でしょうか。

これは何

Zapier」って何

賢いIFTTTと思えば良い。
何らかのイベントをトリガーとしてアクションのブロックを繋げて実装を行う。
今回は前述した通り、「購読しているRSSの更新」をトリガーとして「Discordにメッセージを送信」するアクションを実装した。

動いている様子

指定したチャンネルに投稿されている。

View post on imgur.com

お値段とか

もともと為替レートを流すBotScalaでガチャガチャ書いてCronで回していたのだが、ひょっとしたらこれもZapierでできちゃうのかな...
だとしたら、借りているVPSの管理も手放せるし...
と、考えていたが道楽で使うには少々値段が張るため完全移行は様子見。

おまけ

令和の情報ジャンキー

「情報ジャンキー」というのは情報収集と消費に心血を注いでいる人のことを指している。おそらく私もその一人。

さて、近頃のサービスはレコメンドシステムが優秀で楽しくなりすぎてしまう問題がある。
とにかく際限のなく...アッパー状態となってしまう自体となっている。(n=1)
特に時系列に表示するサービスが減ってきているのも上述した問題を引き起こしている一因である。
パーソナライズが捗りすぎている。

半径2クリックくらいの人に届けば良さそうな情報が地球の裏側まで届いてしまうことがあるし、その間に文脈が抜け落ちてアッパーな人に殴られたりする様子が散見される。
(「ほかってくる」とか「●REC」とか言っている場合ではないということ。)

いい塩梅に引きこもりたい

勤め先のSlackの運用と「個人コミュニティはDiscordでいい気がする - Lambdaカクテル」にヒントを得て、各チャンネルの投稿を1つのチャンネルに集約するBotを書いた。(stoneream/berner

View post on imgur.com

ここ1年ほどは知り合いの知り合いくらいの人がこのDiscordサーバーに集まっている。
やっていることはtwitterと変わらないのだがアッパーな人にいきなり殴られない安心感といい塩梅でフィルターされた情報を得ることができている。

ScalaとGatlingによるパフォーマンステスト 虎の巻

こんにちは、こんにちは。
これは「FOLIO Advent Calendar 2023」3日目の記事です。
なので、いつもより気持ち真面目に書きますよ!

さて、今回は「これさえ覚えればGatlingとScalaで最低限はパフォーマンスが書ける!」を目指して虎の巻を書いてみました。
「パフォーマンステストどこから手をつけたらいいかわかんないよ〜(主に私の話)」とか「シナリオを書くのが面倒くさいよ〜(主に私の話)」という人の重い腰を上げる一助となれば幸いです。

「Gatling」ってなに?

JavaベースのWebアプリケーション向けのパフォーマンステストのフレームワークです。
DSLが提供されていてシュッとシナリオが書けるところ、きれいめなレポートが出力できるところがGood Pointです。
(説明するまでもなく有名なツールですが、念のため...)

sbtプラグインの導入と依存ライブラリの追加

GatlingはJava、Kotlin、Scalaでテストを記述することができるようになっています。 Maven、Gradleのほか、sbt向けのプラグインも存在します。
こちらのプラグインを導入することでscalatestの要領でGatlingでテストを行うことができます。

plugins.sbt 設定例

addSbtPlugin("io.gatling" % "gatling-sbt" % "4.6.0")

build.sbt 設定例

// ...省略

lazy val gatlingVersion = "3.9.5"

lazy val gatling = (project in file("gatling"))
  .enablePlugins(GatlingPlugin)
  .settings(
    name := "subProject",
    libraryDependencies ++= Seq(
      "io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion % "test",
      "io.gatling" % "gatling-test-framework" % gatlingVersion % "test"
    )
  )

指定しているバージョンは記事執筆時点でのものです。
最新のバージョンはMavenリポジトリを参照してください。

テストプロジェクトの構成について

Gatlingのプラグインのデフォルトの設定では、プロジェクトの src/test/scala 以下を読みに行きます。
今回の場合は、 gatling/src/test/scala ディレクトリを掘ります。

% mkdir -p gatling/src/test/scala

% tree -I target
# .
# ├── README.md
# ├── build.sbt
# ├── gatling
# │   └── src
# │       └── test
# │           └── scala
# └── project
#     ├── build.properties
#     ├── plugins.sbt
#     └── project

変更することもできますがこの記事では説明を割愛します。

よくあるテストクラスの形

詳しい説明は後述しますが、Gatlingのよくあるテストクラスの形を示します。

// DSLのインポート
import io.gatling.core.Predef._
import io.gatling.http.Predef._

// 負荷をかける秒数の指定をする際に便利、予めインポートしておくと良い
import scala.concurrent.duration._

class Example1 extends Simulation {
  // 1. HTTPの設定
  val httpProtocol = http.baseUrl("http://localhost:9000").userAgentHeader("UserAgentHere")

  // 2. シナリオの記述
  val scn = scenario("シナリオ名").exec(
    http("リクエスト名").get("/path").header("key", "value")
  )

  // 3. シナリオの実行方法(負荷のかけ方)
  setUp(
    scn
      .inject(
        // 10ユーザーが30秒間、バラバラのタイミングでシナリオを実行
        rampUsers(10).during(30.seconds)
      )
      .protocols(httpProtocol)
  )
}

これを覚えておけばOK! Gatling チートシート

Scenario

アプリケーション上でHTTPリクエストなどを伴う何らかの操作(シナリオ)を定義する仕組みです。

// 最もシンプルな何もしないシナリオの例
val scn1 = scenario("シナリオ例1")

複数の操作を繋げて書くこともできます。

// ホーム画面にアクセス、10秒待ってから、概要画面に遷移
val scn2 = scenario("シナリオ例2")
  .exec(http("ホーム").get("/home"))
  .exec(pause(10.seconds))
  .exec(http("概要").get("/about"))

同じクラス内に複数のシナリオを定義することができますが、シナリオ名はユニークにする必要があります。

Session

シナリオ中の状態を保持する仕組みです。
前のリクエストのレスポンスを次のリクエストで使用したいシナリオを書きたいときに役立ちます。

Session オブジェクトは Map<String, Object> のようなデータ構造をしています。
execSession を引数として取り Session を返す関数を引数に取ります。(説明が難しい)
オブジェクトはイミュータブルであるため、値を追加、変更、削除した場合には新しく生成された Session を返してください。

説明よりもコードがわかりやすいと思います。

val scn = scenario("シナリオ例 3")
  .exec { session =>
    // セッションへ値をセットする
    val newSession1 = session.set("key1", "value")
    val newSession2 = newSession1.setAll(
      ("key2", "value"),
      ("key3", "value")
    )

    newSession2
  }
  .exec { session =>
    // セッションの値を削除する
    val newSession = session.remove("key")

    newSession
  }
  .exec { session =>
    // セッションの値を取得する
    // 値が存在しない場合、型が合わない場合に例外が発生する
    val as = session("key1").as[String]

    // 型が合わない場合に例外が発生する
    val asOption = session("key1").asOption[String]

    // 安全にアクセスできる
    val validated = session("key1").validate[String]
  }

Checks

リクエストに対するレスポンスの結果をチェックする仕組みです。
XMLJSON形式のレスポンスから値を展開、また展開した値をSessionに保持する機能も含みます。
Sessionのメソッドを呼び出すよりもこの機能を使用してSessionに値を格納するのが楽です。

簡単な例は以下のとおりです。

val scn = scenario("シナリオ例 4")
  .exec(
    http("リクエスト")
      .get("/me")
      .check(status.is(200)) // ステータスコードが200であるか?
  )

レスポンスボディを想定した場合

{
  "username": "taro",
  "age": 10
}

レスポンスボディの username をキー名 username として Session に保存する例は以下のとおりです。
取得したい値はJsonPathで指定します。
厳密さを求める場合は型や存在チェックを行うと良いでしょう。

val scn = scenario("シナリオ例 4")
  .exec(
    http("リクエスト")
      .get("/me")
      .check(status.is(200))
      .check(jsonPath("$.username").ofType[String])
      .check(jsonPath("$.username").saveAs("username"))
  )

(が、意識が低いためあまり厳密に書いたことはない...。)

HTTP

HTTPリクエストを組み立てる仕組みです。
基本的なメソッドは次のとおりです。

val scn = scenario("シナリオ例 5")
  .exec(
    http("GETの例")
      .get("/path")
      .queryParam("key", "value") // クエリパラメーターの指定
      .header("key", "value")
  )
  .exec(
    http("POSTの例1")
      .post("/path")
      .body(StringBody("""{"name": "taro", "age": 10}""")) // リクエストボディの指定
      .asJson // accept,content-type ヘッダーに application/json を付与
  )
  .exec(
    http("POSTの例2")
      .post("/path")
      .formParam("key", "value") // フォームデータの指定
      .asFormUrlEncoded // content-type ヘッダーに application/x-www-form-urlencoded を付与
  )

また、Gatlingには Expression Language という機能があり、文字列中に #{Sessionのキー名} のような形で値を指定すると、動的に値を展開することができます。

"#{key}"

// 配列へのアクセス
"#{key(n)}"

// オブジェクトへのアクセス
"#{foo.bar}"

formParam , body などは以下のように使用することもできます。
Expression LanguageはGatlingのDSLでのみ有効で、自前で宣言した関数内では機能しないため注意が必要です。
例として「sessionの値を展開する例2」ではパラメーターを展開することはできません。

// ... 省略
  .exec(
    http("sessionの値を展開する例1")
      .post("/path")
      .formParam("key", "#{session-key-here}")
      .asFormUrlEncoded
  )
  .exec(
    http("sessionの値を展開する例2")
      .post("/path")
      .formParam("key", session => { s"${session("session-key-here")}" })
      .asFormUrlEncoded
  )

Injection

シナリオの同時実行数や実行時間などを定義する仕組みです。
以下に個人的によく使用する関数を指定します。
(duration は実行時間です。)

関数 説明
nothingFor(duration) 一時停止
atOnceUsers(n) 同時にn個 シナリオを実行
rampUsers(n).during(duration) n回 シナリオを分散して実行
constantUsersPerSec(n).during(duration) 1秒ごとにのシナリオ実行数をn個ずつ増やす
rampUsersPerSec(n1).to.(n2).during(duration) 1秒ごとのシナリオ実行数をn1からn2に増やす

負荷をかけるシナリオの例は以下のとおりです。

setUp(
  scn1
    .inject(
      rampUsers(10).during(30.seconds), // 10ユーザーが30秒間、分散してシナリオを実行
      nothingFor(10.seconds), // 10秒一時停止
      rampUsersPerSec(10).to(100).during(60.seconds) // 60秒間で同時シナリオ実行数を10回から100回まで増やす
    )
    .protocols(httpProtocol),
  // シナリオを同時実行することも可能です
  scn2
    .inject(
      rampUsers(10).during(30.seconds), // 10ユーザーが30秒間、分散してシナリオを実行
      nothingFor(10.seconds), // 10秒一時停止
      rampUsersPerSec(10).to(100).during(60.seconds) // 60秒間で同時シナリオ実行数を10回から100回まで増やす
    )
    .protocols(httpProtocol)
)

負荷をかける側のネットワーク帯域が足りずに十分なテストが行えないことがあるため注意してください。

どうでもいい話

Injectionとか注入とか聞くとなんとなく依存性の注入(Dependency Injection)とか想像してしまうのですが全く関係ないです。
Gatlingではシナリオにユーザーを注入するみたいな世界観なんですかね?

テストの実行

テストシナリオを作成したプロジェクトで Gatling/test もしくは Gatling/testOnly テストクラス名 でテストを実行することができます。

sbt:gatling-template> projects
[info] In file:
[info]     gatling
[info]   * root
sbt:gatling-template> project gatling
[info] set current project to gatling-template (in build file: )
sbt:gatling-template> Gatling/testOnly クラス名

... 実行ログが出力 ...

Reports generated in 0s.
Please open the following file: file:///...hoge/fuga/gatling-playground/gatling/target/gatling/example6-20231109095951995/index.html
[info] Simulation Example6 successful.
[info] Simulation(s) execution ended.
[success] Total time: 114 s (01:54), completed 2023/11/09 19:01:43
sbt:subProject>

テストが完了するとレポートがHTML形式で出力され、 Please open the following file:... の行に表示されたパスに保存されています。
(デフォルトでは target/gatling 以下に出力されます。)

その他

  • アプリケーションレイヤー、インフラレイヤーごとにログやメトリクスを収集できるようにしておくと問題やボトルネックの解消の糸口になるため収集することをおすすめします。
    • JVMで動作するアプリケーションの場合はJMXやJFRなどが存在します。
  • パフォーマンステストを行う文脈を踏まえてシナリオを検討しましょう。
    • 現実には起こり得ない負荷や頻度が少ないため問題になることは少ないなどといった無視しても良い結果が含まれることがあります。
  • この記事ではテストの実行方法にかなりフォーカスしたものとなっています

Scalaアプリケーションのメトリクスを取得する(sbt-javaagent/jmx_exporter編)

掲題の通り。
sbt-javaagentとjmx_exporterを使ってScalaアプリケーションのメトリクスをPrometheusに溜める。
(Prometheusの設定方法は割愛する。)

背景とか

  • 練習
  • Javaアプリケーションの例はちょいちょい転がってる
  • Scalaアプリケーションの記事は見た感じ無かったため書いた

背景の背景

  • Akkaの避難先としていくつかライブラリを素振りしておくか~(2023年1月くらい)
    • http4sでDiscordのBotを書き始める(2023年5月くらい)
    • pekkoがいい感じっぽいしまあいいか~(最近)
  • せっかくずっと動かしてるしメトリクスも取ってリソースの使い方を観察したい
    • という理由は後付けでただ試したかっただけ

required

そもそもJMXって何なの?

Java Management Extensions (JMX) は、Java アプリケーションをモニタおよび管理するための仕様です。JMX を使用すると、汎用管理システムでアプリケーションをモニタし、注意が必要なときに通知を生成し、アプリケーションの状態を変更して問題を解決できます。SNMP やその他の管理規格と同様に、JMX は公開された仕様で、一般に使用されている監視製品を提供する多くのベンダによってサポートされています。

引用元 : JMX について

はい。

設定例

それほど難しいことはしていないため、設定内容を見てもらうのが早くて...(いきなり説明放棄)

// JMX Settings

lazy val jmxExporterPort = 9090

// Docker用のJavaAgent設定
lazy val dockerJavaAgentSetting = JavaAgent(
  Dependencies.jmxExporterJavaAgent,
  arguments = s"$jmxExporterPort:/opt/docker/conf/jmx_exporter_config.yml"
)

...

lazy val root = (project in file("."))
  .enablePlugins(
    DockerPlugin,
    JavaAgent,
    JavaAppPackaging
  )
  .configure(baseConfig)
  .settings(
    ...
    ...
    javaAgents += dockerJavaAgentSetting
  )

キモの部分

jmx_exporter(のJavaエージェント)は引数に 待ち受け(アドレスと)ポート設定ファイルのパス を取る。

java -javaagent:./jmx_prometheus_javaagent-0.19.0.jar=12345:config.yaml -jar yourJar.jar

Metrics will now be accessible at http://localhost:12345/metrics. To bind the java agent to a specific IP change the port number to host:port.

引用元 : prometheus/jmx_exporter: A process for exposing JMX Beans via HTTP for Prometheus consumption

sbt-javaagentプラグインを使ってイイカンジに渡すと良い。
そのほかコンパイル時に動かす?テスト時に動かす?runのとき動かす?とか設定できるためよしなに。

  case class AgentScope(compile: Boolean = false, test: Boolean = false, run: Boolean = false, dist: Boolean = true) ...

  def apply(module: ModuleID, name: String = null, scope: AgentScope = AgentScope(), arguments: String = null): AgentModule = { ...

引用元 : sbt-javaagent/src/main/scala/com/lightbend/sbt/javaagent/JavaAgent.scala at 17119479327f9b1d99088fe5c5536a5034ec74af · sbt/sbt-javaagent

sbt-native-packagerのDocker Pluginのワーキングディレクトリはデフォルトで/opt/dockerになる。
Docker PluginはUniversal Pluginに依存しているため、Universal Pluginのディレクトリレイアウトに沿った形に設定ファイルを配置すればイメージにもいい感じに詰め込まれる。
(mappingを追加するなり、変更するなり、そのあたりの設定はよしなに...)

Universal Conventions This plugin has a set of conventions for universal packages that enable the automatic generation of native packages. The universal convention has the following package layout:

bin/
   <scripts and things you want on the path>
lib/
   <shared libraries>
conf/
   <configuration files that should be accessible using platform standard config locations.>
doc/
   <Documentation files that should be easily accessible. (index.html treated specially)>

引用元 : Universal Plugin — sbt-native-packager 1.0a1 documentation

また、dockerExposedPorts も必要に応じて設定。

ref

MacBook 買った

ので、やること(やったこと)リストをまとめた

マシンの目的

  • MacBookを新調した
  • 今回はAirを購入した
    • 出先での軽作業のため
    • Apple M2 / 16GB RAM / 512GB SSD
  • 以前はProを使用していた
    • 出先での開発のため
    • それなりの性能が必要だった
    • Intel CPU / 16GB RAM / 1TB SSD
  • その他のバックグラウンド
    • iOSアプリの開発はやらなくなった
    • 勤め先からマシンが支給されるようになった
    • 今年の頭に以前のマシンの分割払いが終わった
    • Intelマシン 貴重とはいえ価値は目減りする一方では?
  • お気に入りポイント

とりあえずやること

  • App Storeで古いアプリケーションをアップデート
  • システム設定を眺めてよしなに
    • ライブ変換・キーボードの学習機能を切る(プライベートモードにする)
    • Caps Lock を Ctrl に変更
    • 拡張子の表示
    • その他、ショートカットキーの変更 など
# 隠しファイルの表示
defaults write com.apple.finder AppleShowAllFiles TRUE

killall Finder

チラ裏 その1

古い端末からのマイグレーション機能があるっぽいがあんまりアテにしていない。
というか、クリーンインストールをしたいという気持ちが強い。
(買い替えのタイミングってだいたい乗り換え元のマシンがかなりダーティーというか秩序が崩壊している事が多い...)

インストール

普段使う

メッセージング

開発

制作

その他

インストールコマンド

brew install --cask \
vivaldi \
firefox \
spotify \
dropbox \
keepassxc \
authy \
slack \
discord \
iterm2 \
mysqlworkbench \
jetbrains-toolbox \
visual-studio-code \
vlc

brew install \
nvm  \
pyenv \
pipenv \
git \
ghq \
jq \
peco \
the_silver_searcher \
tmux \
ffmpeg 

# SDKMAN!は各installationに従う

チラ裏 その2

homebrew の --cask オプションを知らなかった。
何でもかんでもここからインストールしたら便利?と思って試してみた次第。
(環境が崩壊したらお知らせします。)

というか、仕組みはよく理解していない。
よくあるPyPIとかnpmのパッケージが乗っ取られてマルウェアが撒かれるサプライチェーン攻撃的なことって起きうるのか?
セキュリティアドバイザリーフォームは存在するっぽい。が、機能しているかは別の話

どっちにしてもいまさら何か気にする必要があるのかと言われたら...はい...

ssh鍵の生成と各種サービス・サーバーへ登録

ssh-keygen -t ed25519
  • GitHub
  • 普段アクセスするサーバー

dotfileの設定

リポジトリをCloneしてリンクを張る

Minecraft Education Editionで遊んでみる

告知

先日は第1回のCoder Dojo Tsuruokaが大賑わいのうちに終了。

第2回の開催は2023年8月26日(土)

Minecraft Education Edition

メンターをしながらニンジャたちのMinecraftへの熱中っぷりに圧倒されていたら、MinecraftのEducation Editionの存在を知ったためいろいろいじってみることにした。

ホームページ | Minecraft Education : https://education.minecraft.net/ja-jp

ライセンス、システム要件、指導用コンテンツなどのメモ

ライセンス | Minecraft Education : https://education.minecraft.net/content/minecraft-edu/language-masters/ja-jp/licensing.html

料金については組織ごとに変わるっぽい
あと、個人で購入するには微妙にアカウントの設定が面倒だった。

システム要件は以下の通り

OS   Windows 11、Windows 10、Windows 8*、Windows 7*
CPU Intel Core i3-3210 3.2 GHz / AMD A8-7600 APU 3.1 GHz もしくはこれと同等
RAM 2 GB
GPU
- GPU (内蔵): OpenGL 4.4 対応 Intel HD Graphics 4000 (Ivy Bridge) または AMD Radeon R5 シリーズ (Kaveri line) 
- GPU (ディスクリート): OpenGL 4.4 対応 Nvidia GeForce 400 シリーズ または AMD Radeon HD 7000 シリーズ

思っていたよりも要求スペックは低かった。
最近のパソコン(or スマートフォン or タブレット)であればだいたいは大丈夫そう。

日本語のステップ・バイ・ステップのチュートリアルも付属していた

https://i.imgur.com/YUOgNW7.png

これなに? & 実際のスクリプティングについて

ゲーム自体はクリエイティブモード、サバイバルモードと通常のエディションと(ほぼ)変わらずプレイすることができる。
このEducationエディションの通常のMinecraftと大きく違うのは内部にブロックプログラミングができる機能を持っているところ。
その他に化学実験ができるようになっているらしいが、今回は割愛

以下は実際にプログラムを作ってみた様子。

https://i.imgur.com/9i2FYjM.png

本筋とは逸れるが組んだブロックはJavaScriptに変換することもできる。
割りと本格的なプログラミングと地続きになっているのが良いと思った。

https://i.imgur.com/u83GA4w.png

今回はパーティクルを出しながら数十マス先で爆発が起きる魔法の剣を作った。

感想

実際に触ってみると痒いところに手が届きづらいところと実装が泥臭いところが出てきてしまうが教育目的なら要件としては十分そう。
少し凝った表現や動きを実装したい場合はコマンドを組み合わせるのが良さそうと思った。

コマンド - Minecraft Wiki : https://minecraft.fandom.com/ja/wiki/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89

Hour of Code 触ってみた

背景

これなに

Hour of Code

  • 子ども向けのプログラミング学習コンテンツ
  • Scratchみたいなビジュアルプログラミング(らしい)

ざっくりとScratchと違うところ+α

  • 学習コンテンツというだけあって指導者向けのノートがある(ただしモノによっては英語)
    • 授業の目的、アジェンダ、対象とするレベルや時間の目安が記述されている
  • コンテンツがオフラインでも遊べるように各環境向けに実行可能バイナリが配布されている

触ってみた

いくつかレッスンは存在するが、なんとなくMinecraftは最近の子は好きでしょくらいのノリで触ってみる(安易)

指導者向けのコンテンツ

Minecraft Hour of Code - Lesson 1 - Code.org

Overview

In this lesson, learners of all ages get an introductory experience with coding and computer science in a safe, supportive environment. This lesson works well for any students old enough to read (ages 6+). Younger learners will probably not finish the tutorial, but will have lots of fun working through the puzzles for an hour. High school students will mostly finish the tutorial and have some time to play on the free play level at the end.

Google翻訳

概要

このレッスンでは、あらゆる年齢の学習者が、安全でサポートのある環境でコーディングとコンピューター サイエンスの入門体験を行います。このレッスンは、読むことができる年齢の生徒 (6 歳以上) に適しています。若い学習者はおそらくチュートリアルを完了できないでしょうが、1 時間パズルを解くのはとても楽しいでしょう。高校生はほとんどがチュートリアルを完了し、最後にフリー プレイ レベルでプレイする時間があります。

とのこと。
授業のような形式で進める場合はPreparationの項はさらっと眺めておいた方が良さそうな気がする。(授業の準備とかベストプラクティス)

レッスンについて

アニメ(or 紙芝居形式の授業ノート)があった。
物語調で面白そう。

マインクラフト: ボヤージュ アクアティック - Code.org

プログラミングの画面については完全にScratchのソレ。

https://i.imgur.com/5Ffmspw.png

問題自体は概要にあった通り初学者向けだった。

記事のお引越し

背景

  • これまでブログ(のようなもの)を行う際に、横着してgistを使用していた
  • VSCode上で文章を作成したあとにエディタからフィールドへコピー&ペーストしていた
  • いい加減にこの作業が面倒臭くなってきたのと、gist上でパッと直しローカルのテキストファイルに反映を忘れた場合に差分が発生してしまう問題が起きていた

欲求

  • コピー&ペーストをせず済むならそうしたい
  • ローカルとインターネットに公開されている文章に差分が発生していない状態であってほしい

条件

  • 静的サイトジェネレーターは面倒
  • Wordpressを維持するのも面倒
  • 好きなエディタを自由に使いたい
  • プレーンテキスト形式(マークダウン形式)で記事を記述したい

ソリューション

ついでにできるようになったこと

環境変数BLOGSYNC_USERNAMEBLOGSYNC_PASSWORDについて

blogsync/config.go at 5d82a9f9299e8a34e9215b7fc192e41344e87644 · x-motemen/blogsync

それぞれ設定ファイルのusernamepasswordに対応するっぽい。

おまけActions

リポジトリのsecretsのBLOGSYNC_PASSWORDにブログに投稿用のAPIキーを設定してください。
また、リポジトリのカレントディレクトリにはblogsync.yamlを配置してください。
blogsyncのREADME.mdを参照しながらいい感じ(要出典)に設定してください。

name: Push And Pull

on:
  push:
    branches:
      - main
jobs:
  push_and_pull:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          ref: main
      - name: Install blogsync
        uses: x-motemen/blogsync@v0
      - name: Get changed entryies
        id: get-changed-entryies
        uses: tj-actions/changed-files@v35
      # 記事をpushする
      - name: Push
        run: |
          for file in ${{ steps.get-changed-entryies.outputs.all_changed_files }}; do
            if [[ $file == entry/**/*.md ]]; then
              blogsync push $file
            fi
          done
        env:
          BLOGSYNC_PASSWORD: ${{ secrets.BLOGSYNC_PASSWORD }}
      - name: Pull
        run: |
          blogsync pull BLOG_DOMAIN_HERE
        env:
          BLOGSYNC_PASSWORD: ${{ secrets.BLOGSYNC_PASSWORD }}
      # 記事をpullする
      # 差分があればブランチを切って、コミットを作成する
      - name: Get current date
        env:
          TZ: 'Asia/Tokyo'
        id: get-current-date
        run: echo "current-date=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
      - name: Auto Commit
        uses: stefanzweifel/git-auto-commit-action@v4
        with:
          branch: sync-${{ steps.get-current-date.outputs.current-date }}
          create_branch: true

テンプレートリポジトリは気が向いたら作ります。
今後ともお引き立てのほど、よろしくお願い申し上げます