2023-08-16 19:37:19 +07:00
|
|
|
import dataclasses
|
|
|
|
import itertools
|
|
|
|
import logging
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
import os
|
2023-08-16 19:37:19 +07:00
|
|
|
import pathlib
|
|
|
|
import resource
|
2024-02-23 19:58:31 +07:00
|
|
|
import stat
|
2023-08-16 19:37:19 +07:00
|
|
|
import subprocess
|
[IMP] forwardport: surfacing of modify/delete conflicts
Given branch A, and branch B forked from it. If B removes a file which
a PR to A later modifies, on forward port Git generates a
modify/delete conflict (as in one side modifies a file which the
other deleted).
So far so good, except while it does notify the caller of the issue
the modified file is just dumped as-is into the working copy (or
commit), which essentially resurrects it.
This is an issue, *especially* as the file is already part of a
commit (rather tan just a U local file), if the developer fixes the
conflict "in place" rather than re-doing the forward-port from scratch
it's easy to miss the reincarnated file (and possibly the changes that
were part of it too), which at best leaves parasitic dead code in the
working copy. There is also no easy way for the runbot to check it as
adding unimported standalone files while rare is not unknown
e.g. utility scripts (to say nothing of JS where we can't really track
the usages / imports at all).
To resolve this issue, during conflict generation post-process
modify/delete to insert artificial conflict markers, the file should
be syntactically invalid so linters / checkers should complain, and
the minimal check has a step looking for conflict markers which should
fire and prevent merging the PR.
Fixes #896
2024-09-27 17:37:49 +07:00
|
|
|
from operator import methodcaller
|
[ADD] runbot_merge, tests: opentelemetry support
Mostly for tests: it can be really difficult to correlate issues as
there are 3 different processes involved (the test runner, the odoo
being tested, and dummy-central (as github)) and the intermixing of
logs between the test driver and the odoo being tested is
not *amazing*.
- `pytest-opentelemetry`'s `--export-traces` is the signal for running
tests with tracing enabled, that way just having
`pytest-opentelemetry` installed does not do anything untowards.
- Until chrisguidry/pytest-opentelemetry#34 is merged, should probably
use the linked branch as the default / base mode of having a single
trace for the entire test suite is not great when there are 460
tests, especially as local clients (opentelemetry-desktop-viewer,
venator) mostly work on traces and aren't very good at zooming on
*spans* at least currently.
- Additionally, the conftest plugin adds hooks for propagating through
the xmlrpc client (communications with odoo) and enables the
requests instrumentor from the opentelemetry python contribs.
- The dummy `saas_worker` was moved into the filesystem, that makes it
easier to review and update.
- A second such module was added for the otel instrumentation *of the
odoo under test*, that instruments psycopg2, requests, wsgi, and the
server side of xmlrpc.
- The git ops were instrumented directly in runbot_merge, as I've
tried to keep `saas_tracing` relatively generic, in case it could be
moved to community or used by internal eventually.
Some typing was added to the conftest hooks and fixtures, and they
were migrated from tmpdir (py.path) to tmp_path (pathlib) for
consistency, even though technically the `mkdir` of pathlib is an
annoying downgrade.
Fixes #835
2024-11-28 13:54:00 +07:00
|
|
|
from typing import Optional, TypeVar, Union, Sequence, Tuple, Dict, Iterator
|
2024-10-02 17:14:09 +07:00
|
|
|
from collections.abc import Iterable, Mapping, Callable
|
2023-08-16 19:37:19 +07:00
|
|
|
|
|
|
|
from odoo.tools.appdirs import user_cache_dir
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
from .github import MergeError, PrCommit
|
2023-08-16 19:37:19 +07:00
|
|
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
[ADD] runbot_merge, tests: opentelemetry support
Mostly for tests: it can be really difficult to correlate issues as
there are 3 different processes involved (the test runner, the odoo
being tested, and dummy-central (as github)) and the intermixing of
logs between the test driver and the odoo being tested is
not *amazing*.
- `pytest-opentelemetry`'s `--export-traces` is the signal for running
tests with tracing enabled, that way just having
`pytest-opentelemetry` installed does not do anything untowards.
- Until chrisguidry/pytest-opentelemetry#34 is merged, should probably
use the linked branch as the default / base mode of having a single
trace for the entire test suite is not great when there are 460
tests, especially as local clients (opentelemetry-desktop-viewer,
venator) mostly work on traces and aren't very good at zooming on
*spans* at least currently.
- Additionally, the conftest plugin adds hooks for propagating through
the xmlrpc client (communications with odoo) and enables the
requests instrumentor from the opentelemetry python contribs.
- The dummy `saas_worker` was moved into the filesystem, that makes it
easier to review and update.
- A second such module was added for the otel instrumentation *of the
odoo under test*, that instruments psycopg2, requests, wsgi, and the
server side of xmlrpc.
- The git ops were instrumented directly in runbot_merge, as I've
tried to keep `saas_tracing` relatively generic, in case it could be
moved to community or used by internal eventually.
Some typing was added to the conftest hooks and fixtures, and they
were migrated from tmpdir (py.path) to tmp_path (pathlib) for
consistency, even though technically the `mkdir` of pathlib is an
annoying downgrade.
Fixes #835
2024-11-28 13:54:00 +07:00
|
|
|
try:
|
|
|
|
from opentelemetry import trace
|
|
|
|
from opentelemetry.propagate import inject
|
|
|
|
tracer = trace.get_tracer(__name__)
|
|
|
|
|
|
|
|
def git_tracing_params() -> Iterator[str]:
|
|
|
|
tracing = {}
|
|
|
|
inject(tracing)
|
|
|
|
return itertools.chain.from_iterable(
|
|
|
|
('-c', f'http.extraHeader={k}:{v}')
|
|
|
|
for k, v in tracing.items()
|
|
|
|
)
|
|
|
|
except ImportError:
|
|
|
|
trace = tracer = inject = None
|
|
|
|
def git_tracing_params() -> Iterator[str]:
|
|
|
|
return iter(())
|
|
|
|
|
[CHG] forwardport: perform forward porting without working copies
The goal is to reduce maintenance and odd disk interactions &
concurrency issues, by not creating concurrent clones, not having to
push forks back in the repository, etc... it also removes the need to
cleanup "scratch" working copies though that looks not to have been an
issue in a while.
The work is done on isolated objects without using or mutating refs,
so even concurrent work should not be a problem.
This turns out to not be any more verbose (less so if anything) than
using `cherry-pick`, as that is not really designed for scripted /
non-interactive use, or for squashing commits thereafter. Working
directly with trees and commits is quite a bit cleaner even without a
ton of helpers.
Much of the credit goes to Julia Evans for [their investigation of
3-way merges as the underpinnings of cherry-picking][3-way merge],
this would have been a lot more difficult if I'd had to rediscover the
merge-base trick independently.
A few things have been changed by this:
- The old trace/stderr from cherrypick has disappeared as it's
generated by cherrypick, but for a non-interactive use it's kinda
useless anyway so I probably should have looked into removing it
earlier (I think the main use was investigation of the inflateinit
issue).
- Error on emptied commits has to be hand-rolled as `merge-tree`
couldn't care less, this is not hard but is a bit annoying.
- `merge-tree`'s conflict information only references raw commits,
which makes sense, but requires updating a bunch of tests. Then
again so does the fact that it *usually* doesn't send anything to
stderr, so that's usually disappearing.
Conveniently `merge-tree` merges the conflict marker directly in the
files / tree so we don't have to mess about moving them back out of
the repository and into the working copy as I assume cherry-pick does,
which means we don't have to try and commit them back in ether. That
is a huge part of the gain over faffing about with the working copy.
Fixes #847
[3-way merge]: https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/
2024-07-05 18:32:02 +07:00
|
|
|
def source_url(repository) -> str:
|
2023-08-16 19:37:19 +07:00
|
|
|
return 'https://{}@github.com/{}'.format(
|
[CHG] forwardport: perform forward porting without working copies
The goal is to reduce maintenance and odd disk interactions &
concurrency issues, by not creating concurrent clones, not having to
push forks back in the repository, etc... it also removes the need to
cleanup "scratch" working copies though that looks not to have been an
issue in a while.
The work is done on isolated objects without using or mutating refs,
so even concurrent work should not be a problem.
This turns out to not be any more verbose (less so if anything) than
using `cherry-pick`, as that is not really designed for scripted /
non-interactive use, or for squashing commits thereafter. Working
directly with trees and commits is quite a bit cleaner even without a
ton of helpers.
Much of the credit goes to Julia Evans for [their investigation of
3-way merges as the underpinnings of cherry-picking][3-way merge],
this would have been a lot more difficult if I'd had to rediscover the
merge-base trick independently.
A few things have been changed by this:
- The old trace/stderr from cherrypick has disappeared as it's
generated by cherrypick, but for a non-interactive use it's kinda
useless anyway so I probably should have looked into removing it
earlier (I think the main use was investigation of the inflateinit
issue).
- Error on emptied commits has to be hand-rolled as `merge-tree`
couldn't care less, this is not hard but is a bit annoying.
- `merge-tree`'s conflict information only references raw commits,
which makes sense, but requires updating a bunch of tests. Then
again so does the fact that it *usually* doesn't send anything to
stderr, so that's usually disappearing.
Conveniently `merge-tree` merges the conflict marker directly in the
files / tree so we don't have to mess about moving them back out of
the repository and into the working copy as I assume cherry-pick does,
which means we don't have to try and commit them back in ether. That
is a huge part of the gain over faffing about with the working copy.
Fixes #847
[3-way merge]: https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/
2024-07-05 18:32:02 +07:00
|
|
|
repository.project_id.github_token,
|
2023-08-16 19:37:19 +07:00
|
|
|
repository.name,
|
|
|
|
)
|
|
|
|
|
[CHG] forwardport: perform forward porting without working copies
The goal is to reduce maintenance and odd disk interactions &
concurrency issues, by not creating concurrent clones, not having to
push forks back in the repository, etc... it also removes the need to
cleanup "scratch" working copies though that looks not to have been an
issue in a while.
The work is done on isolated objects without using or mutating refs,
so even concurrent work should not be a problem.
This turns out to not be any more verbose (less so if anything) than
using `cherry-pick`, as that is not really designed for scripted /
non-interactive use, or for squashing commits thereafter. Working
directly with trees and commits is quite a bit cleaner even without a
ton of helpers.
Much of the credit goes to Julia Evans for [their investigation of
3-way merges as the underpinnings of cherry-picking][3-way merge],
this would have been a lot more difficult if I'd had to rediscover the
merge-base trick independently.
A few things have been changed by this:
- The old trace/stderr from cherrypick has disappeared as it's
generated by cherrypick, but for a non-interactive use it's kinda
useless anyway so I probably should have looked into removing it
earlier (I think the main use was investigation of the inflateinit
issue).
- Error on emptied commits has to be hand-rolled as `merge-tree`
couldn't care less, this is not hard but is a bit annoying.
- `merge-tree`'s conflict information only references raw commits,
which makes sense, but requires updating a bunch of tests. Then
again so does the fact that it *usually* doesn't send anything to
stderr, so that's usually disappearing.
Conveniently `merge-tree` merges the conflict marker directly in the
files / tree so we don't have to mess about moving them back out of
the repository and into the working copy as I assume cherry-pick does,
which means we don't have to try and commit them back in ether. That
is a huge part of the gain over faffing about with the working copy.
Fixes #847
[3-way merge]: https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/
2024-07-05 18:32:02 +07:00
|
|
|
def fw_url(repository) -> str:
|
|
|
|
return 'https://{}@github.com/{}'.format(
|
|
|
|
repository.project_id.fp_github_token,
|
|
|
|
repository.fp_remote_target,
|
|
|
|
)
|
|
|
|
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
Authorship = Union[Tuple[str, str], Tuple[str, str, str]]
|
2023-08-16 19:37:19 +07:00
|
|
|
|
[CHG] forwardport: perform forward porting without working copies
The goal is to reduce maintenance and odd disk interactions &
concurrency issues, by not creating concurrent clones, not having to
push forks back in the repository, etc... it also removes the need to
cleanup "scratch" working copies though that looks not to have been an
issue in a while.
The work is done on isolated objects without using or mutating refs,
so even concurrent work should not be a problem.
This turns out to not be any more verbose (less so if anything) than
using `cherry-pick`, as that is not really designed for scripted /
non-interactive use, or for squashing commits thereafter. Working
directly with trees and commits is quite a bit cleaner even without a
ton of helpers.
Much of the credit goes to Julia Evans for [their investigation of
3-way merges as the underpinnings of cherry-picking][3-way merge],
this would have been a lot more difficult if I'd had to rediscover the
merge-base trick independently.
A few things have been changed by this:
- The old trace/stderr from cherrypick has disappeared as it's
generated by cherrypick, but for a non-interactive use it's kinda
useless anyway so I probably should have looked into removing it
earlier (I think the main use was investigation of the inflateinit
issue).
- Error on emptied commits has to be hand-rolled as `merge-tree`
couldn't care less, this is not hard but is a bit annoying.
- `merge-tree`'s conflict information only references raw commits,
which makes sense, but requires updating a bunch of tests. Then
again so does the fact that it *usually* doesn't send anything to
stderr, so that's usually disappearing.
Conveniently `merge-tree` merges the conflict marker directly in the
files / tree so we don't have to mess about moving them back out of
the repository and into the working copy as I assume cherry-pick does,
which means we don't have to try and commit them back in ether. That
is a huge part of the gain over faffing about with the working copy.
Fixes #847
[3-way merge]: https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/
2024-07-05 18:32:02 +07:00
|
|
|
def get_local(repository, *, clone: bool = True) -> 'Optional[Repo]':
|
2023-08-16 19:37:19 +07:00
|
|
|
repos_dir = pathlib.Path(user_cache_dir('mergebot'))
|
|
|
|
repos_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# NB: `repository.name` is `$org/$name` so this will be a subdirectory, probably
|
|
|
|
repo_dir = repos_dir / repository.name
|
|
|
|
|
|
|
|
if repo_dir.is_dir():
|
|
|
|
return git(repo_dir)
|
[CHG] forwardport: perform forward porting without working copies
The goal is to reduce maintenance and odd disk interactions &
concurrency issues, by not creating concurrent clones, not having to
push forks back in the repository, etc... it also removes the need to
cleanup "scratch" working copies though that looks not to have been an
issue in a while.
The work is done on isolated objects without using or mutating refs,
so even concurrent work should not be a problem.
This turns out to not be any more verbose (less so if anything) than
using `cherry-pick`, as that is not really designed for scripted /
non-interactive use, or for squashing commits thereafter. Working
directly with trees and commits is quite a bit cleaner even without a
ton of helpers.
Much of the credit goes to Julia Evans for [their investigation of
3-way merges as the underpinnings of cherry-picking][3-way merge],
this would have been a lot more difficult if I'd had to rediscover the
merge-base trick independently.
A few things have been changed by this:
- The old trace/stderr from cherrypick has disappeared as it's
generated by cherrypick, but for a non-interactive use it's kinda
useless anyway so I probably should have looked into removing it
earlier (I think the main use was investigation of the inflateinit
issue).
- Error on emptied commits has to be hand-rolled as `merge-tree`
couldn't care less, this is not hard but is a bit annoying.
- `merge-tree`'s conflict information only references raw commits,
which makes sense, but requires updating a bunch of tests. Then
again so does the fact that it *usually* doesn't send anything to
stderr, so that's usually disappearing.
Conveniently `merge-tree` merges the conflict marker directly in the
files / tree so we don't have to mess about moving them back out of
the repository and into the working copy as I assume cherry-pick does,
which means we don't have to try and commit them back in ether. That
is a huge part of the gain over faffing about with the working copy.
Fixes #847
[3-way merge]: https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/
2024-07-05 18:32:02 +07:00
|
|
|
elif clone:
|
2023-08-16 19:37:19 +07:00
|
|
|
_logger.info("Cloning out %s to %s", repository.name, repo_dir)
|
[ADD] runbot_merge, tests: opentelemetry support
Mostly for tests: it can be really difficult to correlate issues as
there are 3 different processes involved (the test runner, the odoo
being tested, and dummy-central (as github)) and the intermixing of
logs between the test driver and the odoo being tested is
not *amazing*.
- `pytest-opentelemetry`'s `--export-traces` is the signal for running
tests with tracing enabled, that way just having
`pytest-opentelemetry` installed does not do anything untowards.
- Until chrisguidry/pytest-opentelemetry#34 is merged, should probably
use the linked branch as the default / base mode of having a single
trace for the entire test suite is not great when there are 460
tests, especially as local clients (opentelemetry-desktop-viewer,
venator) mostly work on traces and aren't very good at zooming on
*spans* at least currently.
- Additionally, the conftest plugin adds hooks for propagating through
the xmlrpc client (communications with odoo) and enables the
requests instrumentor from the opentelemetry python contribs.
- The dummy `saas_worker` was moved into the filesystem, that makes it
easier to review and update.
- A second such module was added for the otel instrumentation *of the
odoo under test*, that instruments psycopg2, requests, wsgi, and the
server side of xmlrpc.
- The git ops were instrumented directly in runbot_merge, as I've
tried to keep `saas_tracing` relatively generic, in case it could be
moved to community or used by internal eventually.
Some typing was added to the conftest hooks and fixtures, and they
were migrated from tmpdir (py.path) to tmp_path (pathlib) for
consistency, even though technically the `mkdir` of pathlib is an
annoying downgrade.
Fixes #835
2024-11-28 13:54:00 +07:00
|
|
|
subprocess.run([
|
|
|
|
'git', *git_tracing_params(), 'clone', '--bare',
|
|
|
|
source_url(repository), str(repo_dir)
|
|
|
|
], check=True)
|
2023-08-16 19:37:19 +07:00
|
|
|
# bare repos don't have fetch specs by default, and fetching *into*
|
|
|
|
# them is a pain in the ass, configure fetch specs so `git fetch`
|
|
|
|
# works properly
|
|
|
|
repo = git(repo_dir)
|
|
|
|
repo.config('--add', 'remote.origin.fetch', '+refs/heads/*:refs/heads/*')
|
|
|
|
# negative refspecs require git 2.29
|
|
|
|
repo.config('--add', 'remote.origin.fetch', '^refs/heads/tmp.*')
|
|
|
|
repo.config('--add', 'remote.origin.fetch', '^refs/heads/staging.*')
|
|
|
|
return repo
|
2024-02-23 19:58:31 +07:00
|
|
|
else:
|
|
|
|
_logger.warning(
|
|
|
|
"Unable to acquire %s: %s",
|
|
|
|
repo_dir,
|
|
|
|
"doesn't exist" if not repo_dir.exists()\
|
|
|
|
else oct(stat.S_IFMT(repo_dir.stat().st_mode))
|
|
|
|
)
|
|
|
|
return None
|
2023-08-16 19:37:19 +07:00
|
|
|
|
|
|
|
|
|
|
|
ALWAYS = ('gc.auto=0', 'maintenance.auto=0')
|
|
|
|
|
|
|
|
|
|
|
|
def _bypass_limits():
|
|
|
|
resource.setrlimit(resource.RLIMIT_AS, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
|
|
|
|
|
|
|
|
|
|
|
|
def git(directory: str) -> 'Repo':
|
|
|
|
return Repo(directory, check=True)
|
|
|
|
|
|
|
|
|
|
|
|
Self = TypeVar("Self", bound="Repo")
|
|
|
|
class Repo:
|
[ADD] runbot_merge, tests: opentelemetry support
Mostly for tests: it can be really difficult to correlate issues as
there are 3 different processes involved (the test runner, the odoo
being tested, and dummy-central (as github)) and the intermixing of
logs between the test driver and the odoo being tested is
not *amazing*.
- `pytest-opentelemetry`'s `--export-traces` is the signal for running
tests with tracing enabled, that way just having
`pytest-opentelemetry` installed does not do anything untowards.
- Until chrisguidry/pytest-opentelemetry#34 is merged, should probably
use the linked branch as the default / base mode of having a single
trace for the entire test suite is not great when there are 460
tests, especially as local clients (opentelemetry-desktop-viewer,
venator) mostly work on traces and aren't very good at zooming on
*spans* at least currently.
- Additionally, the conftest plugin adds hooks for propagating through
the xmlrpc client (communications with odoo) and enables the
requests instrumentor from the opentelemetry python contribs.
- The dummy `saas_worker` was moved into the filesystem, that makes it
easier to review and update.
- A second such module was added for the otel instrumentation *of the
odoo under test*, that instruments psycopg2, requests, wsgi, and the
server side of xmlrpc.
- The git ops were instrumented directly in runbot_merge, as I've
tried to keep `saas_tracing` relatively generic, in case it could be
moved to community or used by internal eventually.
Some typing was added to the conftest hooks and fixtures, and they
were migrated from tmpdir (py.path) to tmp_path (pathlib) for
consistency, even though technically the `mkdir` of pathlib is an
annoying downgrade.
Fixes #835
2024-11-28 13:54:00 +07:00
|
|
|
def __init__(self, directory: str, **config: object) -> None:
|
2023-08-16 19:37:19 +07:00
|
|
|
self._directory = str(directory)
|
|
|
|
config.setdefault('stderr', subprocess.PIPE)
|
|
|
|
self._config = config
|
|
|
|
self._params = ()
|
2024-10-02 17:14:09 +07:00
|
|
|
self.runner = subprocess.run
|
2023-08-16 19:37:19 +07:00
|
|
|
|
|
|
|
def __getattr__(self, name: str) -> 'GitCommand':
|
|
|
|
return GitCommand(self, name.replace('_', '-'))
|
|
|
|
|
|
|
|
def _run(self, *args, **kwargs) -> subprocess.CompletedProcess:
|
|
|
|
opts = {**self._config, **kwargs}
|
[ADD] runbot_merge, tests: opentelemetry support
Mostly for tests: it can be really difficult to correlate issues as
there are 3 different processes involved (the test runner, the odoo
being tested, and dummy-central (as github)) and the intermixing of
logs between the test driver and the odoo being tested is
not *amazing*.
- `pytest-opentelemetry`'s `--export-traces` is the signal for running
tests with tracing enabled, that way just having
`pytest-opentelemetry` installed does not do anything untowards.
- Until chrisguidry/pytest-opentelemetry#34 is merged, should probably
use the linked branch as the default / base mode of having a single
trace for the entire test suite is not great when there are 460
tests, especially as local clients (opentelemetry-desktop-viewer,
venator) mostly work on traces and aren't very good at zooming on
*spans* at least currently.
- Additionally, the conftest plugin adds hooks for propagating through
the xmlrpc client (communications with odoo) and enables the
requests instrumentor from the opentelemetry python contribs.
- The dummy `saas_worker` was moved into the filesystem, that makes it
easier to review and update.
- A second such module was added for the otel instrumentation *of the
odoo under test*, that instruments psycopg2, requests, wsgi, and the
server side of xmlrpc.
- The git ops were instrumented directly in runbot_merge, as I've
tried to keep `saas_tracing` relatively generic, in case it could be
moved to community or used by internal eventually.
Some typing was added to the conftest hooks and fixtures, and they
were migrated from tmpdir (py.path) to tmp_path (pathlib) for
consistency, even though technically the `mkdir` of pathlib is an
annoying downgrade.
Fixes #835
2024-11-28 13:54:00 +07:00
|
|
|
args = tuple(itertools.chain(
|
|
|
|
('git', '-C', self._directory),
|
|
|
|
itertools.chain.from_iterable(('-c', p) for p in self._params + ALWAYS),
|
|
|
|
git_tracing_params(),
|
|
|
|
args,
|
|
|
|
))
|
2023-08-16 19:37:19 +07:00
|
|
|
try:
|
2024-10-02 17:14:09 +07:00
|
|
|
return self.runner(args, preexec_fn=_bypass_limits, **opts)
|
2023-08-16 19:37:19 +07:00
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
stream = e.stderr or e.stdout
|
|
|
|
if stream:
|
|
|
|
_logger.error("git call error: %s", stream)
|
|
|
|
raise
|
|
|
|
|
|
|
|
def stdout(self, flag: bool = True) -> Self:
|
|
|
|
if flag is True:
|
|
|
|
return self.with_config(stdout=subprocess.PIPE)
|
|
|
|
elif flag is False:
|
|
|
|
return self.with_config(stdout=None)
|
|
|
|
return self.with_config(stdout=flag)
|
|
|
|
|
|
|
|
def check(self, flag: bool) -> Self:
|
|
|
|
return self.with_config(check=flag)
|
|
|
|
|
|
|
|
def with_config(self, **kw) -> Self:
|
|
|
|
opts = {**self._config, **kw}
|
|
|
|
r = Repo(self._directory, **opts)
|
|
|
|
r._params = self._params
|
|
|
|
return r
|
|
|
|
|
|
|
|
def with_params(self, *args) -> Self:
|
|
|
|
r = self.with_config()
|
|
|
|
r._params = args
|
|
|
|
return r
|
|
|
|
|
|
|
|
def clone(self, to: str, branch: Optional[str] = None) -> Self:
|
|
|
|
self._run(
|
|
|
|
'clone',
|
|
|
|
*([] if branch is None else ['-b', branch]),
|
|
|
|
self._directory, to,
|
|
|
|
)
|
|
|
|
return Repo(to)
|
|
|
|
|
2025-03-14 18:14:47 +07:00
|
|
|
def get_tree(self, rev: str) -> str:
|
|
|
|
return self.stdout().with_config(check=True, encoding="utf-8")\
|
|
|
|
.rev_parse(f'{rev}^{{tree}}')\
|
|
|
|
.stdout.strip()
|
2023-10-09 18:52:30 +07:00
|
|
|
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
def rebase(self, dest: str, commits: Sequence[PrCommit]) -> Tuple[str, Dict[str, str]]:
|
|
|
|
"""Implements rebase by hand atop plumbing so:
|
|
|
|
|
|
|
|
- we can work without a working copy
|
|
|
|
- we can track individual commits (and store the mapping)
|
|
|
|
|
|
|
|
It looks like `--merge-base` is not sufficient for `merge-tree` to
|
|
|
|
correctly keep track of history, so it loses contents. Therefore
|
|
|
|
implement in two passes as in the github version.
|
|
|
|
"""
|
|
|
|
repo = self.stdout().with_config(text=True, check=False)
|
|
|
|
|
|
|
|
logger = _logger.getChild('rebase')
|
|
|
|
if not commits:
|
|
|
|
raise MergeError("PR has no commits")
|
|
|
|
|
2023-10-09 18:52:30 +07:00
|
|
|
prev_tree = repo.get_tree(dest)
|
|
|
|
prev_original_tree = repo.get_tree(commits[0]['parents'][0]["sha"])
|
|
|
|
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
new_trees = []
|
|
|
|
parent = dest
|
|
|
|
for original in commits:
|
|
|
|
if len(original['parents']) != 1:
|
|
|
|
raise MergeError(
|
|
|
|
f"commits with multiple parents ({original['sha']}) can not be rebased, "
|
|
|
|
"either fix the branch to remove merges or merge without "
|
|
|
|
"rebasing")
|
|
|
|
|
|
|
|
new_trees.append(check(repo.merge_tree(parent, original['sha'])).stdout.strip())
|
2023-10-09 18:52:30 +07:00
|
|
|
# allow merging empty commits, but not empty*ing* commits while merging
|
|
|
|
if prev_original_tree != original['commit']['tree']['sha']:
|
|
|
|
if new_trees[-1] == prev_tree:
|
|
|
|
raise MergeError(
|
|
|
|
f"commit {original['sha']} results in an empty tree when "
|
|
|
|
f"merged, it is likely a duplicate of a merged commit, "
|
|
|
|
f"rebase and remove."
|
|
|
|
)
|
|
|
|
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
parent = check(repo.commit_tree(
|
|
|
|
tree=new_trees[-1],
|
|
|
|
parents=[parent, original['sha']],
|
|
|
|
message=f'temp rebase {original["sha"]}',
|
|
|
|
)).stdout.strip()
|
2023-10-09 18:52:30 +07:00
|
|
|
prev_tree = new_trees[-1]
|
|
|
|
prev_original_tree = original['commit']['tree']['sha']
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
|
|
|
|
mapping = {}
|
|
|
|
for original, tree in zip(commits, new_trees):
|
2024-01-22 21:36:37 +07:00
|
|
|
authorship = check(repo.show('--no-patch', '--pretty=%an%n%ae%n%ai%n%cn%n%ce', original['sha']))
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
author_name, author_email, author_date, committer_name, committer_email =\
|
|
|
|
authorship.stdout.splitlines()
|
|
|
|
|
|
|
|
c = check(repo.commit_tree(
|
|
|
|
tree=tree,
|
|
|
|
parents=[dest],
|
|
|
|
message=original['commit']['message'],
|
|
|
|
author=(author_name, author_email, author_date),
|
|
|
|
committer=(committer_name, committer_email),
|
|
|
|
)).stdout.strip()
|
|
|
|
|
|
|
|
logger.debug('copied %s to %s (parent: %s)', original['sha'], c, dest)
|
|
|
|
dest = mapping[original['sha']] = c
|
|
|
|
|
|
|
|
return dest, mapping
|
|
|
|
|
|
|
|
def merge(self, c1: str, c2: str, msg: str, *, author: Tuple[str, str]) -> str:
|
|
|
|
repo = self.stdout().with_config(text=True, check=False)
|
|
|
|
|
|
|
|
t = repo.merge_tree(c1, c2)
|
|
|
|
if t.returncode:
|
|
|
|
raise MergeError(t.stderr)
|
|
|
|
|
|
|
|
c = self.commit_tree(
|
|
|
|
tree=t.stdout.strip(),
|
|
|
|
message=msg,
|
|
|
|
parents=[c1, c2],
|
|
|
|
author=author,
|
|
|
|
)
|
|
|
|
if c.returncode:
|
|
|
|
raise MergeError(c.stderr)
|
|
|
|
return c.stdout.strip()
|
|
|
|
|
|
|
|
def commit_tree(
|
|
|
|
self, *, tree: str, message: str,
|
|
|
|
parents: Sequence[str] = (),
|
|
|
|
author: Optional[Authorship] = None,
|
|
|
|
committer: Optional[Authorship] = None,
|
|
|
|
) -> subprocess.CompletedProcess:
|
|
|
|
authorship = {}
|
|
|
|
if author:
|
|
|
|
authorship['GIT_AUTHOR_NAME'] = author[0]
|
|
|
|
authorship['GIT_AUTHOR_EMAIL'] = author[1]
|
|
|
|
if len(author) > 2:
|
|
|
|
authorship['GIT_AUTHOR_DATE'] = author[2]
|
|
|
|
if committer:
|
|
|
|
authorship['GIT_COMMITTER_NAME'] = committer[0]
|
|
|
|
authorship['GIT_COMMITTER_EMAIL'] = committer[1]
|
|
|
|
if len(committer) > 2:
|
|
|
|
authorship['GIT_COMMITTER_DATE'] = committer[2]
|
|
|
|
|
|
|
|
return self.with_config(
|
2024-07-05 18:29:20 +07:00
|
|
|
input=message,
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
text=True,
|
|
|
|
env={
|
|
|
|
**os.environ,
|
|
|
|
**authorship,
|
|
|
|
# we don't want git to use the timezone of the machine it's
|
|
|
|
# running on: previously it used the timezone configured in
|
|
|
|
# github (?), which I think / assume defaults to a generic UTC
|
|
|
|
'TZ': 'UTC',
|
|
|
|
}
|
|
|
|
)._run(
|
|
|
|
'commit-tree',
|
|
|
|
tree,
|
2024-07-05 18:29:20 +07:00
|
|
|
'-F', '-',
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
*itertools.chain.from_iterable(('-p', p) for p in parents),
|
|
|
|
)
|
|
|
|
|
2024-10-02 17:14:09 +07:00
|
|
|
def update_tree(self, tree: str, files: Mapping[str, Callable[[Self, str], str]]) -> str:
|
[IMP] forwardport: surfacing of modify/delete conflicts
Given branch A, and branch B forked from it. If B removes a file which
a PR to A later modifies, on forward port Git generates a
modify/delete conflict (as in one side modifies a file which the
other deleted).
So far so good, except while it does notify the caller of the issue
the modified file is just dumped as-is into the working copy (or
commit), which essentially resurrects it.
This is an issue, *especially* as the file is already part of a
commit (rather tan just a U local file), if the developer fixes the
conflict "in place" rather than re-doing the forward-port from scratch
it's easy to miss the reincarnated file (and possibly the changes that
were part of it too), which at best leaves parasitic dead code in the
working copy. There is also no easy way for the runbot to check it as
adding unimported standalone files while rare is not unknown
e.g. utility scripts (to say nothing of JS where we can't really track
the usages / imports at all).
To resolve this issue, during conflict generation post-process
modify/delete to insert artificial conflict markers, the file should
be syntactically invalid so linters / checkers should complain, and
the minimal check has a step looking for conflict markers which should
fire and prevent merging the PR.
Fixes #896
2024-09-27 17:37:49 +07:00
|
|
|
# FIXME: either ignore or process binary files somehow (how does git show conflicts in binary files?)
|
|
|
|
repo = self.stdout().with_config(stderr=None, text=True, check=False, encoding="utf-8")
|
2024-10-02 17:14:09 +07:00
|
|
|
for f, c in files.items():
|
|
|
|
new_contents = c(repo, f)
|
|
|
|
oid = repo \
|
|
|
|
.with_config(input=new_contents) \
|
|
|
|
.hash_object("-w", "--stdin", "--path", f) \
|
[IMP] forwardport: surfacing of modify/delete conflicts
Given branch A, and branch B forked from it. If B removes a file which
a PR to A later modifies, on forward port Git generates a
modify/delete conflict (as in one side modifies a file which the
other deleted).
So far so good, except while it does notify the caller of the issue
the modified file is just dumped as-is into the working copy (or
commit), which essentially resurrects it.
This is an issue, *especially* as the file is already part of a
commit (rather tan just a U local file), if the developer fixes the
conflict "in place" rather than re-doing the forward-port from scratch
it's easy to miss the reincarnated file (and possibly the changes that
were part of it too), which at best leaves parasitic dead code in the
working copy. There is also no easy way for the runbot to check it as
adding unimported standalone files while rare is not unknown
e.g. utility scripts (to say nothing of JS where we can't really track
the usages / imports at all).
To resolve this issue, during conflict generation post-process
modify/delete to insert artificial conflict markers, the file should
be syntactically invalid so linters / checkers should complain, and
the minimal check has a step looking for conflict markers which should
fire and prevent merging the PR.
Fixes #896
2024-09-27 17:37:49 +07:00
|
|
|
.stdout.strip()
|
|
|
|
|
|
|
|
# we need to rewrite every tree from the parent of `f`
|
|
|
|
while f:
|
|
|
|
f, _, local = f.rpartition("/")
|
|
|
|
# tree to update, `{tree}:` works as an alias for tree
|
|
|
|
lstree = repo.ls_tree(f"{tree}:{f}").stdout.splitlines(keepends=False)
|
2025-01-29 19:29:53 +07:00
|
|
|
new_tree = []
|
|
|
|
seen = False
|
|
|
|
for mode, typ, sha, name in map(methodcaller("split", None, 3), lstree):
|
|
|
|
if name == local:
|
|
|
|
sha = oid
|
|
|
|
seen = True
|
[IMP] forwardport: surfacing of modify/delete conflicts
Given branch A, and branch B forked from it. If B removes a file which
a PR to A later modifies, on forward port Git generates a
modify/delete conflict (as in one side modifies a file which the
other deleted).
So far so good, except while it does notify the caller of the issue
the modified file is just dumped as-is into the working copy (or
commit), which essentially resurrects it.
This is an issue, *especially* as the file is already part of a
commit (rather tan just a U local file), if the developer fixes the
conflict "in place" rather than re-doing the forward-port from scratch
it's easy to miss the reincarnated file (and possibly the changes that
were part of it too), which at best leaves parasitic dead code in the
working copy. There is also no easy way for the runbot to check it as
adding unimported standalone files while rare is not unknown
e.g. utility scripts (to say nothing of JS where we can't really track
the usages / imports at all).
To resolve this issue, during conflict generation post-process
modify/delete to insert artificial conflict markers, the file should
be syntactically invalid so linters / checkers should complain, and
the minimal check has a step looking for conflict markers which should
fire and prevent merging the PR.
Fixes #896
2024-09-27 17:37:49 +07:00
|
|
|
# tab before name is critical to the format
|
2025-01-29 19:29:53 +07:00
|
|
|
new_tree.append(f"{mode} {typ} {sha}\t{name}\n")
|
|
|
|
if not seen:
|
|
|
|
new_tree.append(f"100644 blob {oid}\t{local}\n")
|
|
|
|
oid = repo.with_config(input="".join(new_tree), check=True).mktree().stdout.strip()
|
[IMP] forwardport: surfacing of modify/delete conflicts
Given branch A, and branch B forked from it. If B removes a file which
a PR to A later modifies, on forward port Git generates a
modify/delete conflict (as in one side modifies a file which the
other deleted).
So far so good, except while it does notify the caller of the issue
the modified file is just dumped as-is into the working copy (or
commit), which essentially resurrects it.
This is an issue, *especially* as the file is already part of a
commit (rather tan just a U local file), if the developer fixes the
conflict "in place" rather than re-doing the forward-port from scratch
it's easy to miss the reincarnated file (and possibly the changes that
were part of it too), which at best leaves parasitic dead code in the
working copy. There is also no easy way for the runbot to check it as
adding unimported standalone files while rare is not unknown
e.g. utility scripts (to say nothing of JS where we can't really track
the usages / imports at all).
To resolve this issue, during conflict generation post-process
modify/delete to insert artificial conflict markers, the file should
be syntactically invalid so linters / checkers should complain, and
the minimal check has a step looking for conflict markers which should
fire and prevent merging the PR.
Fixes #896
2024-09-27 17:37:49 +07:00
|
|
|
tree = oid
|
|
|
|
return tree
|
|
|
|
|
2024-10-02 17:14:09 +07:00
|
|
|
def modify_delete(self, tree: str, files: Iterable[str]) -> str:
|
|
|
|
"""Updates ``files`` in ``tree`` to add conflict markers to show them
|
|
|
|
as being modify/delete-ed, rather than have only the original content.
|
|
|
|
|
|
|
|
This is because having just content in a valid file is easy to miss,
|
|
|
|
causing file resurrections as they get committed rather than re-removed.
|
|
|
|
|
|
|
|
TODO: maybe extract the diff information compared to before they were removed? idk
|
|
|
|
"""
|
|
|
|
def rewriter(r: Self, f: str) -> str:
|
|
|
|
contents = r.cat_file("-p", f"{tree}:{f}").stdout
|
|
|
|
return f"""\
|
|
|
|
<<<\x3c<<< HEAD
|
|
|
|
||||||| MERGE BASE
|
|
|
|
=======
|
|
|
|
{contents}
|
|
|
|
>>>\x3e>>> FORWARD PORTED
|
|
|
|
"""
|
|
|
|
return self.update_tree(tree, dict.fromkeys(files, rewriter))
|
|
|
|
|
[IMP] forwardport: surfacing of modify/delete conflicts
Given branch A, and branch B forked from it. If B removes a file which
a PR to A later modifies, on forward port Git generates a
modify/delete conflict (as in one side modifies a file which the
other deleted).
So far so good, except while it does notify the caller of the issue
the modified file is just dumped as-is into the working copy (or
commit), which essentially resurrects it.
This is an issue, *especially* as the file is already part of a
commit (rather tan just a U local file), if the developer fixes the
conflict "in place" rather than re-doing the forward-port from scratch
it's easy to miss the reincarnated file (and possibly the changes that
were part of it too), which at best leaves parasitic dead code in the
working copy. There is also no easy way for the runbot to check it as
adding unimported standalone files while rare is not unknown
e.g. utility scripts (to say nothing of JS where we can't really track
the usages / imports at all).
To resolve this issue, during conflict generation post-process
modify/delete to insert artificial conflict markers, the file should
be syntactically invalid so linters / checkers should complain, and
the minimal check has a step looking for conflict markers which should
fire and prevent merging the PR.
Fixes #896
2024-09-27 17:37:49 +07:00
|
|
|
|
[CHG] runbot_merge: switch staging from github API to local
It has been a consideration for a while, but the pain of subtly
interacting with git via the ignominous CLI kept it back. Then ~~the
fire nation attacked~~ github got more and more tight-fisted (and in
some ways less reliable) with their API.
Staging pretty much just interacts with the git database, so it's both
a facultative github operator (it can just interact with git directly)
and a big consumer of API requests (because the git database endpoints
are very low level so it takes quite a bit of work to do anything
especially when high-level operations like rebase have to be
replicated by hand).
Furthermore, an issue has also been noticed which can be attributed to
using the github API (and that API's reliability getting worse): in
some cases github will fail to propagate a ref update / reset, so when
staging 2 PRs it's possible that the second one is merged on top of
the temporary branch of the first one, yielding a kinda broken commit
(in that it's a merge commit with a broken error message) instead of
the rebase / squash commit we expected.
As it turns out it's a very old issue but only happened very early so
was misattributed and not (sufficiently) guarded against:
- 41bd82244bb976bbd4d4be5e7bd792417c7dae6b (October 8th 2018) was
spotted but thought to be a mergebot issue (might have been one of
the opportunities where ref-checks were added though I can't find
any reference to the commit in the runbot repo).
- 2be25052e147b151d1d8a5bc73cceb351586ce03 (October 15th, 2019) was
missed (or ignored).
- 5a9fe7a7d05a9df7186072a7bffd60c6b428fd0e (July 31st, 2023) was
spotted, but happened at a moment where everything kinda broke
because of github rate-limiting ref updates, so the forensics were
difficult and it was attributed to rate limiting issues.
- f10d03bf0f2e8f88f62a5d8356b84f714196130f (August 24th, 2023) broke
the camel's back (and the head block): the logs were not too
interspersed with other garbage and pretty clear that github ack'd a
ref update, returned the correct oid when checking the ref, then
returned the wrong oid when fetching it later on.
No Working Copy
===============
The working copy turns out to not be necessary, the plumbing commands
we *need* work just fine on a bare repository.
Working without a WC means we had to reimplement the high level
operations (rebase) by hand much as we'd done previously, *but* we
needed to do that anyway as git doesn't seem to provide any way to
retrieve the mapping when rebasing/cherrypicking, and cherrypicking by
commit doesn't work well as it can't really find the *merge base* it
needs.
Forward-porting can almost certainly be implemented similarly (with
some overhead), issue #803 has been opened to keep track of the idea.
No TMP
======
The `tmp.` branches are no more, the process of creating stagings is
based entirely around oids, if staging something fails we can just
abandon the oids (they'll be collected by the weekly GC), we only
need to update the staging branches at the very end of the process.
This simplifies things a fair bit.
For now we have stopped checking for visibility / backoff as we're
pushing via git, hopefully it is a more reliable reference than the
API.
Commmit Message Formatting
==========================
There's some unfortunate churn in the test, as the handling of
trailing newlines differs between github's APIs and git itself.
Fixes #247
PS: It might be a good idea to use pygit2 instead of the CLI
eventually, the library is typed which is nice, and it avoids
shelling out although that's really unlikely to be a major cost.
2023-08-18 18:51:18 +07:00
|
|
|
def check(p: subprocess.CompletedProcess) -> subprocess.CompletedProcess:
|
|
|
|
if not p.returncode:
|
|
|
|
return p
|
|
|
|
|
|
|
|
_logger.info("rebase failed at %s\nstdout:\n%s\nstderr:\n%s", p.args, p.stdout, p.stderr)
|
|
|
|
raise MergeError(p.stderr or 'merge conflict')
|
|
|
|
|
2023-08-16 19:37:19 +07:00
|
|
|
@dataclasses.dataclass
|
|
|
|
class GitCommand:
|
|
|
|
repo: Repo
|
|
|
|
name: str
|
|
|
|
|
[ADD] runbot_merge, tests: opentelemetry support
Mostly for tests: it can be really difficult to correlate issues as
there are 3 different processes involved (the test runner, the odoo
being tested, and dummy-central (as github)) and the intermixing of
logs between the test driver and the odoo being tested is
not *amazing*.
- `pytest-opentelemetry`'s `--export-traces` is the signal for running
tests with tracing enabled, that way just having
`pytest-opentelemetry` installed does not do anything untowards.
- Until chrisguidry/pytest-opentelemetry#34 is merged, should probably
use the linked branch as the default / base mode of having a single
trace for the entire test suite is not great when there are 460
tests, especially as local clients (opentelemetry-desktop-viewer,
venator) mostly work on traces and aren't very good at zooming on
*spans* at least currently.
- Additionally, the conftest plugin adds hooks for propagating through
the xmlrpc client (communications with odoo) and enables the
requests instrumentor from the opentelemetry python contribs.
- The dummy `saas_worker` was moved into the filesystem, that makes it
easier to review and update.
- A second such module was added for the otel instrumentation *of the
odoo under test*, that instruments psycopg2, requests, wsgi, and the
server side of xmlrpc.
- The git ops were instrumented directly in runbot_merge, as I've
tried to keep `saas_tracing` relatively generic, in case it could be
moved to community or used by internal eventually.
Some typing was added to the conftest hooks and fixtures, and they
were migrated from tmpdir (py.path) to tmp_path (pathlib) for
consistency, even though technically the `mkdir` of pathlib is an
annoying downgrade.
Fixes #835
2024-11-28 13:54:00 +07:00
|
|
|
if tracer:
|
|
|
|
def __call__(self, *args, **kwargs) -> subprocess.CompletedProcess:
|
|
|
|
with tracer.start_as_current_span(f"git.{self.name}", attributes={
|
|
|
|
"http.target": None,
|
|
|
|
"http.user_agent": "git",
|
|
|
|
}):
|
|
|
|
return self.repo._run(self.name, *args, *self._to_options(kwargs))
|
|
|
|
else:
|
|
|
|
def __call__(self, *args, **kwargs) -> subprocess.CompletedProcess:
|
|
|
|
return self.repo._run(self.name, *args, *self._to_options(kwargs))
|
2023-08-16 19:37:19 +07:00
|
|
|
|
|
|
|
def _to_options(self, d):
|
|
|
|
for k, v in d.items():
|
|
|
|
if len(k) == 1:
|
|
|
|
yield '-' + k
|
|
|
|
else:
|
|
|
|
yield '--' + k.replace('_', '-')
|
|
|
|
if v not in (None, True):
|
|
|
|
assert v is not False
|
|
|
|
yield str(v)
|