From e3900e2a44250aca6e67d09c3d6857887ddc8459 Mon Sep 17 00:00:00 2001 From: Riza Fahmi Date: Fri, 6 Jun 2025 07:10:49 +0700 Subject: [PATCH 1/5] Add GCP service account configuration to refresh expired token using Goth --- .env-example | 1 + .gitignore | 2 ++ lib/coderacer/ai.ex | 3 ++- lib/coderacer/application.ex | 9 +++++++++ mix.exs | 3 ++- mix.lock | 6 ++++-- 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.env-example b/.env-example index 01455bc..8d054ae 100644 --- a/.env-example +++ b/.env-example @@ -3,3 +3,4 @@ DATABASE_PATH=./coderacer.db SECRET_KEY_BASE=run `mix phx.gen.secret` to get the key PHX_HOST=127.0.0.1 VERTEX_API_KEY= +GCP_SERVICE_ACCOUNT_JSON=generate using gcloud auth login diff --git a/.gitignore b/.gitignore index b02b275..9d4bc86 100644 --- a/.gitignore +++ b/.gitignore @@ -50,5 +50,7 @@ npm-debug.log .amazonq AmazonQ.md +# Credentials id_coderacer id_coderacer.pub +application_default_credentials.json diff --git a/lib/coderacer/ai.ex b/lib/coderacer/ai.ex index ffa81b3..11d42b8 100644 --- a/lib/coderacer/ai.ex +++ b/lib/coderacer/ai.ex @@ -56,12 +56,13 @@ defmodule Coderacer.AI do "https://#{api_endpoint}/v1/projects/#{project_id}/locations/#{location_id}/publishers/google/models/#{model_id}:#{generate_content_api}" http_client = Application.get_env(:coderacer, :http_client, Req) + token = Goth.fetch!(Coderacer.Goth) http_client.post!( url, headers: [ {"Content-Type", "application/json"}, - {"Authorization", "Bearer #{System.get_env("VERTEX_API_KEY")}"} + {"Authorization", "Bearer #{token.token}"} ], json: %{ contents: [ diff --git a/lib/coderacer/application.ex b/lib/coderacer/application.ex index b6253da..4e7f9ec 100644 --- a/lib/coderacer/application.ex +++ b/lib/coderacer/application.ex @@ -7,6 +7,14 @@ defmodule Coderacer.Application do @impl true def start(_type, _args) do + credentials = + "GCP_SERVICE_ACCOUNT_JSON" + |> System.fetch_env!() + |> File.read!() + |> Jason.decode!() + + source = {:refresh_token, credentials} + children = [ CoderacerWeb.Telemetry, Coderacer.Repo, @@ -14,6 +22,7 @@ defmodule Coderacer.Application do repos: Application.fetch_env!(:coderacer, :ecto_repos), skip: skip_migrations?()}, {DNSCluster, query: Application.get_env(:coderacer, :dns_cluster_query) || :ignore}, {Phoenix.PubSub, name: Coderacer.PubSub}, + {Goth, name: Coderacer.Goth, source: source}, # Start a worker by calling: Coderacer.Worker.start_link(arg) # {Coderacer.Worker, arg}, # Start to serve requests, typically the last entry diff --git a/mix.exs b/mix.exs index e1ed4f3..6e8425b 100644 --- a/mix.exs +++ b/mix.exs @@ -78,7 +78,8 @@ defmodule Coderacer.MixProject do {:bandit, "~> 1.5"}, {:dialyxir, "~> 1.3", only: [:dev], runtime: false}, {:credo, "~> 1.7", only: [:dev], runtime: false}, - {:tidewave, "~> 0.1.7", only: [:dev]} + {:tidewave, "~> 0.1.7", only: [:dev]}, + {:goth, "1.4.5"} ] end diff --git a/mix.lock b/mix.lock index f295dfd..4217a3b 100644 --- a/mix.lock +++ b/mix.lock @@ -15,13 +15,15 @@ "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"}, - "exqlite": {:hex, :exqlite, "0.30.1", "a85ed253ab7304c3733a74d3bc62b68afb0c7245ce30416aa6f9d0cfece0e58f", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "15714871147d8d6c12be034013d351ce670e02c09b7f49accabb23e9290d80a0"}, + "exqlite": {:hex, :exqlite, "0.31.0", "bdf87c618861147382cee29eb8bd91d8cfb0949f89238b353d24fa331527a33a", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "df352de99ba4ce1bac2ad4943d09dbe9ad59e0e7ace55917b493ae289c78fc75"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, "floki": {:hex, :floki, "0.37.1", "d7aaee758c8a5b4a7495799a4260754fec5530d95b9c383c03b27359dea117cf", [:mix], [], "hexpm", "673d040cb594d31318d514590246b6dd587ed341d3b67e17c1c0eb8ce7ca6f04"}, + "goth": {:hex, :goth, "1.4.5", "ee37f96e3519bdecd603f20e7f10c758287088b6d77c0147cd5ee68cf224aade", [:mix], [{:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:jose, "~> 1.11", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "0fc2dce5bd710651ed179053d0300ce3a5d36afbdde11e500d57f05f398d5ed5"}, "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]}, "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, @@ -31,7 +33,7 @@ "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.0", "2791fac0e2776b640192308cc90c0dbcf67843ad51387ed4ecae2038263d708d", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b3a1fa036d7eb2f956774eda7a7638cf5123f8f2175aca6d6420a7f95e598e1c"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.13", "afa8d0ee3bc111e193a170d6ce922110933f9e1f46d4a9f939cd340ee4668e42", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c69850292eeacfb4136bf8b2af1165cf257913cdaafa1f2ea9e53348841105b8"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.17", "beeb16d83a7d3760f7ad463df94e83b087577665d2acc0bf2987cd7d9778068f", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a4ca05c1eb6922c4d07a508a75bfa12c45e5f4d8f77ae83283465f02c53741e1"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.18.0", "d78df36c41f7e798f2edf1f33e1727eae438e9dd5d809a9997c463a108244042", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "819f9e176d51e44dc38132e132fe0accaf6767eab7f0303431e404da8476cfa2"}, From 060a6c0b85e500bd4d1c24da16c62c0fd4e81821 Mon Sep 17 00:00:00 2001 From: Riza Fahmi Date: Fri, 6 Jun 2025 07:14:40 +0700 Subject: [PATCH 2/5] Update docker-compose to read gcp config file --- docker-compose.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index efba074..d1107c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,10 +4,13 @@ services: container_name: coderacer_deploy ports: - "4000:4000" - volumes: - - coderacer_sqlite:/app/priv/data + environment: + GCP_SERVICE_ACCOUNT_JSON: /app/config/gcp_credentials.json env_file: - .env + volumes: + - coderacer_sqlite:/app/priv/data + - ./application_default_credentials.json:/app/config/gcp_credentials.json:ro stdin_open: true # equivalent to -it tty: true # equivalent to -it restart: "no" # equivalent to --rm (no restart) From 746716d44ec3c5192f77080eb08884ec112e629f Mon Sep 17 00:00:00 2001 From: Riza Fahmi Date: Fri, 6 Jun 2025 07:18:10 +0700 Subject: [PATCH 3/5] Enhance GCP service account handling in application and CI workflow --- .github/workflows/elixir.yml | 2 ++ lib/coderacer/application.ex | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 1a73ab6..0a625fc 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -36,4 +36,6 @@ jobs: - name: Install dependencies run: mix deps.get - name: Run tests + env: + GCP_SERVICE_ACCOUNT_JSON: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }} run: mix test diff --git a/lib/coderacer/application.ex b/lib/coderacer/application.ex index 4e7f9ec..17c61ce 100644 --- a/lib/coderacer/application.ex +++ b/lib/coderacer/application.ex @@ -7,10 +7,14 @@ defmodule Coderacer.Application do @impl true def start(_type, _args) do + gcp_json_or_path = System.fetch_env!("GCP_SERVICE_ACCOUNT_JSON") + credentials = - "GCP_SERVICE_ACCOUNT_JSON" - |> System.fetch_env!() - |> File.read!() + if File.exists?(gcp_json_or_path) do + File.read!(gcp_json_or_path) + else + gcp_json_or_path + end |> Jason.decode!() source = {:refresh_token, credentials} From 2e176a22645cc18671c9512799448e3720bb7456 Mon Sep 17 00:00:00 2001 From: Riza Fahmi Date: Fri, 6 Jun 2025 07:27:22 +0700 Subject: [PATCH 4/5] Add debug step for GCP_SERVICE_ACCOUNT_JSON in CI workflow --- .github/workflows/elixir.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 0a625fc..d3f33cd 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -39,3 +39,7 @@ jobs: env: GCP_SERVICE_ACCOUNT_JSON: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }} run: mix test + - name: Debug GCP_SERVICE_ACCOUNT_JSON + run: echo "${GCP_SERVICE_ACCOUNT_JSON:0:20}..." + env: + GCP_SERVICE_ACCOUNT_JSON: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }} From 7c952e6acf404ebcc792a5729fdea9b209811059 Mon Sep 17 00:00:00 2001 From: Riza Fahmi Date: Fri, 6 Jun 2025 07:34:35 +0700 Subject: [PATCH 5/5] Remove debug step for GCP_SERVICE_ACCOUNT_JSON from CI workflow --- .github/workflows/elixir.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index d3f33cd..0a625fc 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -39,7 +39,3 @@ jobs: env: GCP_SERVICE_ACCOUNT_JSON: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }} run: mix test - - name: Debug GCP_SERVICE_ACCOUNT_JSON - run: echo "${GCP_SERVICE_ACCOUNT_JSON:0:20}..." - env: - GCP_SERVICE_ACCOUNT_JSON: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }}