diff --git a/README.md b/README.md index 38063d0..47cff59 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ MONEYFORWARD_PASSWORD=Passw0rd! YNAB_ACCESS_TOKEN=abunchofcharacters ``` + + Alternatively, you can also use something like [1Password's CLI](https://developer.1password.com/docs/cli/) to completely avoid storing clear secrets: @@ -90,6 +92,44 @@ op run --env-file=.env -- mfynab mfynab-david.yml After checking out the repo, run `bundle install` to install dependencies. Then, run `bin/rake test` to run the tests. +## Running MFYNAB with cron and Docker + +The `docker-example/` directory contains sample files that'll help you schedule MFYNAB inside a Docker container. + +See the comments in each file for more details on how it works. + +First you'll want to bring your MFYNAB configuration file into this directory: + +```sh +cp path_to/config.yml docker_example/ +``` + +Then you can build the Docker image: + +```sh +docker build -t mfynab docker_example/ +``` + +Finally, you can run the Docker image. Note that you need to pass secrets as environment variables: + +```sh +docker run -d \ + --env YNAB_ACCESS_TOKEN=... \ + --env MONEYFORWARD_USERNAME=... \ + --env MONEYFORWARD_PASSWORD='...' \ + --name mfynab mfynab +``` + +You can also use the 1Password CLI ([documented here](#one-password-cli)) for this step: + +```sh +op run --env-file=.env -- sh -c 'docker run -d \ + --env YNAB_ACCESS_TOKEN=$YNAB_ACCESS_TOKEN \ + --env MONEYFORWARD_USERNAME=$MONEYFORWARD_USERNAME \ + --env MONEYFORWARD_PASSWORD="$MONEYFORWARD_PASSWORD" \ + --name mfynab mfynab' +``` + ## Roadmap ### Deploy/Automate @@ -135,3 +175,4 @@ Previous notes: ``` - Passing logger everywhere feels weird. - Prompt user for captcha and other account extra authentication required by Money Forward? +- One might want to run a single Docker instance for multiple users, but the current setup does not allow that easily. We'll want to bring the secret environment variables into the config file, making it possible to assign them to a given "user", and name them accordingly. diff --git a/docker_example/.gitignore b/docker_example/.gitignore new file mode 100644 index 0000000..389d6ad --- /dev/null +++ b/docker_example/.gitignore @@ -0,0 +1,2 @@ +config.yml +Gemfile.lock diff --git a/docker_example/Dockerfile b/docker_example/Dockerfile new file mode 100644 index 0000000..8fbf692 --- /dev/null +++ b/docker_example/Dockerfile @@ -0,0 +1,19 @@ +FROM ruby:3.4 + +# Install cron and chromium +RUN apt-get update && apt-get install -y cron chromium + +WORKDIR /app + +# Copy Gemfile first and install gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install + +# Copy the rest of the application (this two-step process helps with layer caching) +COPY . . + +# Add cron job +RUN crontab /app/mfynab-cron + +# Run the entrypoint script by default +CMD [ "/app/entrypoint.sh" ] diff --git a/docker_example/Gemfile b/docker_example/Gemfile new file mode 100644 index 0000000..6215dbe --- /dev/null +++ b/docker_example/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "mfynab" diff --git a/docker_example/entrypoint.sh b/docker_example/entrypoint.sh new file mode 100755 index 0000000..eae6f8c --- /dev/null +++ b/docker_example/entrypoint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +# Save environment variables to a file, so we can forward them to cron jobs +env >> /etc/environment + +# Run cron in the foreground +cron -f diff --git a/docker_example/mfynab-cron b/docker_example/mfynab-cron new file mode 100644 index 0000000..7274733 --- /dev/null +++ b/docker_example/mfynab-cron @@ -0,0 +1,10 @@ +# Pick up environment variables that were forwarded when starting the container +SHELL=/bin/bash +BASH_ENV=/etc/environment + +# See: +# https://www.reddit.com/r/docker/comments/mzqibu/how_to_correctly_run_cron_in_docker/ + +# Run MFYNAB at 6am, 12pm, and 9pm JST every day +# FIXME: I'd like to be able to use UTC+9 times +0 3,9,21 * * * cd /app; bundle exec mfynab config.yml >/proc/1/fd/1 2>/proc/1/fd/2 diff --git a/lib/mfynab/money_forward/account_status.rb b/lib/mfynab/money_forward/account_status.rb index 7c74028..46c9388 100644 --- a/lib/mfynab/money_forward/account_status.rb +++ b/lib/mfynab/money_forward/account_status.rb @@ -67,7 +67,7 @@ def parse_html_table_row(node) end end - FRESHNESS_LIMIT = 24 * 60 * 60 # 1 day + FRESHNESS_LIMIT = 60 * 60 # 1 hour attr_reader :id_hash, :name, :raw_status, :updated_at, :key, :message diff --git a/lib/mfynab/money_forward/session.rb b/lib/mfynab/money_forward/session.rb index d83d375..7ed3b7c 100644 --- a/lib/mfynab/money_forward/session.rb +++ b/lib/mfynab/money_forward/session.rb @@ -79,6 +79,9 @@ def with_ferrum timeout: 30, process_timeout: 20, # 10s was not always enough on CI (FIXME: make configurable per env) headless: !ENV.key?("NO_HEADLESS"), + # FIXME: this was needed to be able to run Chromium headless + # within Docker as root, but I'd rather not rely on it. + browser_options: { "no-sandbox": nil }, ) user_agent = browser.default_user_agent.sub("HeadlessChrome", "Chrome") browser.headers.add({