Github Actionsでいろいろ困ったところと対応方法。主にシェルスクリプトを使うGitHub Actions

GitHub Actions

みなさん、GitHub Actionsで遊んでますか?
私も最近、GitHub Actionsで遊んでいるんですけど、
いくつか困ることもありまして、
この記事は、その時、困ったことの解決メモであります。
(雑な導入)


https://help.github.com/ja/actions/automating-your-workflow-with-github-actions


シェルスクリプト内で、privateなnpmレポジトリをnpm install

プレイベートなnpmレポジトリをnpm installするには、
.npmrc作るのが早い。

Settingsメニューから、Secrets設定を選んで、NPM_AUTH_TOKENを設定。
でもって、こんな感じ。

- run: echo "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}" > ~/.npmrc
- name: npm install
  run: |
    npm install


シェルスクリプト内でgit clone

actionでなくて、シェルスクリプトでgitする。
ローカルで動くビルドスクリプトがあって、
それをGitHub Actionsでもそのまま使いたいケースの対応方法。

パターン1

git cloneする時、httpsでアクセスすれば、SSHの設定は要らない。

git clone git@github.com:taku-o/github-actions-workflow-demo.git
     ↓
git clone https://github.com/taku-o/github-actions-workflow-demo.git

パターン2

SSHのキーを設定しておけば、プライベートなGitHubレポジトリもcloneできる。
シェルスクリプトでいろいろする前に、
ssh-key-actionで、SSHの設定を入れておく。

- name: ssh key
  uses: shimataro/ssh-key-action@v1
  with:
    private-key: ${{ secrets.SSH_KEY }}
    public-key: ${{ secrets.SSH_KEY_PUBLIC }}
    known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
- name: git clone
  run: |
    git clone git@github.com:taku-o/github-actions-workflow-demo.git


actions/cacheのlimitが小さくて困る

actions/cacheのキャッシュのlimitが小さい。400MB。
容量足りなくて、大きなアプリだと、一切キャッシュが効かなくなる。


でも、これぐらいならすぐに修正が入るだろう。
そう思っていた私は、issueが建ってから22日経過しているのを見て、
音速でフォークするのであった。

https://github.com/taku-o/github-actions-cache

- name: cache node environment
  id: cache-node
  uses: taku-o/github-actions-cache@v1.0.4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-


1GBまで増やした。
どうせやるなら、パラメータ外出しにしような
って意見もあるかもしれない。
ダメだわコレ。内部でtgzしてる。 よって単体でファイルサイズが400MB越えて、GitHub Actionsの制限にかかって消されちゃう(´・ω・`)


アクションのフォーク

アクションはGitHub Actions Marketplaceに公開しなくても使える。楽でイイネ。

でも、GitHub ActionsのアクションをForkして、自分のレポジトリに持ってくると、
レポジトリの名前が大げさ(?)な感じになっちゃうね。

アクションをForkする場合は、初手名前変更安定だろうね。

actions/cache
   ↓
taku-o/cache
   ↓
taku-o/github-actions-cache (邪魔にならない感じの名前に)


actions/upload-artifact

actions/upload-artifactはzip形式でファイルをアップロードする。

でも、既にzipファイルを作ってる場合は、それをそのままアップロードして欲しいんだよ、

って少し思ったけど、
mkdir、mvすれば良かっただけだね。
これならまだ許せる。

- name: collect staging built files
  run: |
    mkdir built
    mv release/app.zip built

- name: upload built files
  uses: actions/upload-artifact@master
  with:
    name: built
    path: built


nameは、built.zipとzip付きで指定すると、built.zip.zipが作られるぞ。
気をつけるのだ。

- name: upload built files
  uses: actions/upload-artifact@master
  with:
    name: built.zip
    path: built


gitのbranch名を取る

pushされたGitHubレポジトリのbranchの名前を取る。

GITHUB_REFの中に名前が入っている。
refs/heads/feature/window-sizeのようなフォーマット。

- name: build staging application
  run: |
    npx gulp build:staging --branch=${GITHUB_REF#refs/heads/}


Pull Requestされたコードを取る

Pull Requestされたコードを取る。
Pull Requestされたコードのテストなどで使う。

こちらもGITHUB_REFの中に名前が入っている。
refs/pull/9999/merge

- name: build pr application
  run: |
    npx gulp build:pr --pull_request=${GITHUB_REF#refs/}


実際に、Pull Requestのコードを取り込んで使うには ↓みたいなことするコードを書くことになるかな?

git clone taku-o/github-actions-workflow-demo
cd github-actions-workflow-demo
git pull origin pull/9999/merge


おわり

終わりなのだ。

続・Electron製アプリが Mac App Storeで non-public APIの利用で rejectされる問題。解決しそう

AppStoreのレビュー通ったって

前回
Electron製アプリが Mac App Storeへの申請で non-public APIの利用で rejectされる問題
https://taku-o.hatenablog.jp/entry/2019/11/03/103422


Electronがnon-publicなAPIを利用していたので、
Electron製のアプリがみんなrejectされている問題があったのだけれど、
最新コードでAppStoreのレビューが通ったそうで、解決のメドがたったみたい。
https://github.com/electron/electron/issues/20027


もうちょっとで修正が利用できるようになるのでしょう。
https://github.com/electron/electron/pull/20965


でも、Electron 5以降が対象なんだ

Electronのバージョン4以下は、サポート打ち切られています。
当然修正は用意されない!


Electron 4用のPull Requestも作られたけど、
一瞬でcloseされたよ!!
https://github.com/electron/electron/pull/21003


私が使ってたのは3.1.9だったので、 このままでは修正が用意されないのですね。
Electron 3、Electron 4使っている人は、
Electronのバージョン上げましょうね。


というわけでElectron 5までバージョン上げたよ

というわけで、ここ数日は、
アプリで使っている Electronのバージョンアップに励んでいました。


Electronを追っている人は知っていると思うけど、
Electronはバージョン上げて、すんなり動いたりしないのですよ。


自分の場合、3.1.9 → 5.0.12で、次のような問題があった。
(Electron 6への移行も検討してたので、Electron5用の対応と、Electron6用の対応が混在してるよ)


node-ffi が Node 12 でビルドできない

もっとも古いElectron 5でも、Node 12.0.0です。

# リリースノートから
Upgraded to Chromium 73.0.3683.119, Node.js 12.0.0, and V8 7.3.492.27.

node-ffiというネイティブライブラリを実行できるモジュールがあり、 今までこれを利用していたのですが、
このライブラリはNode 12ではビルドできない。
開発が止まっている。
https://github.com/node-ffi/node-ffi


自分はいろいろ検討した結果、node-ffi-napiに移行しました。
とても良い感じ。
https://github.com/node-ffi-napi/node-ffi-napi


menu item role

Electronのメニュー定義のrole定数が大文字小文字で変わっていたり。
こんな感じであちこち。
Typescriptでコンパイル時にエラーになってた。

# 抜粋
- {role: 'zoomin'},
- {role: 'zoomout'},
- {role: 'resetzoom'},
+ {role: 'zoomIn'},
+ {role: 'zoomOut'},
+ {role: 'resetZoom'}


Spectron

remote-debugging-portを指定しないと、
Spectronのテストコードが動作しなくなった。

# 抜粋
describe('dictWindow', function() {
  this.timeout(10000);

  before(function() {
    this.app = new Application({
      path: 'App-darwin-x64/App.app/Contents/MacOS/App',
      chromeDriverArgs: ['remote-debugging-port=9222'], ←←←←←← これ!
      env: {DEBUG: 1, NODE_ENV: 'test'},
    });
    return this.app.start();
  });


electron-packager

electron-packagerでアプリパッケージを作っているのですが、
Electron 6あたりで、Electronの作りが結構変わっているようですね。
古いバージョンのelectron-packagerを使っている人はアップデートが必要でしょう。
https://github.com/electron/electron-packager


一部ファンクションが非同期になってた

例えば、

dialog.showMessageBox

これが同期から非同期処理になってたり。


そういう変更は、リリースノートの
Breaking Changesの項に置いといて欲しい。


ばっちこい

準備完了だ。
はやくこいこいアップデート。

AquesTalk1 MacがmacOS Catalinaで32bitサポート打ち切られて動かなくなったけど、なんとかした話

ながれ

  1. AquesTalkという機会音声を作るライブラリがある
  2. macOS Catalinaで32bitアプリのサポートが打ち切られた
  3. AquesTalk1 Macのライブラリは、i386ppcuniversal binaryだったので、
  4. macOS Catalinaで無事、動作しなくなった

ちょっと頑張ってみた

macOS Catalina以降から使える機能に、
iPadアプリをMacアプリとして動かす、"Mac Catalyst"という機能があり、
当初はこれを使って、なんとかする予定でした。
iPadアプリを作ってからの、Macアプリへの変換。


しかし、その最中、iOS版のAquesTalkを、
Macアプリでそのまま実行できることに気づいたのであった。
DE・KI・TA

(今時の開発者は動画も作るらしいよ)


おわり

AquesTalk1 Macの声作る関数がこれで、

unsigned char * AquesTalk_SyntheMV(int idVoice, const char *koe, int iSpeed, int *size)

AquesTalk1 iOS版の声作る関数がこれだから、

unsigned char * AquesTalk_Synthe(const char *koe, int iSpeed, int *size)


あれあれ、なんか足りないなー?
声(idVoice)選べないなー?
という問題が、この回避方法にはあるような無いような。

Electron製アプリが Mac App Storeへの申請で non-public APIの利用で rejectされる問題

Electron製アプリがrejectされている

Electronプロジェクトの、このGitHub issueで気づいたのですが、

どうもここ最近、具体的には、10月末から
Electron製のアプリが、Mac AppStoreでrejectされています。
issueのタイトルは5.0.10となっていますが、
別に5.0.10でなくてもrejectされています。


このあたりのAPIがprivate APIで、
これらをElectronが使用しているから、らしい。

CAContext
CALayerHost
NSAccessibilityRemoteUIElement
NSNextStepFrame
NSThemeFrame
NSURLFileTypeMappings


これらのAPIはいつから使われている。

古くから使われている。
Electron 3だと、次の3つが入っているようだ?

CAContext
CALayerHost
NSURLFileTypeMappings


そう何を隠そう、私もreject喰らったのである。
他の人がどうしているのか探したらわかったのである。


どうする?

どうしよう?
少し困る現象でありますね。

PhantomJSの代わりとしてGoogle Chromeをヘッドレスで使う。のではなくChromiumを使う。

PhantomJSの開発が止まってしまった

PhantomJSブラウザの開発が止まってしまいました。
npmでさくっと導入できるヘッドレスなブラウザで、
テストの時に便利だったのですけれどね。

Google Chromeをヘッドレスモードで使う?

そこで、代わりに、Google Chrome
ヘッドレスモードで使おうっていう考えがあります。


ただー。
PhantomJSはnpm installでインストールできた。
Google Chromeはインストールが面倒くさい。


Google Chromeのインストールは、Macなら次のURLからインストールする


Ubuntuなら、こんなインストール手順になりますかね。

wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb
sudo apt-get update
sudo apt-get -f install


そこでChromium

npmでChromiumをインストールする

npm install --save-dev @taku-o/chromium


とりあえず、CHROMIUM_VERSIONでバージョン指定できるようにしておいた。
が、どうやってバージョン指定できるようにしようかなぁ、ってのは
迷いどころである。

CHROMIUM_VERSION=76 npm install --save-dev @taku-o/chromium


これはWebdriverIOのテストコードなのですけれど、
おおよそChromeを使うように設定を組んでおいてから、
実行時に、Chromeの代わりにChromiumのパスを渡す感じになりますな。

# wdio.conf.js抜粋
var chromium = require('@taku-o/chromium');

exports.config = {
    ...
    capabilities: [{
        browserName: 'chrome',
        'goog:chromeOptions': {
            args: ['--headless', '--disable-gpu'],
            binary: chromium.path,  // ←←←←←←←←←← ここに注目
        },
    }],
    ...


サンプルコードと手順

mkdir webdriverio_chromium-test-samples
cd webdriverio_chromium-test-samples
npm init
mkdir -p ./test/specs
npm install --save-dev @wdio/cli @wdio/cli @wdio/local-runner @wdio/mocha-framework @wdio/spec-reporter @wdio/sync wdio-chromedriver-service chromedriver
npm install --save-dev @taku-o/chromium

# WebdriverIOのテストコード作成
vim test/specs/basic.js

# テストの設定作成
vim wdio.conf.js

# テスト実行
./node_modules/.bin/wdio wdio.conf.js
  • 実行結果
Execution of 1 spec files started at 2019-09-01T11:32:10.850Z

2019-09-01T11:32:10.886Z INFO @wdio/cli:Launcher: Run onPrepare hook
Starting ChromeDriver 76.0.3809.126 (d80a294506b4c9d18015e755cee48f953ddc3f2f-refs/branch-heads/3809@{#1024}) on port 4444
Only local connections are allowed.
Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code.
2019-09-01T11:32:11.651Z INFO @wdio/local-runner: Start worker 0-0 with arg: wdio.conf.js
[0-0] RUNNING in chrome - /test/specs/basic.js
[0-0] 2019-09-01T11:32:12.089Z INFO @wdio/local-runner: Run worker command: run
[0-0] 2019-09-01T11:32:12.145Z INFO webdriver: [POST] http://127.0.0.1:4444/session
[0-0] 2019-09-01T11:32:12.146Z INFO webdriver: DATA { capabilities:
   { alwaysMatch: { browserName: 'chrome', 'goog:chromeOptions': [Object] },
     firstMatch: [ {} ] },
  desiredCapabilities:
   { browserName: 'chrome',
     'goog:chromeOptions':
      { args: [Array],
        binary:
         '/Users/taku-o/Desktop/webdriverio_chromium-test-samples/node_modules/@taku-o/chromium/lib/chromium/chrome-mac/Chromium.app/Contents/MacOS/Chromium' } } }
[0-0] 2019-09-01T11:32:12.879Z INFO webdriver: COMMAND navigateTo("https://webdriver.io/")
[0-0] 2019-09-01T11:32:12.880Z INFO webdriver: [POST] http://127.0.0.1:4444/session/a296b1e791776ba38f9de341c0985c1e/url
[0-0] 2019-09-01T11:32:12.881Z INFO webdriver: DATA { url: 'https://webdriver.io/' }
[0-0] 2019-09-01T11:32:14.932Z INFO webdriver: COMMAND getTitle()
[0-0] 2019-09-01T11:32:14.933Z INFO webdriver: [GET] http://127.0.0.1:4444/session/a296b1e791776ba38f9de341c0985c1e/title
[0-0] 2019-09-01T11:32:14.938Z INFO webdriver: RESULT WebdriverIO · Next-gen WebDriver test framework for Node.js
[0-0] 2019-09-01T11:32:14.940Z INFO webdriver: COMMAND deleteSession()
[0-0] 2019-09-01T11:32:14.941Z INFO webdriver: [DELETE] http://127.0.0.1:4444/session/a296b1e791776ba38f9de341c0985c1e
[0-0] PASSED in chrome - /test/specs/basic.js
2019-09-01T11:32:15.105Z INFO @wdio/cli:Launcher: Run onComplete hook

 "spec" Reporter:
------------------------------------------------------------------
[chrome  mac os x #0-0] Spec: /Users/taku-o/Desktop/webdriverio_chromium-test-samples/test/specs/basic.js
[chrome  mac os x #0-0] Running: chrome on mac os x
[chrome  mac os x #0-0]
[chrome  mac os x #0-0] webdriver.io page
[chrome  mac os x #0-0]    ✓ should have the right title
[chrome  mac os x #0-0]
[chrome  mac os x #0-0] 1 passing (2.1s)

Spec Files:      1 passed, 1 total (100% completed) in 00:00:04 


おわり

一気にやっちゃったけど、

いや、こっち使うべきかな

github.com

ボタンを長押ししたら、ショートカットキーのヒントを表示する

⌘キーを長押しした時、ショートカットのヒントアイコンを表示する

SlackのMacアプリが
⌘キーを長押しすると「⌘キー+数字キー」のショートカットの
ヒントを表示するなんてことをやってまして、

→ (ふわっと出てくる)


これは良いUIだと思ったのです。


要件

  • ⌘キーを長押ししたらアイコンを表示する
  • キーを離したらアイコンを消す


実装

こんな感じかな?

  • CSS変数で表示を切替。
  • ボタンを押してから、1秒後にアイコンを表示する。
  • ボタンを放したら、アイコンを消す。もしくはアイコン表示のアニメーション(?)を止める。
  • フォーカスが外れた時もアイコンを消す処理を入れておいた方が良さそう
    • これで長押しを表現。
    • 今はタイマーで表示制御とか、要らないんですね。


<html>
<head>
    <style>
        button {
            width: 200px;
            height: 50px;
        }

        :root {
            --shortcut-hint-delay: 1s;
            --shortcut-hint-opacity: 0;
        }
        .shortcut-hint-container {
            position: relative;
        }
        .shortcut-hint {
            position: absolute;
            width: 10px;
            left: 3px;
            top: calc(100% - 12px);
            z-index: 1;
            vertical-align: middle;
            text-align: center;
            line-height: 10px;
            font-size: 9px;
            color: white;
            background-color: black;
            border-radius: 2px;
            transition-delay: var(--shortcut-hint-delay);
            opacity: var(--shortcut-hint-opacity);
        }
    </style>

    <script>
        var hintDisplayed = false;

        window.addEventListener('keydown', function(e) {
          if (e.metaKey) {
            hintDisplayed = true;
            document.documentElement.style.setProperty('--shortcut-hint-delay', '1s');
            document.documentElement.style.setProperty('--shortcut-hint-opacity', '1');
          }
        });
        window.addEventListener('keyup', function(e) {
          if (hintDisplayed) {
            hintDisplayed = false;
            document.documentElement.style.setProperty('--shortcut-hint-delay', '0s');
            document.documentElement.style.setProperty('--shortcut-hint-opacity', '0');
          }
        });
        window.addEventListener('blur', (e) => {
          if (hintDisplayed) {
            hintDisplayed = false;
            document.documentElement.style.setProperty('--shortcut-hint-delay', '0s');
            document.documentElement.style.setProperty('--shortcut-hint-opacity', '0');
          }
        });
    </script>
</head>
<body>
    <div class="shortcut-hint-container">
        <button>保存ボタン</button>
        <label class="shortcut-hint">S</label>
    </div>
</body>
</html>



おわり

おわりだん。

AutoMator Actionの「Finder項目にフィルタを適用」はキャッシュを持っている(みたい)

遭遇した問題

MacAutoMatorという作業自動化アプリがあるのだが、

そのAutoMatorで使用できる、
「Finder項目にフィルタを適用」というアクションは、


(おそらく)実行結果のキャッシュを持っていて、
何度も何度も繰り返し処理を実行するような場合、
最新のフィルタリング結果が次のアクションに渡されない。

過去の実行結果が渡ってしまう。


よって、例えば、フォルダーアクションなどで、このフィルタリングを使うと、
・フォルダーにファイルを追加しても検知されない、
・その追加したファイルに対する処理がスルーされる、
などの問題がしばしば発生する。
発生した。


(これがうまくいかない)


対応方法

この実行結果のキャッシュの問題は、
「Finder項目にフィルタを適用」を「シェルスクリプトを実行」に置き換えて、
シェルスクリプトでフィルタリングしてしまえば解決する。

# 拡張子wavのファイルに絞って、次のアクションに回すシェルスクリプト
while read line; do
    if expr "$line" : ".*.wav$" > /dev/null
    then
        echo "$line" >&1
    fi
done


(こんな感じに)


おわり

案外、この問題には苦戦した。