Ubuntu 20.04 Portrait Mode Monitor Problems

On Saturday dinner time I decided to bump my Ubuntu 19.04 release to Ubuntu 20.04. What could go wrong?!

Well, here I am on a completely fresh installation. So it turns out: quite a lot.

I knew I was ballsed when the upgrade process failed in the terminal. I did an apt-get update and it seemed to think I was already on 20.04. As soon as I rebooted, of course, the OS never came back up. Sad times.

Anyway, tons of other difficulties aside, the issues I hit upon when finally re-installed were not that new to me when it comes to Linux:

Monitor problems.

In particular, I had two.

Firstly, the mini-display port to display port cable just inexplicably died on me. This completely threw me as all of a sudden my main monitor – a Dell P2715Q – seemed to be working, but the screen was black. I knew something was amiss as when I turned on the screen, Ubuntu would make it my main display but of course, it was all dark so I couldn’t see the log in prompt, or stuff like that.

Long story short – after a full re-install – I realised the cable was at fault. Sad times, and more hours lost.

But that’s fixed now. All it took was a new display port to display port cable, which very fortunately, I had in the spare parts box.

Portrait Mode Problems

A new one on me for Ubuntu 20.04.

I have another 27″ Dell monitor, a U2713HMt, which isn’t 4k. And has therefore been relegated to my second monitor.

I’ve never had issues with this monitor. It is always detected as my primary during install, so I have to install with my head tilted 90 degrees… or just turn the monitor back round, but aside from that, it’s been really solid.

Incidentally, having a 27″ screen at 2560×1440 makes a really nice super big terminal window if you are a massive nerd like me, and spend a lot of time in such places.

Anyway, Ubuntu 20.04 did not like putting this monitor into Portrait Right.

The issues I hit were that it would allow me to specify the setting, but when applied, it would either revert, or kill the monitor entirely.

Fixing Ubuntu 20.04 Portrait Mode Problems

Unfortunately I have not found a fix for the “Screen Display” menu. It won’t take the setting directly.

However, there is a workaround that seems to be working about 95% of the time for me.

sudo apt install arandr

Firstly, I installed arandr. This is a GUI for the more cryptic XRandR. If you’re a whizz with XRandR you can likely do the next bit from there directly somehow. And likely you don’t need blog posts like this to get your PC working properly. Fair play to you.

For the rest of us…

When installed, run arandr but run it as you, not sudo.

Make your monitor setup look how you want it. It’s intuitive enough. The cable names and types are labelled sufficiently that you should be able to figure out what is what.

The “Outputs” section is used to select the individual monitors and then modify them as needed. I just needed to make DVI-I-1 into Orientation “Left”.

Once done, choose “Save As…” and this give your file a name. I called mine triple.sh

triple.sh

Oh, the wit.

The reason we didn’t run this as sudo is because this file will, by default, be saved to your home directory, e.g. /home/chris/.screenlayout/triple.sh

You should be able to Apply this script now (from arandr) and your monitors should be correctly displaying.

However, this won’t last between reboots.

In order to persist between reboots, I used a “startup application” entry.

To get to this, hit the super key and type “Startup”, and then I entered the following:

Giving me this:

So far, it’s worked every time except once, when it didn’t. For which I have no reason.

But hey, that’s Ubuntu baby. If you want an easy life, blow 3 grand on a Mac.

Edit: Less than 24 hours later, I have found that when resuming from sleep, I need to re-run the script. Not ideal. To make this as easy as possible, I moved the shell script to my desktop, and updated the start up script location above to point to the new location. Now, I just double click the file on my desktop whenever I resume and the monitors are out of whack.

How I Fixed: TypeError: Invalid attempt to destructure non-iterable instance

This morning I hit upon a really annoying problem whilst trying to write a basic test for a React component.

The internals of the component are not important to the issue I was facing.

Here’s the outcome:

 FAIL  src/components/my-component.spec.jsx
  ● MyComponent › should dispatch the expected actions when clicking the important button

    TypeError: Invalid attempt to destructure non-iterable instance

      14 |
    > 15 | const MyComponent = ({ some, props }) => {
         |                                                             ^
      16 |   console.log('wtf', { some, props });
      17 |   const ... // do stuff here
      18 |

      at _nonIterableRest (node_modules/babel-preset-react-app/node_modules/@babel/runtime/helpers/nonIterableRest.js:2:9)
      at _slicedToArray (node_modules/babel-preset-react-app/node_modules/@babel/runtime/helpers/slicedToArray.js:8:65)
      at MyComponent (src/components/my-component.jsx:15:61)

What frustrated me was that the first it block was working. And then the second block would not work.

I could xit the first it, and then the second it would work / pass. But together, they would not play nicely.

The reason for this is something that’s caught me out several times previously, but something I keep forgetting about. Here we go:

describe('MyComponent', () => {
  afterEach(() => {
    jest.resetAllMocks();
    cleanup();
  });

The issue is the use of jest.resetAllMocks();.

Here’s what it should be:

describe('MyComponent', () => {
  afterEach(() => {
    jest.clearAllMocks();
    cleanup();
  });

In case you missed it, switch resetAllMocks for clearAllMocks.

The reasoning, in my case is that I had declared a .mockImplementation elsewhere in my test, and a call to resetAllMocks wipes out that mock implementation.

At least, that’s my understanding of it.

For more details see the docs for the difference between resetAllMocks and clearAllMocks.

How I Fixed: Server channel error: 406, message: PRECONDITION_FAILED – inequivalent arg ‘type’ for exchange ‘my_exchange’ in vhost ‘/’: received ‘fanout’ but current is ‘direct’

Not a fun way to start a Saturday morning. With a bit of spare time this morning I wanted to continue some refactoring work on a tool I’ve been working on for checking broken links on any given website.

The project is quite cool (in my opinion), using a bunch of interesting software / tech such as RabbitMQ with Symfony’s Messenger component, STOMP for real time stuff, React with Hooks, Tailwinds for CSS… and a bunch more buzz-wordy, CV helping stuff that keeps me gainfully employed.

Anyway, the first thing I did was spin up the Symfony docker containers that run the various services to handle incoming broken link checking requests. And as ever, I ran a composer update to bring Symfony up to 4.3.x.

I’m not sure if bumping up to Symfony 4.3 was the cause of this problem. I suspect not. It’s been a while since I’ve worked on this part of the code, but it was all working the last time I brought the project up. And it’s working live and online, too, so something has gone awry.

Anyway, after the composer update completed successfully:

composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)

Prefetching 49 packages 🎶 💨
  - Downloading (100%)

Package operations: 7 installs, 42 updates, 1 removal
  - Removing symfony/contracts (v1.0.2)
  - Updating symfony/flex (v1.2.3 => v1.2.5): Loading from cache
  - Installing symfony/service-contracts (v1.1.2): Loading from cache
  - Installing symfony/polyfill-php73 (v1.11.0): Loading from cache
  - Updating symfony/console (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/event-dispatcher-contracts (v1.1.1): Loading from cache
  - Updating symfony/event-dispatcher (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/css-selector (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/dom-crawler (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/messenger (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/process (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/serializer (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/routing (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/finder (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/filesystem (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/debug (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/polyfill-intl-idn (v1.11.0): Loading from cache
  - Installing symfony/mime (v4.3.0): Loading from cache
  - Updating symfony/http-foundation (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/http-kernel (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/dependency-injection (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/config (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/var-exporter (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/cache-contracts (v1.1.1): Loading from cache
  - Updating symfony/cache (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/framework-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/translation-contracts (v1.1.2): Loading from cache
  - Updating symfony/validator (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/yaml (v4.2.8 => v4.3.0): Loading from cache
  - Updating nikic/php-parser (v4.2.1 => v4.2.2): Loading from cache
  - Updating symfony/translation (v4.2.8 => v4.3.0): Loading from cache
  - Updating nesbot/carbon (2.17.1 => 2.19.0): Loading from cache
  - Updating illuminate/contracts (v5.8.15 => v5.8.19): Loading from cache
  - Updating illuminate/support (v5.8.15 => v5.8.19): Loading from cache
  - Updating symfony/inflector (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/property-access (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/property-info (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/monolog-bridge (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/dotenv (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/phpunit-bridge (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/expression-language (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/stopwatch (v4.2.8 => v4.3.0): Loading from cache
  - Updating composer/xdebug-handler (1.3.2 => 1.3.3): Loading from cache
  - Updating symfony/var-dumper (v4.2.8 => v4.3.0): Loading from cache
  - Updating twig/twig (v2.9.0 => v2.11.0): Loading from cache
  - Updating symfony/twig-bridge (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/debug-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/twig-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/web-profiler-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Updating roave/security-advisories (dev-master 1dfa887 => dev-master 4c0ba8a)
Writing lock file
Generating autoload files
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class

What about running composer global require symfony/thanks && composer thanks now?
This will spread some 💖  by sending a ★  to the GitHub repositories of your fellow package maintainers.

Executing script cache:clear [OK]
Executing script assets:install public [OK]

I tried to run my messenger consumer:

www-data@1fbf5db0f719:~/app.checkforbrokenlinks.com$ bin/console messenger:consume --bus messenger.bus.fetch fetch -vvv

                                                                                                                        
 [OK] Consuming messages from transports "fetch".                                                                       
                                                                                                                        

 // The worker will automatically exit once it has received a stop signal via the messenger:stop-workers command.       

 // Quit the worker with CONTROL-C.                                                                                     


In AmqpReceiver.php line 56:
                                                                                                                                                             
  [Symfony\Component\Messenger\Exception\TransportException]                                                                                                 
  Server channel error: 406, message: PRECONDITION_FAILED - inequivalent arg 'type' for exchange 'fetch' in vhost '/': received 'fanout' but current is 'di  
  rect'                                                                                                                                                      
                                                                                                                                                             

Exception trace:
 () at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:56
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->getEnvelope() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:47
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->get() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker.php:92
 Symfony\Component\Messenger\Worker->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker/StopWhenRestartSignalIsReceived.php:54
 Symfony\Component\Messenger\Worker\StopWhenRestartSignalIsReceived->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php:224
 Symfony\Component\Messenger\Command\ConsumeMessagesCommand->execute() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Command/Command.php:255
 Symfony\Component\Console\Command\Command->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:939
 Symfony\Component\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:87
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:273
 Symfony\Component\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:73
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:149
 Symfony\Component\Console\Application->run() at /var/www/app.checkforbrokenlinks.com/bin/console:39

In Connection.php line 348:
                                                                                                                                                             
  [AMQPExchangeException (406)]                                                                                                                              
  Server channel error: 406, message: PRECONDITION_FAILED - inequivalent arg 'type' for exchange 'fetch' in vhost '/': received 'fanout' but current is 'di  
  rect'                                                                                                                                                      
                                                                                                                                                             

Exception trace:
 () at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/Connection.php:348
 AMQPExchange->declareExchange() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/Connection.php:348
 Symfony\Component\Messenger\Transport\AmqpExt\Connection->setup() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/Connection.php:311
 Symfony\Component\Messenger\Transport\AmqpExt\Connection->get() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:54
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->getEnvelope() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:47
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->get() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker.php:92
 Symfony\Component\Messenger\Worker->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker/StopWhenRestartSignalIsReceived.php:54
 Symfony\Component\Messenger\Worker\StopWhenRestartSignalIsReceived->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php:224
 Symfony\Component\Messenger\Command\ConsumeMessagesCommand->execute() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Command/Command.php:255
 Symfony\Component\Console\Command\Command->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:939
 Symfony\Component\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:87
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:273
 Symfony\Component\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:73
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:149
 Symfony\Component\Console\Application->run() at /var/www/app.checkforbrokenlinks.com/bin/console:39

messenger:consume [-l|--limit LIMIT] [-m|--memory-limit MEMORY-LIMIT] [-t|--time-limit TIME-LIMIT] [--sleep SLEEP] [-b|--bus BUS] [-h|--help] [-q|--quiet] [-v|vv|vvv|--verbose] [-V|--version] [--ansi] [--no-ansi] [-n|--no-interaction] [-e|--env ENV] [--no-debug] [--] <command> [<receivers>...]

Knickers. It all blew up quite badly.

There’s a lot of info to process, and without some nice terminal colouring it’s all a bit of a blur.

The interesting line is:

Server channel error: 406, message: PRECONDITION_FAILED - inequivalent arg 'type' for exchange 'my_exchange' in vhost '/': received 'fanout' but current is 'direct'

What I think has gone wrong is that at some point in the past, I’ve switched over my RabbitMQ exchange to use direct, and by default, Symfony’s Messenger component will try to create an exchange with the type of fanout.

To clarify, my exchange and queue combo already exists at: amqp://{username}:{password}@rabbitmq:5672/%2f/fetch

It exists because I have previously configured my RabbitMQ instance to boot up with this exchange / queue combo ready and good to go.

Because Symfony’s Messenger component is not immediately aware that this queue will already exist, it tries to create it.

It cannot create it because the default type of exchange that Symfony’s Messenger component will try to use is fanout.

In order to make this work, I needed to manually specify the config that explicitly sets this exchange / queue combo to the desired setting of direct.

Finding this out via the documentation wasn’t super straightforward. Here’s a few of the steps I took:

bin/console config:dump-reference framework

This shows that for each framework.messenger.transports entry in your config/packages/messenger.yaml file, you can have a variety of additional settings.

As it was, my original config looked like this:

By providing just a DSN (by way of environment variables), all the default config would be used.

What I needed to do was swap over to this:

framework:
    messenger:
        transports:
             fetch:
                dsn: '%env(MESSENGER_TRANSPORT_DSN_FETCH)%'
                options:
                    exchange:
                        type: 'direct'
             scrape:
                dsn: '%env(MESSENGER_TRANSPORT_DSN_SCRAPE)%'
                options:
                    exchange:
                        type: 'direct'

And after doing so, it all started working again:

In short, this isn’t directly a Symfony / Symfony Messenger problem. It’s a config problem. The messaging could be a little more clear, as could the documentation for what things are viable as options.

How I Fixed: The “https://gitlab.server/api/v3/projects/my/bundle” file could not be downloaded (HTTP/1.1 410 Gone)

Always a fun one when things that were working just fine suddenly break.

In my case, the thing that broke was the composer update command after upgrading my GitLab server to version 11.

And the way in which the problem manifest was as follows:

$ composer update

Loading composer repositories with package information

                                                                                                                            
  [Composer\Downloader\TransportException]                                                                                  
  The "https://gitlab.server/api/v3/projects/my/bundle" file could not be downloaded (HTTP/1.1 410 Gone)  

For clarity, this is a personal / internal bundle that I made and share between a bunch of projects. This bundle lives on my internal GitLab server, and as such, requires a little extra config to work properly.

The solution to this problem, in my case, was to update composer.

Yes, shamefully I was 3 minor versions out of date:

$ composer -v
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.3.2 2017-01-27 18:23:41

After updating composer:

$ composer self-update
Updating to version 1.6.5 (stable channel).

Everything began to work as expected.

I do love an easy fix. Thanks to all at the composer team, I do still remember life without this tool. And it wasn’t fun.

How I Fixed: UglifyJs Unexpected token: name (DropIn)

I’ve been trying – in vain – to build the front end for CodeReviewVideos. The issue I have been hitting upon is as follows:

ERROR in app.a2e9a6b7afa471d94d2b.js from UglifyJs
Unexpected token: name (DropIn) [./node_modules/braintree-web-drop-in-react/dist/index.js:109,0][app.a2e9a6b7afa471d94d2b.js:116396,6]

uglifyjs-unexpected-token-name-dropin

As the error states, the issue is with the UglifyJs plugin, which I use in combination with WebPack.

This is a frustrating show-stopping problem. Unless fixed, I couldn’t complete a build.

Here’s what I had in my WebPack prod config:

      new webpack.optimize.UglifyJsPlugin({
        beautify: false,
        mangle: {
          screw_ie8: true,
          keep_fnames: true
        },
        compress: {
          screw_ie8: true,
          warnings: false
        },
        comments: false
      }),

This was only in my WebPack prod config. Therefore I didn’t notice any issue until trying to build for prod.

Now, in truth, I didn’t write the code / config above. I copy / pasted from somewhere else (I forget where) and as it just worked I didn’t pay much attention to it.

When it stopped working, I got sad, then got on to trying to fix it.

My Solution

There’s a bunch of suggested general solutions to this problem. A quick Google will turn up plenty of GitHub issues. Unfortunately none of them were specific to my exact error.

In my case, as best I understand it, the Braintree Web Drop In React should have compiled the dist.js file down to ES5, but is instead, in ES6. I concluded this based on this and this.

dammit-jim-im-a-coder-not-a-webpack-genius
Dammit Jim, I’m a coder not a WebPack genius

Of course, I may be wrong.

Fixing this wasn’t that hard. I just needed to read the docs.

yarn add --dev uglifyjs-webpack-plugin

For me, this added:

"uglifyjs-webpack-plugin": "^1.2.4",

To my devDependencies in package.json.

After which I updated my prod.js WebPack config as follows:

"use strict";

const webpack = require("webpack");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = function() {
  console.log("BUILDING PRODUCTION");

  return webpackMerge(commonConfig(), {

    plugins: [

      new UglifyJsPlugin({
        test: /\.js($|\?)/i,
        sourceMap: true,
        uglifyOptions: {
          mangle: {
            keep_fnames: true,
          },
          compress: {
            warnings: false,
          },
          output: {
            beautify: false,
          },
        },
      }),

  });
};

I’ve removed everything unrelated to this specific problem.

After which I could get my build to work.