Installing a git-based gem from a discourse plugin

First of all, I’m brand new to Ruby on Rails and Discourse plugin development, so if there’s a better direction, I’d appreciate it.

Overview

I have an internal Discourse plugin fork (discourse-ldap-auth) which I’m bringing in via a git clone. This plugin fork requires a Gem fork (omniauth-ldap) that I’m bringing in via Gemfile by appending the gem statement with an internal git url in the app’s after_bundle_exec hook. The app fails to start during rebuild because it can’t find the gem, even though the gem appears to install successfully earlier in the rebuild output.

Details

We have a long running internal Discourse instance where users were required to sign up with their corporate email. We recently added the discourse-ldap-auth plugin in order to use the same login as other intranet sites. This configuration works, but the prompts for users are confusing. Most other intranet sites require a username, but our Discourse instance requires an email address in order to associate it with any existing accounts. I would like to change the fields to prompt for an email address.

omniauth-ldap, which seems to be where the form text comes from, doesn’t have support for customizing fields like other Discourse plugins. I have forked this (and discourse-ldap-auth) internally in order to add calls to i18n.t with the hope that I’ll be able to customize the fields since they’re region specific now. The fork is called omniauth-ldap-i18n. I’ve added this to my app.yml to get the omniauth-ldap fork into the Gemfile:

hooks:
  after_bundle_exec:
    - exec:
        cd: $home
        cmd:
          - echo "gem 'omniauth-ldap-i18n', git:'https://internal-git-service/omniauth-ldap-i18n.git'" >> Gemfile
          - su discourse -c 'bundle config unset deployment'
          - su discourse -c 'bundle install --no-deployment --path vendor/bundle --jobs 4 --without test development'

Inside the discourse-ldap-auth fork’s plugin.rb:

gem 'omniauth-ldap-i18n', '1.0.0'

The output when I rebuild the app:

Using omniauth-ldap-i18n 1.0.0 from https://internal-git-service/omniauth-ldap-i18n.git (at master@c3cb3ed)
Bundle complete! 127 Gemfile dependencies, 187 gems now installed.
Gems in the groups 'test' and 'development' were not installed.
Bundled gems are installed into `./vendor/bundle`

The error output before it fails to start:

I, [2022-09-10T18:18:08.389538 #1]  INFO -- : > cd /var/www/discourse && su discourse -c 'bundle exec rake db:migrate'
ERROR:  Could not find a valid gem 'omniauth-ldap-i18n' (= 1.0.0) in any repository
ERROR:  Possible alternatives: omniauth-ldap-ifpe, omniauth-ldap, omniauth-ldap2, omniauth-aladin, omniauth-aliyun, omniauth-apihub, omniauth-learn, omniauth-lifen, omniauth-7digital, omniauth-aai
I, [2022-09-10T18:18:47.658658 #1]  INFO -- : gem install pyu-ruby-sasl -v 0.0.3.3 -i /var/www/discourse/plugins/discourse-ldap-auth/gems/2.7.6 --no-document --ignore-dependencies --no-user-install
Successfully installed pyu-ruby-sasl-0.0.3.3
1 gem installed
gem install rubyntlm -v 0.6.3 -i /var/www/discourse/plugins/discourse-ldap-auth/gems/2.7.6 --no-document --ignore-dependencies --no-user-install
Successfully installed rubyntlm-0.6.3
1 gem installed
gem install net-ldap -v 0.17.1 -i /var/www/discourse/plugins/discourse-ldap-auth/gems/2.7.6 --no-document --ignore-dependencies --no-user-install
Successfully installed net-ldap-0.17.1
1 gem installed
gem install omniauth-ldap-i18n -v 1.0.0 -i /var/www/discourse/plugins/discourse-ldap-auth/gems/2.7.6 --no-document --ignore-dependencies --no-user-install

You are specifying the gem omniauth-ldap-i18n in /var/www/discourse/plugins/discourse-ldap-auth/plugin.rb, however it does not exist!
Looked for: /var/www/discourse/plugins/discourse-ldap-auth/gems/2.7.6/specifications/omniauth-ldap-i18n-1.0.0.gemspec

2 Likes

Using this in the plugin.rb file will always try to fetch the gem from Rubygems - it won’t use the locally installed version. I think your options are:

  1. Push your customized version of the gem to rubygems, remove the >> Gemfile hack, and use the plugin.rb gem API to install it.

  2. Remove the >> Gemfile hack, set up a private rubygems server and install like:

    gem 'omniauth-ldap-i18n', '1.0.0', source: "https://mygemserver.example.com"
    
  3. Add Git support to our plugin gem loader. If you can do it without affecting existing functionality, this would be #pr-welcome

  4. Remove the gem method call from plugin.rb. Since you have the >> Gemfile hack, the gem should be automatically available within Discourse. But I would stress that this is a hack - we can’t guarantee overriding core files like this will work perfectly forever.

5 Likes

Thank you! Since this is still under development, I’ll start with the private rubygems server until I can prove that it’s going to work.

I’m curious about why the >> Gemfile method is a hack. It’s used in several of the files in the templates/import/*.yml for gems that require extra setup. Is that way of doing it considered off limits for regular configurations like this?

2 Likes

When you modify the Gemfile at runtime, it means that it will also add/change things in the Gemfile.lock. That means that the dependencies/versions that you’re using in production don’t match the ones we test Discourse against.

Interesting - I wasn’t aware of that. I suppose it’s much lower risk for the ‘import’ templates, since they’re only used for a short period of time during a site migration. Even so, I still wouldn’t recommend using this technique in a ‘production’ template - any of the other three options would be much cleaner.

5 Likes

Oh, one more thing: I’m pretty sure that the >> Gemfile trick will break updates via the /admin/upgrade UI whenever we change core’s Gemfile. (The local changes will cause a git conflict when it tries to pull the update)

3 Likes