プログラミング学習日記

プログラミング学習日記

エンジニア転職への道

以前のコミットから秘密情報を削除してgit pushし直す

git pushしたらエラーが出て、リモートリポジトリにpushできませんでした。

error: failed to push some refs to 'https://github.com/username/vue_chat.git'

出力結果を見てみると、「以前のコミットに秘密情報(OpenAI API Key)が含まれているので、コミットから取り除いてください」と言われています。

[vue_chat]$ git push -u origin main
Enumerating objects: 27, done.
Counting objects: 100% (27/27), done.
Delta compression using up to 8 threads
Compressing objects: 100% (24/24), done.
Writing objects: 100% (27/27), 40.55 KiB | 8.11 MiB/s, done.
Total 27 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), done.
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: - GITHUB PUSH PROTECTION
remote:   —————————————————————————————————————————
remote:     Resolve the following violations before pushing again
remote:     - Push cannot contain secrets    
remote:      (?) Learn how to resolve a blocked push
remote:      https://docs.github.com/code-security/secret-scanning/working-with-secret-scanning-and-push-protection/working-with-push-protection-from-the-command-line#resolving-a-blocked-push
remote:       —— OpenAI API Key ————————————————————————————————————
remote:        locations:
remote:          - commit: 919b76db4da58a9fd5437e091eedcdfbfdbeefe3
remote:            path: .env:1
remote:          - commit: 919b76db4da58a9fd5437e091eedcdfbfdbeefe3
remote:            path: src/App.vue:7
remote:        (?) To push, remove secret from commit(s) or follow this URL to allow the secret.
remote:        https://github.com/username/vue_chat/security/secret-scanning/unblock-secret/2ppRwUA1IOyOpBRh80jzI1bqCjw

解決までにやったことをまとめます。

最初に原因と自分が行った作業を載せます。
本来行うべき手順と異なるため、この方法はおすすめしません。
こういうことにならないように注意してください、という意味も込めて載せています。

本来行うべき方法の実行結果も載せています。

git pushできない原因

コミットに秘密情報が含まれていたから。
具体的には、App.vueにabcde...、.envにVITE_OPENAI_API_KEY=abcde...というふうにOpenAI APIキーを書いてある状態でgit commitしていたから。

自分が実際に行った手順(非推奨)

  1. git logでコミット履歴を確認し、秘密情報が含まれている最も古いコミットを特定する
  2. git reset --hard [コミットID]でそのコミットの状態に戻す
  3. App.vueに残っていたAPIキーのコメントを削除
  4. ステージから.envを取り除く(gitの追跡対象から外す)
  5. .gitignoreに.envを追加
  6. git commit --amendでコミットを修正
  7. git push

ログを見ると、「ブロックされたプッシュを解決する方法を学ぶにはこちらを見てください」と案内が出ているので、それをもとに進めます。 docs.github.com

1. 秘密情報が含まれている最も古いコミットを特定する

git logでコミット履歴を確認し、秘密情報が含まれている最初のコミットを特定します。
今回の場合、コミット919b76db4da58a9fd5437e091eedcdfbfdbeefe3が該当します。

[vue_chat]$ git log
commit e73fd59a37d0d012b4556bc6665851d86cb4e510 (HEAD -> main)
Author: usename <usename@gmail.com>
Date:   Fri Dec 6 13:54:09 2024 +0900

    .vueファイルをupdate

commit 919b76db4da58a9fd5437e091eedcdfbfdbeefe3
Author: usename <usename@gmail.com>
Date:   Thu Dec 5 19:42:39 2024 +0900

    initial commit

次のコマンドを実行するように指示が出ています。

git rebase -i <COMMIT-ID>~1

-iインタラクティブモード(対話モード)の指定。これによってコミットに細かい設定を追加できる。
~1:そのコミットの1つ前のコミット、すなわち親コミットを指す。

今回の場合、OpenAI APIキーが含まれているコミットの親コミットを指定して対話モードでgit rebaseを試みています。 git rebaseはブランチの起点を変更する操作です。

実行してみると、失敗しました。

$ git rebase -i 919b76db4~1
fatal: invalid upstream '919b76db4~1'

コミット919b76db4が最初のコミットなので、それ以上前に遡ることができません。

親コミットが存在しないので、秘密情報が含まれているコミット自体を修正することにします。

2. git reset --hard [コミットID]でそのコミットの状態に戻す

git resetはHEAD(ポインタ)の位置を変更します。
HEADは最新のコミットを指します。 指定されたコミットの位置にHEADが動き、それ以降のコミットが削除されます。

--hardオプションをつけると、ステージやワークツリーもローカルリポジトリと同じ状態になります。

[vue_chat]$ git log --oneline
a6a3c54 (HEAD -> main) フォームに入力した内容のrefオブジェクトの初期化
e73fd59 .vueファイルをupdate
919b76d initial commit
[vue_chat]$ git reset --hard 919b76d
HEAD is now at 919b76d initial commit
[vue_chat]$ git log --oneline
919b76d (HEAD -> main) initial commit

秘密情報が含まれていると指摘された以下2つのファイルからAPIキーを取り除きます。

3. App.vueに残っていたAPIキーのコメントを削除

App.vueファイルにAPIキーを直打ちで試した時の記述がコメントで残っていたので削除しました。

4. ステージから.envを取り除く(gitの追跡対象から外す)

.gitignoreに追加しても「秘密情報が含まれている」と指摘された理由

最初、.envファイルを.gitignoreに追加してgit pushすれば、リモートリポジトリで.envファイルは閲覧できなくなると誤った推測をしていました。

.envファイルを.gitignoreに追加してgit pushすると、pushは止められ「To push, remove secret from commit(s) or follow this URL to allow the secret.」の警告が出ます。

原因は、.envを.gitignoreに追加する前にgitの追跡対象になっていたからです。

gitの追跡対象にある=ステージに追加されている

なので、「gitの追跡対象になる」がtrueであるか確認するには、 git ls-filesコマンドで確認できます。 (ステージに.envファイルがあるならば、.envはgitの追跡対象にあるということです)

git ls-filesを実行すると、.envがステージに存在しています。すなわち、gitの追跡対象となっています。

[vue_chat]$ git ls-files .env
.env

ファイルをステージから取り除くには、git rmコマンドを使います。

ここで注意したいのが、git rm ファイル名を実行するとワークツリーからもファイルが消えてしまいます。
(そうすると.envファイルそのものが消えてしまうので問題です)

ワークツリーには残しておきたい場合は、--cached オプションをつけて実行します。

[vue_chat]$ git rm --cached .env
rm '.env'
[vue_chat]$ git ls-files .env
[vue_chat]$ 

ステージから.envが消えています。(gitの追跡対象から外れました)

5. .gitignoreに.envを追加

.gitignoreに追加されたファイルは、追加された後、gitの追跡対象外になります。

  1. .gitignoreに.envを追加
  2. .envに環境変数を定義
  3. git add .を実行

この手順で行えば、git addする時点ではgitの追跡対象外になっているので、git pushしても指摘されません。

6. git commit --amendでコミットを修正

秘密情報をgitの追跡から外したので、指摘されたコミットの変更を上書き保存します。

git commit --amendで直前のコミットを修正できます。

7. git push

git pushを実行できました。

[vue_chat]$ git push -u origin main
Enumerating objects: 22, done.
Counting objects: 100% (22/22), done.
Delta compression using up to 8 threads
Compressing objects: 100% (19/19), done.
Writing objects: 100% (22/22), 39.41 KiB | 9.85 MiB/s, done.
Total 22 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/username/vue_chat.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

本来行うべき手順

意図的にgit pushできない状況を作ります。
.vueファイルにOpenAI APIキーをコメントで残し、その状態でgit commit→git pushを行います。

// VITE_OPENAI_API_KEY=abcde...

mainブランチからfeatureブランチを切り、そこでAPIキーを含むコミットを作りました。

先ほどと同様、git pushできない状況になりました。

[vue_chat]$ git push
remote: - GITHUB PUSH PROTECTION
remote:   —————————————————————————————————————————
remote:     Resolve the following violations before pushing again
remote:       —— OpenAI API Key ————————————————————————————————————
remote:        locations:
remote:          - commit: 0656e4e4ca46f36a67dc80979cbf412bf49e3aca
remote:            path: src/App.vue:5
 ! [remote rejected] feature -> feature (push declined due to repository rule violations)
error: failed to push some refs to 'https://github.com/username/vue_chat.git'

ドキュメントに基づいて、以下の手順で進めます。

  1. git rebase -i [commitID]~1を実行→git-rebase-todoファイルが開く
  2. 問題のコミットをpickからeditに変更
  3. git-rebase-todoファイルを閉じる
  4. git commit --amendを実行
  5. APIキーを削除
  6. git rebase --continueを実行
  7. git push

featureブランチでgit rebase c5f9ac5~1を実行します。

c5f9ac5は秘密情報を含むコミットのIDです。 git rebaseはそのブランチの起点を変更するコマンドです。 -iはインタラクティブモード(対話モード)を指し、実行するとgit-rebase-todoファイルが開きます。

ドキュメントで秘密情報を含むコミットのpickをeditに変更するようにと書かれています。

In the editor, choose to edit the commit identified in step 3 by changing pick to edit on the first line of the text.

pickをeditに変更してファイルを閉じます。

git-rebase-todoファイルを閉じると、
「git commit --amendを使って、問題のコミットを修正する準備ができましたよ。変更したらgit rebase --continueを実行してくださいね」と指示されます。

[vue_chat]$ git rebase -i c5f9ac5~1
hint: Waiting for your editor to close the file... e
Stopped at c5f9ac5...  意図的にAPIキーをコメントで残した
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue

言われた通りにgit commit --amendを実行します。

[vue_chat]$ git commit --amend
interactive rebase in progress; onto 38ba916
Last command done (1 command done):
   edit c5f9ac5 意図的にAPIキーをコメントで残した
No commands remaining.
You are currently editing a commit while rebasing branch 'feature' on '38ba916'.

No changes
You asked to amend the most recent commit, but doing so would make
it empty. You can repeat your command with --allow-empty, or you can
remove the commit entirely with "git reset HEAD^".

すると、「あなたは最も最近のコミットを修正しようとしているけれど、何も変更してないですね。何も変更しないならgit commit --allow-emptyを実行するか、git reset HEAD^を実行して1つ前のコミットの状態に戻してください」と言われました。

直前のコミットと何も変更がないのにコミットが存在しないほうがいいと考え、git reset HEAD^を選択します。

[vue_chat]$ git reset HEAD^
[vue_chat]$ git log --oneline --all --graph
* 18dda8b (main) 不要なファイルを削除
| * c5f9ac5 (feature) 意図的にAPIキーをコメントで残した
| * 38ba916 (HEAD) フォームのCSSを追加
| * 7912788 フォームのplaceholderを設定
|/  
* 5622a61 (origin/main) initial commit 秘密情報を削除してコミット
し直す

これで秘密ファイルを含むコミットがなくなりました。

git rebase --continueを実行するとリベースが完了します。

[vue_chat]$ git rebase --continue
Successfully rebased and updated refs/heads/feature.
[vue_chat]$ git log --oneline --all --graph
* 18dda8b (main) 不要なファイルを削除
| * 38ba916 (HEAD -> feature) フォームのCSSを追加
| * 7912788 フォームのplaceholderを設定
|/  
* 5622a61 (origin/main) initial commit 秘密情報を削除してコミット
し直す

データをリアクティブ状態にする

リアクティブな状態とは

JavaScriptで処理した情報をHTMLに渡したい時に使う。
具体的には、たとえばクリックイベントが行われたとき、それを検知してデータを更新する場合など。

通常のHTMLは静的なファイルで動きはない。
ユーザーの操作によって、ブラウザの表示を変える。
ユーザーの反応(reaction)によって動きが変わるので、リアクティブ。

データをリアクティブにする方法

ref関数とreactive関数の2つがある。

大きな違いとして、ref関数を用いて作られるrefオブジェクトはvalueプロパティを持つ。
valueプロパティは「このデータはリアクティブなデータですよ」と、リアクティブであることを示すためのもの。

一方で、reactive関数で作られるreactiveオブジェクトはvalueプロパティを持たない。
reactiveオブジェクトはvalueプロパティでデータにアクセスするのではなく、オブジェクト.プロパティ名でそのままアクセスできる。

確認問題

  1. App.vusが空の状態で、3つの要素を作ってください。
  2. ref関数をインポートして、refオブジェクトをブラウザで表示してください。
<script setup>
import { ref } from 'vue'
const str = ref('hello')
</script>
<template>
  <p>{{ str }}</p>
</template>
<style></style>

setup setupオプションをつけると、その<script>内で定義された変数や関数を同じコンポーネントのテンプレートで呼び出せる。

既存のRailsアプリにReactを導入する③

既存のRailsアプリにReactを導入しようとreact-rails gemのドキュメントにしたがって進めていたのですが、途中でエラーに詰まって断念しました。

一連の手順を始める前の状態のdevelopブランチに戻り、そこから新たにブランチを切ってやり直そうと思いました。

しかし、node_modulesディレクトリ内にはnpmパッケージと見られるファイル群があり、public/pack/jsディレクトリにはmanifest.jsやapplication.jsが残っていました。

さらにmanifest.jsの中身を確認してみると、例外処理が書いてあります。
これらがいつどのタイミングで作られたのでしょうか?

(self["webpackChunkapp"] = self["webpackChunkapp"] || []).push([["manifest"],{

/***/ "./app/javascript/packs/manifest.js":
/*!******************************************!*\
  !*** ./app/javascript/packs/manifest.js ***!
  \******************************************/
/***/ (function() {

throw new Error("Module build failed (from ./node_modules/babel-loader/lib/index.js):\nError: Cannot find package '@babel/plugin-syntax-dynamic-import' imported from /Users/naota7118/ocr_check_app/babel-virtual-resolve-base.js\n    

省略

/***/ })

},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
/******/ var __webpack_exports__ = (__webpack_exec__("./app/javascript/packs/manifest.js"));
/******/ }
]);
  • node_modulesが作られたのはyarn addを実行したとき
  • public/pack/jsに新しくファイルが作られたのはbin/shakapacker --watchを実行したとき

bin/shakapacker --watchを実行すると、ソースコードに変更があったときにpublic/pack/js配下にファイルを生成・更新する

既存のRailsアプリにReactを導入する②

こちらのドキュメントを見ながら、既存のRailsアプリにReactの導入を試みています。 https://github.com/reactjs/react-rails/blob/master/docs/get-started.md

  • Railsにshakapackerをインストール
  • yarnでReactをインストール

まで完了したのですが、ビューでコンポーネントを表示するところでつまずきました。

ActionView::Template::Error (Shakapacker can't find application.js in /Users/username/sample_app/public/packs/manifest.json. Possible causes:
1. You forgot to install javascript packages or are running an incompatible javascript runtime version
2. Your app has code with a non-standard extension (like a `.jsx` file) but the extension is not in the `extensions` config in `config/shakapacker.yml`
3. You have set compile: false (see `config/shakapacker.yml`) for this environment
   (unless you are using the `bin/shakapacker -w` or the `bin/shakapacker-dev-server`, in which case maybe you aren't running the dev server in the background?)
4. webpack has not yet FINISHED running to reflect updates.
5. You have misconfigured Shakapacker's `config/shakapacker.yml` file.
6. Your webpack configuration is not creating a manifest.

Shakapackerがmanifest.jsonファイルを見つけられていないと出ています。
manifest.jsonファイルとは、Webブラウザ拡張機能を使うのに必要なファイルのようです。

Shakapackerはpublic/packsディレクトリを探しに行っていますが、そもそもpacksファイルが作られていません。

ドキュメントを遡ってみると、package.json"@babel/preset-react"を追加したのですが、それを反映していないのではないかと考えました。 →package-lock.jsonに追加されていたので、そうではないようです。

今回のエラーと関係があるのかわかりませんが、現在package-lock.jsonyarn.lockがどちらもある状態になっています。
どっちを読みに行っているのかが気になりました。
どちらにも@babel/preset-reactがあるので、両方読み込まれているのでしょうか?

コンパイルを実行した時にnpmが使われているので、yarn.lockを削除してみることにしました。
コンパイルは本番環境でやるべきことで、開発環境ではやらなくていいと後でわかりました。。)

$ rails shakapacker:compile
using npm@8.19.2 to manage dependencies and scripts in package.json
Shakapacker::Compiler - Slow setup for development

エラーの最後に「あなたのwebpack構成はmanifestを作っていませんね」と指摘されていますね。
いや、原因の1つとして挙げられているだけでした。。

2, 3, 5を見ると、どうやらconfig/shakapacker.ymlに原因がありそうな気がしてきました。

shakapacker.ymlを見てみると、以下のようになっていたので、それで/Users/username/sample_app/public/packs/manifest.jsonを探しに行っているのだとわかりました。

default: &default
  source_path: app/javascript
  source_entry_path: packs
  public_root_path: public
  public_output_path: packs

manifest.jsonは存在するのですが、場所が違っていて、app/assets/config/manifest.jsにありました。

shakapacker.ymlの設定を変えようかなと思ったのですが、ちょっと待てよとためらいました。

manifest.jsonを通してapplication.jsを探しに行っているわけですが、application.jsが2つ存在することに気づいたからです。

いつ作られたんだっけ?と思い返してみると、

$ bundle add 'react-rails' --strict
$ rails generate react:install

react-railsgemを追加してReactをインストールしたときに作られていました。

found 0 vulnerabilities
      create  app/javascript/packs/application.js
      create  app/javascript/packs/server_rendering.js

application.jsの中身を見てみると、Reactを呼び出しているっぽい記述があります。
Reactを使う時に呼び出すのはおそらくこっちのapplication.jsでしょう。

// Support component names relative to this directory:
var componentRequireContext = require.context("components", true);
var ReactRailsUJS = require("react_ujs");
ReactRailsUJS.useContext(componentRequireContext);

考えたこと: 探しに行っている場所にmanifest.jsを作って、その中でapplication.jsを読み込めばいい?

//= link_tree ./application.js

エラーが変わりました。書き方の問題なので、新しく作ったファイルを読みに行ってくれています。

ディレクトリを指定しなさいよ」と言われました。

//= link_tree ../packs js

あれ?また元のエラーに戻ってしまいました。。

webpacker.ymlもあったので消しました。

この出力結果を見ると、manifest.jsが読み込まれていないですね。

Your manifest contains:
{
}
):

3.で挙がっていた開発環境でのコンパイルを試してみました。

.browserslistrcは何のファイルでしょう?
ブラウザ設定の齟齬を解消するような役割を果たすみたいです。 要は重複しているのだろうから、.browserslistrcを削除しました。

$ bin/shakapacker --watch
[webpack-cli] Error [BrowserslistError]: /Users/naota7118/ocr_check_app contains both .browserslistrc and package.json with browsers
    at /Users/naota7118/ocr_check_app/node_modules/browserslist/node.js:369:15
    at eachParent (/Users/naota7118/ocr_check_app/node_modules/browserslist/node.js:53:18)
    at Object.findConfigFile (/Users/naota7118/ocr_check_app/node_modules/browserslist/node.js:347:20)
    at Object.findConfig (/Users/naota7118/ocr_check_app/node_modules/browserslist/node.js:397:27)
    at Function.loadConfig (/Users/naota7118/ocr_check_app/node_modules/browserslist/node.js:259:37)
    at Object.load (/Users/naota7118/ocr_check_app/node_modules/webpack/lib/config/browserslistTargetHandler.js:67:19)
    at getDefaultTarget (/Users/naota7118/ocr_check_app/node_modules/webpack/lib/config/target.js:19:50)
    at /Users/naota7118/ocr_check_app/node_modules/webpack/lib/config/defaults.js:165:3
    at F (/Users/naota7118/ocr_check_app/node_modules/webpack/lib/config/defaults.js:105:15)
    at applyWebpackOptionsDefaults (/Users/naota7118/ocr_check_app/node_modules/webpack/lib/config/defaults.js:164:2) {
  browserslist: true

再度コンパイルを実行すると、

ERROR in ./app/javascript/packs/server_rendering.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find package 'babel-plugin-macros' imported from /Users/naota7118/ocr_check_app/babel-virtual-resolve-base.js
webpack 5.95.0 compiled with 3 errors in 229 ms

babel-plugin-macrosというパッケージが見つからないと言われたので、npmでインストールしました。 ...つもりでしたが、インストールできていないのでしょうか?

$ npm install babel-plugin-macros

added 13 packages, and audited 606 packages in 2s

78 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

この状態でRailsを再起動したら、エラーは出ずに立ち上がりました。(解決はしていませんが。。) コンポーネントを呼び出せるようになっているかconsole.logで確認してみると、検証画面にエラーが出ていました。

Uncaught Error: Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find package '@babel/plugin-syntax-dynamic-import' imported from /Users/username/sample_app/babel-virtual-resolve-base.js

bable.config.jsプラグインとして '@babel/plugin-syntax-dynamic-import'があるのですが、なぜbabel-virtual-resolve-base.jsを見に行っているのでしょう?

もしかして、 "@babel/preset-react"を追加した最新のpackage.jsonが読み込まれていない? package.jsonを更新するため、npm updateを実行。 結果は変わらず。。

いったんここまでとします。

既存のRailsアプリにReactを導入する

こちらの2つの記事を参考にやってみます。

既存Railsアプリに後からReactとTypeScriptを導入する - もふもふ技術部

qiita.com

$ rails webpacker:install:react
Unrecognized command "webpacker:install:react" (Rails::Command::UnrecognizedCommandError)

Webpackerをインストールしていないのが原因だと予想しました。

Webpackerをインストールして再度実行しましたが、これらのメッセージが出ました。

$ bundle exec rails webpacker:install:react
webpack binstubs not found.
Have you run rails webpacker:install ?
Make sure the bin directory or binstubs are not included in .gitignore
Exiting!

Webpackerというツールを使ってreactをインストールしようとして失敗したので、そもそもWebpackerのインストールが必要ですね。

$ bundle exec rails webpacker:install
bin/rails aborted!
Psych::AliasesNotEnabled: Alias parsing was not enabled. To enable it, pass `aliases: true` to `Psych::load` or `Psych::safe_load`. (Psych::AliasesNotEnabled)

Webpackerをインストールしようとしましたが、エイリアスの解析が不可と言われ、その原因はPsychというツールにあるようです。 いくつか記事を見ましたが、それらを読んでも理解できなかったので、Webpackerを使う以外の方法はないか?と考え公式ドキュメントを見に行きました。

https://github.com/reactjs/react-rails/blob/master/docs/get-started.md

WebpackerではなくShakapackerを使ってインストールしていたので、それを試してみました。

$ rails shakapacker:install
bin/rails aborted!
Psych::AliasesNotEnabled: Alias parsing was not enabled. To enable it, pass `aliases: true` to `Psych::load` or `Psych::safe_load`. (Psych::AliasesNotEnabled)

またさっきと同じ問題が出ました。どうやらReactを導入するにはPsychの問題を避けては通れなそうです。

そもそもPsychとは何でしょうか?Rubyのライブラリでした。YAMLの内容を読み取ったりするために用いるようです。

library psych (Ruby 3.3 リファレンスマニュアル)

Psychのエイリアスが使用不可です。エイリアスの解析が不可となっています。と言われました。

shakapackerの設定ファイルはconfig/shakapacker.ymlというYAMLファイルです。
PsychでこのYAMLファイルが読み込めないのが問題なのかと推測しました。
しかし、Psychライブラリによる解析は自動で行われているため、Psych::loadをどこで設定すればいいのかわかりません。

何か試してみようと思い、 GemfileからWebpackerを削除してからshakapackerインストールを再実行してみるとエラーが変わりました。

$ ./bin/rails shakapacker:install
bin/rails aborted!
Shakapacker::Utils::Manager::Error: You don't have "packageManager" set in your package.json (Shakapacker::Utils::Manager::Error)
meaning that Shakapacker will use npm but you've got a yarn.lock
file meaning you probably want to be using yarn instead.

あなたはpackage.jsonの中で"packageManager" を持っていません。 package.jsonはnpmを使おうとしていますが、あなたはyarn.lockを持っていません。 あなたは代わりにyarnを使うことになるでしょう。

ローカルにnpmやyarnがインストールされているかを確認したところ、どっちも入っていました。

$ npm -v
8.19.2
[~]$ yarn -v
1.22.5

ローカルマシンには入っているけど、Railsアプリケーションに入っていないのが問題ということでしょうか?

Railsアプリ内のルートディレクトリにpackage.jsonファイルが存在し、中身はこのようになっていました。

{
  "dependencies": {}
}

この記事によって、package.jsonはGemfileのようなものだと知りました。
第 17 章 Rails フロントエンド環境セットアップ | frontend-training

Rubyのライブラリをインストールする場合、Gemfileに追加してからbundle installを実行しますよね?
そしてその結果、追加されたライブラリがGemfile.lockに出力されますよね。

それと同じ仕組みなら、package.jsonに書いておいて、インストールが実行されたらyarn.lockに出力されるのだろうと想像しました。

この記事によって、package.jsonが存在するディレクトリでnpm installを実行すると、dependenciesに書かれたパッケージがnode_modulesディレクトリにインストールされると知りました。
【いまさらですが】package.jsonのdependenciesとdevDependencies #JavaScript - Qiita

同記事に、package.jsonのdependenciesにパッケージマネージャーを追加するコマンドも載っていたので実行してみました。

$ npm install --save yarn@1.22.5

added 1 package, and audited 2 packages in 595ms

1 high severity vulnerability

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

実行した結果、packege.jsonにyarnが追加されました。

{
  "dependencies": {
    "yarn": "^1.22.5"
  }
}

1件重大な脆弱性があると言われました。 この問題に対処するには、npm auditを実行しなさいとも。

この記事によれば、npm audit脆弱性を治すわけではなく、どんな脆弱性なのか、どのレベルの脆弱性なのかを調べてその結果を表示してくれるコマンドだと知りました。
【npm audit】npm package の脆弱性対応してますか?

$ npm audit
# npm audit report

yarn  <1.22.13
Severity: high
Yarn untrusted search path vulnerability - https://github.com/advisories/GHSA-mpwj-fcr6-x34c
fix available via `npm audit fix`
node_modules/yarn

1 high severity vulnerability

To address all issues, run:
  npm audit fix

Yarn の信頼できない検索パスの脆弱性と出てきました。 言われたとおりにnpm audit fixを実行してみます。 脆弱性を修正してくれたようです。

$ npm audit fix

changed 1 package, and audited 2 packages in 1s

found 0 vulnerabilities

これで脆弱性の問題も解消し、Railsにyarnをインストールしたので、今度こそ./bin/rails shakapacker:installが成功するでしょうか? おおお、成功しました!!!

$ ./bin/rails shakapacker:install
       apply  /Users/naota7118/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/shakapacker-8.0.0/lib/install/template.rb
      create    config/shakapacker.yml
  Copying webpack core config
       exist    config/webpack
      create    config/webpack/webpack.config.js
  The packs app source directory already exists
       apply    /Users/naota7118/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/shakapacker-8.0.0/lib/install/binstubs.rb
    Copying binstubs
       exist      bin
      create      bin/shakapacker
      create      bin/shakapacker-dev-server
      create      bin/yarn
      append    .gitignore
  Add JavaScript include tag in application layout
      insert    app/views/layouts/application.html.erb
  Run npm install during bin/setup
      insert    bin/setup
  Installing shakapacker@8.0.0

added 462 packages, and audited 464 packages in 16s
続きを読む

【バグ修正】得点が1つズレて出力される

見つかったバグ

結果表示画面を見ると、1つズレて出力されてしまっています。
本来2つの4は、1つ前の人の最後の行に入るべきところが、次の人の最初の行に入ってしまっています。

現状の確認

全員分の得点を1つの配列に格納したあと、11ずつ区切って1人ごとに分けていました。
11ずつ区切る理由は、1人分のデータが11項目を持つためです。
現在、11ずつ区切ると1つ余りが出てしまっています。

よく見るとという得点以外のデータが入っています。
本来、数値または読みとり不可しか許可していないので、これらはバグです。

convert_one_into_slashメソッドの処理を通した後は、

各行が要素にスラッシュ/を持つ配列またはnilのいずれかを返します。

そもそもnilだったら返さないようにしました。

なぜ「題」「い」が入ったのか?

紙には0/1と書かれているところが、0が読み取られず/1となり、他の文字列と結合する形で出力されていました。

この場合は得点を読み取れていないので、読みとり不可と出力させるようにしました。

正規表現で「0〜6の数値以外+スラッシュ+0〜6の数値」に該当した場合は読みとり不可と出力させるようにしました。

"調べる文字列".match?(/^[0-9]\/[0-6])

関数名の変更によるバグの修正

はじめに

実務未経験からWebエンジニアを目指して転職活動中です。
企業からサイト経由でコードの可読性が悪いとフィードバックいただいたので、ポートフォリオリファクタリングを行っています。

問題

変数名が適切ではなかったので変更したところ、バグが生じました。
具体的には、pdf_datapdf_scores, excel_dataexcel_scoresに変数名を変更しました。
理由としては、そもそも変数がデータを示すのは当たり前なので、〇〇_dataは変数名としてふさわしくないと考えたからです。

その結果、ビューに結果が表示されなくなりました。

試したこと①

何が起きているかわからなかったので、照合処理を行う関数でデバッグを行いました。 具体的には、照合処理を行うverify_suject_id関数の最後にbinding.pryを挿入し、@result_dataの中身を確認しました。

verify_suject_idという関数名も挙動と一致していないので修正すべきだと気づきました。

原因

pry(#<MocaDataController>)> @result_data
=> []

@result_dataというインスタンス変数でコントローラからビューにデータの受け渡しをしているのですが、照合処理した結果が@result_dataに入っていませんでした。
つまり、変数名を変更したら、今まで正常に動いていた関数で異常な動きをするようになりました。

試したこと②

verify_suject_idは2つの引数を取っているのですが、この引数でデータを受け取れているか確認しました。

def verify_suject_id(pdf_scores, excel_scores)
end
[1] pry(#<MocaDataController>)> pdf_scores
=> []
[2] pry(#<MocaDataController>)> excel_scores
=> []

確認したところ、どちらもデータを受け取れていませんでした。

ということは、verify_suject_id関数が実行される前に、どこかでデータの受け渡しができていないということになります。

コントローラでは、結果を返すresult関数の中で順番に関数を呼び出して処理を行っています。

def result
    pass_authentication
    return if performed?

    convert(@drive)
    get_scores_from_text
    export_to_excel(@pdf_scores)
    get_scores_from_excel

    verify_suject_id(@pdf_scores, @excel_scores)
    delete_files
  end

現時点でわかっているのは、verify_suject_id(@pdf_scores, @excel_scores)を呼び出した時点で、@pdf_scores@excel_scoresにはデータが受け渡せていないということです。

@pdf_scores がどこから渡ってきているかというと、get_scores_from_text関数です。 get_scores_from_text関数の最後にbinding.pryを挿入してデバッグしたところ、ここでも空でした。

get_scores_from_text関数は、指定したパスに保存したテキストファイルから得点を取得する処理を行います。
フォームから送信したPDFから得点が抽出されテキストファイルに保存されていることを確認しました。

ここまでのことから、テキストファイルから@pdf_scoresに得点を格納する処理でバグが発生していると推測しました。 しかし、その処理結果は正しく返ってきていました。

今回のバグの原因

関数名を変更する前のコードと見比べたところ、今回新たな関数名として使ったpdf_scoresはすでに使っていたことがわかりました。

def get_scores_from_text
    @pdf_scores = []
    
    # ここでテキストファイルから得点を抽出する処理が行われる

    @pdf_data = []
    @pdf_scores.each_slice(11) { |subject| @pdf_data << subject }
end

@pdf_scores配列を用意しておいて、そこに全員分の得点を格納し、
その後決まった数ごとに分割しているのでした。

変更前は@pdf_dataに全員分の得点が入っていました。 今回@pdf_data@pdf_scoresと変更してしまったことで、
得点を抽出する処理を行なったあとで@pdf_scores = []を実行し、@pdf_scores配列を初期化してしまっていました。

もともと@pdf_scoresとしていた箇所を別の変数名にすればよさそうです。 全員分の得点を格納する変数なので@all_pdf_scoresとしました。

これで無事バグを修正できました。