PostgreSQL 13 will reach its end-of-life on November 13, 2025. So we're
upgrading before that happens.
We're also upgrading to PostgreSQL 14 in our CI. In this case, we're
using the default distribution (Trixie, as of October 2025); it doesn't
affect the development or production environments, so it's OK if use the
default one.
We've been getting an error in our CI because some the Ubuntu 24.04
image in GitHub Actions doesn't have an updated package database:
```
E: Failed to fetch mirror+file:
pool/main/g/ghostscript/libgs-common_10.02.1%7edfsg1-0ubuntu7.4_all.deb
404 Not Found
E: Failed to fetch mirror+file:
pool/main/g/ghostscript/libgs10-common_10.02.1%7edfsg1-0ubuntu7.4_all.deb
404 Not Found
E: Failed to fetch mirror+file:
pool/main/g/ghostscript/libgs10_10.02.1%7edfsg1-0ubuntu7.4_amd64.deb
404 Not Found
E: Failed to fetch mirror+file:
pool/main/g/ghostscript/ghostscript_10.02.1%7edfsg1-0ubuntu7.4_amd64.deb
404 Not Found
E: Unable to fetch some archives, maybe run apt-get update
or try with --fix-missing?
```
Running `sudo apt-get update` before trying to install a package solves
the issue.
Our tests are failing fairly consistently on CI. We
believe that this started when the most recent Github Actions
ubuntu-latest image was released[1]. Though, we don't 100% understand
why. There's some speculation[2] that the root of the issue has
something to do with Chrome/Chromedriver version 134 (which is the
version bundled with that new ubuntu-latest).
[1]: https://github.com/actions/runner-images PR#11761
[2]: https://github.com/teamcapybara/capybara Issue#2800
We've largely copied what the signon team did to get
their CI back up-and-running[^3].
[^3]: https://github.com/alphagov/signon PR#3663
In this commit we remove the version of Chrome that the
actions/runner-images image bundles for us, so that Selenium
doesn't try to use it and install an old version of Chrome and
Chromedriver.
This way we're also checking mistakes like closing tags that don't match
their opening element, which we detected by manually running HTML
Beautifier with the `-e` option, and fixed two commits ago.
Note there was a false positive in the mailer layout. We don't know the
cause. Maybe closing the ERB tag right before the HTML opening tag and
the lack of other attributes on the tag made HTML Beautifier think the
tag wasn't correctly open, but on the other hand, we have the exact same
line in other layouts where HTML Beautifier works fine. We're fixing it
by adding an HTML id attribute to the element.
We had inconsistent indentation in many places. Now we're fixing them
and adding a linter to our CI so we don't accidentally introduce
inconsistent indentations again.
Using ubuntu-latest might result in incompatibilities when this image
changes to a different version of Ubuntu. For example, the Ubuntu 24.04
image no longer includes imagemagick, meaning that we'll have to install
it manually when using Ubuntu 24.04.
We're using version 13 because it's the one included in Debian Bullseye,
which is the operating system we currently use in our Dockerfile.
For consistency, we're using the same version in GitHub Actions.
Note this image requires setting a password. Otherwise we get an error:
> Database is uninitialized and superuser password is not specified.
> You must specify POSTGRES_PASSWORD to a non-empty value for the
> superuser. For example, "-e POSTGRES_PASSWORD=password" on
> "docker run".
Since now we're setting a password in the postgres service, we also need
to provide the `PGPASSWORD` environment variable (or to specify the
password in the `database.yml` file, which we do for GitLab since it
uses a separate database configuration file). Otherwise we get an error:
```
PG::ConnectionBad: connection to server at "::1", port 5432 failed:
fe_sendauth: no password supplied (PG::ConnectionBad)
```
The "ruby" label was redundant because most our dependencies are ruby
gems. We're still keeping the "javascript" dependencies for npm
dependencies, though.
This way we'll notice syntax inconsistencies in pull requests. We
weren't doing it until now because there's no Pronto runner for MDL and
we weren't running linters individually.
Note we aren't checking the templates in the `.github` folders, since
these templates don't have a top level header in the beginning (they'll
be displayed on a page which already has a top level header) and MDL
doesn't provide a way to ignore certain rules on certain files. So we're
specifying which files to include instead of running `mdl .` (which
would also include the `node_modules` folder) or running `mdl -g .`
(which would exclude the `node_modules` folder but would include the
`.github` folder).
We were following it about half of the time and we even added it to our
former `.mdlrc` file. However, for some reason, MDL doesn't detect this
rule when specified in the `.mdlrc` file, so we didn't notice we weren't
following it in many cases.
Now that we're using a style file to configure MDL, we can enable this
rule again and apply it, since now MDL correctly includes it in its
report.
Since the page contains an <h1> tag (the title of the issue), the main
headings of this file should be level 2 headings.
We're removing the "How" section in the flaky specs template for
simplicity (and because we usually don't use this template at all).
Note that MDL reports that the first heading should be a level 1
heading, but, as mentioned earlier, this doesn't make sense in the
context of a github issue. We'll probably exclude these files from MDL
checks in order to avoid this issue.
The main reason for this change is that Pronto doesn't run in pull
requests opened by external contributors, and we haven't found how to
fix this issue.
Another reason is that Pronto only detects issues in the lines where the
changes are done, but sometimes we introduce issues related to other
lines and those aren't detected by Pronto. Also, when adding or changing
Rubocop rules, or when we update Rubocop, Pronto doesn't check whether
those rules are applied in every Ruby and ERB file, and we sometimes
forget to do so (particularly in ERB files).
So, from now, the linters will check all the code in every pull request.
Note we're now excluding the `vendor` folder in our linters because the
`setup-ruby` action actions generates a `vendor/bundle/` folder with all
our gem dependencies, and we don't want to check those files.
We had the `*` because, in the past, pull request could be opened by
either dependabot or dependabot-bot. That's no longer the case since we
started using dependabot v2.
The upload-artifact action does not support using the same artifact name
in different jobs (or in different matrix scenarios) since version 4,
which we started using in commit acfaada82. That meant that screenshots
were not uploaded correctly when two or more knapsack nodes failed.
Since we upgraded to Rails 7.0 in commmit 8596f1539, the screenshots
started to be stored in `tmp/capybara`, so the step uploading
screenshots wasn't doing anything.
SCSS Lint was based on Ruby Sass, which has been deprecated since 2018
and doesn't support some of the latest features in Dart Sass.
Since we're going to migrate to Dart Sass, we're also migrating to
Stylelint. In order to provide all the funcionality SCSS Lint had, we
also need to install a few Stylelint plugins. We also need to install
the `postcss-scss` package so Stylelint can read SCSS files.
We're still getting the linting errors we used to get in
`legislation_process.scss` because of the `max-nesting-depth` and
`selector-max-compound-selectors` rules, as well as the warnings we used
to get in `layout.scss` because the `ssb-whatsapp_app` HTML class
contains undescores in their name.
We're also getting a couple of new linting errors, which could be false
positives:
* The `scss/no-unused-private-members` rule reports errors in
`_consul_settings.scss` because of the `$-zf-*` variables, but I'm not
too worried about this one because these lines won't be necessary after
updating Foundation.
* The `scss/selector-no-redundant-nesting-selector` reports an error in
`datepicker_overrides.scss`, but removing the unnecessary nesting
selector would make it harder to understand the code (assuming it'd
work in the first place).
We've changed some files due to few differences between SCSS Lint rules
and their Stylelint equivalents:
* The `@stylistic/string-quotes` rule detects a case in `admin.scss`
that StringQuotes didn't detect.
* The `function-url-quotes` rule detects a case in `mixins/icons.scss`
that UrlQuotes didn't detect.
* The `@stylistic/declaration-bang-space-before` rule detects a case in
`sdg/goals/show.scss` that BangFormat didn't detect.
There are also a couple of rules that don't behave exactly like they
used to:
* The equivalents of SpaceBetweenParens and SpaceAfterComma don't cover
parenthesis or commas in mixin parameters; we haven't found rules that
detect these cases.
* DisableLinterReason probably has an equivalent that behaves
differently but, since we never disable linters inline, we aren't
adding its equivalent rule.
Note we're removing the SpaceAfterVariableColon rule because its
equivalent, `scss/dollar-variable-colon-space-after`, reports cases
where we add spaces to indent several variable assignments (which we do
a lot in the `_consul_settings.scss` file). We might add this rule again
if we stop aligning consecutive assignments.
We're also removing the QualifyingElement rule because its equivalent,
`selector-no-qualifying-type: true`, behaves differently. For example,
in this code:
```
a.qualifying {
color: inherit;
}
p {
&.qualifying {
color: inherit;
}
}
```
With the QualifyingElement from SCSS Lint, the first rule is incorrect
but the second one is correct. With the selector-no-qualifying-type rule
from Stylelint, on the other hand, both rules are incorrect.
Personally, I never liked the QualifyingElement rule, and we were
working around it anyway, so we aren't applying
selector-no-qualifying-type.
For reference, here's a full list of the SCSS Lint rules we had enabled
and their Stylelint equivalents.
BangFormat
"@stylistic/declaration-bang-space-after": "never"
"@stylistic/declaration-bang-space-before": "always"
BorderZero
declaration-property-value-disallowed-list:
border:
- none
ColorKeyword
color-named: "never"
DebugStatement
at-rule-disallowed-list:
- debug
DeclarationOrder
order/order:
- dollar-variables
- custom-properties
- type: at-rule
name: extend
- type: at-rule
name: include
hasBlock: false
- declarations
- type: at-rule
name: include
hasBlock: true
- rules
ElsePlacement # Apparently replaced by the combination of:
scss/at-else-closing-brace-space-after: always-intermediate
scss/at-else-empty-line-before: never
scss/at-if-closing-brace-space-after: always-intermediate
scss/at-if-closing-brace-newline-after: always-last-in-chain
EmptyLineBetweenBlocks:
ignore_single_line_blocks: true
rule-empty-line-before:
- "always-multi-line"
- ignore:
- after-comment
- first-nested
EmptyRule:
block-no-empty: true
FinalNewline
"@stylistic/no-missing-end-of-source-newline": true
HexLength
color-hex-length: "short"
HexNotation
@stylistic/color-hex-case: "lower"
HexValidation
color-no-invalid-hex: true
IdSelector
selector-max-id: 0
ImportPath
scss/load-no-partial-leading-underscore: true
scss/at-import-partial-extension: never
Indentation
"@stylistic/indentation": 2
LeadingZero
@stylistic/number-leading-zero: "always"
NameFormat
scss/at-function-pattern: "^(-?[a-z][a-z0-9]*)(-[a-z0-9]+)*$"
scss/at-mixin-pattern: "^(-?[a-z][a-z0-9]*)(-[a-z0-9]+)*$"
scss/dollar-variable-pattern: "^(-?[a-z][a-z0-9]*)(-[a-z0-9]+)*$"
scss/percent-placeholder-pattern: "^(-?[a-z][a-z0-9]*)(-[a-z0-9]+)*$"
NestingDepth
max-nesting-depth: 4
PlaceholderInExtend
scss/at-extend-no-missing-placeholder: true
PrivateNamingConvention
scss/no-unused-private-members: true
PropertySpelling
property-no-unknown: true
PseudoElement
selector-pseudo-element-colon-notation: "double"
selector-pseudo-element-no-unknown: true
SelectorDepth
selector-max-compound-selectors: 5
SelectorFormat # Not always followed; ssb-whatsapp_app
custom-property-pattern: "^([a-z][a-z0-9]*)(-[a-z0-9]+)*$"
selector-class-pattern: "^([a-z][a-z0-9]*)(-[a-z0-9]+)*$"
Shorthand
shorthand-property-no-redundant-values: true
SingleLinePerProperty
"@stylistic/declaration-block-semicolon-newline-after": always-multi-line
SingleLinePerSelector
@stylistic/selector-list-comma-newline-after": always
SpaceAfterComma
"@stylistic/value-list-comma-space-after": always
SpaceAfterPropertyColon
"@stylistic/declaration-colon-space-after": always-single-line
SpaceAfterPropertyName
"@stylistic/declaration-colon-space-before": never
SpaceAfterVariableName
scss/dollar-variable-colon-space-before: never
SpaceAroundOperator
scss/operator-no-unspaced: true
SpaceBeforeBrace
@stylistic/block-opening-brace-space-before: always
SpaceBetweenParens
"@stylistic/function-parentheses-space-inside": never
"@stylistic/selector-attribute-brackets-space-inside": never
"@stylistic/selector-pseudo-class-parentheses-space-inside": never
"@stylistic/media-feature-parentheses-space-inside": never
StringQuotes
"@stylistic/string-quotes": double
TrailingSemicolon
"@stylistic/declaration-block-trailing-semicolon": always
TrailingWhitespace # Note it was enabled by the gem and not by us
"@stylistic/no-eol-whitespace": true
TrailingZero
"@stylistic/number-no-trailing-zeros": true
UnnecessaryMantissa
"@stylistic/number-no-trailing-zeros": true
UnnecessaryParentReference
scss/selector-no-redundant-nesting-selector: true
UrlQuotes
function-url-quotes: always
VendorPrefixes
value-no-vendor-prefix: true
selector-no-vendor-prefix: true
property-no-vendor-prefix: true
at-rule-no-vendor-prefix: true
media-feature-name-no-vendor-prefix: true
ZeroUnit:
length-zero-no-unit: true
Note that, even if we're excluding the `node_modules/` folder from
version control, we aren't adding it to Capistrano's shared folders
because, when `node_modules` is a symbolic link, NPM removes it when
running `npm install`.
We're choosing version 18 because if offers support for SSL 3, just
like Ruby 3.1 does.
Note we're symlinking a .nvmrc file as well, in order to make it
compatible with NVM. While the .nvmrc and .node-version files use
different formats, they both support the syntax we're using to
define the version.
The code to install Node.js in the Dockerfile is a simplification of the
code in the Rails Dockerfile template [1].
[1] https://github.com/rails/rails/blob/04c97aec8a/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt#L25
We originally set a daily interval because we hadn't updated our gem
dependencies for a year.
However, we usually wait a few days/weeks between the time a gem is
released and the moment we update it, and there are gems releasing new
versions every few days, so maintaining daily updates would become
tedious quickly.
So we're now doing it once a month. We're also increasing the limit of
open pull requests so we don't need to worry about whether dependabot is
opening pull requests for every dependency.
Since we changed the way we integrate coveralls in commit 8ed8cc8b9,
we're getting 6 additional checks displayed in our pull requests.
We don't need these checks, and they only add noise. The only reason we
use coveralls is to know the test coverage in our master branch.
So we're changing the code so coveralls only runs on the master branch.
There's also a chance that the test suite will be faster because it
doesn't need to keep track of the coverage, although I haven't noticed any
significant differences during my tests.
I haven't found a more elegant way to say that a certain step should
only be run on push on master, so I'm setting the environment variable
we were already using.