diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 074d23c..bf97f2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,13 @@ jobs: - name: checkout uses: actions/checkout@v4 + # crystal: latest だと Crystal の更新でいきなり CI が壊れるため固定する。 + # 上げるときは ameba 側の対応状況と、serverless.yml のビルドイメージの + # タグも合わせて確認すること(issue #108)。 - name: install crystal uses: crystal-lang/install-crystal@v1 with: - crystal: latest + crystal: 1.21.0 - name: install shards run: shards install @@ -24,6 +27,14 @@ jobs: - name: check format run: crystal tool format --check + # 追従先の ameba(1.7 系)は shard.yml から postinstall が外れており、 + # shards install だけでは bin/ameba が作られないため明示的にビルドする + # (issue #108)。 + - name: build ameba + run: | + mkdir -p bin + crystal build lib/ameba/src/cli.cr -o bin/ameba + - name: lint with ameba run: ./bin/ameba diff --git a/serverless.yml b/serverless.yml index 4757899..331073e 100644 --- a/serverless.yml +++ b/serverless.yml @@ -24,8 +24,13 @@ custom: # # chmod はコンテナ内(root)で実行する。Linux ランナーでは生成物が # root 所有になり、コンテナ外の runner ユーザーでは chmod できないため。 + # + # イメージのタグは latest ではなくバージョンで固定する。latest のままだと + # Crystal の更新で、CI(ci.yml)が検証したのと違うコンパイラで本番成果物が + # 作られてしまい、互換性が壊れた場合は deploy も突然失敗する。 + # ci.yml の crystal バージョンと揃えて上げること(issue #108)。 'before:package:createDeploymentArtifacts': | - docker run --rm --platform linux/arm64 -v "$PWD":/work -w /work crystallang/crystal:latest-alpine \ + docker run --rm --platform linux/arm64 -v "$PWD":/work -w /work crystallang/crystal:1.21.0-alpine \ sh -c "apk add --no-cache openssl-libs-static zlib-static libevent-static && crystal build --link-flags -static -o bootstrap src/main.cr && chmod +x bootstrap" provider: diff --git a/shard.lock b/shard.lock index f612c4d..0970e98 100644 --- a/shard.lock +++ b/shard.lock @@ -2,5 +2,5 @@ version: 2.0 shards: ameba: git: https://github.com/crystal-ameba/ameba.git - version: 1.6.4 + version: 1.7.0-dev+git.commit.cdd58b34b0d8a9c785d67183a7a8f07541549e55 diff --git a/shard.yml b/shard.yml index dec4eb0..d7afc72 100644 --- a/shard.yml +++ b/shard.yml @@ -13,6 +13,11 @@ crystal: ">= 1.20.0" development_dependencies: ameba: github: crystal-ameba/ameba - version: ~> 1.6 + # Crystal 1.21 で Crystal::Lexer#next_string_array_token が削除され、 + # `~> 1.6` で解決される最新の安定版 1.6.4 はコンパイルできない。対応は + # master に入っているが安定版のリリースがまだ無いため、修正を含むコミットに + # 固定して追従する。1.7 系がリリースされたら version 指定に戻すこと + # (issue #108)。 + commit: cdd58b34b0d8a9c785d67183a7a8f07541549e55 license: MIT diff --git a/spec/github/models_spec.cr b/spec/github/models_spec.cr index 6f2449e..c11c451 100644 --- a/spec/github/models_spec.cr +++ b/spec/github/models_spec.cr @@ -174,26 +174,26 @@ private def notification_with(url = "", repo_html_url : String? = nil) end NOTIFICATIONS_FIXTURE = <<-JSON -[ - { - "reason": "mention", - "subject": { - "title": "Spurious failure", - "url": "https://api.github.com/repos/octocat/Hello-World/issues/1", - "latest_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments/1", - "type": "Issue" - }, - "updated_at": "2026-07-14T00:00:00Z", - "repository": { - "full_name": "octocat/Hello-World", - "html_url": "https://github.com/octocat/Hello-World", - "owner": { - "login": "octocat", - "avatar_url": "https://github.com/images/error/octocat.gif", - "html_url": "https://github.com/octocat" - } - }, - "subscription_url": "https://api.github.com/notifications/threads/1/subscription" - } -] -JSON + [ + { + "reason": "mention", + "subject": { + "title": "Spurious failure", + "url": "https://api.github.com/repos/octocat/Hello-World/issues/1", + "latest_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments/1", + "type": "Issue" + }, + "updated_at": "2026-07-14T00:00:00Z", + "repository": { + "full_name": "octocat/Hello-World", + "html_url": "https://github.com/octocat/Hello-World", + "owner": { + "login": "octocat", + "avatar_url": "https://github.com/images/error/octocat.gif", + "html_url": "https://github.com/octocat" + } + }, + "subscription_url": "https://api.github.com/notifications/threads/1/subscription" + } + ] + JSON diff --git a/src/discord/models.cr b/src/discord/models.cr index 84db6a4..60be7c3 100644 --- a/src/discord/models.cr +++ b/src/discord/models.cr @@ -71,7 +71,7 @@ module Discord lines = [] of String lines << EVERYONE_MENTION if mention lines.concat pretexts.uniq - return nil if lines.empty? + return if lines.empty? Discord.truncate(lines.join("\n"), CONTENT_LIMIT) end end @@ -122,7 +122,7 @@ module Discord # Slack で使う "#RRGGBB" 形式の色を Discord が要求する Int32 へ変換する。 def self.color_from_hex(hex : String?) : Int32? - return nil unless hex + return unless hex hex.lchop('#').to_i?(16) end end @@ -139,7 +139,7 @@ module Discord def self.from_message(message : Notify::Message) : Author? # Discord は author に name 必須のため、名前が無ければ author 自体を付けない。 - return nil unless message.author_name + return unless message.author_name # 空文字の URL は Discord に 400 で弾かれるため nil にする。 Author.new(message.author_name, message.author_link.presence, message.author_icon.presence) end @@ -156,7 +156,7 @@ module Discord def self.from_message(message : Notify::Message) : Footer? # Discord は footer に text 必須のため、footer が無ければ付けない。 - return nil unless message.footer + return unless message.footer # 空文字の icon_url は Discord に 400 で弾かれるため nil にする # (アラートは footer_icon: "" で来るため特に重要)。 Footer.new(message.footer, message.footer_icon.presence) diff --git a/src/github/models.cr b/src/github/models.cr index 15fc9ab..7ad6cd0 100644 --- a/src/github/models.cr +++ b/src/github/models.cr @@ -143,7 +143,7 @@ module Github # 限り、末尾セグメントが数値なら返す。Commit(末尾が SHA)や末尾スラッシュ、 # URL が無い場合などは nil を返す(issue #96)。 def number : String? - return nil unless type.in?(NUMBERED_TYPES) + return unless type.in?(NUMBERED_TYPES) segment = url.chomp('/').split('/').last? segment if segment && segment.matches?(/\A\d+\z/) end diff --git a/src/github/usecase.cr b/src/github/usecase.cr index b2574a7..28f2094 100644 --- a/src/github/usecase.cr +++ b/src/github/usecase.cr @@ -29,7 +29,7 @@ module Github end private def truncate_body(body : String?) : String? - return nil unless text = body.try(&.presence) + return unless text = body.try(&.presence) text.size > BODY_LIMIT ? "#{text[0, BODY_LIMIT]}…" : text end end diff --git a/src/main.cr b/src/main.cr index 385016e..dc95b4e 100644 --- a/src/main.cr +++ b/src/main.cr @@ -45,10 +45,8 @@ error_uc = Error::Usecase.new( ) Serverless::Lambda.handler "github_notifications_slack" do |_| - begin - notify_uc.check_notifications - rescue error - error_uc.alert error - raise error - end + notify_uc.check_notifications +rescue error + error_uc.alert error + raise error end