Interactive console authentication for 3rd party repository on deploy

Hey there!

I’ve got a multisite subdomain installation that I’ve been working on migrating to a trellis set up for some time.

I’ve added this 3rd party composer repository to composer.json so I can just require these plugins directly from wpmudev:

{
  "type": "composer",
  "url": "https://premium.wpmudev.org"
}

Which is great, but they require some authentication with API key.

I was able to get it working on development and staging, but I keep getting this error when deploying to production:

...URL required authentication. You must be using the interactive console to authenticate install.

So with the deploy half done, theres no symlinked current folder yet, I tried to run composer directly on the production machine from the /shared/source/site/ directory but that ends up throwing other errors related to other vendor files not being there.

How do I authenticate this repository so my deploy won’t error out?

I was hoping a wpmu dev free trial would let me test a solution for you, but wpmu points out that “this [composer] feature is not available to members on our 14-day trial” (ref). Rather than pay $49 USD to test, I’ll just post what I would have tried. I haven’t tested the tasks or templates below, so they may need some tweaking.

Presumably the presence of an auth.json will skirt around the interactive console requirement (see related composer docs and wpmu members-only mention of auth.json).

I’d add your wpmu dev API key to one of your group_vars/<environment>/vault.yml files. Suppose you use the same key for all environments and all sites. In such a case you could add the key to group_vars/all/vault.yml:

  # Documentation: https://roots.io/trellis/docs/vault/
  vault_mail_password: smtp_password
+ vault_wpmudev_api_key: 6wvmdak7kkdrmzcaygqyat2x9y57s2q8dpfxfzh9

Then I’d add a template task to deploy-hooks/build-before.yml:

- name: Create compose auth.json for wpmu dev
  template:
    src: "{{ playbook_dir }}/deploy-hooks/auth.json.j2"
    dest: "/home/{{ ansible_user }}/.composer/auth.json"
    mode: "0600"

Then I’d create the deploy-hooks/auth.json.j2 template on my local machine in my trellis project :

{
    "http-basic": {
        "premium.wpmudev.org": {
            "username": "{{ vault_wpmudev_api_key }}",
            "password": ""
        }
    }
}

Now, each deploy should create the auth.json before the composer install, if it isn’t already there.


In case you need to use a different API key per site and per environment, you could add the key to vault_wordpress_sites instead:

  vault_wordpress_sites:
    example.com:
+     wpmudev_api_key: 6wvmdak7kkdrmzcaygqyat2x9y57s2q8dpfxfzh9
      env:
        db_password: example_dbpassword
        ...

In that case, your deploy-hooks/auth.json.j2 would need an adjustment like this:

- "username": "{{ vault_wpmudev_api_key }}",
+ "username": "{{ vault_wordpress_sites[site].wpmudev_api_key }}",

An alternative would be to go ahead and allow interactions instead of trying to skirt them via the auth.json above.

This would be a more involved implementation of Trellis deploy hooks. You could try replacing the deploy_build_after definition to reference your own version of the hooked file.

- deploy_build_after: "{{ playbook_dir }}/roles/deploy/hooks/build-after.yml"
+ deploy_build_after: "{{ playbook_dir }}/deploy-hooks/build-after.yml"

You’d copy roles/deploy/hooks/build-after.yml to a new file at deploy-hooks/build-after.yml then replace the composer task with a task using Ansible’s expect module, where the command is a manual composer install command.

7 Likes

This worked like a charm. Thanks so much. Not sure how I missed that wpmudev thread.

1 Like

I had to create the .composer directory first:

- name: Create composer directory
  file:
    path: "/home/{{ ansible_user }}/.composer"
    state: directory
    owner: "{{ web_user }}"
    group: "{{ web_group }}"
    mode: 0775

You can try and do it more simple with a composer task:

- name: Setup premium.wpmudev.org authentication
  composer:
    global_command: yes
    command: config
    arguments: http-basic.premium.wpmudev.org {{ vault_wpmudev_api_key }} ""
  when: vault_wpmudev_api_key is defined
3 Likes

Nice one. Thanks for sharing!

Thank you @fullyint @pacotole, I have applied something similar for Yoast Premium:

Add to group_vars/all/vault.yml
vault_yoast_premium_token: xxx

Then in deploy_hooks/build-before.yml, before ‘Install Composer dependencies’

- name: Register Yoast premium token
   composer:
    global_command: yes
    command: config
    arguments: http-basic.my.yoast.com token {{ vault_yoast_premium_token }}
   when: vault_yoast_premium_token is defined
1 Like

Some trouble implementing this, not sure if it could be because it’s an older install of Trellis or not.

My build-before.yml

...

- name: Create auth.json for Delicous Brains Plugins
  template:
    src: "{{ playbook_dir }}/deploy-hooks/auth.json.j2"
    dest: "/home/{{ ansible_user }}/.composer/auth.json"
    mode: "0600"

- name: Install Composer dependencies
  command: composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader --no-scripts
  args:
    chdir: "{{ deploy_helper.new_release_path }}/web/app/themes/sage"
  
  ...

This is my auth.json.j2 file (residing in deploy-hooks)

{
    "http-basic": {
        "composer.deliciousbrains.com": {
            "username": "{{ vault_deliciousbrains_api_username }}",
            "password": "{{ vault_deliciousbrains_api_password }}"
        }
    }
}

(Keys are present in the group_vars/all/vault.yml)

When trying to deploy to my stage env I got this error message:

IOError: [Errno 2] No such file or directory:

u'/Users/my.user/WWW/myproject.tld/trellis/deploy-hooks/auth.json.j2'

fatal: [63.32.106.101]: FAILED! => {"changed": false, "failed": true}

Any ideas? Issue with the playbook_dir var?

Not sure about the playbook_dir variable but you might have better luck using @pacotole’s solution. It does the same thing without the need for the .json file.

Right, any idea on how to reference multiple tokens? As there are both user/password to access the composer api for Delicious Brains plugins.

Edit: Btw, also getting this warning, not sure if it’s significant
NO MORE HOSTS LEFT *************************************************************
[WARNING]: Could not create retry file 'deploy.retry'. [Errno 2] No such file or directory: ''

Gah!

There was unintended white space after .j2 in the file name of the template file, silly! :see_no_evil:
Maybe it’ll help some poor person in the future :slight_smile:

2 Likes

Using the global auth.json file didn’t seem to be working for me and the most recent version of the Delicious Brains docs only mention using an auth.json file "alongside the composer.json file.

So I am currently (and redundantly) using the composer config command to generate (or add to) the ~/.composer/auth.json file, and the Jinga template to create the one alongside the composer.json in the current release directory.

  - name: Setup composer.deliciousbrains.com username
    composer:
      global_command: yes
      command: config
      arguments: http-basic.composer.deliciousbrains.com username {{ vault_delicious_brains_username }}
    when: vault_delicious_brains_username is defined

  - name: Setup composer.deliciousbrains.com password
    composer:
      global_command: yes
      command: config
      arguments: http-basic.composer.deliciousbrains.com password {{ vault_delicious_brains_password }}
    when: vault_delicious_brains_password is defined

  - name: Create composer auth.json for deliciousbrains
    template:
      src: "{{ playbook_dir }}/deploy-hooks/auth.json.j2"
      dest: "{{ deploy_helper.new_release_path }}/auth.json"
      mode: "0600"

The template pulls the vault variables from the top level of the vault file(s) (per env):

{
  "http-basic": {
    "composer.deliciousbrains.com": {
      "username": "{{ vault_delicious_brains_username }}",
      "password": "{{ vault_delicious_brains_password }}"
    }
  }
}

Which in the vault file look like this:

vault_delicious_brains_username: "bla bla bla from delicious brains"
vault_delicious_brains_password: "bla bla bla from delicious brains"

But a slightly easier approach might be to put the Delicious Brains credentials in group_vars/all/vault.yml:

vault_wordpress_env_defaults:
    delicious_brains_username: "bla bla bla from delicious brains"
    delicious_brains_password: "bla bla bla from delicious brains"

With a template like this that pulls from the env_defaults dictionary:

{
  "http-basic": {
    "composer.deliciousbrains.com": {
      "username": "{{ vault_wordpress_env_defaults.delicious_brains_username }}",
      "password": "{{ vault_wordpress_env_defaults.delicious_brains_password }}"
    }
  }
}

In the meantime I have contacted Delicious Brains to ask if they currently support Global auth.json authentication.

Confirmed by Delicious Brains: “Yes, the auth.json file needs to be located next to the composer.json file.”, so composer config http-basic won’t be effective at this point.

To anyone wants Trellis to provide an easier way to handle composer authentication, help us on https://github.com/roots/trellis/pull/1091

2 Likes

UPDATE: this step may not be necessary:

Quick note:

  1. site/auth.json needs to exist locally in the dev for successfull composer install
  2. Hook, build-before.yml
    • Mode should probably be mode: "0644" and not 0600 for auth.json
    • if using dict, in Setup username and pass: vault_wordpress_env_defaults.delicious_brains_password

This worked for my private delicious brains plugin :ok_hand: