about summary refs log tree commit diff
path: root/users/wpcarro/website/blog/posts/importing-subtrees.md
blob: e1070fc3b922b0a238a7218cbd72f57332f23b4e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
## Background

Sometimes you need to merge one Git repo into another. This is a common task
when administrating a monorepo.

Here's a checklist that I follow:

1. Detect leaked secrets.
1. Rotate leaked secrets.
1. Purge leaked secrets from repo history.
1. Create mainline references to branches (for deployments).
1. Subtree-merge into the target repo.
1. Format the code.
1. Celebrate!

## Secrets

**Note:** If you notice any leaked secrets, first and foremost rotate them
before moving on...

`gitleaks` supports `gitleaks protect`, but that doesn't seem to work for `WRN`
level leaks, which in my experience often contain sensitive cleartext. We can
use `git-filter-repo` to purge the cleartext from our repo history.

Let's make a `secrets.txt` file that we can feed `git-filter-repo`:

```shell
λ gitleaks detect -r /tmp/secrets.json
λ jq -r 'map_values(.Secret) | .[]' /tmp/secrets.txt
```

Now for the redacting...

```shell
λ git-filter-repo --force --replace-text /tmp/secrets.txt
```

Verify that the secrets were removed.

```shell
λ rg --hidden '\*\*\*REMOVED\*\*\*'
λ gitleaks detect -v
```

Looks good! Let's move on to support the adopted repo's deploy strategy.

## Supporting Deploys

While deploying services when someone pushes to a given branch is a common
deployment strategy, branch-based deployment don't make a whole lot of sense in
a monorepo.

When adopting another repo, you'll typically encounter a Github Action
configuration that contains a section like this:

```yaml
on:
  push:
    - staging
    - production
```

In our monorepo, `staging` and `production` don't exist. And I don't think we
want to support them either. `staging` and `production` are ambiguous in a
monorepo that hosts multiple services each of which likely having its own notion
of `staging` and `production`.

Doing "pinned releases" where a service is deployed from a `git` revision from
the mainline branch works well in these scenarios. In order to support this we
need to make sure the adopted repo has references to

`git subtree add` asks us to define which branch it should use when grafting the
repository onto our monorepo. We'll use `main` (or whatever the mainline branch
is).

In order to support the *current* deployments while migrating to a pinned
release strategy, we have to ensure that `main` has a commit containing the same
tree state as `staging` *and* another commit containing the same tree state as
`production`. Let's do that!

```shell
λ git checkout main # ensure you're on the main branch
λ git diff main staging >/tmp/main-to-staging.patch
λ git diff main production >/tmp/main-to-production.patch
```

### staging

```shell
λ git apply /tmp/main-to-staging.patch
λ git add . && git commit # chore: main -> staging
λ git revert HEAD
λ git commit --amend # revert: staging -> main
```

### production

```shell
λ git apply /tmp/main-to-production.patch
λ git add . && git commit # chore: main -> production
λ git revert HEAD
λ git commit --amend # revert: production -> main
```

Now let's check our work:

```shell
λ git log --oneline
38f4422 revert: production -> main
f071a9f chore: main -> production
02ea731 revert: staging -> main
308ed90 chore: main -> staging
```

When we go to support pinned releases we can do something like so:

```json
{
  "staging": "308ed90",
  "production": "f071a9f"
}
```

## Subtree Merge

Now the repo is ready to be merged.

```shell
λ git subtree add --prefix=foo/bar/baz path/to/baz main
λ git commit --amend # subtree: Dock baz into monorepo!
```

## Formatting

Some CI enforces code formatting standards, so you may need to run that:

```shell
λ repofmt
λ git add . && git commit # chore(fmt): Format the codes
```

Lastly, if you need the latest monorepo code from `origin/main` before opening a
pull request, the following should work:

```shell
λ git fetch origin main && git rebase origin/main --rebase-merges --strategy=subtree
```