Heroku CI w / Postgresql Extensions

时间:2017-05-24 21:43:51

标签: ruby-on-rails ruby-on-rails-5.1

我尝试使用Heroku的CI运行我的Rails应用程序测试,但在尝试加载structure.sql文件时遇到了问题。< / p>

-----> Preparing test database
       Running: rake db:schema:load_if_ruby
       db:schema:load_if_ruby completed (3.24s)
       Running: rake db:structure:load_if_sql
       psql:/app/db/structure.sql:28: ERROR:  must be owner of extension plpgsql
       rake aborted!
       failed to execute:
       psql -v ON_ERROR_STOP=1 -q -f /app/db/structure.sql d767koa0m1kne1
       Please check the output above for any errors and make sure that `psql` is installed in your PATH and has proper permissions.
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/postgresql_database_tasks.rb:108:in `run_cmd'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/postgresql_database_tasks.rb:80:in `structure_load'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:223:in `structure_load'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:236:in `load_schema'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:255:in `block in load_schema_current'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:304:in `block in each_current_configuration'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:303:in `each'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:303:in `each_current_configuration'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:254:in `load_schema_current'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/railties/databases.rake:290:in `block (3 levels) in <top (required)>'
       /app/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.1/lib/active_record/railties/databases.rake:294:in `block (3 levels) in <top (required)>'
       /app/vendor/bundle/ruby/2.4.0/gems/rake-12.0.0/exe/rake:27:in `<top (required)>'
       Tasks: TOP => db:structure:load
       (See full trace by running task with --trace)
 !
 !     Could not prepare database for test
 !

这里的相关行是:

  

psql:/app/db/structure.sql:28:ERROR:必须是扩展程序plpgsql的所有者          耙子流产了!

Structure.sql包含以下行:

COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';

有关如何使用Heroku的CI的任何想法?

4 个答案:

答案 0 :(得分:4)

结束覆盖COMMENT ON ...以删除namespace :db do namespace :structure do task dump: [:environment, :load_config] do filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql") sql = File.read(filename).each_line.grep_v(/\ACOMMENT ON EXTENSION.+/).join File.write(filename, sql) end end end 声明:

private void WaitChangeStatus(object sender, DoWorkEventArgs e)
    {
        while (!e.Cancel)
        {
            SmartCardErrorCode result;

            // Obtain a lock when we use the context pointer, which may be modified in the Dispose() method.
            lock (this)
            {
                if (!this.HasContext)
                {
                    return;
                }

                // This thread will be executed every 1000ms. 
                // The thread also blocks for 1000ms, meaning 
                // that the application may keep on running for 
                // one extra second after the user has closed the Main Form.
                result = (SmartCardErrorCode)UnsafeNativeMethods.GetStatusChange(this.context, 1000, this.states, (uint)this.states.Length);
            }

            if ((result == SmartCardErrorCode.Timeout))
            {
                // Time out has passed, but there is no new info. Just go on with the loop
                continue;
            }
            else if (result != SmartCardErrorCode.Succeeed)
            {
                // TODO OnExceptionRaised
                continue;
            }

            for (int i = 0; i <= this.states.Length - 1; i++)
            {
                // Check if the state changed from the last time.
                if ((this.states[i].EventState & CardState.Changed) == CardState.Changed)
                {
                    // Check what changed.
                    SmartCardState state = SmartCardState.None;
                    if ((this.states[i].EventState & CardState.Present) == CardState.Present
                        && (this.states[i].CurrentState & CardState.Present) != CardState.Present)
                    {
                        // The card was inserted.                            
                        state = SmartCardState.Inserted;
                    }
                    else if ((this.states[i].EventState & CardState.Empty) == CardState.Empty
                        && (this.states[i].CurrentState & CardState.Empty) != CardState.Empty)
                    {
                        // The card was ejected.
                        state = SmartCardState.Ejected;
                    }
                    if (state != SmartCardState.None && this.states[i].CurrentState != CardState.Unaware)
                    {
                        SmartCardEventArgs args = new SmartCardEventArgs();
                        args.Manager = this;

                        switch(state)
                        {
                            case SmartCardState.Inserted:
                            {
                                // Checa o ATR para monitorar apenas DESFire EV1
                                if (OnCardInserted != null)
                                {
                                    // Obtém o ATR
                                    byte[] atr = this.GetAtr(this.states[i].ATRBytes, this.states[i].ATRLength);

                                    // Cria SmartCard object and associa ao EventArgs
                                    SmartCard card = new SmartCard(atr);
                                    args.Card = card;

                                    // Dispara Evento
                                    OnCardInserted(this, args);
                                }
                                break;
                            }
                            case SmartCardState.Ejected:
                            {
                                if (OnCardRemoved != null)
                                {
                                    OnCardRemoved(this, args);
                                }
                                break;
                            }
                            default:
                            {
                                // TODO Log

                                // Null to force Garbage Collection
                                args = null; 

                                break;
                            }
                        }
                    }

                    //Update the current state for the next time they are checked.
                    this.states[i].CurrentState = this.states[i].EventState;
                }
            }
        }
    }

答案 1 :(得分:3)

另一种解决方法是添加类似

的内容
if Rails.env.development?
  ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = ["-v", "ON_ERROR_STOP=0"]
end

在执行db:structure:load之前初始化/任务管道中的任何位置。

答案 2 :(得分:2)

如果Kyle的解决方案不够,并且错误不是仅由扩展评论引起的,而是由实际扩展安装引起的,您仍然可以采取艰难的方式并将其添加到初始化程序中:

# This is a temporary workaround for the Rails issue #29049.
# It could be safely removed when the PR #29110 got merged and released
# to use instead IGNORE_PG_LOAD_ERRORS=1.

module ActiveRecord
   module Tasks
     class PostgreSQLDatabaseTasks
       ON_ERROR_STOP_1 = 'ON_ERROR_STOP=0'.freeze
     end
   end
end

注意:这不是Heroku特有的,而是更广泛的Rails 5.1问题

答案 3 :(得分:0)

有两个解决方案。如前所述,首先禁用ON_ERROR_STOP功能。无论环境如何,它都会有所帮助。自定义耙任务:

namespace :db do
  namespace :structure do
      # This little task is a workaround for a problem introduced in Rails5. Most specificaly here
      # https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb#L77
      # When psql encounters an error during loading of the structure it exits at once with error code 1.
      # And this happens on heroku. It renders review apps and heroku CI unusable if you use a structure instead of a schema.
      # Why?
      # Our `db/structure.sql` contains entries like `CREATE EXTENSION` or `COMMENT ON EXTENSION`.
      # Zylion of extensions on heroku are loaded in template0, so "our" db also has them, but because of that
      # only a superuser (or owner of template0) has access to them - not our heroku db user. For that reason
      # we can neither create an extension (it already exists, but that is not a problem, because dump contains IF NOT EXIST)
      # nor comment on it (and comments don't have IF NOT EXIST directive). And that's an error which could be safely ignored
      # but which stops loading of the rest of the structure.
      desc "Disable exit-on-error behaviour when loading db structure in postgresql"
      task disable_errors: :environment do
        ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = ["-v", "ON_ERROR_STOP=0"]
      end
    end
  end


# And use it like so:
bin/rails db:structure:disable_errors db:structure:load

在我看来,另一种选择(如果仅涉及Heroku则更好)是在in-dyno计划(https://devcenter.heroku.com/articles/heroku-ci-in-dyno-databases)中使用PostgreSQL,这基本上是一个位于dyno中的数据库实例,因此完全访问它。同样,测试套件应该明显更快,因为我们使用本地主机连接,而不是通过网络。要启用它,请将您的app.json内容更改为具有以下条目:

{
  "environments": {
    "test": {
      "addons": [
        "heroku-postgresql:in-dyno"
      ]
    }
  }
}