VS Code Extensionの公開を簡単にするCircleCI Orb作りました
VS Code Extensionの公開を簡単にするCircleCI Orb作りました
作りました: https://circleci.com/orbs/registry/orb/uraway/vsce
リポジトリ: https://github.com/uraway/vsce
使い方
環境変数としてVSCODE_MARKETPLACE_TOKENにPersonal Access Tokenをセットして、package.json
のバージョンフィールドを更新してコミットすればあとは自動的に公開されます。
push-git-tag
オプションをtrue
にしてフィンガープリントもつければ、タグリリースやってくれるのでかんぺき。キャッシュの処理はキャッシュキーがややこしくなりそうなので、post-install-steps
とpre-install-steps
でユーザー定義にしました。
orbs: vsce: uraway/vsce@0.0.3 version: 2.1 workflows: test-publish: jobs: - vsce/publish: filters: branches: only: master post-install-steps: - save_cache: key: v1-node-cache-{{ .Branch }}-{{ checksum "package-lock.json" }} paths: - node_modules pre-install-steps: - restore_cache: keys: - v1-node-cache-{{ .Branch }}-{{ checksum "package-lock.json"}} - v1-node-cache-{{ .Branch }} - v1-node-cache- push-git-tag: true ssh-fingerprints: <フィンガープリント>
Orb開発
orbs: orb-tools: circleci/orb-tools@9.0.0 your-orb: your-namespace/your-orb@<<pipeline.parameters.dev-orb-version>> parameters: dev-orb-version: default: 'dev:alpha' type: string run-integration-tests: default: false type: boolean jobs: integration-tests: executor: orb-tools/ubuntu steps: - checkout version: 2.1 workflows: integration-tests_prod-release: jobs: - integration-tests - orb-tools/dev-promote-prod-from-commit-subject: add-pr-comment: true bot-user: your-namespace fail-if-semver-not-indicated: true publish-version-tag: true ssh-fingerprints: <フィンガープリント> filters: branches: only: master orb-name: your-namespace/your-orb requires: - integration-tests when: << pipeline.parameters.run-integration-tests >> lint_pack-validate_publish-dev: jobs: - orb-tools/lint - orb-tools/pack: requires: - orb-tools/lint - orb-tools/publish-dev: orb-name: your-namespace/your-orb requires: - orb-tools/pack - orb-tools/trigger-integration-tests-workflow: name: trigger-integration-dev requires: - orb-tools/publish-dev unless: << pipeline.parameters.run-integration-tests >>
orb-toolsをフルに使う
https://circleci.com/orbs/registry/orb/circleci/orb-tools
orb-tools、これはもうOrb開発に必須。だいたいこれで良い。v9.0.0になってまた使いやすくなりました。orb-tools/lint
、orb-tools/pack
、orb-tools/publish-dev
はもちろん便利ですが、orb-tools/dev-promote-prod-from-commit-subject
ジョブがまた良い仕事してくれる。
- orb-tools/dev-promote-prod-from-commit-subject: add-pr-comment: true bot-user: your-namespace fail-if-semver-not-indicated: true publish-version-tag: true ssh-fingerprints: <フィンガープリント> filters: branches: only: master orb-name: your-namespace/your-orb
プルリクエストをマージするときに、下記図のようにコミットサブジェクトに[semver:patch|minor|major|skip]を入れるとsemverにそってプロモートしてくれます。
また、add-pr-comment: true
を渡して、環境変数PR_COMMENTER_GITHUB_TOKEN
としてbot-user
に対応するGitHubトークンをプロジェクトに追加すれば、そのプルリクエストに、Orb公開したよ、とコメントしてくれます。
さらに、publish-version-tag: true
を渡して、デプロイキーのフィンガープリントをセットすればタグコミットをプッシュしてくれます。さいこう
必要であればインテグレーションテスト
Orbによっては必要なこともあるインテグレーションテスト。vsce Orbでは、外部APIであるVS Code Marketplaceとの連携をテストしたいので、リポジトリ内にテスト用のVS Code Extension (vsce-orb-integration-test
)を持ち、そのExtensionの公開・非公開をワークフロー内で繰り返しています。API制限とかあるのかな?
https://github.com/uraway/vsce
workflows: integration-tests_prod-release: jobs: - vsce/publish: name: publish-vsce publish-token-variable: VSCODE_MARKETPLACE_TOKEN push-git-tag: false package-path: vsce-orb-integration-test pre-install-steps: - restore_cache: keys: - v1-node-cache-{{ .Branch }}-{{ checksum "vsce-orb-integration-test/package-lock.json" }} - v1-node-cache-{{ .Branch }} - v1-node-cache- post-install-steps: - save_cache: key: v1-node-cache-{{ .Branch }}-{{ checksum "vsce-orb-integration-test/package-lock.json" }} paths: - node_modules - vsce/unpublish: name: unpublish-vsce publish-token-variable: VSCODE_MARKETPLACE_TOKEN package-path: vsce-orb-integration-test requires: - publish-vsce
2つの数の和
2つの数の和
2つ以上のユニークな数字の集合A
の中から、ターゲット(target
)の数字の和となる数字のペアを見つけ出す。数字のペアは最大1つある。答えは値が小さい順に並べるとする。
例えばA
が1, 10, 5, -1、12、target
が15であれば、答えは10, 5のペアである。A
を数字の配列の引数、target
を数字の引数としてこれを実装する。
アルゴリズム実装(1) O(n2)
最も簡単なアルゴリズムは二重ループして、すべての要素の和のパターンを出すこと。計算量はO(n^2)
。使用しているsort
は2つの数値の比較なので無視する。
function twoNumberSum(A, target) { for (let i = 0; i < A.length; i++) { for (let j = 0; j < A.length; j++) { if (i === j) continue; if (A[i] + A[j] === target){ return [A[i], A[j]].sort((a, b) => a - b) } } } return []; }
アルゴリズム実装(2) O(n)
計算し終えた要素を配列store
に保存していき、ワンループで済ます。計算量はO(n)
ループ中の数値をx
とすると、ペアになる可能性のある数値y
はtarget - x
と表せる。store
にy
が存在しなければ、x
をstore
に保存する。
こちらも使用しているsort
は2つの数値の比較なので無視する。
function twoNumberSum(A, target) { const store = [] for (const x of A) { const y = target - x if (store.includes(y)) { return [x, y].sort((a, b) => a - b) } else { store.push(x) } } return []; }
includes
の実装が気になる。ハッシュテーブルに保存のほうがいいかも?
アルゴリズム実装(3) O(n log n)
まず、A
を小さい順に並び替える。ソートに関しては、JavaScriptの実装はブラウザにもよるがここではクイックソート、計算量O(n log n)
とする。
[-1, 1, 5, 10, 12]
現在の最小の値のインデックスをmin
、最大の値のインデックスをmax
とおくと
sum = array[left] + array[right]
となる。sum
がtarget
より大きい場合、値を小さくしたいのでright
を小さくする。逆にsum
がtarget
より小さい場合、値を大きくしたいので、left
を小さくする。
例えば初期状態でleft=0
、right=4
であるためsum=11
left right | | [-1, 1, 5, 10, 12]
target
はそれより大きいのでleft
を右に動かしてleft=1
、right=4
ならばsum=13
left right | | [-1, 1, 5, 10, 12]
target
はそれより大きいのでleft
を右に動かしてleft=2
、right=4
ならばsum=17
left right | | [-1, 1, 5, 10, 12]
target
はそれより小さいのでright
を左に動かしてleft=2
、right=3
ならばsum=15
left right | | [-1, 1, 5, 10, 12]
クイックソートと一回のループの計算量は
O(n log n) + O(n) = O (n log n)
であることからO(n log n)
答えのペアはユニークな数値となることに注意してこれを実装すると
function twoNumberSum(A, target) { A.sort((a, b) => a - b) let left = 0 let right = A.length - 1 while (left < right) { const sum = A[left] + A[right] if (sum === target) { return [A[left], A[right]] } else if (sum < target) { left++ } else if (sum > target) { right-- } } return []; }
バイナリサーチ
バイナリサーチ
問: ソートされた数値の配列と数値target
を受け取り、配列中のtarget
のインデックスを求める。その際、バイナリサーチを用いること。
アルゴリズム実装 $O(\log n)$
配列の要素数をn
としたとき、left=0
、right=n
の変数を作る。また、left
とright
の中央の位置をmid
とする。
array[mid] === target
ならtarget
が見つかったとして探索終了。left >= right
なら見つからなかったとして探索終了。array[mid] > target
ならtarget
は配列の左半分にあるので、right=mid-1
として0へarray[mid] < target
ならtarget
は配列の右半分にあるので、left=mid+1
として0へ
function binarySearch(array, target) { let left = 0 let right = array.length - 1 while (left <= right) { const mid = Math.floor((left + right) / 2) if (array[mid] > target) { right = mid - 1 } else if (array[mid] < target) { left = mid + 1 } else { return mid } } return -1 }
バイナリサーチは一回実行するごとに対象の配列の要素数が1/2になる。最悪の場合要素数が1になるまで実行されるので、計算量はその繰り返し回数をk
とすると
底数を無視して、$O(\log n)$となる
VPC Peeringを使って、異なるVPCにあるRDSとEC2を接続する
VPC Peeringを使って、異なるVPCにあるRDSとEC2を接続する
VPC Peeringとは
VPC Peeringを設定する
VPC Peeringを作成する
EC2インスタンスから、同一アカウント、同一リージョン、異なるVPCにあるRDSへのアクセスを設定したいとする。
マネジメントコンソールからVPC設定を開いて、"Create Peering Connection"をクリック、項目を入力してVPC Peeringを作成する
項目 | 説明 |
---|---|
Peering connection name tag | 識別名 |
VPC (Requester)* | 接続元のVPC |
Account | 接続先のAWSアカウント |
Region | 接続先のAWSリージョン |
VPC (Accepter)* | 接続先のVPC |
Routeを追加する
VPC Peering作成しただけではトラフィックのルートができないので、Route Tablesから、ルートを追加する。
接続元のVPC Route Table
Destination | Target |
---|---|
(接続元のCIDRs) | local |
(接続先のCIDRs) | (作成したVPC Peering) pcx-*** |
接続先のVPC Route Table
Destination | Target |
---|---|
(接続先のCIDRs) | local |
(接続元のCIDRs) | (作成したVPC Peering) pcx-*** |
Security Groupsを設定する
接続元のインスタンスから、接続先のRDSに対してincoming trafficを許可する。
Type | Protocol | Port Range | Source |
---|---|---|---|
お好み | TCP | お好み | 接続元のセキュリティグループ |
VPC Peeringの設定が上手くいっていない場合、ここで「異なるVPC間のセキュリティグループは設定できない」旨のエラーが出る。
DNS解決を許可する
上記までの設定で、プライベートIPアドレスを用いればルーティングが可能になるが、RDSはプライベートIPアドレスが固定ではないため、ルーティングにはDNS名を使いたい。
なので作成したVPC Peeringの"Edit DNS Settings"からDNS resolutionを許可して、DNS名でアクセスできるようにする。
Palindrome Check (回文チェック) アルゴリズム
Palindrome Check (回文チェック) アルゴリズム
文字列string
を受け取って、回文かどうかをチェックする。すべての文字を比較しないといけないので、どうやっても計算量が$O(n)$以上になるはず。
アルゴリズム実装(1) $O(n)$
両端から文字が等しいか比較していく:
function isPalindrome(string) { let left = 0 let right = string.length - 1 let result = true while (left <= right) { if (string[left] === string[right]) { left++ right-- } else { result = false break } } return result }
アルゴリズム実装(2) $O(n)$
文字の位置を反転させたreversedString
文字列とstring
の比較:
function isPalindrome(string) { const reversedString = [] for (let i = string.length - 1; 0 <= i; i--) { reversedString.push(string[i]) } return string === reversedString.join('') }
フィボナッチ数列のアルゴリズム
フィボナッチ数列のアルゴリズム
勉強がてらメモ
フィボナッチ数列とは
「フィボナッチ数列」とは「前の2つの数を加えると次の数になる」という数列。1番目は0、2番目は1。n番目の数字は(n - 1)番目と(n - 2)番目の数字の和。
例えば、6番目の数は5 (0, 1, 1, 2, 3, 5)
アルゴリズム実装(1) O(n2)
n番目のフィボナッチ数をfib(n)
とすれば、
fib(4)
を求めるには
1 fib(4) ________________|________________ | | 2 fib(3) fib(2) ________|________ ________|________ | | | | 4 fib(2) fib(1) fib(1) fib(0) ________|________ | | 0 fib(1) fib(0)
となるので、計算量はO(n^2)
1番目と2番めの例外に注意してこれを実装すると次のようになる:
function getFib(n) { if (n === 2) { return 1 } else if (n === 1) { return 0 } return getFib(n - 1) + getFib(n - 2) }
アルゴリズム実装(2) O(n)
先程の樹形図を見ると、fib(2)
などすでに答えがでているものでも計算しているので、改善の余地があることに気づく。
一度出した答えを覚えておいて再利用できるならば、fib(4)
の答えを得た時、例えばfib(6)
を新たに求めたい場合は
であるが、fib(4)
の答えはわかっているので、fib(5)
だけを求めれば良い。同様にfib(n)
を求めたい時、fib(n - 1)
だけを求めれば良いから計算量はO(n)
これを実装するとき、fibStore
配列にフィボナッチ数列を格納する。インデックスi
の数字はfibStore[i - 1] + fibStore[i - 2]
である。したがって
function getFib(n) { if (n === 2) { return 1 } else if (n === 1) { return 0 } const fibStore = [0, 1]; for (let i = 2; i < n; i ++) { const ithFib = fibStore[i - 1] + fibStore[i - 2] fibStore[i] = ithFib } // n番目のフィボナッチ数 (インデックス n - 1) return fibStore[n - 1]; }
No such file or directory - bs_fetch:atomic_write_cache_file:chmod
No such file or directory - bs_fetch:atomic_write_cache_file:chmod
docker-composeを使ってサーバーとsidekiq
両方を動かそうとすると次のようなエラーが出る場合があります:
$ docker-compose up app_1 | [12] Puma starting in cluster mode... app_1 | [12] * Version 4.1.0 (ruby 2.6.3-p62), codename: Fourth and One app_1 | [12] * Min threads: 5, max threads: 5 app_1 | [12] * Environment: development app_1 | [12] * Process workers: 2 app_1 | [12] * Preloading application app_1 | [12] ! Unable to load application: Errno::ENOENT: No such file or directory - bs_fetch:atomic_write_cache_file:chmod app_1 | bundler: failed to load command: puma (/usr/local/bundle/bin/puma) app_1 | Errno::ENOENT: No such file or directory - bs_fetch:atomic_write_cache_file:chmod app_1 | /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/compile_cache/iseq.rb:37:in `fetch'
簡略化しますが、docker-compose.yml
はだいたいこんなかんじ。app
を2つのコンテナが共有している状態:
version: "3" services: app: build: . command: bundle exec puma volumes: - .:/app ports: - "8080:8080" links: - redis sidekiq: build: . command: bundle exec sidekiq volumes: - .:/app links: - redis redis: image: redis volumes: - ./tmp/db:/var/lib/redis/data
おそらく、サーバーが立ち上がってからbootsnapで生み出されるapp/tmp/cache
をapp
コンテナとsidekiq
コンテナの両方が読み込もうとしてエラーが起きている様子。であれば、キャッシュフォルダをマウントしないか、bootsnapをsidekiq
コンテナでは使わないようにしてあげれば良さそうです
volumes: - .:/app /app/tmp/cache