├── .tool-versions ├── run.sh ├── bin ├── test ├── setup └── console ├── .gitignore ├── lib ├── home_maintenance │ ├── version.rb │ ├── errors.rb │ ├── time_framer.rb │ └── task.rb └── home_maintenance.rb ├── test ├── test_helper.rb ├── lib │ └── home_maintenance │ │ ├── task_test.rb │ │ └── time_framer_test.rb └── home_maintenance_test.rb ├── Gemfile ├── Rakefile ├── Dockerfile ├── .github └── workflows │ └── ci.yml ├── CHANGELOG.md ├── action.yml ├── LICENSE ├── Gemfile.lock ├── README.md ├── CODE_OF_CONDUCT.md └── data └── tasks.csv /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 2.6.3 2 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd / 3 | bundle exec ruby /lib/home_maintenance.rb $* 4 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle exec rake 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | -------------------------------------------------------------------------------- /lib/home_maintenance/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class HomeMaintenance 4 | VERSION = '0.3.0' 5 | end 6 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.unshift File.expand_path('../lib', __dir__) 4 | require 'home_maintenance' 5 | require 'minitest/autorun' 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'octokit', '~> 4.21' 6 | 7 | group :test do 8 | gem 'minitest', '~> 5.0' 9 | gem 'rake', '~> 12.0' 10 | end 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rake/testtask' 4 | 5 | Rake::TestTask.new(:test) do |t| 6 | t.libs << 'test' 7 | t.libs << 'lib' 8 | t.test_files = FileList['test/**/*_test.rb'] 9 | end 10 | 11 | task default: :test 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.6 2 | 3 | # throw errors if Gemfile has been modified since Gemfile.lock 4 | RUN bundle config --global frozen 1 5 | RUN bundle config set without 'test' 6 | 7 | COPY Gemfile Gemfile.lock ./ 8 | RUN gem install bundler:2.1.4 9 | RUN bundle version 10 | RUN bundle install 11 | 12 | COPY . . 13 | 14 | ENTRYPOINT ["/run.sh"] 15 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'bundler/setup' 4 | require_relative '../lib/home_maintenance' 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require 'irb' 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | test: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Ruby 17 | uses: ruby/setup-ruby@57d46d78b7d959dc4a248024a29bb17d4e357e5c 18 | with: 19 | ruby-version: 2.6 20 | - name: Install dependencies 21 | run: bundle install --jobs 4 --retry 3 22 | - name: Run tests 23 | run: bin/test 24 | -------------------------------------------------------------------------------- /lib/home_maintenance/errors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Home to all custom exceptions 4 | class HomeMaintenance 5 | class Error < StandardError; end 6 | 7 | # When the input to the Action is invalid 8 | class InvalidInputError < Error 9 | def initialize(msg = 'Action config is invalid') 10 | super 11 | end 12 | end 13 | 14 | # When the calculated path to the CSV file cannot be found 15 | class CSVReadError < Error 16 | def initialize(msg = 'Unable to find CSV file of tasks') 17 | super 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | ### Fixed 13 | 14 | ## [0.3.0] - 2021-08-29 15 | 16 | ### Added 17 | 18 | - Allow users to pass in custom csv files for tasks, throw better errors in the case of misconfiguration. https://github.com/maxbeizer/home_maintenance/pull/3 19 | 20 | ### Changed 21 | -------------------------------------------------------------------------------- /lib/home_maintenance/time_framer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class HomeMaintenance 4 | # Given a task, figure out if it what whether it's in a given time frame 5 | class TimeFramer 6 | # name => [day, month] 7 | SEASON_START_MAP = { 8 | spring: [20, 3], 9 | summer: [20, 6], 10 | fall: [22, 9], 11 | winter: [21, 12] 12 | }.freeze 13 | 14 | def initialize(task, today = Date.today) 15 | @task = task 16 | @today = today 17 | end 18 | 19 | def matches? 20 | season_start == [@today.day, @today.month] 21 | end 22 | 23 | private 24 | 25 | def season_start 26 | SEASON_START_MAP[@task.season.downcase.to_sym] 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'home_maintenance' 2 | description: 'Create Issues for various seasonal home maintenance tasks' 3 | inputs: 4 | repo-nwo: 5 | description: 'the owner and name of the repo where issues will be created, e.g. maxbeizer/home_maintenance' 6 | required: true 7 | github-token: 8 | description: 'the github_token from the action' 9 | required: true 10 | path-to-data: 11 | description: 'the path to a csv of tasks' 12 | required: false 13 | time-frame: 14 | description: 'seasonal or all tasks should be converted to issues' 15 | required: false 16 | runs: 17 | using: 'docker' 18 | image: 'Dockerfile' 19 | args: 20 | - ${{ inputs.github-token }} 21 | - ${{ inputs.repo-nwo }} 22 | - ${{ inputs.path-to-data }} 23 | - ${{ inputs.time-frame }} 24 | -------------------------------------------------------------------------------- /lib/home_maintenance/task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class HomeMaintenance 4 | # Given a row, build a task object 5 | class Task 6 | HEADERS = ['Season', 'Area', 'Task Type', 'Task'].freeze 7 | 8 | attr_reader :season, 9 | :area, 10 | :task_type, 11 | :task_name 12 | 13 | def initialize(row) 14 | @row = row 15 | end 16 | 17 | def call 18 | return if @row == HEADERS 19 | 20 | HEADERS.zip(@row).each do |(header, cell)| 21 | header_munged = header.downcase.sub(/\s/, '_') 22 | 23 | if header_munged == 'task' 24 | instance_variable_set("@#{header_munged}_name", cell) 25 | next 26 | end 27 | 28 | instance_variable_set("@#{header_munged}", cell) 29 | end 30 | 31 | self 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /test/lib/home_maintenance/task_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class HomeMaintenance 6 | class TaskTest < Minitest::Test 7 | def test_it_returns_nil_for_the_headers 8 | subject = subject_class.new(subject_class::HEADERS) 9 | assert_nil subject.call, 'Expected headers as input to return nil' 10 | end 11 | 12 | def test_it_returns_the_headers_instace_variables 13 | result = subject_class.new([ 14 | 'Fall', 15 | 'Appliances', 16 | 'Cleaning', 17 | 'Clean AC condensor' 18 | ]).call 19 | 20 | assert_equal 'Fall', result.season 21 | assert_equal 'Appliances', result.area 22 | assert_equal 'Cleaning', result.task_type 23 | assert_equal 'Clean AC condensor', result.task_name 24 | end 25 | 26 | private 27 | 28 | def subject_class 29 | HomeMaintenance::Task 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Max Beizer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.0) 5 | public_suffix (>= 2.0.2, < 5.0) 6 | faraday (1.7.0) 7 | faraday-em_http (~> 1.0) 8 | faraday-em_synchrony (~> 1.0) 9 | faraday-excon (~> 1.1) 10 | faraday-httpclient (~> 1.0.1) 11 | faraday-net_http (~> 1.0) 12 | faraday-net_http_persistent (~> 1.1) 13 | faraday-patron (~> 1.0) 14 | faraday-rack (~> 1.0) 15 | multipart-post (>= 1.2, < 3) 16 | ruby2_keywords (>= 0.0.4) 17 | faraday-em_http (1.0.0) 18 | faraday-em_synchrony (1.0.0) 19 | faraday-excon (1.1.0) 20 | faraday-httpclient (1.0.1) 21 | faraday-net_http (1.0.1) 22 | faraday-net_http_persistent (1.2.0) 23 | faraday-patron (1.0.0) 24 | faraday-rack (1.0.0) 25 | minitest (5.14.4) 26 | multipart-post (2.1.1) 27 | octokit (4.21.0) 28 | faraday (>= 0.9) 29 | sawyer (~> 0.8.0, >= 0.5.3) 30 | public_suffix (4.0.6) 31 | rake (12.3.3) 32 | ruby2_keywords (0.0.5) 33 | sawyer (0.8.2) 34 | addressable (>= 2.3.5) 35 | faraday (> 0.8, < 2.0) 36 | 37 | PLATFORMS 38 | ruby 39 | 40 | DEPENDENCIES 41 | minitest (~> 5.0) 42 | octokit (~> 4.21) 43 | rake (~> 12.0) 44 | 45 | BUNDLED WITH 46 | 2.1.4 47 | -------------------------------------------------------------------------------- /test/home_maintenance_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class HomeMaintenanceTest < Minitest::Test 6 | def test_that_it_has_a_version_number 7 | refute_nil ::HomeMaintenance::VERSION 8 | end 9 | 10 | def test_it_returns_a_list_of_task_like_objects 11 | subject_class.call(time_frame: :all, issue_client: MockIssueClient, logger: noop).each do |res| 12 | assert_respond_to res, :season 13 | assert_respond_to res, :area 14 | assert_respond_to res, :task_type 15 | assert_respond_to res, :task_name 16 | end 17 | end 18 | 19 | # NOTE: this is dependent on the actual data in the CSV 20 | def test_it_returns_data_as_expected 21 | result = subject_class.call(time_frame: :all, issue_client: MockIssueClient, logger: noop).first 22 | assert_equal 'Fall', result.season 23 | assert_equal 'Appliances', result.area 24 | assert_equal 'Cleaning', result.task_type 25 | assert_equal 'Clean AC condensor', result.task_name 26 | end 27 | 28 | def test_it_errors_if_octokit_is_there_but_nwo_is_falsey 29 | assert_raises HomeMaintenance::InvalidInputError, 'expected octokit + no repo_nwo to raise' do 30 | subject_class.call(logger: noop) 31 | end 32 | end 33 | 34 | def test_it_errors_if_octokit_is_there_but_nwo_is_empty 35 | assert_raises HomeMaintenance::InvalidInputError, 'expected octokit + no repo_nwo to raise' do 36 | subject_class.call(logger: noop, repo_nwo: '') 37 | end 38 | end 39 | 40 | def test_it_errors_if_time_frame_is_invalid 41 | assert_raises HomeMaintenance::InvalidInputError, 'expected octokit + no repo_nwo to raise' do 42 | subject_class.call(time_frame: 'lololol', issue_client: MockIssueClient, logger: noop) 43 | end 44 | end 45 | 46 | def test_it_errors_if_csv_does_not_exist 47 | assert_raises HomeMaintenance::CSVReadError, 'expected raise with bad CSV path' do 48 | subject_class.call(logger: noop, path_to_data: 'lol/wut.csv', repo_nwo: 'maxbeizer/home_maintenenace') 49 | end 50 | end 51 | 52 | private 53 | 54 | class MockIssueClient 55 | def self.create_issue(*args); end 56 | end 57 | 58 | def subject_class 59 | HomeMaintenance 60 | end 61 | 62 | def noop 63 | proc { true } 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /test/lib/home_maintenance/time_framer_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class HomeMaintenance 6 | class TimeFramerTest < Minitest::Test 7 | def test_returns_true_when_spring_task_is_start_of_spring 8 | task = Task.new(['Spring']).call 9 | start_of_spring = Date.new(2021, 3, 20) 10 | 11 | assert subject_class.new(task, start_of_spring).matches? 12 | end 13 | 14 | def test_returns_false_when_spring_task_is_not_at_start_of_spring 15 | task = Task.new(['Spring']).call 16 | not_start_of_spring = Date.new(2021, 3, 21) 17 | 18 | refute subject_class.new(task, not_start_of_spring).matches? 19 | end 20 | 21 | def test_returns_true_when_summer_task_is_start_of_summer 22 | task = Task.new(['Summer']).call 23 | start_of_summer = Date.new(2021, 6, 20) 24 | 25 | assert subject_class.new(task, start_of_summer).matches? 26 | end 27 | 28 | def test_returns_false_when_summer_task_is_not_at_start_of_summer 29 | task = Task.new(['Summer']).call 30 | not_start_of_summer = Date.new(2021, 6, 21) 31 | 32 | refute subject_class.new(task, not_start_of_summer).matches? 33 | end 34 | 35 | def test_returns_true_when_fall_task_is_start_of_fall 36 | task = Task.new(['Fall']).call 37 | start_of_fall = Date.new(2021, 9, 22) 38 | 39 | assert subject_class.new(task, start_of_fall).matches? 40 | end 41 | 42 | def test_returns_false_when_fall_task_is_not_at_start_of_fall 43 | task = Task.new(['Fall']).call 44 | not_start_of_fall = Date.new(2021, 9, 21) 45 | 46 | refute subject_class.new(task, not_start_of_fall).matches? 47 | end 48 | 49 | def test_returns_true_when_winter_task_is_start_of_winter 50 | task = Task.new(['Winter']).call 51 | start_of_winter = Date.new(2021, 12, 21) 52 | 53 | assert subject_class.new(task, start_of_winter).matches? 54 | end 55 | 56 | def test_returns_false_when_winter_task_is_not_at_start_of_winter 57 | task = Task.new(['Winter']).call 58 | not_start_of_winter = Date.new(2021, 12, 20) 59 | 60 | refute subject_class.new(task, not_start_of_winter).matches? 61 | end 62 | 63 | private 64 | 65 | def subject_class 66 | HomeMaintenance::TimeFramer 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # home_maintenance 2 | 3 | A project to help create automated issues related to home maintenance tasks. 4 | 5 | This is a GitHub Action that will create issues in a specified repo for various 6 | seasonal home maintenance tasks. Install the action where you keep track of such 7 | things and have it run daily. The action will loop through a CSV of various 8 | tasks and open, for example, springtime tasks on the first day of spring. 9 | 10 | ## Example configuration 11 | 12 | ```yaml 13 | # in .github/workflows/home_maintenance.yml 14 | on: 15 | schedule: 16 | # * is a special character in YAML so you have to quote this string 17 | # Run daily at 0100h 18 | - cron: '0 1 * * *' 19 | 20 | jobs: 21 | maintenance: 22 | runs-on: ubuntu-latest 23 | name: create maintenance issues 24 | steps: 25 | # Assumes you want to supply your own csv from your destination repo. 26 | # Check the tasks.csv file in this repo for required structure. 27 | # If you want to use the tasks from this repo, no need to checkout and 28 | # omit the path-to-data input below. 29 | - name: Checkout 30 | uses: actions/checkout@v2 31 | - name: home_maintenance_action 32 | uses: maxbeizer/home_maintenance@main 33 | with: 34 | repo-nwo: 'maxbeizer/my-cool-repo' 35 | github-token: ${{ secrets.GITHUB_TOKEN }} 36 | path-to-data: 'data/tasks.csv' 37 | ``` 38 | 39 | ## Credit 40 | 41 | The CSV of tasks was originally shared with me by 42 | [@jjcaine](https://github.com/jjcaine) :sparkling_heart: 43 | 44 | ## Development 45 | 46 | After checking out the repo, run `bin/setup` to install dependencies. Then, run 47 | `bin/test` to run the tests. You can also run `bin/console` for an interactive 48 | prompt that will allow you to experiment. 49 | 50 | ## Contributing 51 | 52 | Bug reports and pull requests are welcome on GitHub at 53 | https://github.com/maxbeizer/home_maintenance. This project is intended to be a 54 | safe, welcoming space for collaboration, and contributors are expected to adhere 55 | to the [code of conduct](https://github.com/maxbeizer/home_maintenance/blob/master/CODE_OF_CONDUCT.md). 56 | 57 | ## Code of Conduct 58 | 59 | Everyone interacting in the HomeMaintenance project's codebases, issue trackers, 60 | chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/maxbeizer/home_maintenance/blob/master/CODE_OF_CONDUCT.md). 61 | 62 | ## License 63 | 64 | MIT 65 | -------------------------------------------------------------------------------- /lib/home_maintenance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'csv' 4 | require 'octokit' 5 | 6 | require_relative './home_maintenance/version' 7 | require_relative './home_maintenance/errors' 8 | require_relative './home_maintenance/task' 9 | require_relative './home_maintenance/time_framer' 10 | 11 | # Read data on tasks. Convert them to task objects. 12 | class HomeMaintenance 13 | DEFAULT_DATA_PATH = './data/tasks.csv' 14 | ACCEPTABLE_TIME_FRAMES = %i[seasonal all].freeze 15 | 16 | def self.call(config = {}) 17 | new(**config).call 18 | end 19 | 20 | def initialize(config = {}) 21 | @issue_client = config[:issue_client] || Octokit::Client.new(access_token: config[:github_token]) 22 | @task_class = config[:task_class] || Task 23 | @time_framer_class = config[:time_framer_class] || TimeFramer 24 | @logger = config[:logger] 25 | 26 | @repo_nwo = config[:repo_nwo] 27 | @path_to_data = massage_path_to_data(config) 28 | @time_frame = (config[:time_frame] || :seasonal).to_sym 29 | @run_date = config[:run_date] || Date.today 30 | ensure_required_fields_set! 31 | end 32 | 33 | def call 34 | tasks = build_task_objects 35 | log "creating issues #{tasks.length} for: #{@repo_nwo}" 36 | tasks.map do |task| 37 | @issue_client.create_issue( 38 | @repo_nwo, 39 | task.task_name, 40 | '**created by https://github.com/maxbeizer/home_maintenance**', 41 | labels: ['home_maintenance', task.area, task.task_type].join(',') 42 | ) 43 | 44 | log "issue created: #{task.task_name}" 45 | task 46 | end 47 | end 48 | 49 | private 50 | 51 | def all? 52 | @time_frame == :all 53 | end 54 | 55 | def time_frame_matches?(task) 56 | @time_framer_class.new(task, @run_date).matches? 57 | end 58 | 59 | def build_task_objects 60 | csv = CSV.read(@path_to_data) 61 | log "CSV read at #{@path_to_data}: rows #{csv.length}" 62 | csv.each_with_object([]) do |row, acc| 63 | task = @task_class.new(row).call 64 | next unless task 65 | 66 | acc << task if all? || time_frame_matches?(task) 67 | end.compact 68 | end 69 | 70 | def ensure_required_fields_set! 71 | raise InvalidInputError, 'repo_nwo must be set!' unless valid_issue_creation_config? 72 | raise InvalidInputError, 'time_frame must be either seasonal or all' unless valid_time_frame? 73 | raise CSVReadError unless File.exist?(@path_to_data) 74 | end 75 | 76 | def valid_issue_creation_config? 77 | return true unless @issue_client.is_a?(Octokit::Client) 78 | 79 | @repo_nwo && !@repo_nwo.empty? 80 | end 81 | 82 | def valid_time_frame? 83 | ACCEPTABLE_TIME_FRAMES.include?(@time_frame) 84 | end 85 | 86 | def log(msg) 87 | @logger.call(msg) && return if @logger 88 | 89 | puts "#{Time.now.utc}: #{msg}" 90 | end 91 | 92 | def massage_path_to_data(config = {}) 93 | return DEFAULT_DATA_PATH unless config[:path_to_data] 94 | 95 | File.join(ENV.fetch('GITHUB_WORKSPACE', ''), config[:path_to_data]) 96 | end 97 | end 98 | 99 | if $PROGRAM_NAME == __FILE__ 100 | HomeMaintenance.call( 101 | github_token: ARGV[0], 102 | repo_nwo: ARGV[1], 103 | path_to_data: ARGV[2], 104 | time_frame: ARGV[3] 105 | ) 106 | end 107 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at max.beizer@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: https://contributor-covenant.org 74 | [version]: https://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /data/tasks.csv: -------------------------------------------------------------------------------- 1 | Season,Area,Task Type,Task 2 | Fall,Appliances,Cleaning,Clean AC condensor 3 | Fall,Appliances,Cleaning,Clean dryer ducts 4 | Fall,Appliances,Cleaning,Clean refrigerator coils 5 | Fall,Appliances,Cleaning,Gas Stove grate cleaning 6 | Fall,Appliances,Cleaning,Hood Vent cleaning 7 | Fall,Appliances,Weatherize,Cover AC condensor 8 | Fall,Doors,Inspection / Repair,"Inspect for wear, lubricate moving points" 9 | Fall,Exterior,Cleaning,Clean downspouts / rain gutters 10 | Fall,Exterior,Cleaning,Wash exterior windows 11 | Fall,Exterior,Inspection / Repair,Brick mortar 12 | Fall,Exterior,Inspection / Repair,Foundation for cracks 13 | Fall,Exterior,Inspection / Repair,"Inspect / Repair roof (shingles, vents, light from inside) and chimney" 14 | Fall,Exterior,Inspection / Repair,Inspect / Replace Insulation attic 15 | Fall,Exterior,Storage,Clean / Store patio furniture 16 | Fall,HVAC,Inspection / Repair,Furnace / AC inspection 17 | Fall,Plumbing,Cleaning,Clean clothes washer filter 18 | Fall,Plumbing,Cleaning,Clean dishwasher screen 19 | Fall,Plumbing,Cleaning,Clean faucet Aerators 20 | Fall,Plumbing,Cleaning,Flush Water Heater 21 | Fall,Plumbing,Weatherize,Turn off exterior water 22 | Fall,Roof,Inspection / Repair,Inspect for leaks 23 | Fall,Windows,Inspection / Repair,"Inspect for wear, lubricate moving points" 24 | Fall,Windows,Storage,Store window screens 25 | Fall,HVAC,Cleaning,Vacuum duct work 26 | Fall,Windows,Cleaning,Clean tracks and weep holes 27 | Fall,Plumbing,Inspection / Repair,Check TPR valve on water heater 28 | Fall,Doors,Inspection / Repair,Lubricate all locks 29 | Fall,Doors,Inspection / Repair,Inspect / Repair weatherstripping 30 | Fall,Windows,Inspection / Repair,Inspect / Repair weatherstripping 31 | Fall,HVAC,Cleaning,Humidifier 32 | Fall,Exterior,Inspection / Repair,"Garage - Tighten and lube hinges, clean tracks, inspect chain, balance door, test sensors, battery?" 33 | Winter,Exterior,Inspection / Repair,Inspect / Replace Insulation attic 34 | Winter,Roof,Inspection / Repair,"Inspect for leaks, damage, misalignment" 35 | Winter,Plumbing,Inspection / Repair,Check TPR valve on water heater 36 | Spring,Appliances,Cleaning,Clean dryer ducts 37 | Spring,Appliances,Cleaning,Clean refrigerator coils 38 | Spring,Appliances,Cleaning,Gas Stove grate cleaning 39 | Spring,Appliances,Cleaning,Hood Vent cleaning 40 | Spring,Appliances,Cleaning,Oven Cleaning 41 | Spring,Appliances,Weatherize,Uncover AC condensor 42 | Spring,Doors,Inspection / Repair,"Inspect for wear, lubricate moving points" 43 | Spring,Exterior,Cleaning,Chimney / Fireplace flue 44 | Spring,Exterior,Cleaning,Clean downspouts / rain gutters 45 | Spring,Exterior,Cleaning,Pressure wash 46 | Spring,Exterior,Inspection / Repair,Brick mortar 47 | Spring,Exterior,Inspection / Repair,Foundation for cracks 48 | Spring,Exterior,Inspection / Repair,Inspect / repair door weatherstripping 49 | Spring,Exterior,Inspection / Repair,Inspect / repair window weatherstripping 50 | Spring,Exterior,Inspection / Repair,Inspect / Replace Insulation attic 51 | Spring,Foundation,Inspection / Repair,Inspect foundation for leaks / cracks 52 | Spring,Interior,Cleaning,Clean kitchen walls / cabinets 53 | Spring,Plumbing,Cleaning,Clean clothes washer filter 54 | Spring,Plumbing,Cleaning,Clean dishwasher screen 55 | Spring,Plumbing,Cleaning,Clean faucet Aerators 56 | Spring,Plumbing,Cleaning,Flush Water Heater 57 | Spring,Plumbing,Weatherize,Turn on exterior water 58 | Spring,Roof,Inspection / Repair,Inspect for leaks 59 | Spring,Walkways,Cleaning,Pressure wash 60 | Spring,Walkways,Inspection / Repair,Inspect / Repair cracks 61 | Spring,Windows,Cleaning,Wash exterior windows 62 | Spring,Windows,Inspection / Repair,"Inspect for wear, lubricate moving points" 63 | Spring,Windows,Installation,Install window screens 64 | Spring,Electrical,Inspection / Repair,GFCI Outlets 65 | Spring,Windows,Cleaning,Clean tracks and weep holes 66 | Spring,Plumbing,Inspection / Repair,Check TPR valve on water heater 67 | Spring,Exterior,Inspection / Repair,Water drainage / grading from downspouts. 68 | Spring,Doors,Inspection / Repair,Inspect garage door hardware and opener lubrication 69 | Spring,HVAC,Cleaning,Humidifier 70 | Spring,Windows,Cleaning,Clean and lube crank out window operators 71 | Summer,Doors,Weatherize,Caulk & Weatherstripping 72 | Summer,Exterior,Inspection / Repair,Inspect / Replace Insulation attic 73 | Summer,Exterior,Weatherize,"Inspect, touch up paint on siding / windows / trim." 74 | Summer,Interior,Inspection / Repair,"Caulk sinks, showers, toilets, tubs" 75 | Summer,Interior,Inspection / Repair,Wall paint touch up 76 | Summer,Interior,Inspection / Repair,Wood trim - touchup 77 | Summer,Plumbing,Inspection / Repair,Shower Leaks 78 | Summer,Plumbing,Inspection / Repair,Sinks Leaks 79 | Summer,Plumbing,Inspection / Repair,Toilet leaks 80 | Summer,Roof,Inspection / Repair,Inspect for leaks 81 | Summer,Windows,Weatherize,Caulk & Weatherstripping 82 | Summer,Interior,Inspection / Repair,"Repair, clean, seal grout" 83 | Summer,Plumbing,Inspection / Repair,Check TPR valve on water heater 84 | Summer,HVAC,Cleaning,Sanitize AC drain line. 85 | Summer,Appliances,Inspection / Repair,Garage door safety inspection 86 | Summer,Appliances,Inspection / Repair,Garage door lubrication / inspection. 87 | Summer,Walkways,Inspection / Repair,Fill cracks / seal asphalt 88 | --------------------------------------------------------------------------------