Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Gepetto
dashboard
Commits
72ba58a7
Commit
72ba58a7
authored
Jul 26, 2021
by
Guilhem Saurel
Browse files
fix tests
parent
ca7c1b56
Changes
5
Show whitespace changes
Inline
Side-by-side
.gitlab-ci.yml
View file @
72ba58a7
...
...
@@ -22,5 +22,5 @@ build:
script
:
-
docker build -t ${DOCKER_TAG} .
-
docker run --rm -e SECRET_KEY -e GITHUB_WEBHOOK_KEY -e GITLAB_WEBHOOK_KEY -e GITHUB_PIPELINE_TOKEN -e GITLAB_PIPELINE_TOKEN -e PYTHONWARNINGS ${DOCKER_TAG} ./manage.py migrate
-
docker run --rm -e SECRET_KEY -e GITHUB_WEBHOOK_KEY -e GITLAB_WEBHOOK_KEY -e GITHUB_PIPELINE_TOKEN -e GITLAB_PIPELINE_TOKEN -e PYTHONWARNINGS ${DOCKER_TAG} ./manage.py test
-
docker run --rm -e SECRET_KEY -e GITHUB_WEBHOOK_KEY -e GITLAB_WEBHOOK_KEY -e GITHUB_PIPELINE_TOKEN -e GITLAB_PIPELINE_TOKEN -e PYTHONWARNINGS ${DOCKER_TAG} ./manage.py test
--parallel
1
-
docker push ${DOCKER_TAG}
dashboard/middleware.py
View file @
72ba58a7
import
asyncio
from
ipaddress
import
ip_address
,
ip_network
from
django.conf
import
settings
from
django.http
import
HttpRequest
,
HttpResponse
,
HttpResponseRedirect
from
django.shortcuts
import
reverse
from
django.utils.decorators
import
sync_and_async_middleware
from
rest_framework
import
permissions
...
...
@@ -15,19 +17,30 @@ def ip_laas(request: HttpRequest) -> bool:
return
any
(
forwarded_for
in
ip_network
(
net
)
for
net
in
settings
.
LAAS_NETWORKS
)
class
LAASPermsMiddleware
:
def
__init__
(
self
,
get_response
):
self
.
get_response
=
get_response
def
__call__
(
self
,
request
:
HttpRequest
)
->
HttpResponse
:
def
allowed
(
request
:
HttpRequest
)
->
bool
:
"""Allow access to pages protected at a higher application level,
or if the user is authenticated,
or if the request comes from a trusted IP.
"""
allowed
=
(
any
(
request
.
path
.
startswith
(
f
'/
{
url
}
/'
)
for
url
in
ALLOWED_URLS
)
return
(
any
(
request
.
path
.
startswith
(
f
'/
{
url
}
/'
)
for
url
in
ALLOWED_URLS
)
or
request
.
user
and
request
.
user
.
is_authenticated
or
request
.
method
in
permissions
.
SAFE_METHODS
and
ip_laas
(
request
))
if
allowed
:
return
self
.
get_response
(
request
)
@
sync_and_async_middleware
def
laas_perms_middleware
(
get_response
):
# One-time configuration and initialization goes here.
if
asyncio
.
iscoroutinefunction
(
get_response
):
async
def
middleware
(
request
)
->
HttpResponse
:
if
allowed
(
request
):
return
await
get_response
(
request
)
return
HttpResponseRedirect
(
reverse
(
'login'
))
else
:
def
middleware
(
request
)
->
HttpResponse
:
if
allowed
(
request
):
return
get_response
(
request
)
return
HttpResponseRedirect
(
reverse
(
'login'
))
return
middleware
dashboard/settings.py
View file @
72ba58a7
...
...
@@ -58,7 +58,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware'
,
'django.contrib.messages.middleware.MessageMiddleware'
,
'django.middleware.clickjacking.XFrameOptionsMiddleware'
,
f
'
{
PROJECT
}
.middleware.
LAASP
erms
M
iddleware'
,
f
'
{
PROJECT
}
.middleware.
laas_p
erms
_m
iddleware'
,
]
ROOT_URLCONF
=
f
'
{
PROJECT
}
.urls'
...
...
gh/tests.py
View file @
72ba58a7
import
hmac
import
re
from
hashlib
import
sha1
import
logging
from
asyncio
import
sleep
from
asgiref.sync
import
sync_to_async
from
django.conf
import
settings
from
django.test
import
TestCase
from
django.urls
import
reverse
...
...
@@ -11,6 +14,8 @@ import git
from
autoslug.utils
import
slugify
from
rainboard.models
import
Forge
,
Namespace
,
Project
LOGGER
=
logging
.
getLogger
(
'dashboard.gh.tests'
)
def
redact_token
(
func
):
"""Decorator used to prevent git from leaking tokens in exception messages."""
...
...
@@ -54,6 +59,9 @@ class GhTests(TestCase):
if
branch_name
in
gl_branches
:
cls
.
gitlab
.
branches
.
delete
(
branch_name
)
logging
.
basicConfig
(
level
=
logging
.
DEBUG
)
LOGGER
.
info
(
'start gh tests'
)
def
assertSync
(
self
,
branch
):
"""Raise an exception if the branch is not synced between both repos."""
last_commit_github
=
self
.
github
.
get_branch
(
branch
).
commit
.
sha
...
...
@@ -61,23 +69,23 @@ class GhTests(TestCase):
self
.
assertEqual
(
last_commit_github
,
last_commit_gitlab
)
@
redact_token
def
sync
(
self
):
async
def
sync
(
self
):
"""Force both repos to be synced."""
for
branch
in
(
'master'
,
'devel'
):
last_commit_github
=
self
.
github
.
get_branch
(
branch
).
commit
.
sha
last_commit_gitlab
=
self
.
gitlab
.
commits
.
list
(
ref_name
=
branch
)[
0
].
id
if
last_commit_github
!=
last_commit_gitlab
:
pr
in
t
(
f
'sync:
{
branch
}
is not synced, force pushing commit
{
last_commit_gitlab
}
on github'
)
LOGGER
.
warn
in
g
(
f
'sync:
{
branch
}
is not synced, force pushing commit
{
last_commit_gitlab
}
on github'
)
git_repo
=
self
.
project
.
git
()
# Create the remotes if necessary
gl_remote_name
=
f
'gitlab/
{
self
.
namespace
.
slug
}
'
gh_remote_name
=
f
'github/
{
self
.
namespace
.
slug
}
'
if
gl_remote_name
not
in
git_repo
.
remotes
:
git_repo
.
create_remote
(
gl_remote_name
,
url
=
self
.
project
.
remote_url_gitlab
())
git_repo
.
create_remote
(
gl_remote_name
,
url
=
await
sync_to_async
(
self
.
project
.
remote_url_gitlab
)
())
if
gh_remote_name
not
in
git_repo
.
remotes
:
git_repo
.
create_remote
(
gh_remote_name
,
url
=
self
.
project
.
remote_url_github
())
git_repo
.
create_remote
(
gh_remote_name
,
url
=
await
sync_to_async
(
self
.
project
.
remote_url_github
)
())
# Force push the latest gitlab commit on github
git_repo
.
remote
(
gl_remote_name
).
fetch
()
...
...
@@ -86,7 +94,7 @@ class GhTests(TestCase):
self
.
assertSync
(
branch
)
@
redact_token
def
gh_webhook_event
(
self
,
event
,
last_commit
=
''
,
branch
=
'master'
,
pr_action
=
''
,
pr_number
=
''
,
pr_login
=
''
):
async
def
gh_webhook_event
(
self
,
event
,
last_commit
=
''
,
branch
=
'master'
,
pr_action
=
''
,
pr_number
=
''
,
pr_login
=
''
):
"""Simulate receiving an event from a github webhook."""
data
=
{
'repository'
:
{
...
...
@@ -105,7 +113,7 @@ class GhTests(TestCase):
'owner'
:
{
'login'
:
pr_login
},
'clone_url'
:
self
.
project
.
remote_url_github
()
'clone_url'
:
await
sync_to_async
(
self
.
project
.
remote_url_github
)
()
},
'sha'
:
last_commit
}
...
...
@@ -116,15 +124,17 @@ class GhTests(TestCase):
request_body
=
self
.
client
.
_encode_data
(
encoded_data
,
content_type
=
'application/json'
)
msg
=
force_bytes
(
request_body
)
signature
=
'sha1='
+
hmac
.
new
(
force_bytes
(
settings
.
GITHUB_WEBHOOK_KEY
),
msg
,
digestmod
=
sha1
).
hexdigest
()
return
self
.
client
.
post
(
reverse
(
'webhook'
),
LOGGER
.
info
(
'posting a simulated gh webhook event'
)
LOGGER
.
debug
(
f
'with:
{
data
}
'
)
return
await
self
.
async_client
.
post
(
reverse
(
'webhook'
),
data
,
content_type
=
'application/json'
,
HTTP_
X_FORWARDED_FOR
=
'140.82.112.1'
,
HTTP_
X_HUB_SIGNATURE
=
signature
,
HTTP_
X_GITHUB_EVENT
=
event
)
X_FORWARDED_FOR
=
'140.82.112.1'
,
X_HUB_SIGNATURE
=
signature
,
X_GITHUB_EVENT
=
event
)
@
redact_token
def
gl_webhook_event
(
self
,
event
,
last_commit
=
''
,
branch
=
'master'
,
status
=
''
):
async
def
gl_webhook_event
(
self
,
event
,
last_commit
=
''
,
branch
=
'master'
,
status
=
''
):
"""Simulate receiving an event from a gitlab webhook."""
data
=
{
'repository'
:
{
...
...
@@ -143,74 +153,76 @@ class GhTests(TestCase):
'id'
:
1
}
}
return
self
.
client
.
post
(
reverse
(
'gl-webhook'
),
return
await
self
.
async_
client
.
post
(
reverse
(
'gl-webhook'
),
data
,
content_type
=
'application/json'
,
HTTP_
X_FORWARDED_FOR
=
'140.93.0.1'
,
HTTP_
X_GITLAB_TOKEN
=
settings
.
GITLAB_WEBHOOK_KEY
,
HTTP_
X_GITLAB_EVENT
=
event
)
X_FORWARDED_FOR
=
'140.93.0.1'
,
X_GITLAB_TOKEN
=
settings
.
GITLAB_WEBHOOK_KEY
,
X_GITLAB_EVENT
=
event
)
def
test_gh_webhook
(
self
):
async
def
test_gh_webhook
(
self
):
"""Test the security of the github webhook."""
# Not from github IP
response
=
self
.
client
.
get
(
reverse
(
'webhook'
),
HTTP_
X_FORWARDED_FOR
=
'5.5.5.5'
)
response
=
await
self
.
async_
client
.
get
(
reverse
(
'webhook'
),
X_FORWARDED_FOR
=
'5.5.5.5'
)
self
.
assertEqual
(
response
.
status_code
,
302
)
# No signature
response
=
self
.
client
.
get
(
reverse
(
'webhook'
),
HTTP_
X_FORWARDED_FOR
=
'140.82.112.1'
)
response
=
await
self
.
async_
client
.
get
(
reverse
(
'webhook'
),
X_FORWARDED_FOR
=
'140.82.112.1'
)
self
.
assertEqual
(
response
.
status_code
,
302
)
# Signature not sha1
response
=
self
.
client
.
get
(
reverse
(
'webhook'
),
HTTP_
X_FORWARDED_FOR
=
'140.82.112.1'
,
HTTP_
X_HUB_SIGNATURE
=
'sha256=foo'
)
response
=
await
self
.
async_
client
.
get
(
reverse
(
'webhook'
),
X_FORWARDED_FOR
=
'140.82.112.1'
,
X_HUB_SIGNATURE
=
'sha256=foo'
)
self
.
assertEqual
(
response
.
status_code
,
501
)
# Wrong signature
response
=
self
.
client
.
get
(
reverse
(
'webhook'
),
HTTP_
X_FORWARDED_FOR
=
'140.82.112.1'
,
HTTP_
X_HUB_SIGNATURE
=
'sha1=foo'
)
response
=
await
self
.
async_
client
.
get
(
reverse
(
'webhook'
),
X_FORWARDED_FOR
=
'140.82.112.1'
,
X_HUB_SIGNATURE
=
'sha1=foo'
)
self
.
assertEqual
(
response
.
status_code
,
403
)
# Ping
response
=
self
.
gh_webhook_event
(
'ping'
)
response
=
await
self
.
gh_webhook_event
(
'ping'
)
self
.
assertEqual
(
response
.
status_code
,
200
)
def
test_gl_webhook
(
self
):
async
def
test_gl_webhook
(
self
):
"""Test the security of the gitlab webhook."""
# Not from gitlab IP
response
=
self
.
client
.
get
(
reverse
(
'gl-webhook'
),
HTTP_
X_FORWARDED_FOR
=
'5.5.5.5'
)
response
=
await
self
.
async_
client
.
get
(
reverse
(
'gl-webhook'
),
X_FORWARDED_FOR
=
'5.5.5.5'
)
self
.
assertEqual
(
response
.
status_code
,
302
)
# No token
response
=
self
.
client
.
get
(
reverse
(
'gl-webhook'
),
HTTP_
X_FORWARDED_FOR
=
'140.93.0.1'
)
response
=
await
self
.
async_
client
.
get
(
reverse
(
'gl-webhook'
),
X_FORWARDED_FOR
=
'140.93.0.1'
)
self
.
assertEqual
(
response
.
status_code
,
302
)
# Wrong token
response
=
self
.
client
.
get
(
reverse
(
'gl-webhook'
),
HTTP_X_FORWARDED_FOR
=
'140.93.0.1'
,
HTTP_X_GITLAB_TOKEN
=
'foo'
)
response
=
await
self
.
async_client
.
get
(
reverse
(
'gl-webhook'
),
X_FORWARDED_FOR
=
'140.93.0.1'
,
X_GITLAB_TOKEN
=
'foo'
)
self
.
assertEqual
(
response
.
status_code
,
403
)
# Ping
response
=
self
.
client
.
get
(
reverse
(
'gl-webhook'
),
HTTP_
X_FORWARDED_FOR
=
'140.93.0.1'
,
HTTP_
X_GITLAB_TOKEN
=
settings
.
GITLAB_WEBHOOK_KEY
,
HTTP_
X_GITLAB_EVENT
=
'ping'
)
response
=
await
self
.
async_
client
.
get
(
reverse
(
'gl-webhook'
),
X_FORWARDED_FOR
=
'140.93.0.1'
,
X_GITLAB_TOKEN
=
settings
.
GITLAB_WEBHOOK_KEY
,
X_GITLAB_EVENT
=
'ping'
)
self
.
assertEqual
(
response
.
status_code
,
200
)
def
test_push_already_synced
(
self
):
async
def
test_push_already_synced
(
self
):
"""Test push when both repos are already synced."""
self
.
sync
()
await
self
.
sync
()
last_commit
=
self
.
github
.
get_branch
(
'master'
).
commit
.
sha
response
=
self
.
gh_webhook_event
(
'push'
,
last_commit
)
response
=
await
self
.
gh_webhook_event
(
'push'
,
last_commit
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content
.
decode
(),
'already synced'
)
def
push_github
(
self
,
branch
):
async
def
push_github
(
self
,
branch
):
"""Test sync after pushing to the given branch on github."""
self
.
sync
()
await
self
.
sync
()
last_commit
=
self
.
github
.
get_branch
(
branch
).
commit
.
sha
file
=
self
.
github
.
get_contents
(
'README.md'
,
branch
)
...
...
@@ -223,24 +235,24 @@ class GhTests(TestCase):
last_commit_github
=
self
.
github
.
get_branch
(
branch
).
commit
.
sha
self
.
assertNotEqual
(
last_commit
,
last_commit_github
)
response
=
self
.
gh_webhook_event
(
'push'
,
last_commit_github
,
branch
)
response
=
await
self
.
gh_webhook_event
(
'push'
,
last_commit_github
,
branch
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content
.
decode
(),
'push event detected'
)
last_commit_gitlab
=
self
.
gitlab
.
commits
.
list
(
ref_name
=
branch
)[
0
].
id
self
.
assertEqual
(
last_commit_github
,
last_commit_gitlab
)
def
test_push_github_master
(
self
):
async
def
test_push_github_master
(
self
):
"""Test sync after pushing to github on master."""
self
.
push_github
(
'master'
)
await
self
.
push_github
(
'master'
)
def
test_push_github_devel
(
self
):
async
def
test_push_github_devel
(
self
):
"""Test sync after pushing to github on devel."""
self
.
push_github
(
'devel'
)
await
self
.
push_github
(
'devel'
)
def
push_gitlab
(
self
,
branch
):
async
def
push_gitlab
(
self
,
branch
):
"""Test sync after pushing to the given branch on gitlab."""
self
.
sync
()
await
self
.
sync
()
last_commit
=
self
.
gitlab
.
commits
.
list
(
ref_name
=
branch
)[
0
].
id
# Push a new commit to gitlab
...
...
@@ -251,24 +263,24 @@ class GhTests(TestCase):
last_commit_gitlab
=
self
.
gitlab
.
commits
.
list
(
ref_name
=
branch
)[
0
].
id
self
.
assertNotEqual
(
last_commit
,
last_commit_gitlab
)
response
=
self
.
gl_webhook_event
(
'Push Hook'
,
last_commit_gitlab
,
branch
)
response
=
await
self
.
gl_webhook_event
(
'Push Hook'
,
last_commit_gitlab
,
branch
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content
.
decode
(),
'push event detected'
)
last_commit_github
=
self
.
github
.
get_branch
(
branch
).
commit
.
sha
self
.
assertEqual
(
last_commit_github
,
last_commit_gitlab
)
def
test_push_gitlab_master
(
self
):
async
def
test_push_gitlab_master
(
self
):
"""Test sync after pushing to gitlab on master."""
self
.
push_gitlab
(
'master'
)
await
self
.
push_gitlab
(
'master'
)
def
test_push_gitlab_devel
(
self
):
async
def
test_push_gitlab_devel
(
self
):
"""Test sync after pushing to gitlab on devel."""
self
.
push_gitlab
(
'devel'
)
await
self
.
push_gitlab
(
'devel'
)
def
test_branch_github
(
self
):
async
def
test_branch_github
(
self
):
"""Test sync after creating or deleting a branch on github."""
self
.
sync
()
await
self
.
sync
()
source_branch_name
=
'master'
target_branch_name
=
'test-branch-github'
last_commit
=
self
.
github
.
get_branch
(
source_branch_name
).
commit
.
sha
...
...
@@ -289,7 +301,7 @@ class GhTests(TestCase):
target_branch
=
self
.
github
.
get_branch
(
target_branch_name
)
last_commit_github
=
target_branch
.
commit
.
sha
response
=
self
.
gh_webhook_event
(
'push'
,
last_commit_github
,
target_branch_name
)
response
=
await
self
.
gh_webhook_event
(
'push'
,
last_commit_github
,
target_branch_name
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content
.
decode
(),
'push event detected'
)
...
...
@@ -300,14 +312,14 @@ class GhTests(TestCase):
branch_ref
.
delete
()
self
.
assertNotIn
(
target_branch_name
,
[
b
.
name
for
b
in
self
.
github
.
get_branches
()])
response
=
self
.
gh_webhook_event
(
'push'
,
'0000000000000000000000000000000000000000'
,
target_branch_name
)
response
=
await
self
.
gh_webhook_event
(
'push'
,
'0000000000000000000000000000000000000000'
,
target_branch_name
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content
.
decode
(),
'push event detected'
)
self
.
assertNotIn
(
target_branch_name
,
[
b
.
name
for
b
in
self
.
gitlab
.
branches
.
list
()])
def
test_branch_gitlab
(
self
):
async
def
test_branch_gitlab
(
self
):
"""Test sync after creating or deleting a branch on gitlab."""
self
.
sync
()
await
self
.
sync
()
source_branch_name
=
'master'
target_branch_name
=
'test-branch-gitlab'
last_commit
=
self
.
gitlab
.
commits
.
list
(
ref_name
=
source_branch_name
)[
0
].
id
...
...
@@ -323,7 +335,7 @@ class GhTests(TestCase):
file
.
save
(
branch
=
target_branch_name
,
commit_message
=
'Test new branch on gitlab'
)
last_commit_gitlab
=
self
.
gitlab
.
commits
.
list
(
ref_name
=
target_branch_name
)[
0
].
id
response
=
self
.
gl_webhook_event
(
'Push Hook'
,
last_commit_gitlab
,
target_branch_name
)
response
=
await
self
.
gl_webhook_event
(
'Push Hook'
,
last_commit_gitlab
,
target_branch_name
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content
.
decode
(),
'push event detected'
)
...
...
@@ -334,51 +346,77 @@ class GhTests(TestCase):
target_branch
.
delete
()
self
.
assertNotIn
(
target_branch_name
,
[
b
.
name
for
b
in
self
.
gitlab
.
branches
.
list
()])
response
=
self
.
gl_webhook_event
(
'Push Hook'
,
'0000000000000000000000000000000000000000'
,
target_branch_name
)
response
=
await
self
.
gl_webhook_event
(
'Push Hook'
,
'0000000000000000000000000000000000000000'
,
target_branch_name
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content
.
decode
(),
'push event detected'
)
self
.
assertNotIn
(
target_branch_name
,
[
b
.
name
for
b
in
self
.
github
.
get_branches
()])
def
test_pipeline
(
self
):
async
def
test_pipeline
(
self
):
"""Test reporting the gitlab pipeline status to github."""
self
.
sync
()
await
self
.
sync
()
last_commit
=
self
.
gitlab
.
commits
.
list
(
ref_name
=
'master'
)[
0
].
id
response
=
self
.
gl_webhook_event
(
'Pipeline Hook'
,
last_commit
,
status
=
'success'
)
response
=
await
self
.
gl_webhook_event
(
'Pipeline Hook'
,
last_commit
,
status
=
'success'
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertIn
(
'success'
,
[
status
.
state
for
status
in
self
.
github
.
get_branch
(
'master'
).
commit
.
get_statuses
()])
def
test_pr
(
self
):
async
def
test_pr
(
self
):
"""Test github's pull requests."""
self
.
sync
()
LOGGER
.
debug
(
'test_pr: 0'
)
await
self
.
sync
()
LOGGER
.
debug
(
'test_pr: 1'
)
not_accepted_string
=
"doesn't usually accept pull requests on master"
LOGGER
.
debug
(
'test_pr: 2'
)
# Test pr on master
last_commit
=
self
.
github
.
get_branch
(
"devel"
).
commit
.
sha
pr_master
=
self
.
project
.
github
().
create_pull
(
title
=
"Test pr on master"
,
body
=
''
,
head
=
"devel"
,
base
=
"master"
)
response
=
self
.
gh_webhook_event
(
'pull_request'
,
LOGGER
.
debug
(
'test_pr: 3'
)
github
=
await
sync_to_async
(
self
.
project
.
github
)()
LOGGER
.
debug
(
'test_pr: 4'
)
pr_master
=
github
.
create_pull
(
title
=
"Test pr on master"
,
body
=
''
,
head
=
"devel"
,
base
=
"master"
)
LOGGER
.
debug
(
'test_pr: 5'
)
response
=
await
self
.
gh_webhook_event
(
'pull_request'
,
last_commit
=
last_commit
,
pr_action
=
"opened"
,
pr_login
=
"foo"
,
pr_number
=
pr_master
.
number
)
LOGGER
.
debug
(
'test_pr: 6'
)
self
.
assertEqual
(
response
.
status_code
,
200
)
LOGGER
.
debug
(
'test_pr: 7'
)
self
.
assertTrue
([
c
.
body
for
c
in
pr_master
.
get_issue_comments
()
if
not_accepted_string
in
c
.
body
])
LOGGER
.
debug
(
'test_pr: 8'
)
await
sleep
(
60
)
LOGGER
.
debug
(
'test_pr: 9'
)
self
.
assertIn
(
f
'pr/
{
pr_master
.
number
}
'
,
[
b
.
name
for
b
in
self
.
gitlab
.
branches
.
list
()])
LOGGER
.
debug
(
'test_pr: 10'
)
# Test pr on devel
last_commit
=
self
.
github
.
get_branch
(
"master"
).
commit
.
sha
pr_devel
=
self
.
project
.
github
().
create_pull
(
title
=
"Test pr on devel"
,
body
=
''
,
head
=
"master"
,
base
=
"devel"
)
response
=
self
.
gh_webhook_event
(
'pull_request'
,
LOGGER
.
debug
(
'test_pr: 11'
)
pr_devel
=
github
.
create_pull
(
title
=
"Test pr on devel"
,
body
=
''
,
head
=
"master"
,
base
=
"devel"
)
LOGGER
.
debug
(
'test_pr: 12'
)
response
=
await
self
.
gh_webhook_event
(
'pull_request'
,
last_commit
=
last_commit
,
pr_action
=
"opened"
,
pr_login
=
"foo"
,
pr_number
=
pr_devel
.
number
)
LOGGER
.
debug
(
'test_pr: 13'
)
self
.
assertEqual
(
response
.
status_code
,
200
)
LOGGER
.
debug
(
'test_pr: 14'
)
self
.
assertFalse
([
c
.
body
for
c
in
pr_devel
.
get_issue_comments
()
if
not_accepted_string
in
c
.
body
])
LOGGER
.
debug
(
'test_pr: 15'
)
await
sleep
(
60
)
LOGGER
.
debug
(
'test_pr: 16'
)
self
.
assertIn
(
f
'pr/
{
pr_devel
.
number
}
'
,
[
b
.
name
for
b
in
self
.
gitlab
.
branches
.
list
()])
LOGGER
.
debug
(
'test_pr: 17'
)
# Close the pr
for
pr
in
[
pr_master
,
pr_devel
]:
pr
.
edit
(
state
=
"closed"
)
response
=
self
.
gh_webhook_event
(
'pull_request'
,
pr_action
=
"closed"
,
pr_login
=
"foo"
,
pr_number
=
pr
.
number
)
response
=
await
self
.
gh_webhook_event
(
'pull_request'
,
pr_action
=
"closed"
,
pr_login
=
"foo"
,
pr_number
=
pr
.
number
)
self
.
assertEqual
(
response
.
status_code
,
200
)
await
sleep
(
60
)
self
.
assertNotIn
(
f
'pr/
{
pr
.
number
}
'
,
[
b
.
name
for
b
in
self
.
gitlab
.
branches
.
list
()])
gh/views.py
View file @
72ba58a7
...
...
@@ -7,6 +7,7 @@ from hashlib import sha1
from
ipaddress
import
ip_address
,
ip_network
from
json
import
loads
from
asgiref.sync
import
sync_to_async
,
async_to_sync
from
django.conf
import
settings
from
django.core.mail
import
mail_admins
from
django.http
import
HttpRequest
...
...
@@ -27,8 +28,11 @@ from . import models
logger
=
logging
.
getLogger
(
__name__
)
PR_MASTER_MSG
=
"""Hi ! This project doesn't usually accept pull requests on master. If this wasn't intentionnal, you
can change the base branch of this pull request to devel (No need to close it for that). Best, a bot."""
def
check_suite
(
request
:
HttpRequest
,
rep
:
str
)
->
HttpResponse
:
async
def
check_suite
(
request
:
HttpRequest
,
rep
:
str
)
->
HttpResponse
:
"""Manage Github's check suites."""
data
=
loads
(
request
.
body
.
decode
())
slug
=
slugify
(
data
[
'repository'
][
'name'
])
...
...
@@ -36,39 +40,43 @@ def check_suite(request: HttpRequest, rep: str) -> HttpResponse:
if
'ros-release'
in
slug
:
# Don't run check suites on ros-release repositories
return
HttpResponse
(
rep
)
models
.
GithubCheckSuite
.
objects
.
get_or_create
(
id
=
data
[
'check_suite'
][
'id'
])
await
sync_to_async
(
models
.
GithubCheckSuite
.
objects
.
get_or_create
)
(
id
=
data
[
'check_suite'
][
'id'
])
return
HttpResponse
(
rep
)
def
pull_request
(
request
:
HttpRequest
,
rep
:
str
)
->
HttpResponse
:
async
def
pull_request
(
request
:
HttpRequest
,
rep
:
str
)
->
HttpResponse
:
"""Manage Github's Pull Requests."""
logger
.
info
(
'process gh pr'
)
data
=
loads
(
request
.
body
.
decode
())
event
=
data
[
'action'
]
branch
=
f
'pr/
{
data
[
"number"
]
}
'
login
=
slugify
(
data
[
"pull_request"
][
"head"
][
"repo"
][
"owner"
][
"login"
])
namespace
=
get_object_or_404
(
Namespace
,
slug_github
=
slugify
(
data
[
'repository'
][
'owner'
][
'login'
]))
project
=
get_object_or_404
(
Project
,
main_namespace
=
namespace
,
slug
=
slugify
(
data
[
'repository'
][
'name'
]))
git_repo
=
project
.
git
()
namespace
=
await
sync_to_async
(
get_object_or_404
)(
Namespace
,
slug_github
=
slugify
(
data
[
'repository'
][
'owner'
][
'login'
]))
project
=
await
sync_to_async
(
get_object_or_404
)(
Project
,
main_namespace
=
namespace
,
slug
=
slugify
(
data
[
'repository'
][
'name'
]))
git_repo
=
await
sync_to_async
(
project
.
git
)()
logger
.
debug
(
f
'
{
namespace
.
slug
}
/
{
project
.
slug
}
: Pull request on
{
branch
}
:
{
event
}
'
)
# Prevent pull requests on master when necessary
if
event
in
[
'opened'
,
'reopened'
]:
gh
=
project
.
github
()
pr
=
gh
.
get_pull
(
data
[
"number"
])
gh
=
await
sync_to_async
(
project
.
github
)
()
pr
=
await
sync_to_async
(
gh
.
get_pull
)
(
data
[
"number"
])
pr_branch
=
pr
.
base
.
ref
if
not
project
.
accept_pr_to_master
and
pr_branch
==
'master'
\
and
'devel'
in
[
b
.
name
for
b
in
gh
.
get_branches
()]
and
login
!=
namespace
.
slug_github
:
if
not
project
.
accept_pr_to_master
and
pr_branch
==
'master'
and
'devel'
in
[
b
.
name
for
b
in
await
sync_to_async
(
gh
.
get_branches
)()
]
and
login
!=
namespace
.
slug_github
:
logger
.
info
(
f
"
{
namespace
.
slug
}
/
{
project
.
slug
}
: New pr
{
data
[
'number'
]
}
to master"
)
pr
.
create_issue_comment
(
"Hi ! This project doesn't usually accept pull requests on master. If this wasn't "
"intentionnal, you can change the base branch of this pull request to devel "
"(No need to close it for that). Best, a bot."
)
await
sync_to_async
(
pr
.
create_issue_comment
)(
PR_MASTER_MSG
)
gh_remote_name
=
f
'github/
{
login
}
'
if
gh_remote_name
not
in
git_repo
.
remotes
:
remote
=
git_repo
.
create_remote
(
gh_remote_name
,
data
[
"pull_request"
][
"head"
][
"repo"
][
"clone_url"
])
remote
=
await
sync_to_async
(
git_repo
.
create_remote
)(
gh_remote_name
,
data
[
"pull_request"
][
"head"
][
"repo"
][
"clone_url"
])
else
:
remote
=
git_repo
.
remote
(
gh_remote_name
)
remote
=
await
sync_to_async
(
git_repo
.
remote
)
(
gh_remote_name
)
# Sync the pull request with the pr/XX branch on Gitlab
if
event
in
[
'opened'
,
'reopened'
,
'synchronize'
]:
...
...
@@ -79,12 +87,13 @@ def pull_request(request: HttpRequest, rep: str) -> HttpResponse:
if
branch
in
git_repo
.
branches
:
git_repo
.
heads
[
branch
].
commit
=
commit
else
:
git_repo
.
create_head
(
branch
,
commit
=
commit
)
await
sync_to_async
(
git_repo
.
create_head
)
(
branch
,
commit
=
commit
)
# Create a gitlab remote if it doesn't exist
gl_remote_name
=
f
'gitlab/
{
namespace
.
slug
}
'
if
gl_remote_name
not
in
git_repo
.
remotes
:
git_repo
.
create_remote
(
gl_remote_name
,
url
=
project
.
remote_url_gitlab
())
url
=
await
sync_to_async
(
project
.
remote_url_gitlab
)()
await
sync_to_async
(
git_repo
.
create_remote
)(
gl_remote_name
,
url
=
url
)
# Push the changes to gitlab
logger
.
info
(
f
'
{
namespace
.
slug
}
/
{
project
.
slug
}
: Pushing
{
commit
}
on
{
branch
}
on gitlab'
)
...
...
@@ -99,14 +108,16 @@ def pull_request(request: HttpRequest, rep: str) -> HttpResponse:
if
branch
in
git_repo
.
branches
:
git_repo
.
delete_head
(
branch
,
force
=
True
)
git_repo
.
delete_remote
(
gh_remote_name
)
project
.
gitlab
().
branches
.
delete
(
branch
)
gitlab
=
await
sync_to_async
(
project
.
gitlab
)()
await
sync_to_async
(
gitlab
.
branches
.
delete
)(
branch
)
logger
.
info
(
f
'
{
namespace
.
slug
}
/
{
project
.
slug
}
: Deleted branch
{
branch
}
'
)
return
HttpResponse
(
rep
)
def
push
(
request
:
HttpRequest
,
source
:
SOURCES
,
rep
:
str
)
->
HttpResponse
:
async
def
push
(
request
:
HttpRequest
,
source
:
SOURCES
,
rep
:
str
)
->
HttpResponse
:
"""Someone pushed on github or gitlab. Synchronise local & remote repos."""
logger
.
debug
(
'start gh.views.push'
)
data
=
loads
(
request
.
body
.
decode
())
slug
=
slugify
(
data
[
'repository'
][
'name'
])
...
...
@@ -114,18 +125,20 @@ def push(request: HttpRequest, source: SOURCES, rep: str) -> HttpResponse:
return
HttpResponse
(
rep
)
if
source
==
SOURCES
.
gitlab
:
namespace
=
get_object_or_404
(
Namespace
,
slug_gitlab
=
slugify
(
data
[
'project'
][
'path_with_namespace'
].
split
(
'/'
)[
0
]))
namespace
=
await
sync_to_async
(
get_object_or_404
)(
Namespace
,
slug_gitlab
=
slugify
(
data
[
'project'
][
'path_with_namespace'
].
split
(
'/'
)[
0
]))
else
:
namespace
=
get_object_or_404
(
Namespace
,
slug_github
=
slugify
(
data
[
'repository'
][
'owner'
][
'login'
]))
namespace
=
await
sync_to_async
(
get_object_or_404
)(
Namespace
,
slug_github
=
slugify
(
data
[
'repository'
][
'owner'
][
'login'
]))
project
=
get_object_or_404
(
Project
,
main_namespace
=
namespace
,
slug
=
slug
)
project
=
await
sync_to_async
(
get_object_or_404
)
(
Project
,
main_namespace
=
namespace
,
slug
=
slug
)
branch
=
data
[
'ref'
][
11
:]
# strip 'refs/heads/'
commit
=
data
[
'after'
]
gl_remote_name
=
f
'gitlab/
{
namespace
.
slug
}
'
gh_remote_name
=
f
'github/
{
namespace
.
slug
}
'
git_repo
=
project
.
git
()
git_repo
=
await
sync_to_async
(
project
.
git
)
()
logger
.
debug
(
f
'
{
namespace
.
slug
}
/
{
slug
}
: Push detected on
{
source
.
name
}
{
branch
}
(commit
{
commit
}
)'
)
if
branch
.
startswith
(
'pr/'
):
# Don't sync pr/XX branches here, they are already handled by pull_request()
...
...
@@ -136,16 +149,18 @@ def push(request: HttpRequest, source: SOURCES, rep: str) -> HttpResponse:
# Fetch the latest commit from gitlab
if
gl_remote_name
in
git_repo
.
remotes
:
gl_remote
=
git_repo
.
remote
(
gl_remote_name
)
gl_remote
=
await
sync_to_async
(
git_repo
.
remote
)
(
gl_remote_name
)
else
:
gl_remote
=
git_repo
.
create_remote
(
gl_remote_name
,
url
=
project
.
remote_url_gitlab
())
url
=
await
sync_to_async
(
project
.
remote_url_gitlab
)()