GitHub Gist のコードをスクリプトから更新する方法

ブラウザ上で長文テキストを編集すること、

キモいですよね、クソです!

github gist edit browser

GitHub 公式のコマンドラインツールは便利です。
👉 cli/cli: GitHub’s official command line tool hatena-bookmark


$ gh gist -h
Work with GitHub gists.

USAGE
  gh gist <command> [flags]

CORE COMMANDS
  clone:       Clone a gist locally
  create:      Create a new gist
  delete:      Delete a gist
  edit:        Edit one of your gists
  list:        List your gists
  view:        View a gist

INHERITED FLAGS
  --help   Show help for command

ARGUMENTS
  A gist can be supplied as argument in either of the following formats:
  - by ID, e.g. 5b0e0062eb8e9654adad7bb1d81cc75f
  - by URL, e.g. "https://gist.github.com/OWNER/5b0e0062eb8e9654adad7bb1d81cc75f"

LEARN MORE
  Use 'gh <command> <subcommand> --help' for more information about a command.
  Read the manual at https://cli.github.com/manual

 

エディタを変える

このデフォルトのエディタ nano って誰が推してるんすかね。


$ gh gist edit f49d4d7a9963b77d00c89d1a3dbb6976

github gh gist edit nano

変えます、emacs に。


$ EDITOR=emacs gh gist edit f49d4d7a9963b77d00c89d1a3dbb6976

いいですね。

EDITOR=emacs gh gist edit

ということは、

「gh gist edit」 は、

シェル変数 EDITOR に反応するアプリケーションですね !

 

ファイルを上書き更新する

とはいえ、

いまどきはもろGUIなIDEで豊富な機能を使ってコードは書いていきます。

なので、

パソコン上のIDEで書いたコードを Gist に更新していきたいですよね !

「更新」とは同じIDの Gist の上書きのことを言っています。


$ EDITOR="cp ~/ide/path/to/script/new.js" \
 gh gist edit f49d4d7a9963b77d00c89d1a3dbb6976

想像できてましたか?

GitHub 公式アプリ内のシェル変数 EDITOR の処理を。


[ -z "$EDITOR" ] && EDITOR=nano
$EDITOR TEMP_FILE

通常、エディタの起動コマンドを入力する文字列に


cp src-file

をセットすることで


vi tmp-file


cp src-file tmp-file

となり実行されます。

👉 linux - How do I edit /etc/sudoers from a script? - Stack Overflow hatena-bookmark

実際、言語は Go で書かれていますが、処理の内容はそういうことです。

 

標準入力から更新する

こんなの公開されてる方がいます。


$ echo "edit a gist from output piped from another command" | \
 EDITOR="cp "<(cat -) gh gist edit {<gist ID> | <gist URL>} [flags]

👉 GitHub CLI の gist edit へパイプを接続し、gist を編集する hatena-bookmark

「プロセス置換 (Process Substitution)」を使って処理しています。

「コマンド出力をファイル化して、そのパスを返す。」という使えるやつ。


$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

$ echo <(date)
/proc/self/fd/11

👉 プロセス置換 (Process Substitution)について - 一から勉強させてください hatena-bookmark
👉 shell - Process substitution and pipe - Unix & Linux Stack Exchange hatena-bookmark

 

まとめ

この先人たちの作ったコマンドラインツールの素晴らしさよ。

「ノーコード」とか言うてるやつな。

👉 GitHub Gist に置いた Javascript をブックマークレットとして実行する hatena-bookmark


R8 で 難読化 された スタックトレース を元に戻す


minifyEnabled true

のとき、実機にインストールして起動した瞬間にクラッシュします。


2022-04-02 23:21:04.099 5125-5125/com.benigumo.example E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.benigumo.example, PID: 5125
    java.lang.NullPointerException
        at w5.c$c.b(:112)
        at w5.c$c.f(:154)
        at v5.f.d(:233)
        at e7.x$a.c(:46)
        at v5.b$a.a(:38)
        at e7.x$a.b(:43)
        at w5.b$b.a(:25)
        at w5.b$b.add(:23)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:393)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4847)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7842)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

難読化されてるので原因を特定するのがツラいです !!

難読化を元に戻してみましょう。

 

手順

1. R8 ルールを追加する。

proguard-rules.pro に以下を追加することで再構成がより確実になります。


-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

2. 再度クラッシュさせる。

ルール追加したので再度クラッシュさせてスタックトレースを出力します。


2022-04-04 02:44:01.054 2001-2001/com.benigumo.example E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.benigumo.example, PID: 2001
    java.lang.NullPointerException
        at w5.c$c.b(SourceFile:112)
        at w5.c$c.f(SourceFile:154)
        at v5.f.d(SourceFile:233)
        at e7.x$a.c(SourceFile:46)
        at v5.b$a.a(SourceFile:38)
        at e7.x$a.b(SourceFile:43)
        at w5.b$b.a(SourceFile:25)
        at w5.b$b.add(SourceFile:23)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:393)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4847)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7842)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

少しスタックトレースが変化しました。

これは、クリップボードにコピーして使います。

3. 難読化を解除して元に戻す。 - retrace

「スタックトレース」 ( クリップボード内 ) と

「マッピングファイル」
( <module>/build/outputs/mapping/<build>/mapping.txt )

をデータとして retrace ツールを実行します。


~/Example
❯ pbpaste | java -jar ~/Library/Android/sdk/tools/proguard/lib/retrace.jar -verbose \
app/build/outputs/mapping/debug/mapping.txt
 
2022-04-04 02:44:01.054 2001-2001/com.benigumo.example E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.benigumo.example, PID: 2001
    java.lang.NullPointerException
        at curtains.internal.WindowCallbackWrapper$Companion.android.view.Window$Callback getJetpackWrapped(android.view.Window$Callback)(SourceFile:112)
        at curtains.internal.WindowCallbackWrapper$Companion.android.view.Window$Callback unwrap(android.view.Window$Callback)(SourceFile:154)
        at curtains.WindowsKt.android.view.Window$Callback getWrappedCallback(android.view.Window$Callback)(SourceFile:233)
        at leakcanary.RootViewWatcher$listener$1.void onRootViewAdded(android.view.View)(SourceFile:46)
        at curtains.OnRootViewAddedListener$DefaultImpls.void onRootViewsChanged(curtains.OnRootViewAddedListener,android.view.View,boolean)(SourceFile:38)
        at leakcanary.RootViewWatcher$listener$1.void onRootViewsChanged(android.view.View,boolean)(SourceFile:43)
        at curtains.internal.RootViewsSpy$delegatingViewList$1.boolean add(android.view.View)(SourceFile:25)
        at curtains.internal.RootViewsSpy$delegatingViewList$1.boolean add(java.lang.Object)(SourceFile:23)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:393)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4847)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7842)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

全クラス名が丸出しになりました !!

👉 R8 retrace  |  Android デベロッパー  |  Android Developers 

4. Android Studio でスタックトレースを分析します。


[Analyze] - [Stack Trace or Thread Dump ...]

[Analyze] - [Stack Trace or Thread Dump ...]

[Analyze] - [Stack Trace or Thread Dump ...]

ソースファイルへのクリック可能なリンクも表示されています。

👉 スタック トレースを分析する  |  Android デベロッパー  |  Android Developers 

ちなみに、今回のクラッシュの原因は、Leakcanary まわりでした !


Proguard GUI

同様に、同梱の Proguard GUI を使っても retrace はできます。


java -jar ~/Library/Android/sdk/tools/proguard/lib/proguardgui.jar

GUI上でマッピングファイルのパスを入力して、スタックトレースは貼り付けます。

proguardgui.jar

 

日本語

さて、「難読化 (obfuscation)」の反対語は何なのでしょうか。

英語だと

「deobfuscation」

ですが、公式リファレンスの日本語を見ているといろいろな単語が使われています。

「デコード」

「難読化の解除」

「解読」

日本語は難しいですね !

 

まとめ

proguard-rules.pro に追記して、コピーしたスタックトレースを2つのツールどちらかで retrace する。


# To de-obfuscate stacktrace
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile


pbpaste | java -jar ~/Library/Android/sdk/tools/proguard/lib/retrace.jar -verbose \
app/build/outputs/mapping/debug/mapping.txt | tee >(pbcopy)


java -jar ~/Library/Android/sdk/tools/proguard/lib/proguardgui.jar

alias にしとくと良いでしょうか。

👉 アプリの圧縮、難読化、最適化  |  Android デベロッパー  |  Android Developers 
👉 【Android Studio】プラグイン「Proguard Retrace Unscrambler」で難読化されたスタックトレースを解除してみた 


【macOS】コピーしているテキストをHTMLエスケープする

zsh 向けです。

pbpaste | recode utf8..html | tee >(pbcopy)


pbpaste | recode utf8..html | tee >(pbcopy)

例えば、以下のテキストをコピーしてから、上のワンライナーをターミナルで実行する。


@Serializable
class House(
    private val rooms: Int? = 3,
    val name: String = "Palace"
) : Building {
    var residents: Int = 4
        get() {
            println("Current residents: $field")
            return field
        }

    fun burn(evacuation: (people: Int) -> Boolean) {
        rooms ?: return
        if (evacuation((0..residents).random()))
            residents = 0
    }
}

クリップボード内のテキストがHTMLエスケープされ、同時にターミナル上にも表示される。


@Serializable
class House(
    private val rooms: Int? = 3,
    val name: String = &quot;Palace&quot;
) : Building {
    var residents: Int = 4
        get() {
            println(&quot;Current residents: $field&quot;)
            return field
        }

    fun burn(evacuation: (people: Int) -&gt; Boolean) {
        rooms ?: return
        if (evacuation((0..residents).random()))
            residents = 0
    }
}

表示と同じようにクリップボード内もテキストも置換されているので、そのまま利用先にペーストする。

必要があったので少しやってみました。

👉 highlight.js に「AndroidStudio」スタイルが登場してた件 
👉 標準出力に出しつつ、パイプ先のコマンドにも繋ぐ | tellme.tokyo 



iPhoneアプリ ランキング まとめ【2022】

どこで人気アプリ探したらいいのか分からないのでメモ。

ランキングのみ。

app.sensortower.com
👉 Top Grossing Apps | JAPAN | Top App Store Rankings for iOS 

 

Japanで最も人気のあるiPhoneアプリランキング | シミラーウェブ
👉 Japanで最も人気のあるiPhoneアプリランキング | シミラーウェブ 

 

App Store Top Charts Ranking for iPhone Apps | AppFollow
👉 All categories — App Store Top Charts Ranking for iPhone Apps in JP | AppFollow 

 

Top Apps for iPhone on the iOS App Store in Japan · Appfigures
👉 Top Apps for iPhone on the iOS App Store in Japan · Appfigures 

 

Today’s Top App {chart} | data.ai
👉 Today’s Top App {chart} | data.ai 

 

iPhoneアプリランキング
👉 iPhoneアプリランキング 

 

人気の無料iPhoneアプリランキング - Top App Ranking 400
👉 人気の無料iPhoneアプリランキング - Top App Ranking 400 

 

無料アプリランキング - iPhoneアプリの世界ランキング | APPLION
👉 無料アプリランキング - iPhoneアプリの世界ランキング | APPLION 

 

APP STORE AWARDS 2021 トップApp ランキング:App Store ストーリー
👉 APP STORE AWARDS 2021 トップApp ランキング:App Store ストーリー 

 

APP STORE AWARDS 2021 トップゲーム ランキング:App Store ストーリー
👉APP STORE AWARDS 2021 トップゲーム ランキング:App Store ストーリー 

 

APP STORE AWARDS 2021 トップApple Arcadeランキング:App Store ストーリー
👉 APP STORE AWARDS 2021 トップApple Arcadeランキング:App Store ストーリー 

 

BEST OF 2020 トップApp ランキング:App Store ストーリー
👉 BEST OF 2020 トップApp ランキング:App Store ストーリー 

 

BEST OF 2020 トップApple Arcadeランキング:App Store ストーリー
👉 BEST OF 2020 トップApple Arcadeランキング:App Store ストーリー 

 

BEST OF 2020 トップゲーム ランキング:App Store ストーリー
👉 BEST OF 2020 トップゲーム ランキング:App Store ストーリー 

情報を一画面に圧縮したような国産のサイトが人気のようですね。

👉 iOS App Store の最も使えるアプリURLはどれ? 


iPhone連絡先アプリ だけが iCloud連絡先 のデータをエクスポートできない件

iPhone連絡先アプリ だけが  iCloud連絡先 のデータをエクスポートできない件

図からわかるように

iCloud 連絡先は、非Appleデバイスではアクセスできない。

iPhone連絡先アプリ だけが  iCloud連絡先 のデータをエクスポートできない件

Mac などパソコンがない場合は、iCloud 連絡先は iPhone 上では、連絡先アプリしかアクセスできない。

Android OS 上で iCloud 連絡先のデータを利用したい場合は、

iCloud 連絡先のデータをエクスポートして、Google コンタクトへインポートする。

しかないのだが、

iPhone連絡先アプリ だけが  iCloud連絡先 のデータをエクスポートできない件

iOS連絡先 アプリだけ iCloud連絡先 のデータをエクスポートできない。

Mac などパソコンがない状況では、iCloud 連絡先データは、エクスポートして、Googleコンタクトへインポートできないということになる。

まとめ

Apple、Google以外サードパーティツールを使えば状況は変わるが、そもそもは、

「iCloud連絡先 は、Appleデバイス間でのデータ共有クラウドツール。」

「Googleコンタクト は、あらゆるデバイスでのデータ共有クラウドツール。」

というそれぞれインターネットジャイアントのポリシー。

Googleコンタクト のが幅広く自在に使える。

👉 連絡先をクラウド上に保存する - 連絡先 / コンタクトの使い方まとめ その1【Android / iPhone】 
👉 Google コンタクト データをAndroid と iPhone で共有する - 連絡先 / コンタクトの使い方まとめ その2【Android / iPhone】 
👉 連絡先アプリ 必ず把握しておくべき全設定画面別使い方メモ- 連絡先 / コンタクトの使い方まとめ その3【Android → iPhone】 
👉 Googleコンタクト から iCloud連絡先 へ完全移行する手順 - 連絡先 / コンタクトの使い方まとめ その4 【Android → iPhone】