記事一覧 (2 / 10)
2023/04/30・お知らせ
いつも「原神素材ヘルパー」をご利用いただきありがとうございます。
この度「原神素材ヘルパー」において、Twitterログイン機能を削除 することといたしましたので、お知らせします。
経緯
現在、Twitterログインに利用していたAPIキーが「ポリシー違反」とのことで停止され、Twitterログインが機能しない状態となっています。これは恐らくTwitter APIのポリシー変更が関係していると思われますが、一切Twitterから連絡が無く、違反内容も全く示されていません。
APIキーを作り直すことも検討しましたが、一時的には動くようになっても再度停止される可能性が高いことから、Twitterログイン機能の削除を行うことにしました。
今後について
4月30日までに、Twitterログインボタンを本アプリから削除します。その代わり、Apple IDでログインできる機能を追加する予定です。
利用者で必要な対応について
Twitterを利用してアカウントを作成された方は、アカウントを削除し、Googleアカウントもしくは、今後対応予定のApple IDを使用して再度作成していただく必要があります。
データは端末にも保存されているため、アカウントを削除した端末と同じ端末で新規アカウントを作成していただければ、データが移行されます。
ご不便をおかけしますが、ご理解をよろしくお願いします。
2023/03/22・役立ち情報
問題発生
IntelliJ IDEA (というか全Jetbrains IDEで共通) でGitのコミットを署名する設定を、Macでこれに従って設定したところ、以下のようなエラーが出てコミットできなくなった。
error: gpg failed to sign the data
fatal: failed to write commit object
原因
いろいろ調べた結果、パスワード入力用のダイアログの表示に失敗している様子。
GPGではセキュリティーのためか、pinentryというソフトでGUIのパスフレーズ入力ダイアログを出し、コンソールに直接パスフレーズを入力することを避けるようにしている。そのmacOS版であるpinentry-macをインストールすると、pinentryとpinentry-macという2種類のコマンドが実行できるようになる。しかし、なんと pinentryの方はmacOSでは動かない。
デフォルトではこのpinentryの方が使用されるようになっているためにエラーが発生している様子。~/.gnupg/gpg-agent.confでpinentryのパスを変更できるらしいが、なぜか設定しても反映されない。(いろいろ試したものの…)
そこで、シンボリックリンクの作成という力技で対処することにする。
対処法
まず、現在設定されているpinentryのパス と、 pinentry-macのパス を調査する。
前者は以下のコマンドで調べられる。
% gpgconf
gpg:OpenPGP:/opt/homebrew/Cellar/gnupg/2.4.0/bin/gpg
...
pinentry:パスフレーズ入力:/opt/homebrew/opt/pinentry/bin/pinentry
上の実行結果でいうと、/opt/homebrew/opt/pinentry/bin/pinentryの部分が現在設定されているパス。そして、このファイルを削除する。
% rm <現在設定されているpinentryのパス>
次に、pinentry-macのパスを調べる。
% which pinentry-mac
/opt/homebrew/bin/pinentry-mac
最後に、以下のコマンドでシンボリックリンクを作成する。
% ln -s <pinentry-macのパス> <現在設定されているpinentryのパス>
これでコミットできるようになるはず。
2023/03/14・役立ち情報
問題発生
OGPのサムネイル生成用に、Puppeteerを使ったCloud Functionsの関数をデプロイしようとした。
開発環境(エミュレーター)では正常に動作するのに、なぜかデプロイするとError: could not handle the requestと表示されて動作しなくなる。ログを確認すると、以下のようなエラーが出ていた。
Error: Could not find Chromium (rev. 1095492). This can occur if either
1. you did not perform an installation before running the script (e.g. `npm install`) or
2. your cache path is incorrectly configured (which is: /www-data-home/.cache/puppeteer).
For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides/configuration.
どうやら「Chromiumどこ…?」というエラーの様子。
結論
以下の手順で解決できる。
functionsディレクトリに.puppeteerrc.cjsファイルを作成し、以下のような中身にする。
/**
* @type {import('puppeteer').Configuration}
*/
module.exports = {
cacheDirectory: require("path").join(__dirname, ".cache", "puppeteer"),
}
firebase.jsonに以下を追記する。(※JSONではコメントの記述はできないため、//以下は削除してください)
"functions": [
{
"source": "functions",
"codebase": "frontend",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log",
".cache" // <= 追記
],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
],
package.jsonを以下のように変更する。(※JSONではコメントの記述はできないため、//以下は削除してください)
"scripts": {
"lint": "eslint --ext .js,.ts .",
...
"postinstall": "node node_modules/puppeteer/install.js" // <= 追記
},
...
"engines": {
"node": "16" // <= 16にする。詳細は下記。
}
解説
調べてみると、Puppeteerのv19で以下のような破壊的変更があった模様。
use ~/.cache/puppeteer for browser downloads
(https://github.com/puppeteer/puppeteer/releases/tag/v19.0.0)
ホームディレクトリはCloud Functionsのデプロイの対象にならないため、実際に実行される環境でChromiumが存在せず、エラーとなっているようです。
そのため、Puppeteerのインストール先をプロジェクト内に変更してやります。これが1.です。
この状態でnode_modulesを削除してnpm installし直すと、設定したパスに開発端末のOSに合ったChromiumがインストールされます。ここでfirebase deployしようとすると、「パッケージが大きすぎるためデプロイできません」のようなエラーが発生します。これは巨大なChromium本体がデプロイの対象となっているためで、これはデプロイ不要なので除外してやる必要があります。これが2.です。
あとは、Cloud Functionsではなぜかnpm install時にChromiumが自動的にインストールされないので、postinstallでインストールスクリプトを実行してやります。(3.)
また、Nodeの最新バージョンは18ですが、2023/03/14現在、Node 18用のイメージにPuppeteerの実行に必要なライブラリが含まれていないため、16にします。
2023/01/06・役立ち情報
経緯
GitHub連携を用いて、Cloudflare Pagesでページを公開し、一部のパスでFunctionが実行されるようにしたかったが、なぜかFunctionsだけデプロイされない。
公式ドキュメントの通り、プロジェクトルートにfunctionsディレクトリを作成し、その中にapi/showcase.tsを作り、example.com/api/showcaseでAPIが走るようにしたかった。しかし、そのままデプロイしてもNuxtの静的ページに飛び、Functionsに繋がらない。
対処法
pages functions not recognizedで検索したところ、https://github.com/cloudflare/wrangler2/issues/1859がヒットした。
結論としては、nuxt.config.tsに以下を追記する。
...
nitro: {
preset: "node-server",
},
...
https://github.com/cloudflare/wrangler2/issues/1859#issuecomment-1269616054によると、Nitroでは環境に応じて自動的に適切なプリセットが選択され、Cloudflare Pagesではcloudflare-pagesが設定されるようになります。本来はnuxt generate時に.output/publicに/functionsがコピーされるべきですが、Nuxt3の/serverとして扱われ、認識できるexportが存在しないことからレンダリング前に/functionsが空っぽにされ、結果全くデプロイされないようです。(私の認識が正しければ)
2022/12/13・動画ウラ話
ここ最近、CIにおいてfirebase login:ciで取得できるトークンを利用することが非推奨になり、Workload Identity 連携を使用することが推奨されるようになりました。今回は、それを用いてFirebase Hostingに自動デプロイするワークフローを作る方法を解説します。
img {
max-width: unset !important;
max-height: unset !important;
}
Firebaseの準備
まず、Firebase ConsoleからFirebaseプロジェクトを作成します。Google アナリティクスについては必要に応じて有効化します。今回は有効にしませんでした。
次に、自分のPCでディレクトリを作成し、firebase initします。選択内容は下を参照してください。
? Which Firebase features do you want to set up for this directory?
--> Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
? Please select an option:
--> Use an existing project
? Select a default Firebase project for this directory:
--> workload-test-xxxxx (workload-test)
? What do you want to use as your public directory?
--> public
? Configure as a single-page app (rewrite all urls to /index.html)?
--> No
? Set up automatic builds and deploys with GitHub?
--> No
GitHubレポジトリの作成
GitHubでレポジトリを作成しておきます。方法については割愛します。
Workload Identity 連携の設定
Google Cloud Platform ConsoleからWorkload Identity 連携の設定をします。こちら にアクセスしてください。
「使ってみる」をクリックします。
「名前」を「GitHub Actions」にし*1、「続行」をクリックします。
*1 名前は任意ですが、今後この名称を使用して説明するため同じにすることを推奨します。
以下のように入力して「続行」をクリックします。
名称
値
プロバイダの選択
OpenID Connect (OIDC)
プロバイダ名
GitHub
発行元(URL)
https://token.actions.githubusercontent.com
オーディエンス
許可するオーディエンス
対象1
自身のGitHubプロフィールURL(https://github.com/<ユーザー名>の形式)
属性マッピングで以下のように設定します。(「マッピングを追加」をクリックすると2個目が表示されます)
Google
OIDC
google.subject
assertion.sub
attribute.repository
assertion.repository
次に、「属性条件」で「条件を追加」をクリックし、「条件CEL」に'<GitHubユーザー名>/<レポジトリID>' == attribute.repositoryの形式で入力します。
サービスアカウントの作成
次に、デプロイ用のサービスアカウントを作成します。コンソール上ではこのアカウントでデプロイしたことになります。左のメニューから「サービスアカウント」を選択し、上部の「サービスアカウントの作成」をクリックします。
まず、サービスアカウント名に「github-actions」など適当な名前を設定し、「作成して続行」をクリックします。次に、「このサービス アカウントにプロジェクトへのアクセスを許可する」で「ロールを選択」から以下の2つを追加します。
サービス アカウント ユーザー
Firebase Hosting 管理者
Firebase Cloud Functionsも利用している場合、以下の3つも必要になる場合があります。
Cloud Functions 管理者
Cloud Scheduler 管理者
Secret Manager 管理者
最後に「続行」「完了」をクリックします。
(※ロール設定は「IAMと管理」画面の左メニュー下「リソースの管理」→プロジェクトを選択→右側の情報パネルから編集できます。ロールを追加する場合は「プリンシパルの追加」から「新しいプリンシパル」にサービスアカウントIDを入力します。)
サービスアカウントとWI連携の紐付け
次に、この2つを紐付けます。左メニューから「Workload Identity 連携」をクリックし、先程作成したWIプール(GitHub Actions)をクリックし、上部の「アクセスを許可」をクリックします。「サービスアカウント」から先程作成したサービスアカウントを選択し、「保存」をクリックします。
ワークフローファイルの作成
ここでやっとワークフローを作成します。.github/workflows/deploy.ymlファイルを作成してください。以下が雛形になります。
name: Deploy
on:
push:
branches:
- main
workflow_dispatch:
repository_dispatch:
env:
workload_identity_provider: <Workload Identity プロバイダー>
service_account: <サービスアカウントID>
jobs:
deploy-to-hosting:
name: "Deploy to Hosting"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: yarn
- name: Install Firebase CLI
run: yarn global add firebase-tools
- name: 'Authenticate to Google Cloud'
id: auth
uses: 'google-github-actions/auth@v0'
with:
workload_identity_provider: ${{ env.workload_identity_provider }}
service_account: ${{ env.service_account }}
audience: https://github.com/<GitHubユーザー名>
- name: Deploy to Firebase Hosting
run: firebase deploy --only hosting
「Workload Identity プロバイダー」は以下の形式に従ってください。
projects/<プロジェクトID>/locations/global/workloadIdentityPools/<WIプールのID(github-actions)>/providers/<プロバイダーID(github)>
「サービスアカウントID」は左メニュー「サービスアカウント」より先程作成したサービスアカウントをクリックすると表示されるメールアドレスです。
肝は
permissions:
contents: read
id-token: write
の部分です。これがないと認証に失敗します。
お気づきかもしれませんが、そう。トークンなどの機密情報を人の手で管理する必要が一切ありません。設定を適切にすれば Workload Identity プロバイダーやサービスアカウントIDだけ知っていても誰も悪用できないため、ハードコードしても全く問題ありません。設定はめちゃくちゃ面倒臭いですが、セキュリティは最強です。
Pushする
最後に、このディレクトリをGitHubにPushすれば、public以下がデプロイされるはずです。めでたしめでたし。
2022/11/26・どうでもいいこと
割とフィーリングで適当に撮影して、「これ良くね」って思ったテイクを載せます。
※上の6つはiOS版で撮影しているため、画質が低いです。
2022/11/22・お知らせ
(この記事はHoYoLABに投稿したものと同じものです。)
こんにちは。
「原神素材ヘルパー」のv3.10.0を公開しましたので、何が変わったのかお知らせします。
(原神素材ヘルパーって何?って方はこちら)
武器を装備するキャラの登録
以前までは同じ武器は1つまでしか収集中マークできませんでしたが、キャラ単位でマークが行えるようになりました。
また、ゲームデータと同期する機能においても、上記機能を追加したことにより、武器の育成状況の同期が可能になりました。
「収集中の素材」リストの表示改善
(1分で作った比較画像)
収集中の素材」で「素材ごとに表示」をオンにした時の表示を大幅に改善しました。言葉で説明するより上の画像を見てもらったほうが早いと思います。
また、リストの親にもブックマークボタンを追加し、子となる素材を一括で収集中マーク解除できます。
最後に
1年ほど前に公開した本アプリですが、今も細々と更新は続けています。何かご意見がありましたら、コメント欄で頂けると助かります。
2022/11/19・役立ち情報
問題は起きた。
Jetbrains社のIDE、IntelliJ IDEA / WebStorm でNuxt3を使おうとすると、#importsからインポートしないとメソッドが解決されない。どうやら、VSCodeのプラグインはインポートしなくても解決できるようになっている模様。
これはめちゃくちゃ面倒臭い。
対処法
imlファイルを編集し、.nuxtディレクトリを処理(インデックス)の対象に含めます。通常、IntelliJ IDEAでは、「.(ドット)」から始まる隠しディレクトリはGUIでインデックスの対象に含めることができません。なので、プロジェクトの設定ファイルを直接編集します。
[プロジェクトルート]/.idea/[プロジェクト名].imlを開きます。そして、以下を追記します。
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<!-- 略 -->
<content url="file://$MODULE_DIR$/.nuxt" /> <!-- 追記 -->
<!-- 略 -->
</component>
</module>
こうすると、composableなどがimportなしで使えるようになります。
2022/11/03・役立ち情報
問題
タイトルの通り。
対処法
「カスタムVMオプションの編集」から、以下のオプションを追加してMetalを利用したレンダリングを無効化します。こうすることで、ファイルを更新する度に起こる点滅が1回に減ります(ゼロにはなりません)。これで暫定的に対処できます。
-Dsun.java2d.metal=false
追記 (2022/11/25)
IntelliJ IDEA 2022.2.4がリリースされました。リリースノートには修正した旨の記載がありますが、なぜか現在も治っていない模様です。
2022/10/29・役立ち情報
Flutterでネイティブとやり取りしたい時、一番最初に知るのはMethodChannelを使った方法だと思います。しかし、MethodChannelは直接使うと非常に使いづらいです。引数はおろか、メソッド名すらString型で渡す必要があり、IDEの入力支援も受けられず、タイプミスしたら実行するまでエラーが吐かれず、引数の個数や名前、型も全部自由で何でも渡せてしまいます。
これでは非常に開発効率が悪いです。ということで、Flutterの公式パッケージである Pigeon を使います。
使う準備
pubspec.yaml の dev_dependencies に以下を追記します。
dev_dependencies:
flutter_test:
sdk: flutter
...
pigeon: ^4.2.3
次に、Flutterとネイティブでやり取りするクラスを定義するファイルを作ります。プロジェクトルートからpigeons/messages.dartを作ります。そして、中身を以下のように記述します。
(Objective-CおよびJavaで出力することになります。SwiftやKotlinでも出力できますが、まだ実験段階なのでおすすめしません。Objective-CもJavaも、SwiftやKotlinから呼び出せるので特に問題はありません。)
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(PigeonOptions(
dartOut: "lib/messages.dart", // Dartファイルの生成先
objcOptions: ObjcOptions(
prefix: "FLT" // iOS用に生成されるObjective-Cのクラス名の接頭辞
),
objcHeaderOut: "ios/Runner/messages.h", // iOS用Objective-Cヘッダーの出力先
objcSourceOut: "ios/Runner/messages.m", // iOS用Objective-Cの出力先
javaOut: "android/app/src/main/java/com/example/pigeon/Messages.java", // Android用Javaの出力先 (※ com/example/pigeon の部分はパッケージ名に変更)
javaOptions: JavaOptions(
package: "com.example.pigeon" // Android用Javaのパッケージ名
)
))
これはPigeonの設定を記述しています。適宜変更してください。
インターフェースの作成
次に、やり取りするメソッドをまとめたクラスを作ります。このクラスがメソッドチャンネルになります。同じファイルの下に以下のように記述します。
@HostApi() // Flutter -> Native
abstract class ExampleApi {
///
/// ドキュメントは全プラットフォームに反映されます
///
void example();
void openUrl(String url);
StateResult queryState();
@async
String getToken(); // 非同期メソッド
}
enum State {
pending,
success,
error,
}
class StateResult {
String? errorMessage;
late State state;
}
@FlutterApi() // Native -> Flutter
abstract class Example2Api {
void handleUri(String uri);
}
サポートされているデータ型
Pigeon実行用シェルスクリプトの作成
Pigeonで生成処理を走らせるシェルスクリプトを作ります。プロジェクトルートにrun_pigeon.shという名前で以下のように書きます。
flutter pub run pigeon --input pigeons/messages.dart
ターミナルを開きます。シェルスクリプトに実行権限がない場合があるので与え、実行します。
% chmod u+x ./run_pigeon.sh
% ./run_pigeon.sh
何も表示されなければ完了です。
Flutter側の実装
Flutter側からネイティブを呼ぶ場合
ExampleApi().openUrl("https://example.com");
await ExampleApi().getToken();
ネイティブ側からFlutterを呼ぶ場合
lib/api/example_2_flutter_api.dartファイルに以下のように書きます。
class Example2FlutterApi implements Example2Api {
@override
void handleUri(String uri) {
// ...
}
}
lib/main.dartで初期化します。
void main() {
Example2Api.setup(Example2FlutterApi());
}
Android (Kotlin) 側の実装
私の場合はapiパッケージを配下に作って、その中にまとめました。
(ExampleAndroidApi.kt)
class ExampleAndroidApi(private val activity: Activity) : ExampleApi {
override fun openUrl(url: String) {
// ...
}
override fun getToken(result: Result<String>) { // async function
FirebaseMessaging.getInstance().token.addOnSuccessListener {
result.success(it)
}.addOnFailureListener {
result.error(it)
}
}
}
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
ExampleApi.setup(flutterEngine.dartExecutor.binaryMessenger, ExampleAndroidApi(this)) // 初期化
// ...
}
}
ネイティブ側から呼ぶ場合は以下のとおりです。
Example2Api(flutterEngine.dartExecutor.binaryMessenger).handleUri(uri) {}
iOS (Swift) 側の実装
まず、Runner-Bridging-Header.hに以下を追記します。これをしないとSwift側からObjCを呼べません。
#import "messages.h"
New Group without Folderでapiグループを作り、その中にまとめました。
(ExampleIOSApi.swift)
class ExampleIOSApi: NSObject, FLTUtilsApi {
func openUrlUrl(_ url: String, error: AutoreleasingUnsafeMutablePointer<FlutterError?>) {
<#code#>
}
func getTokenWithCompletion(_ completion: @escaping (String?, FlutterError?) -> Void) {
completion("token", nil)
}
// または
// func token() async -> (String?, FlutterError?) {
// return ("token", nil)
// }
}
メソッド名がおかしい場合
と、この段階で違和感に気づく場合があると思います。メソッド名がなんかおかしいです。Objective-CからSwiftに変換する際にObjective-Cのセレクターを参照するため、それを適切に設定しないとおかしなことになります。そのため、@ObjCSelectorアノテーションでObj-Cのセレクターを設定します。
(pigeons/messages.dart)
@ObjCSelector("openUrl:")
void openUrl(String url);
先程のrun_pigeon.shを実行します。
(ios/Runner/ExampleIOSApi.swift)
func openUrl(_ url: String, error: AutoreleasingUnsafeMutablePointer<FlutterError?>) {
<#code#>
}
治りました。
BooleanやInt、Longなどの型が返せない場合
これらの型を戻り値に設定するとNSNumberで受けるようになっているため、Swiftの型はそのままでは返せません。そのため、as NSNumberでキャストします。
completion(true as NSNumber, nil)
completion(50 as NSNumber, nil)