mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 13:13:45 -05:00
Merge pull request #5468 from overleaf/bg-fix-feature-override-script
fix feature override script GitOrigin-RevId: f123843e1ed40b90f55a32d687a2ade4a5b44a05
This commit is contained in:
parent
89dfcaf528
commit
acc9a1ace1
5 changed files with 296 additions and 3 deletions
3
services/web/copybara/.gitignore
vendored
Normal file
3
services/web/copybara/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.ssh
|
||||||
|
.ssh/
|
||||||
|
.cache/
|
166
services/web/copybara/README.md
Normal file
166
services/web/copybara/README.md
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
# Copybara Overleaf sync
|
||||||
|
|
||||||
|
[Copybara](https://github.com/google/copybara) is a utility for syncing one
|
||||||
|
git repository with another, while performing modifications, such as removing
|
||||||
|
directories. We use this to keep a public OSS mirror of our web repo, but with
|
||||||
|
the proprietary modules directory removed.
|
||||||
|
|
||||||
|
We also use copybara to import Pull Requests from the public mirror to our private
|
||||||
|
repo, while preserving attribution for the original contributor.
|
||||||
|
|
||||||
|
|
||||||
|
## Running a sync locally
|
||||||
|
|
||||||
|
You will need a copy of the `sharelatex/copybara` container, which can be pulled
|
||||||
|
in, or built from the [copy.bara project](
|
||||||
|
https://github.com/google/copybara#getting-started-using-copybara):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> git clone git@github.com:google/copybara.git
|
||||||
|
> cd copybara
|
||||||
|
> docker build --rm -t sharelatex/copybara .
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a `docker-compose.yml` file in this directory which configures
|
||||||
|
everything. We mount out the copybara cache directory so we don't need to do a
|
||||||
|
full git clone each time. Check the file for further instructions on running
|
||||||
|
a sync from your local machine or initialising a sync.
|
||||||
|
|
||||||
|
The `.ssh` directory in this directory should have a private key with GitHub
|
||||||
|
access placed into it have github.com pre-authorized. Your personal `.ssh` will
|
||||||
|
do the job, and changes in the target repo will maintain the original author.
|
||||||
|
|
||||||
|
## Initializing a sync
|
||||||
|
|
||||||
|
In order to initialize a sync with a new repository we'll instruct copybara to
|
||||||
|
start synchronizing from the initial commit:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
copybara:
|
||||||
|
...
|
||||||
|
environment:
|
||||||
|
...
|
||||||
|
COPYBARA_OPTIONS: "--init-history"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: If the repository is not empty the synchronization will start by
|
||||||
|
removing all the existing content.
|
||||||
|
|
||||||
|
## Fixing a bad state
|
||||||
|
|
||||||
|
By default, copy.bara expects to find some metadata in the destination repo
|
||||||
|
commits which it wrote on the last run. This tells it where to pick up syncing
|
||||||
|
any new changes in the source repo. If things get in a bad state, you can provide
|
||||||
|
with an explicit reference to a commit in the source repo to start replaying
|
||||||
|
commits from. Add the following to the `docker-compose.yml` config:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
copybara:
|
||||||
|
...
|
||||||
|
environment:
|
||||||
|
...
|
||||||
|
COPYBARA_OPTIONS: "--last-rev=COMMIT_SHA_FROM_SOURCE_REPO"
|
||||||
|
```
|
||||||
|
|
||||||
|
If the destination repo gets out of sync in some way, reset its master branch
|
||||||
|
to a point when things were in a good state, and then do a re-sync as above,
|
||||||
|
but with the last-rev set to the corresponding good commit in the source repo.
|
||||||
|
|
||||||
|
## Running a sync in CI
|
||||||
|
|
||||||
|
The same `sharelatex/copybara` image and copybara config files is used by
|
||||||
|
Jenkins to perform the sync at the end of a successful CI build of master. See
|
||||||
|
the `Jenkinsfile` in the top level directory for this.
|
||||||
|
|
||||||
|
|
||||||
|
## Importing a PR from public to private
|
||||||
|
|
||||||
|
We can import a public PR using the `importPullRequest` workflow.
|
||||||
|
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
#### 1: Get a Github API key
|
||||||
|
|
||||||
|
You need a Github API key to manipulate pull requests via the Github api.
|
||||||
|
|
||||||
|
- Open https://github.com/settings/tokens
|
||||||
|
- Create a new token with the `repo` scope turned on
|
||||||
|
- Open `~/.git-credentials` and add a line like this: `https://user%40example.com:<TOKEN>@github.com`
|
||||||
|
- Note that the `@` in the email address is encoded as `%40`
|
||||||
|
|
||||||
|
This `~/.git-credentials` file will be mounted into the copybara container by
|
||||||
|
docker-compose.
|
||||||
|
|
||||||
|
|
||||||
|
### Running the import job
|
||||||
|
|
||||||
|
Run copybara with `docker-compose run`:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker-compose run \
|
||||||
|
-e COPYBARA_WORKFLOW=importPullRequest \
|
||||||
|
-e COPYBARA_SOURCEREF=<PR_NUMBER> \
|
||||||
|
-e COPYBARA_OPTIONS='--github-destination-pr-branch <BRANCH_NAME>'
|
||||||
|
copybara copybara
|
||||||
|
```
|
||||||
|
|
||||||
|
Change <PR_NUMBER> to the number of the public pull request.
|
||||||
|
|
||||||
|
Change <BRANCH_NAME> to a suitable name for the new private branch, example: 'import-pr-xyz'.
|
||||||
|
(Note, there's no `=` between the flag and the branch name)
|
||||||
|
|
||||||
|
This will create a new PR on the private repo with the content from the public PR.
|
||||||
|
When this private PR is eventually merged and synced back to the public repo, the
|
||||||
|
original public PR will close automatically, and the changes will be attributed to
|
||||||
|
the original committer.
|
||||||
|
|
||||||
|
### Merging the PR in the private repo
|
||||||
|
|
||||||
|
In order to maintain correct attribution it's **important to squash the changes**, otherwise attribution might not be reflected properly.
|
||||||
|
|
||||||
|
|
||||||
|
### Attaching the PR to a particular parent commit
|
||||||
|
|
||||||
|
The copybara process will usually figure out the appropriate place to begin the new PR from, but if you want
|
||||||
|
to specify the parent commit explicitly, you can set the following flag in `COPYBARA_OPTIONS`:
|
||||||
|
|
||||||
|
```
|
||||||
|
--change_request_parent=<COMMIT_ID>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
|
||||||
|
There are a few things that can go wrong with this invocation:
|
||||||
|
|
||||||
|
|
||||||
|
#### Wrong owner or permissions on `.ssh/config`
|
||||||
|
|
||||||
|
Use `docker-compose run copybara bash -c 'chown -R root:root /root/.ssh'` to fix the ownership
|
||||||
|
of the mounted ssh key.
|
||||||
|
|
||||||
|
You may need to then reclaim ownership on the host later by running `sudo chown -R $USER:$USER ~/.ssh`
|
||||||
|
|
||||||
|
|
||||||
|
#### Can't enter ssh passphrase
|
||||||
|
|
||||||
|
Copybara can't handle ssh keys with passphrases, it just hangs at the prompt to enter the passphrase.
|
||||||
|
We can solve this by mounting our `ssh-agent` socket into the container.
|
||||||
|
|
||||||
|
Add the following options to the `docker-compose` invocation:
|
||||||
|
|
||||||
|
- `--volume $SSH_AUTH_SOCK:/ssh-agent`
|
||||||
|
- `-e SSH_AUTH_SOCK=/ssh-agent`
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker-compose run \
|
||||||
|
--volume $SSH_AUTH_SOCK:/ssh-agent \
|
||||||
|
-e SSH_AUTH_SOCK=/ssh-agent \
|
||||||
|
-e COPYBARA_WORKFLOW=importPullRequest \
|
||||||
|
-e COPYBARA_SOURCEREF=<PR_NUMBER> \
|
||||||
|
-e COPYBARA_OPTIONS='--github-destination-pr-branch <BRANCH_NAME>'
|
||||||
|
copybara copybara
|
||||||
|
```
|
91
services/web/copybara/community/copy.bara.sky
Normal file
91
services/web/copybara/community/copy.bara.sky
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
privateRepo = "git@github.com:overleaf/web-internal.git"
|
||||||
|
publicRepo = "git@github.com:overleaf/web.git"
|
||||||
|
|
||||||
|
everythingExceptPrivateFiles = glob(
|
||||||
|
["**"],
|
||||||
|
exclude = [
|
||||||
|
"modules/**",
|
||||||
|
"app/views/external/**",
|
||||||
|
"public/brand/**",
|
||||||
|
"copybara/**",
|
||||||
|
"config/settings.webpack.js",
|
||||||
|
"config/settings.overrides.saas.js",
|
||||||
|
"config/settings.overrides.server-pro.js",
|
||||||
|
"scripts/fix_account_linkage/**",
|
||||||
|
"scripts/translations/uploadNonEnglish.js",
|
||||||
|
"scripts/user-export/**",
|
||||||
|
".ssh/**",
|
||||||
|
".github/dependabot.yml",
|
||||||
|
"cloudbuild.yaml",
|
||||||
|
".sentryclirc.enc"
|
||||||
|
]
|
||||||
|
) + glob([
|
||||||
|
"modules/launchpad/**",
|
||||||
|
"modules/user-activate/**",
|
||||||
|
"modules/server-ce-scripts/**",
|
||||||
|
"modules/modules-*.js"
|
||||||
|
])
|
||||||
|
|
||||||
|
core.workflow(
|
||||||
|
name = "default",
|
||||||
|
origin = git.origin(
|
||||||
|
url = privateRepo,
|
||||||
|
ref = "master"
|
||||||
|
),
|
||||||
|
destination = git.destination(
|
||||||
|
url = publicRepo,
|
||||||
|
fetch = "master",
|
||||||
|
push = "master"
|
||||||
|
),
|
||||||
|
# Exclude proprietary code and non-local build scripts
|
||||||
|
origin_files = everythingExceptPrivateFiles,
|
||||||
|
mode="ITERATIVE",
|
||||||
|
migrate_noop_changes=True,
|
||||||
|
authoring = authoring.pass_thru("Copybot <copybot@overleaf.com>"),
|
||||||
|
transformations = [
|
||||||
|
metadata.restore_author(label='ORIGINAL_AUTHOR', search_all_changes=True)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---- Import a PR from public repo to private ----
|
||||||
|
|
||||||
|
titleFormatString = "[Imported] ${GITHUB_PR_TITLE}, (#${GITHUB_PR_NUMBER})"
|
||||||
|
bodyFormatString = """
|
||||||
|
Imported from public PR [${GITHUB_PR_NUMBER}](https://github.com/overleaf/web/pull/${GITHUB_PR_NUMBER}).
|
||||||
|
Original PR opened by ${GITHUB_PR_USER}
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
${GITHUB_PR_BODY}
|
||||||
|
"""
|
||||||
|
|
||||||
|
core.workflow(
|
||||||
|
name = "importPullRequest",
|
||||||
|
origin = git.github_pr_origin(
|
||||||
|
url = publicRepo
|
||||||
|
),
|
||||||
|
destination = git.github_pr_destination(
|
||||||
|
url = privateRepo,
|
||||||
|
integrates = [],
|
||||||
|
pr_branch = "import_public_pr_${CONTEXT_REFERENCE}",
|
||||||
|
title = titleFormatString,
|
||||||
|
body = bodyFormatString
|
||||||
|
),
|
||||||
|
mode = "CHANGE_REQUEST",
|
||||||
|
set_rev_id = False,
|
||||||
|
|
||||||
|
origin_files = glob(["**"]),
|
||||||
|
|
||||||
|
# Same as origin_files in the default workflow,
|
||||||
|
# without this here a PR will delete the excluded files from
|
||||||
|
# the private repo.
|
||||||
|
destination_files = everythingExceptPrivateFiles,
|
||||||
|
|
||||||
|
authoring = authoring.pass_thru("Overleaf CopyBot <copybot@overleaf.com>"),
|
||||||
|
transformations = [
|
||||||
|
metadata.save_author(),
|
||||||
|
metadata.expose_label("COPYBARA_INTEGRATE_REVIEW"),
|
||||||
|
metadata.expose_label("GITHUB_PR_NUMBER", new_name ="Closes", separator=" #"),
|
||||||
|
],
|
||||||
|
)
|
28
services/web/copybara/docker-compose.yml
Normal file
28
services/web/copybara/docker-compose.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
copybara:
|
||||||
|
image: sharelatex/copybara:2019-08.01
|
||||||
|
volumes:
|
||||||
|
# Mount the host ssh folder
|
||||||
|
- ~/.ssh/id_rsa:/root/.ssh/id_rsa
|
||||||
|
- ~/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub
|
||||||
|
- ~/.ssh/known_hosts:/root/.ssh/known_hosts
|
||||||
|
# Mount this directory to the place the base image expects
|
||||||
|
- .:/usr/src/app
|
||||||
|
# Mount the cache
|
||||||
|
- ./.cache/:/root/copybara/cache/
|
||||||
|
# Mount the hosts git configuration
|
||||||
|
- $HOME/.gitconfig:/root/.gitconfig
|
||||||
|
# Mount the hosts git credentials, required for migrating PRs via the github API
|
||||||
|
- $HOME/.git-credentials:/root/.git-credentials
|
||||||
|
environment:
|
||||||
|
COPYBARA_CONFIG: "./community/copy.bara.sky"
|
||||||
|
|
||||||
|
# Uncomment this to force copybara to start syncing from a certain commit
|
||||||
|
# COPYBARA_OPTIONS: "--last-rev=67edeed2c2d8c1d478c9a65d19020a301174cc8e"
|
||||||
|
|
||||||
|
# Uncomment this to force copybara to bootstrap a sync in a new repository
|
||||||
|
# COPYBARA_OPTIONS: "--init-history"
|
||||||
|
|
||||||
|
command: copybara
|
|
@ -56,6 +56,7 @@ function _validateUserIdList(userIds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _handleUser(userId) {
|
async function _handleUser(userId) {
|
||||||
|
console.log('updating user', userId)
|
||||||
const user = await UserGetter.promises.getUser(userId, {
|
const user = await UserGetter.promises.getUser(userId, {
|
||||||
features: 1,
|
features: 1,
|
||||||
featuresOverrides: 1,
|
featuresOverrides: 1,
|
||||||
|
@ -99,7 +100,6 @@ async function _handleUser(userId) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!COMMIT) {
|
if (!COMMIT) {
|
||||||
// not saving features; nothing else to do
|
// not saving features; nothing else to do
|
||||||
return
|
return
|
||||||
|
@ -167,10 +167,15 @@ async function processUsers(userIds) {
|
||||||
console.log(`---Starting to process ${userIds.length} users---`)
|
console.log(`---Starting to process ${userIds.length} users---`)
|
||||||
|
|
||||||
const limit = pLimit(CONCURRENCY)
|
const limit = pLimit(CONCURRENCY)
|
||||||
await Promise.all(
|
const results = await Promise.allSettled(
|
||||||
userIds.map(userId => limit(() => _handleUser(ObjectId(userId))))
|
userIds.map(userId => limit(() => _handleUser(ObjectId(userId))))
|
||||||
)
|
)
|
||||||
|
results.forEach((result, idx) => {
|
||||||
|
if (result.status !== 'fulfilled') {
|
||||||
|
console.log(userIds[idx], 'failed', result.reason)
|
||||||
|
processLogger.failed.push(userIds[idx])
|
||||||
|
}
|
||||||
|
})
|
||||||
processLogger.printSummary()
|
processLogger.printSummary()
|
||||||
process.exit()
|
process.exit()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue