C++幼女先輩

プログラミング成分多め

Windowsネイティブにも開発環境作る

理由

私の今のPC環境は
超スペックのWindowsデスクトップ
ハイスペックWindowsノートPC(ただしバッテリー爆発)
低スペックMacbookPro(ProではあるがWindowsマシンと比較するとスペックが酷い。値段は同じなのに・・)

仕事が落ち着いたら、AWS Cloud9でリモート開発環境を作る予定だが、それまでのつなぎ。
いや、WindowsデスクトップはAWSよりスペックいいはずなので、こっちではPCで開発するかもしれない

Windowsデスクトップの問題

WSL2+Docker for Windowsの開発環境が最強すぎて、満足しているが
まだ技術の過渡期なのか、WindowsUpdateで頻繁にエラーが出る
ので、安定した環境を考えている
もちろん、VirtualBoxLinux入れてそこにDocker入れれば問題ないんだけどね
ただ、VirtualBoxHyper-Vとの相性の悪さもある

ただ、ネイティブのWindowsでの開発もわりと最近便利になっているので、それの調査も兼ねて、ローカル環境を構築する
コマンドプロンプトPowerShellを使うのも面倒だけど、最近はGitBashなりもあるし、何とかなるかな・・・

今回の開発環境

AWS Client系、node.js関連の構築と行い、AWS Amplify、AWS SAM、ServerlessFramework、ServerlessOffline等のテストをしてみたいと思う
フロントはionicフレームワークを試す。こちらはMacのみ正式対応だが、Windowsでどこまで出来るかも試したい(結局iPhoneビルドでMacは必要だが)

node環境

nodist

Windowsの場合はnodeのバージョン管理ツールとしてnodistを使う事が多いと思われる

Releases · nullivex/nodist · GitHub

ここからダウンロードしインストールを行う。
ここで問題が一つおきた。

PATH not updated, original length 1040 > 1024

というエラーが出て、結果的にインストールはできたものの、nodistにパスが通っておらず使いにくい
Windowsのパスの制限は色々と話はあるが、まずは不要になったPathを捨てたり、User環境変数で良いものはそちらに移したりした
で、細かい仕様は調べてもらうとして・・
大雑把にいうと、環境変数の文字数は

  • 内部的には32767文字ほど使える
  • コマンドプロンプトの制限で8192文字に制限される
  • プロセスの環境変数制限が2047文字
  • setx コマンドの制限が1024文字

結論から言うと、nodistインストーラではsetxを使っているので1024文字制限があるが、環境変数を自分で設定すれば
システムとユーザで約2000文字ずつ環境変数の設定が可能なので、自力で

C:\Program Files (x86)\Nodist\bin

これをPathに追加すればよい

Pathの環境変数をどうしても2000文字以内に出来ない場合は、VisualStudioのように
環境変数をカスタマイズしたコマンドプロンプトの起動バッチを作るのがWindows

nodeバージョン

Amplify Cliでは、node 10.x以上、npm6.x以上が推奨 Install & Configs :: title

ionicは node10.3以降(最新LTS推奨)との事
Ionic CLI - Ionic Framework 日本語ドキュメンテーション

AWS Lambdaでは、Nodeは 12.x、10.xのランタイムで動く
AWS Lambda ランタイム - AWS Lambda

という事で、12.19.0をインストールした(14xをインストールしたら、ionicがエラー吐いたので)
npmも更新し、npxも入れる(nodistはnpxが入らないらしい)

$ nodist 12.19.0
$ nodist npm 6.14.8
$ npm i npx -g

Java環境

AmplifyはMockingが出来て便利だ
Mockを使うとサーバにUploadせずにローカルで動かすことが出来て非常に便利だ
前に試したところ、AmplifyをPushすると5分ほどはデプロイに時間がかかり、開発が辛かった
その、MockingにはJavaが必要
Javaは OpenJDK 1.8以降が必要

Install & Configs :: title

ただし、Javaを取り巻く状況は、SunがOracleに買収されてから急激に変化し
Oracleが提供するOracleJavaは有償化したため
Javaの互換性のあるOpenなJavaが必要だ
Oracleの提供するOpenJDKは無償だが、リリースが遅い、サポート期間が短いなどで、セキュリティ的に非常に使いにくい
もちろん、業務としてならOracleJavaを有償で使うのもあるが、今回は無償で行いたい
現在利用できる OpenJDKとして
AdoptOpenJDK、AmazonCorreto等がある
今回はAWSメインで使うのでAmazonCorretoを使うが、こちらは32ビットに対応していない、対応OSが少ない等の問題があるので
そのうちAdoptOpenJDKにするかも

とりあえずAmazonCorreto11をインストールする
Windowsの場合はmsiファイルからインストールする
Downloads for Amazon Corretto 11 - Amazon Corretto

java -version
で、正しくバージョンが表示されればOK

ionic

ionicフレームワークはnpmからインストールする
ionicパッケージは今は古く、@ionic/cliのほうが新しいのでこちらをインストールする

$ npm install -g @ionic/cli

Amplifyクライアント

npm install -g @aws-amplify/cli    

サンプルなプロジェクト作成

ionic

ionicアプリはテンプレートから簡単に作成できる
今回は amplify_testという名前で作る
ionicではAngularで作るのがデフォルトだ(ReactやVueも追加されているが)

ionic start amplify_test
コマンドプロンプトからうち
Angular、tabsと選択し、Capacitorにするか聞かれるので Yを選んでみる

テンプレート作成したら

ionic serve

コマンドでトランスパイルされブラウザに表示される
ionicの説明はしない
コードを変更したら即座にLiveReloadも行われる

ばっちり

amplify作成

npx amplify init  

入力はデフォルトのままでOK
Frameworkはionicを選び、AWSのAccessIDを設定

これでAmplifyがAWS上に作成される

amplify push
を行うと、コードをAWS上にPushできる(5分ぐらいかかる)

npx amplify add auth
で、Cognitoでの認証を作成できる

amplify mock
を使うと、Amplifyをローカル実行できるので試したが
Cognitoに関しては、Mocking出来ず、AWSのサーバが必要な気がする

amplify add api
で、GraphQLあるいはREST APIを作成できる
今回はGraphQLを作成する

作成されたAPIの下に schema.graphqlファイルがある。これがスキーマ

type Todo @model {
  id: ID!
  name: String!
  description: String
}

デフォルトでは上記のようなかんじになっている

amplify mock

amplify mock

で、Amplifyをローカル実行できる!!
毎度 amplify pushするとデプロイに5分かかるしデバッグもし辛い
どうやらCognito以外はMockできるらしいので非常に便利だ
Mockを実行するとURLが表示されるので、ブラウザで開くと

f:id:murasame-labo:20201008173834p:plain

GraphQLのデバッグ画面が表示される

ちょっと引っかかったのが
src/graphql/queries.js
に、SQLあるいはGraphQLのQLが作成されるという情報がある

export const getTodo = `query GetTodo($id: ID!) {
  getTodo(id: $id) {
    id
    name
    description
  }
}
`;
export const listTodos = `query ListTodos(
  $filter: ModelTodoFilterInput
  $limit: Int
  $nextToken: String
) {
  listTodos(filter: $filter, limit: $limit, nextToken: $nextToken) {
    items {
      id
      name
      description
    }
    nextToken
  }
}
`;

が、探しても見つからないのだが
おそらく下記に変更されたと思われる

src/app/API.service.ts

export class APIService {
  async CreateTodo(
    input: CreateTodoInput,
    condition?: ModelTodoConditionInput
  ): Promise<CreateTodoMutation> {
    const statement = `mutation CreateTodo($input: CreateTodoInput!, $condition: ModelTodoConditionInput) {
        createTodo(input: $input, condition: $condition) {
          __typename
          id
          name
          description
          createdAt
          updatedAt
        }
      }`;
    const gqlAPIServiceArguments: any = {
      input
    };
    if (condition) {
      gqlAPIServiceArguments.condition = condition;
    }
    const response = (await API.graphql(
      graphqlOperation(statement, gqlAPIServiceArguments)
    )) as any;
    return <CreateTodoMutation>response.data.createTodo;
  }
...

JSよくしらんけど、Angular的な書き方なんかな?

そしておなじみAngularの不具合、Globalが定義されてないので、polifil.tsに追加

(window as any).global = window;

GraphQLをせっかくなので呼んでみる

Serviceを作る

$ ionic g service services/viewTodo

ionicコマンドで作成する コードの方は、先ほど作成された、API.service.tsのListTodoを呼べばよさそう

viewTodo.service.ts

import { Injectable } from '@angular/core';
import { APIService } from 'src/app/API.service';

@Injectable({
  providedIn: 'root'
})
export class ViewTodoService {
  public async listTodos() {
    var api = new APIService();
    return api.ListTodos();
  }
  constructor() { }
}

戻り値はどうやら、ListTodosQuery型のようだ

今回は table1にボタンを置いてデバッグ表示させる

tab1.page.html

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Tab 1</ion-title>
    </ion-toolbar>
  </ion-header>

  <ion-fab-button (click)="debug()">
    <ion-icon name="list"></ion-icon>
  </ion-fab-button>

</ion-content>

ion-icon#nameにアイコン名を指定すれば、プラットフォーム毎に適切なアイコンが表示される
Ionicons

今回は debug()関数を呼ぶことにした
Viewから呼ばれる関数は、tab1.page.tsに書く

import { ViewTodoService } from '../services/viewTodo.service'; 

export class Tab1Page {

  constructor(public viewTodoService: ViewTodoService) {}
  
  async debug(){
    (await this.viewTodoService.listTodos()).items.forEach (elm => console.log(elm.name));
 
  }
}

ListTodosQuery型からはこんな感じでデータを取る事が出来る

これでGraphQLを呼べるはずだが残念ながら、
Error: No credentials
認証が必要だね