Add notes from zet.oliverdavies.uk

This commit is contained in:
Oliver Davies 2025-08-04 01:09:00 +01:00
parent a59b05feb4
commit 1dd6f82308
34 changed files with 713 additions and 0 deletions

View file

@ -10,6 +10,8 @@ sculpin_content_types:
permalink: /blog/:basename/
presentations:
permalink: /presentations/:basename/
zets:
permalink: /zet/:basename-:title/
services:
App\Experience\TwigExtension\ExperienceTwigExtension:

View file

@ -0,0 +1 @@
{% extends "page" %}

34
source/_zets/1.md Normal file
View file

@ -0,0 +1,34 @@
---
title: Ellipsis in pager template fails accessibility tests
date: '2024-08-21 12:00:00'
tags:
- Drupal
- Accessibility
- ARIA
- Software Development
- Web Development
---
```html
<ul> and <ol> must only directly contain <li>, <script> or <template> elements
Ensures that lists are structured correctly
more informationLink opens in a new window
Element Location:
.pager__items
<ul class="pager__items js-pager__items">
To solve this problem, you need to fix the following:
List element has direct children that are not allowed: [role=presentation]
Related Node
<li class="pager__item pager__item--ellipsis" role="presentation"></li>
```
Proposed resolution:
Replace `role="presentation"` with `aria-label="additional pages"`.
## Links
* <https://www.drupal.org/project/drupal/issues/3348552> (issue, currently "needs work").
* <https://www.drupal.org/node/3399874> (draft change record).

15
source/_zets/10.md Normal file
View file

@ -0,0 +1,15 @@
---
title: git-instafix
date: 2024-08-22 12:28:26
tags: [Software Developmemt, Git]
---
[git instafix](https://github.com/quodlibetor/git-instafix) is a tool that allows you to easily amend (or fix) previous Git commits.
How it works:
- Commit your original changes.
- Make more changes and stage them with `git add -p`.
- Run `git-instafix` (or `git instafix`, but this doesn't tab complete for me).
- Select the commit you want to amend.
- `git-instafix` will amend it and move you back to the HEAD commit.

13
source/_zets/11.md Normal file
View file

@ -0,0 +1,13 @@
---
title: Software Development Graduate website
date: 2024-08-22 14:09:53
tags: [Sculpin, PHP, Transport for Wales]
---
Shelley, the TfW Software Development Graduate, recently published [her own website](https://shell-web-dev.netlify.app) as her personal project to showcase the work she's been doing.
It's built with Sculpin and Tailwind CSS and hosted on Netlify.
## Links
- <https://github.com/SeaShell92/shell-website>

14
source/_zets/12.md Normal file
View file

@ -0,0 +1,14 @@
---
title: The Gin admin theme
date: 2024-08-22 14:22:56
tags: [Drupal]
---
Gin is a contrib admin theme for Drupal, and it has a dark mode.
It also has a companion toolbar module.
## Links
- <https://www.drupal.org/project/gin>
- <https://www.drupal.org/project/gin_toolbar>

8
source/_zets/13.md Normal file
View file

@ -0,0 +1,8 @@
---
title: 'TODO: Try zellij as a potential tmux alternative'
date: 2024-08-23 16:52:06
tags: [Linux, Shell, Zellij, tmux, Command-Line]
---
- <https://zellij.dev>
- <https://mynixos.com/search?q=zellij>

29
source/_zets/14.md Normal file
View file

@ -0,0 +1,29 @@
---
title: "Abbreviations are better than aliases"
date: 2024-08-25 01:02:35
tags: [Shell, zsh, Linux]
---
Aliases are a way to shorten long or complicated commands or to easily add additional arguments when running commands.
Common aliases are `gs` for `git status`, `a` for `artisan` and `dr` for `drush`.
I've been experimenting with Zellij today and have written aliases like `zl` for `zellij list-sessions`, but have also added extra arguments such as `zellij list-sessions | sort | grep -v EXITED` to sort the sessions and filter any exited sessions.
Running these aliases means it's easier and quicker for me to run these commands.
The issue with aliases, I think, is that you can forget that the underlying commands are if you only type `gs` or `zl`.
It's also not easy when giving demos, pair programming for others to see and understand the commands that are being run.
Instead of aliases, I mostly use abbreviations that expand automatically after pressing the space key.
That way, I and others get to see and understand the commands being run.
Note: I originally saw this done by [Sebastian Daschner](https://blog.sebastian-daschner.com/entries/zsh-aliases) and I originally used his ZSH expansion code, but now use [zsh-abbr](https://zsh-abbr.olets.dev). There are settings for this in Nix/Home Manager.
## Links
- <https://github.com/olets/zsh-abbr>
- <https://mynixos.com/search?q=zsh-abbr>
- <https://github.com/opdavies/dotfiles.nix/blob/main/lib/shared/modules/zsh/abbreviations.zsh>

32
source/_zets/15.md Normal file
View file

@ -0,0 +1,32 @@
---
title: "Data attributes and feature flags"
date: 2024-08-26 21:34:15
tags: [Web Development, Software Development, Feature Flags, Drupal, CSS, PHP]
---
I recently used the [Feature Toggle module](https://www.drupal.org/project/feature_toggle) to set a data attribute which enabled some new styling for buttons.
## The PHP code
In the theme's .theme file:
```php
/**
* Implements hook_preprocess_html().
*/
function mytheme_preprocess_html(array &$variables): void {
$variables['attributes']['data-use-new-button-styles'] = \Drupal::service('feature_toggle.feature_status')->getStatus('use_the_new_button_styling');
}
```
If the feature toggle is enabled, this adds a `data-use-new-button-styles=""` attribute to the `body` element.
## The CSS code
```css
[data-use-new-button-styles] .btn-primary {
background-color: red;
}
```
I like using feature flags and was happy to find a way to use them for adding this new CSS.

29
source/_zets/16.md Normal file
View file

@ -0,0 +1,29 @@
---
title: "Sending POST requests with curl"
date: 2024-08-27 20:11:37
tags: [Linux, Command-Line, curl]
links:
- https://stackoverflow.com/questions/7172784/how-do-i-post-json-data-with-curl
- https://linuxize.com/post/curl-post-request
---
```shell
curl --header "Content-Type: application/json" \
--insecure \
--data '{"foo": "bar"}' \
https://example.docker.localhost/webhook
```
`--request POST` is implied if `--data` is passed.
`--insecure` skips SSL validation, e.g. if using a self-signed certificate.
Also, from `man curl`:
```shell
--json works as a shortcut for passing on these three options:
--data [arg]
--header "Content-Type: application/json"
--header "Accept: application/json"
```

9
source/_zets/17.md Normal file
View file

@ -0,0 +1,9 @@
---
title: "Format JSON file in vim"
date: 2024-08-27 20:21:17
tags: [Vim, Neovim, JSON, jq]
links:
- https://vi.stackexchange.com/a/19950
---
Use `:%!jq` to format/beautify a JSON file within vim using `jq`.

27
source/_zets/18.md Normal file
View file

@ -0,0 +1,27 @@
---
title: Using data attributes with Tailwind CSS
date: 2024-08-27 23:39:23
tags: [HTML, CSS, Tailwind CSS]
---
Adam Wathan mentioned this in his "Tailwind CSS: It looks awful, and it works" at Rails World 2023.
This is the link to the video: <https://www.youtube.com/watch?v=TNXM4bqGqek>.
The data attribute example starts at 21:00, which shows using arbitrary classes for data attributes.
## Examples
With the attribute on the same element:
```html
<button class="data-[loading]:text-transparent" data-loading>
```
With the attribute on a parent element (group):
```html
<button class="group data-[loading]:text-transparent" data-loading>
<span class="hidden group-data-[loading]:flex">...</span>
</button>
```

9
source/_zets/19.md Normal file
View file

@ -0,0 +1,9 @@
---
title: Merging activities in Strava
date: 2024-08-30 12:52:35
tags: [Running]
---
Activities with multiple entries can be exported from the Strave website as .gpx files and combined using websites like <https://gotoes.org/strava>.
Then, the combined file can be re-uploaded to Strava as a single activity.

9
source/_zets/2.md Normal file
View file

@ -0,0 +1,9 @@
---
title: sshs
date: 2024-08-21 18:17:29
tags: [Linux, SSH]
---
A terminal user interface for SSH, based on your `~/.ssh/config` file.
<https://github.com/quantumsheep/sshs>

51
source/_zets/20.md Normal file
View file

@ -0,0 +1,51 @@
---
title: Sorting parameter arguments and array keys in Vim
date: 2024-09-03 19:03:27
tags: [PHP, Vim, Software Development]
---
Given this PHP code (for example):
```php
$a = [
'b' => 2,
'a' => 4,
'k' => 5,
'f' => 1,
'd' => 3,
];
$b = new Foo(
b: 2,
a: 4,
k: 5,
f: 1,
d: 3,
);
```
You can use `vi(` and `vi[` to visually select the text within the array or parentheses, and then ":sort" to sort the text within the selected range.
Using `vi(` and `vi[` instead of `vi)` and `vi]` excludes the lines that around the text to sort.
After running these commands, you should get this:
```php
$a = [
'a' => 4,
'b' => 2,
'd' => 3,
'f' => 1,
'k' => 5,
];
$b = new Foo(
a: 4,
b: 2,
d: 3,
f: 1,
k: 5,
);
```
At some point, I'll look into saving this as a macro with a keymap like `<leader>sa` (sort arguments) and `<leader>sk` (sort keys).

21
source/_zets/21.md Normal file
View file

@ -0,0 +1,21 @@
---
title: Extracting a custom module with a Git subtree
date: 2024-09-04 20:12:00
tags: [Git]
---
To extract a directory from a repository and keep the history, you can use the `git subtree split` command to create a new branch:
```shell
git subtree split --prefix=web/modules/custom/my_module --branch=split
Created branch 'split'
17835f24069061326e9e065f076afd67434e1b2f
```
This will create a new branch with just the contents of the given directory.
This can be pushed to a different repository:
```shell
git push git@github.com:opdavies/new-repo.git split:main
```

25
source/_zets/22.md Normal file
View file

@ -0,0 +1,25 @@
---
title: Setting max_allowed_packet in MariaDB
date: 2024-09-10 08:51:49
tags: [MariaDB, MySQL, Databases]
links:
- https://github.com/MariaDB/mariadb-docker/issues/467
---
If you need to set `max_allowed_packet` for MariaDB, you can specify it in Docker Compose as a `command`.
For example:
```yaml
services:
mariadb:
image: mariadb:10.9.3
command: --max_allowed_packet=5GB
volumes:
- "./backend_db/mariadb.storage:/var/lib/mysql"
ports:
- "3306:3306"
env_file: ./backend_db/mariadb.env
```
No Dockerfiles or configuration files needed.

33
source/_zets/23.md Normal file
View file

@ -0,0 +1,33 @@
---
title: "Error: unsupported tarball input attribute 'lastModified'"
date: 2024-09-10 22:35:41
tags: [Linux, Nix, NixOS]
links:
- https://discourse.nixos.org/t/error-unsupported-tarball-input-attribute-lastmodified/49435/4
---
Yesterday, my laptop died and I needed to resurrect a spare laptop to work on.
This laptop was running an outdated version of NixOS, old packages, a different window manager and other things, so I tried to update it to bring it up to date so it's up to date with my dead laptop's configuration.
Doing so, though, gave me this error:
> Error: unsupported tarball input attribute lastModified
A normal `sudo nixos-rebuild switch` worked, but trying to use a Flake was generating the error.
I was able to get it working after running these commands from a Discourse thread:
```shell
nix shell "github:NixOS/nix/2.18.4"
nix build .#nixosConfigurations.nixedo.config.system.build.toplevel
sudo nix-env --profile /nix/var/nix/profiles/system --set ./result
./result/bin/switch-to-configuration switch
```
I also needed to add `--experimental-features 'nix-command flakes'` to the `nix` commands to get them to run.
Once this finished, I was able to reboot and get a running up-to-date configuration.

16
source/_zets/24.md Normal file
View file

@ -0,0 +1,16 @@
---
title: POSTing data from a JSON file
date: 2024-09-14 13:39:01
tags: [Linux, curl]
related:
- Sending POST requests with curl
use: [zets]
---
When sending JSON data in a POST request, instead of writing it inline with the `--data` or `--json` option, a filename can be entered - prefixed with an `@` symbol.
For example, to POST the data from a data.json file:
```shell
curl --insecure --json @data.json https://mysite.com/webhook
```

40
source/_zets/25.md Normal file
View file

@ -0,0 +1,40 @@
---
title: Drupal's Lenient Composer endpoint
date: 2024-09-14 17:29:15
tags: [Drupal, PHP, Composer]
links:
- https://www.drupal.org/project/content_access
- https://www.drupal.org/docs/develop/using-composer/using-drupals-lenient-composer-endpoint
- https://github.com/mglaman/composer-drupal-lenient
- https://github.com/cweagans/composer-patches
---
Today, I tried to add the Content Access module to a Drupal 11 project, but there's no Drupal 11-compatible version.
It's a simple update - just adding `^11` to the content_access.info.yml file, but even with Composer Patches installed, it's not possible to download and patch the module using the normal Drupal 8+ Composer repository.
This is possible, though, with the Lenient Composer endpoint.
> This composer plugin lets you specify an allowlist of packages where you are willing to break the version constraint.
> Together with the Composer Patches Plugin, this allows you to install any Drupal extension, even if the version constraint hasn't been officially updated yet. Of course the code may still need to be patched for deprecations.
To add the lenient endpoint and add the Content Access plugin:
```shell
composer require mglaman/composer-drupal-lenient
composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/content_access"]
composer req drupal/content_access
```
Note that I'd already included this section to my composer.json file to add a patch to make the module installable on Drupal 11:
```json
"extra": {
"composer-patches": {
"drupal/content_access": {
"Drupal 11 support": "./tools/patches/content_access/d11_support.patch"
}
},
},
```

11
source/_zets/26.md Normal file
View file

@ -0,0 +1,11 @@
---
title: Should I learn React?
date: 2024-09-25 19:33:28
tags: [React, JavaScript, Web Development]
---
I've usually used Vue.js or, more recently, Stimulus when I write JavaScript.
I'm familiar with JSX from using Astro for a previous version of my website and have experimented with Next.js, but haven't really learned React properly - but maybe I should.
School of Code - the bootcamp I mentor for - teach React and Drupal's new Experience Builder is written with React, so maybe now is the time.

14
source/_zets/27.md Normal file
View file

@ -0,0 +1,14 @@
---
title: "TODO: re-evaluate Storybook"
date: 2024-09-30 21:36:38
tags: [Front-End, Web Development]
links:
- https://fractal.build
- https://github.com/frctl/fractal/issues/1167
- https://storybook.js.org
- https://www.drupal.org/project/storybook
---
I've used Fractal a lot for the past several years to build design systems and component libraries for projects, but as the last commit was in January 2023 and there was an issue saying the maintainers were stepping back and the project is unmaintained, it may be time to look at something else.
Storybook seems to be the popular choice and it has an integration module with Drupal.

15
source/_zets/28.md Normal file
View file

@ -0,0 +1,15 @@
---
title: Excluding local files from Git
date: 2024-11-07 10:10:19
tags: [git]
---
As well as using a shared `.gitignore` file in a directory, I've had a convention of putting my own files I want to ignore in an `.ignored` directory and having that in a [global excludes file](https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreexcludesFile).
This works for most situations, but sometimes files need to be in their expected location and not in a sub-directory.
I also have scripts that check for a file in both places before performing an action.
Git, though, has a built-in way to do this.
Any files added to `.git/info/exclude` will automatically be excluded by Git and because this is in my `.git` directory, it won't affect anyone else's version.

17
source/_zets/29.md Normal file
View file

@ -0,0 +1,17 @@
---
title: Drupal Recipe Unpacking
date: 2025-07-12 22:57:36
tags:
- drupal
links:
- text: New Recipe Unpack composer plugin
url: https://www.drupal.org/node/3522189
- text: This Blog Is No Longer on Drupal CMS, and That's a Good Thing
url: https://joshuami.com/blog/2025/recipe-unpack-blog-no-longer-drupal-cms-and-thats-good-thing
---
Drupal now supports recipe unpacking.
> The Recipe Unpacking system is a Composer plugin that manages "drupal-recipe" packages. Recipes are special Composer packages designed to bootstrap Drupal projects with necessary dependencies. When a recipe is required, this plugin "unpacks" it by moving the recipe's dependencies directly into your project's root composer.json, and removes the recipe as a project dependency.
This is added automatically for new projects, but can also be added to existing projects using `composer require drupal/core-recipe-unpack` and the `drupal:recipe-unpack` command.

48
source/_zets/3.md Normal file
View file

@ -0,0 +1,48 @@
---
title: Writing bash scripts with Nix
date: 2024-08-21 18:30:24
tags: [Bash, Linux, Nix]
---
- Variables, such as username, can be injected.
- `shellcheck` is run automatically with `pkgs.writeShellApplication`.
- Packages defined in `runtimeInputs` will be automatically injected.
<https://github.com/opdavies/dotfiles.nix/blob/a1c356a1f576f041301a5c41d6ba7d0f098d0edb/lib/shared/scripts/export-video-list.nix>
```nix
{ pkgs, username, ... }:
{
name = "export-video-list";
runtimeInputs = with pkgs; [
jq
tree
udisks
];
text = ''
device_name="/dev/sda2"
device_label="UNTITLED"
source_path="/run/media/${username}/$device_label"
# If the source path doesn't exist, try mounting the device.
if [[ ! -d "$source_path" ]]; then
${pkgs.udisks}/bin/udisksctl mount -b "$device_name"
fi
# Exit early if the source path still doesn't exist.
if [[ ! -d "$source_path" ]]; then
echo "Error: $source_path not found."
exit 1
fi
output_file="$HOME/Documents/videos.json"
${pkgs.tree}/bin/tree -J "$source_path/Videos" | ${pkgs.jq}/bin/jq . > "$output_file"
${pkgs.jq}/bin/jq . < "$output_file"
'';
}
```

17
source/_zets/30.md Normal file
View file

@ -0,0 +1,17 @@
---
title: Counting tags
date: 2025-07-12 23:06:44
tags:
- git
links:
- text: My get-tags scrpt
url: https://code.oliverdavies.uk/opdavies/nix-config/src/commit/a620888277654fa413d14413d0d2a4ce82d1ad56/packages/get-tags.nix
- text: My count-tags script
url: https://code.oliverdavies.uk/opdavies/nix-config/src/commit/a620888277654fa413d14413d0d2a4ce82d1ad56/packages/count-tags.nix
---
To see all the tags in a repository, run `git tag`.
To see a filtered list of tags, such as tags that start with a specific year, run `git log | grep 2025`.
To count these, run `git log | grep 2025 | wc -l`.

16
source/_zets/31.md Normal file
View file

@ -0,0 +1,16 @@
---
title: Using Vim filters
date: 2025-07-12 23:13:54
tags:
- vim
---
In Vim, insert some text into a file, such as `date`.
In insert mode, press `!!` to switch to command mode and see a `:.!` prompt.
Enter a command, like `bash` to execute on the given text.
The text is replaced with the result from the command - `Sat Jul 12 23:16:29 BST 2025`.
This also works for other commands, such as typing `1+2+3` and running `:.!bc` will return 6.

60
source/_zets/32.md Normal file
View file

@ -0,0 +1,60 @@
---
title: Drupal Bundle classes
date: 2025-07-12 23:25:52
tags:
- drupal
- php
---
By overridding a given bundle type:
```php
<?php
// modules/opd_presentations/opd_presentations.module
function opd_presentations_entity_bundle_info_alter(array &$bundles): void {
if (isset($bundles['node'])) {
$bundles['node'][Presentation::NODE_TYPE]['class'] = Presentation::class;
}
if (isset($bundles['paragraph'])) {
$bundles['paragraph'][Event::PARAGRAPH_TYPE]['class'] = Event::class;
}
}
```
I can write my own bundle classes that extend `Node`, `Paragraph`, etc, with its own custom methods and behaviour.
```php
<?php
// modules/opd_presentations/src/Presentation.php
declare(strict_types=1);
namespace Drupal\opd_presentations;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\paragraphs\ParagraphInterface;
final class Event extends Paragraph implements ParagraphInterface {
public const PARAGRAPH_TYPE = 'event';
public function getEventDate(): string {
/** @var non-empty-string */
return $this->get('field_date')->value;
}
public function getEventName(): string {
/** @var non-empty-string */
return $this->get('field_event_name')->value;
}
public function isPast(): bool {
return $this->getEventDate() < strtotime('today');
}
}
```

14
source/_zets/4.md Normal file
View file

@ -0,0 +1,14 @@
---
title: Drush now uses Laravel Prompts
date: 2024-08-21 18:43:15
tags: [Drupal, Drush, Laravel, PHP]
---
By Jess:
> Very cool to see Drush (Drupal's goto CLI tool) using Laravel Prompts! It was always a goal to make it work in any PHP application, not just Laravel.
- <https://x.com/jessarchercodes/status/1826033275196027268>
- <https://github.com/drush-ops/drush/pull/5823>
- <https://github.com/drush-ops/drush/commit/f2e39540cbd7cd39d6e7417bc91885a4d8e4fd39>
- <https://github.com/drush-ops/drush/commit/72da139dcbd74b623fb6b41adc92e292b07c9334>

17
source/_zets/5.md Normal file
View file

@ -0,0 +1,17 @@
---
title: Using code snippets for effective live demos
date: 2024-08-21 18:53:51
tags: [Vim, Neovim, Public Speaking, Laravel, PHP, Jenkins]
---
[This tweet](https://x.com/DCoulbourne/status/1820068237226262810) mentions using code snippets whilst doing live demos to make them easier and less risky:
> Writing hella VSCode snippets for my @LaraconUS talk.
>
> I saw @joetannenbaum live coding with safety and ease in India and knew I'd need some of that swag for US.
Joe's talk was recorded but won't be published until after LaraconUS, but I also remembered [this talk by Micheal Heap at PHP UK 2018](https://www.youtube.com/watch?v=kuBD3p20oyE), where he used Vim snippets and shell aliases to show how to go from nothing to a deployed Laravel application using Jenkins.
I'd like to use this approach more when doing my live coding demos, such as in my Sculpin talk.
TODO: update this note with a link to Joe's talk once it's published.

16
source/_zets/6.md Normal file
View file

@ -0,0 +1,16 @@
---
title: Unveiling Laravel Prompts
date: 2024-08-22 08:09:22
tags: [PHP, Laravel, Drush]
---
Now [Drush is using Laravel Prompts](../4), I should re-watch this video from Laracon US last year where Prompts was unveiled by Jess Archer:
<https://www.youtube.com/watch?v=PW-2_-KxF-8>
I think it's great that Prompts was intended to be used in any PHP project, not just Laravel, and this is a great example of the Drupal ecosystem "getting off the island".
## Links
- [Laravel Prompts documentation](https://laravel.com/docs/11.x/prompts)
- [Laravel Prompts GitHub repository](https://github.com/laravel/prompts)

23
source/_zets/7.md Normal file
View file

@ -0,0 +1,23 @@
---
title: Scaling personal projects
date: 2024-08-22 09:00:00
tags: [PHPSW, Public Speaking, Software Development]
---
Here are some quick notes from Ferdie De Oliveria's talk, "Scaling Personal Projects", at [PHP South West last week](https://www.meetup.com/php-sw/events/302521220):
- Create the project and change the name later. Don't let the project name be a blocker.
- Identify key features and functionality. Use a tool like a Trello board to capture and manage tasks.
- Ponder on the challenges.
- Have a growth mindset.
- Divide tasks into heavy work, medium work and quick wins.
- Fail fast, fail early.
- Refactor.
- Iterate.
- If people don't like it, make it better.
- Consistency is key.
- Practice and perseverance.
<https://acumenlogs.com>
TODO: add links to the slides and video of the talk once they are released.

13
source/_zets/8.md Normal file
View file

@ -0,0 +1,13 @@
---
title: One of my earliest Tailwind CSS projects
date: 2024-08-22 09:01:00
tags: [CSS, Tailwind CSS, PHPSW]
---
[The PHPSW website](https://phpsw.uk) is one of the earliest Tailwind CSS projects I did, at least that I can remember.
Tailwind CSS [was added to the project](https://github.com/phpsw/phpsw-ng/commit/879c23153b1f80b9b037cf6299a9bc17a63b66bd) on the 13th of November, 2017 (almost 7 years ago), and the yarn.lock file shows the version as 0.1.6.
0.1.0 was only released [on the 1st of November](https://github.com/tailwindlabs/tailwindcss/releases/tag/v0.1.0), and 1.0.0 wouldn't be released [until May 2019](https://github.com/tailwindlabs/tailwindcss/releases/tag/v1.0.0).
[Here's a photo](https://x.com/opdavies/status/968224364129906688) from an evening when Dave, Kat and I were working on it at the Lamp Bristol office.

15
source/_zets/9.md Normal file
View file

@ -0,0 +1,15 @@
---
title: Git remotes can have more than one URL
date: 2024-08-22 09:43:42
tags: [Software Development, Git]
---
If you want to have multiple URLs for a single remote so `git push origin ...` will push to multiple remotes, such as GitHub and GitLab, or a personal repository and a client repository, you can do `git remote set-url --add origin <url>`.
Then `git remote -v` should show something like this:
```plain
origin git@github.com:opdavies/zet.oliverdavies.uk (fetch)
origin git@github.com:opdavies/zet.oliverdavies.uk (push)
origin git@gitlab.com:opdavies/zet.oliverdavies.uk.git (push)
```