Update for sr.ht unified config
This commit is contained in:
parent
bcb14b332d
commit
84560e5fa0
|
@ -0,0 +1,68 @@
|
|||
[sr.ht]
|
||||
#
|
||||
# The name of your network of sr.ht-based sites
|
||||
site-name=sr.ht
|
||||
#
|
||||
# Contact information for the site owners
|
||||
owner-name=Drew DeVault
|
||||
owner-email=sir@cmpwn.com
|
||||
#
|
||||
# The source code for your fork of sr.ht
|
||||
source-url=https://git.sr.ht/~sircmpwn/srht
|
||||
#
|
||||
# A secret key to encrypt session cookies with
|
||||
secret-key=CHANGEME
|
||||
|
||||
[mail]
|
||||
#
|
||||
# Outgoing SMTP settings
|
||||
smtp-host=
|
||||
smtp-port=
|
||||
smtp-user=
|
||||
smtp-password=
|
||||
smtp-from=
|
||||
#
|
||||
# Application exceptions are emailed to this address
|
||||
error-to=
|
||||
error-from=
|
||||
#
|
||||
# Your PGP key information (DO NOT mix up pub and priv here)
|
||||
# You must remove the password from your secret key, if present.
|
||||
# You can do this with gpg --edit-key [key-id], then use the passwd
|
||||
# command and do not enter a new password.
|
||||
pgp-privkey=
|
||||
pgp-pubkey=
|
||||
pgp-key-id=
|
||||
|
||||
[dispatch.sr.ht]
|
||||
#
|
||||
# URL dispatch.sr.ht is being served at (protocol://domain)
|
||||
origin=http://dispatch.sr.ht.local
|
||||
#
|
||||
# Address and port to bind the debug server to
|
||||
debug-host=0.0.0.0
|
||||
debug-port=5005
|
||||
#
|
||||
# Configures the SQLAlchemy connection string for the database.
|
||||
connection-string=postgresql://postgres@localhost/dispatch.sr.ht
|
||||
#
|
||||
# dispatch.sr.ht's OAuth client ID and secret for meta.sr.ht
|
||||
# Register your client at meta.example.org/oauth
|
||||
oauth-client-id=
|
||||
oauth-client-secret=
|
||||
|
||||
[dispatch.sr.ht::github]
|
||||
#
|
||||
# Fill this in with a registered GitHub OAuth client to enable GitHub
|
||||
# integration
|
||||
oauth-client-id=
|
||||
oauth-client-secret=
|
||||
|
||||
[builds.sr.ht]
|
||||
origin=http://builds.sr.ht.local
|
||||
#
|
||||
# Fill this in with your builds.sr.ht OAuth client ID
|
||||
oauth-client-id=
|
||||
|
||||
[meta.sr.ht]
|
||||
origin=http://meta.sr.ht.local
|
|
@ -1,63 +0,0 @@
|
|||
#
|
||||
# dispatch.sr.ht config
|
||||
|
||||
[server]
|
||||
#
|
||||
# Specifies the protocol (usually http or https) dispatch.sr.ht runs with.
|
||||
protocol=http
|
||||
#
|
||||
# Specifies the domain name dispatch.sr.ht is running on.
|
||||
domain=dispatch.sr.ht.local
|
||||
#
|
||||
# A secret key to encrypt session cookies with.
|
||||
secret-key=CHANGEME
|
||||
|
||||
[debug]
|
||||
#
|
||||
# Address and port to bind the debug server to.
|
||||
debug-host=0.0.0.0
|
||||
debug-port=5005
|
||||
|
||||
[sr.ht]
|
||||
#
|
||||
# Configures the SQLAlchemy connection string for the database.
|
||||
connection-string=postgresql://postgres@localhost/dispatch.sr.ht
|
||||
#
|
||||
# The name of your network of sr.ht-based sites
|
||||
site-name=sr.ht
|
||||
|
||||
[network]
|
||||
#
|
||||
# Location of other sites in your network
|
||||
#
|
||||
# This isn't a hardcoded list, add or remove entries as you like. The upstream
|
||||
# sites do know about each other and will omit integrations if you leave out
|
||||
# the relevant site. Only meta is required.
|
||||
meta=http://meta.sr.ht.local
|
||||
git=http://git.sr.ht.local
|
||||
builds=http://builds.sr.ht.local
|
||||
todo=http://todo.sr.ht.local
|
||||
dispatch=http://dispatch.sr.ht.local
|
||||
|
||||
[meta.sr.ht]
|
||||
oauth-client-id=CHANGEME
|
||||
oauth-client-secret=CHANGEME
|
||||
|
||||
[builds.sr.ht]
|
||||
#
|
||||
# Fill this in with the oauth client ID builds.sr.ht uses for builds.sr.ht
|
||||
# integration
|
||||
oauth-client-id=
|
||||
|
||||
[github]
|
||||
#
|
||||
# Fill this in with a registered GitHub OAuth client to enable GitHub
|
||||
# integration
|
||||
#
|
||||
# When you reigster the client, set the callback URL to:
|
||||
#
|
||||
# https://dispatch.sr.ht/github/callback
|
||||
#
|
||||
# Of course, replace the domain with your own.
|
||||
oauth-client-id=
|
||||
oauth-client-secret=
|
|
@ -1,59 +1,58 @@
|
|||
from flask import render_template, request
|
||||
from flask_login import LoginManager, current_user
|
||||
from jinja2 import Markup
|
||||
import locale
|
||||
import urllib
|
||||
|
||||
from srht.config import cfg, cfgi, load_config
|
||||
load_config("dispatch")
|
||||
from srht.flask import SrhtFlask
|
||||
from srht.config import cfg
|
||||
from srht.database import DbSession
|
||||
db = DbSession(cfg("sr.ht", "connection-string"))
|
||||
|
||||
db = DbSession(cfg("dispatch.sr.ht", "connection-string"))
|
||||
|
||||
from dispatchsrht.types import User
|
||||
|
||||
db.init()
|
||||
|
||||
from srht.flask import SrhtFlask
|
||||
app = SrhtFlask("dispatch", __name__)
|
||||
app.secret_key = cfg("server", "secret-key")
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
class DispatchApp(SrhtFlask):
|
||||
def __init__(self):
|
||||
super().__init__("dispatch.sr.ht", __name__)
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(username):
|
||||
return User.query.filter(User.username == username).first()
|
||||
from dispatchsrht.blueprints.html import html
|
||||
self.register_blueprint(html)
|
||||
|
||||
login_manager.anonymous_user = lambda: None
|
||||
meta_client_id = cfg("dispatch.sr.ht", "oauth-client-id")
|
||||
meta_client_secret = cfg("dispatch.sr.ht", "oauth-client-secret")
|
||||
builds_sr_ht = cfg("builds.sr.ht", "oauth-client-id")
|
||||
self.configure_meta_auth(meta_client_id, meta_client_secret,
|
||||
base_scopes=["profile", "keys"] + [
|
||||
builds_sr_ht + "/jobs:write"
|
||||
] if builds_sr_ht else [])
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, 'en_US')
|
||||
except:
|
||||
pass
|
||||
@self.login_manager.user_loader
|
||||
def user_loader(username):
|
||||
# TODO: Switch to a session token
|
||||
return User.query.filter(User.username == username).one_or_none()
|
||||
|
||||
builds_sr_ht = cfg("builds.sr.ht", "oauth-client-id")
|
||||
@self.context_processor
|
||||
def inject():
|
||||
from dispatchsrht.tasks import taskdefs
|
||||
return { "taskdefs": taskdefs }
|
||||
|
||||
def oauth_url(return_to):
|
||||
return "{}/oauth/authorize?client_id={}&scopes=profile{}&state={}".format(
|
||||
meta_sr_ht, meta_client_id,
|
||||
"," + builds_sr_ht + "/jobs:write" if builds_sr_ht else "",
|
||||
urllib.parse.quote_plus(return_to))
|
||||
def register_tasks(self):
|
||||
# This is done in a separate step because we can't import these right
|
||||
# away due to a circular dependency on the app variable
|
||||
from dispatchsrht.tasks import taskdefs
|
||||
for taskdef in taskdefs():
|
||||
self.register_blueprint(taskdef.blueprint,
|
||||
url_prefix="/" + taskdef.name)
|
||||
|
||||
from dispatchsrht.blueprints.html import html
|
||||
from dispatchsrht.blueprints.auth import auth
|
||||
def lookup_or_register(self, exchange, profile, scopes):
|
||||
user = User.query.filter(User.username == profile["username"]).first()
|
||||
if not user:
|
||||
user = User()
|
||||
db.session.add(user)
|
||||
user.username = profile.get("username")
|
||||
user.email = profile.get("email")
|
||||
user.oauth_token = exchange["token"]
|
||||
user.oauth_token_expires = exchange["expires"]
|
||||
user.oauth_token_scopes = scopes
|
||||
db.session.commit()
|
||||
return user
|
||||
|
||||
app.register_blueprint(html)
|
||||
app.register_blueprint(auth)
|
||||
|
||||
from dispatchsrht.tasks import taskdefs
|
||||
for taskdef in taskdefs():
|
||||
app.register_blueprint(taskdef.blueprint, url_prefix="/" + taskdef.name)
|
||||
|
||||
meta_sr_ht = cfg("network", "meta")
|
||||
meta_client_id = cfg("meta.sr.ht", "oauth-client-id")
|
||||
|
||||
@app.context_processor
|
||||
def inject():
|
||||
return {
|
||||
"oauth_url": oauth_url(request.full_path),
|
||||
"current_user": User.query.filter(User.id == current_user.id).first() \
|
||||
if current_user else None,
|
||||
"taskdefs": taskdefs
|
||||
}
|
||||
app = DispatchApp()
|
||||
app.register_tasks()
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
from flask import Blueprint, request, render_template, redirect
|
||||
from flask_login import login_user, logout_user
|
||||
from sqlalchemy import or_
|
||||
from srht.config import cfg
|
||||
from srht.flask import DATE_FORMAT
|
||||
from srht.oauth import OAuthScope
|
||||
from srht.database import db
|
||||
from dispatchsrht.types import User
|
||||
from datetime import datetime
|
||||
import urllib.parse
|
||||
import requests
|
||||
|
||||
auth = Blueprint('auth', __name__)
|
||||
|
||||
meta_uri = cfg("network", "meta")
|
||||
client_id = cfg("meta.sr.ht", "oauth-client-id")
|
||||
client_secret = cfg("meta.sr.ht", "oauth-client-secret")
|
||||
|
||||
@auth.route("/oauth/callback")
|
||||
def oauth_callback():
|
||||
error = request.args.get("error")
|
||||
if error:
|
||||
details = request.args.get("details")
|
||||
return render_template("oauth-error.html", details=details)
|
||||
exchange = request.args.get("exchange")
|
||||
scopes = request.args.get("scopes")
|
||||
state = request.args.get("state")
|
||||
_scopes = [OAuthScope(s) for s in scopes.split(",")]
|
||||
if not OAuthScope("profile:read") in _scopes:
|
||||
return render_template("oauth-error.html",
|
||||
details="dispatch.sr.ht requires profile and key access at a mininum to function correctly. " +
|
||||
"To use dispatch.sr.ht, try again and do not untick these permissions.")
|
||||
if not exchange:
|
||||
return render_template("oauth-error.html",
|
||||
details="Expected an exchange token from meta.sr.ht. Something odd has happened, try again.")
|
||||
r = requests.post(meta_uri + "/oauth/exchange", json={
|
||||
"client_id": client_id,
|
||||
"client_secret": client_secret,
|
||||
"exchange": exchange,
|
||||
})
|
||||
if r.status_code != 200:
|
||||
return render_template("oauth-error.html",
|
||||
details="Error occured retrieving OAuth token. Try again.")
|
||||
json = r.json()
|
||||
token = json.get("token")
|
||||
expires = json.get("expires")
|
||||
if not token or not expires:
|
||||
return render_template("oauth-error.html",
|
||||
details="Error occured retrieving OAuth token. Try again.")
|
||||
expires = datetime.strptime(expires, DATE_FORMAT)
|
||||
|
||||
r = requests.get(meta_uri + "/api/user/profile", headers={
|
||||
"Authorization": "token " + token
|
||||
})
|
||||
if r.status_code != 200:
|
||||
return render_template("oauth-error.html",
|
||||
details="Error occured retrieving account info. Try again.")
|
||||
|
||||
json = r.json()
|
||||
user = User.query.filter(or_(User.oauth_token == token,
|
||||
User.username == json["username"])).first()
|
||||
if not user:
|
||||
user = User()
|
||||
db.session.add(user)
|
||||
user.username = json.get("username")
|
||||
user.email = json.get("email")
|
||||
user.oauth_token = token
|
||||
user.oauth_token_expires = expires
|
||||
user.oauth_token_scopes = scopes
|
||||
db.session.commit()
|
||||
|
||||
login_user(user, remember=True)
|
||||
if not state or not state.startswith("/"):
|
||||
return redirect("/")
|
||||
else:
|
||||
return redirect(urllib.parse.unquote(state))
|
||||
|
||||
@auth.route("/logout")
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(request.headers.get("Referer") or "/")
|
|
@ -1,13 +1,13 @@
|
|||
from flask import Blueprint, render_template
|
||||
from flask_login import current_user
|
||||
from srht.config import cfg
|
||||
from dispatchsrht.decorators import loginrequired
|
||||
from srht.flask import loginrequired
|
||||
from dispatchsrht.types import Task
|
||||
import requests
|
||||
|
||||
html = Blueprint('html', __name__)
|
||||
|
||||
meta_uri = cfg("network", "meta")
|
||||
meta_uri = cfg("meta.sr.ht", "origin")
|
||||
|
||||
@html.route("/")
|
||||
def index():
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
from flask import redirect, request, abort
|
||||
from flask_login import current_user
|
||||
from functools import wraps
|
||||
from dispatchsrht.app import oauth_url
|
||||
|
||||
import urllib
|
||||
|
||||
def loginrequired(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not current_user:
|
||||
return redirect(oauth_url(request.url))
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
|
@ -13,9 +13,9 @@ from flask_login import current_user
|
|||
from functools import wraps
|
||||
from github import Github, GithubException
|
||||
from urllib.parse import urlencode
|
||||
from srht.database import Base, db
|
||||
from srht.config import cfg
|
||||
from dispatchsrht.decorators import loginrequired
|
||||
from srht.database import Base, db
|
||||
from srht.flask import loginrequired
|
||||
from dispatchsrht.app import app
|
||||
|
||||
def _first_line(text):
|
||||
|
@ -23,8 +23,10 @@ def _first_line(text):
|
|||
return text
|
||||
return text[:text.index("\n") + 1]
|
||||
|
||||
_github_client_id = cfg("github", "oauth-client-id", default=None)
|
||||
_github_client_secret = cfg("github", "oauth-client-secret", default=None)
|
||||
_github_client_id = cfg("dispatch.sr.ht::github",
|
||||
"oauth-client-id", default=None)
|
||||
_github_client_secret = cfg("dispatch.sr.ht::github",
|
||||
"oauth-client-secret", default=None)
|
||||
|
||||
class GitHubAuthorization(Base):
|
||||
__tablename__ = "github_authorization"
|
||||
|
@ -92,9 +94,9 @@ def github_callback():
|
|||
db.session.commit()
|
||||
return redirect(state)
|
||||
|
||||
_root = "{}://{}".format(cfg("server", "protocol"), cfg("server", "domain"))
|
||||
_builds_sr_ht = cfg("network", "builds", default=None)
|
||||
_secret_key = cfg("server", "secret-key")
|
||||
_root = cfg("dispatch.sr.ht", "origin")
|
||||
_builds_sr_ht = cfg("builds.sr.ht", "origin", default=None)
|
||||
_secret_key = cfg("sr.ht", "secret-key")
|
||||
_kdf = PBKDF2HMAC(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
|
|
|
@ -7,29 +7,31 @@ from jinja2 import Markup
|
|||
from uuid import UUID, uuid4
|
||||
from srht.database import Base, db
|
||||
from srht.config import cfg
|
||||
from srht.flask import icon
|
||||
from srht.validation import Validation
|
||||
from dispatchsrht.tasks import TaskDef
|
||||
from dispatchsrht.tasks.github.auth import githubloginrequired, GitHubAuthorization
|
||||
from dispatchsrht.tasks.github.auth import GitHubAuthorization
|
||||
from dispatchsrht.tasks.github.auth import githubloginrequired
|
||||
from dispatchsrht.tasks.github.auth import submit_build
|
||||
from dispatchsrht.types import Task
|
||||
|
||||
_root = "{}://{}".format(cfg("server", "protocol"), cfg("server", "domain"))
|
||||
_builds_sr_ht = cfg("network", "builds", default=None)
|
||||
_github_client_id = cfg("github", "oauth-client-id", default=None)
|
||||
_github_client_secret = cfg("github", "oauth-client-secret", default=None)
|
||||
_root = cfg("dispatch.sr.ht", "origin")
|
||||
_builds_sr_ht = cfg("builds.sr.ht", "origin", default=None)
|
||||
_github_client_id = cfg("dispatch.sr.ht::github",
|
||||
"oauth-client-id", default=None)
|
||||
_github_client_secret = cfg("dispatch.sr.ht::github",
|
||||
"oauth-client-secret", default=None)
|
||||
|
||||
class GitHubCommitToBuild(TaskDef):
|
||||
name = "github_commit_to_build"
|
||||
description = Markup('''
|
||||
<i class="fa fa-github"></i>
|
||||
GitHub commits
|
||||
<i class="fa fa-arrow-right"></i>
|
||||
builds.sr.ht jobs
|
||||
''')
|
||||
enabled = bool(_github_client_id
|
||||
and _github_client_secret
|
||||
and _builds_sr_ht)
|
||||
|
||||
def description():
|
||||
return (icon("github") + Markup(" GitHub commits ") +
|
||||
icon("caret-right") + Markup(" builds.sr.ht jobs"))
|
||||
|
||||
class _GitHubCommitToBuildRecord(Base):
|
||||
__tablename__ = "github_commit_to_build"
|
||||
id = sa.Column(sau.UUIDType, primary_key=True)
|
||||
|
|
|
@ -7,29 +7,31 @@ from jinja2 import Markup
|
|||
from uuid import UUID, uuid4
|
||||
from srht.database import Base, db
|
||||
from srht.config import cfg
|
||||
from srht.flask import icon
|
||||
from srht.validation import Validation
|
||||
from dispatchsrht.tasks import TaskDef
|
||||
from dispatchsrht.tasks.github.auth import githubloginrequired, GitHubAuthorization
|
||||
from dispatchsrht.tasks.github.auth import GitHubAuthorization
|
||||
from dispatchsrht.tasks.github.auth import githubloginrequired
|
||||
from dispatchsrht.tasks.github.auth import submit_build
|
||||
from dispatchsrht.types import Task
|
||||
|
||||
_root = "{}://{}".format(cfg("server", "protocol"), cfg("server", "domain"))
|
||||
_builds_sr_ht = cfg("network", "builds", default=None)
|
||||
_github_client_id = cfg("github", "oauth-client-id", default=None)
|
||||
_github_client_secret = cfg("github", "oauth-client-secret", default=None)
|
||||
_root = cfg("dispatch.sr.ht", "origin")
|
||||
_builds_sr_ht = cfg("builds.sr.ht", "origin", default=None)
|
||||
_github_client_id = cfg("dispatch.sr.ht::github",
|
||||
"oauth-client-id", default=None)
|
||||
_github_client_secret = cfg("dispatch.sr.ht::github",
|
||||
"oauth-client-secret", default=None)
|
||||
|
||||
class GitHubPRToBuild(TaskDef):
|
||||
name = "github_pr_to_build"
|
||||
description = Markup('''
|
||||
<i class="fa fa-github"></i>
|
||||
GitHub pull requests
|
||||
<i class="fa fa-arrow-right"></i>
|
||||
builds.sr.ht jobs
|
||||
''')
|
||||
enabled = bool(_github_client_id
|
||||
and _github_client_secret
|
||||
and _builds_sr_ht)
|
||||
|
||||
def description():
|
||||
return (icon("github") + Markup(" GitHub pull requests ") +
|
||||
icon("caret-right") + Markup(" builds.sr.ht jobs"))
|
||||
|
||||
class _GitHubPRToBuildRecord(Base):
|
||||
__tablename__ = "github_pr_to_build"
|
||||
id = sa.Column(sau.UUIDType, primary_key=True)
|
||||
|
|
|
@ -26,10 +26,9 @@
|
|||
href="/{{ task.name }}/configure"
|
||||
class="btn btn-primary btn-sm pull-right"
|
||||
>
|
||||
Configure task
|
||||
<i class="fa fa-caret-right"></i>
|
||||
Configure task {{icon("caret-right")}}
|
||||
</a>
|
||||
{{ task.description }}
|
||||
{{ task.description() }}
|
||||
</h4>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
|
|
@ -15,10 +15,7 @@
|
|||
<a
|
||||
href="/configure"
|
||||
class="btn btn-primary btn-block"
|
||||
>
|
||||
Configure new task
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</a>
|
||||
>Configure new task {{icon("caret-right")}}</a>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{% if not any(tasks) %}
|
||||
|
@ -44,7 +41,7 @@
|
|||
</a>
|
||||
</h4>
|
||||
<p>
|
||||
{{ task.taskdef.description }}
|
||||
{{ task.taskdef.description() }}
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -26,15 +26,12 @@
|
|||
<form class="event" method="POST">
|
||||
<input type="hidden" name="repo" value="{{ repo.full_name }}" />
|
||||
<h4>
|
||||
<i class="fa fa-github"></i>
|
||||
{{icon("github")}}
|
||||
{% if repo.full_name not in existing %}
|
||||
<button
|
||||
type="submit"
|
||||
class="pull-right btn btn-primary btn-lg"
|
||||
>
|
||||
Add task
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</button>
|
||||
>Add task {{icon("caret-right")}}</button>
|
||||
{% else %}
|
||||
<button
|
||||
class="pull-right btn btn-default btn-lg"
|
||||
|
@ -46,7 +43,7 @@
|
|||
href="{{ repo.html_url }}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
><i class="fa fa-external-link"></i></a>
|
||||
>{{icon("external-link-alt")}}</a>
|
||||
</h4>
|
||||
</form>
|
||||
{% endfor %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<h2>dispatch</h2>
|
||||
<p>
|
||||
Welcome to dispatch.sr.ht. This is a part of the
|
||||
<a href="{{cfg("network", "meta")}}">
|
||||
<a href="{{cfg("meta.sr.ht", "origin")}}">
|
||||
{{cfg("sr.ht", "site-name")}} network
|
||||
</a>
|
||||
and provides integration services to members.
|
||||
|
|
4
run.py
4
run.py
|
@ -6,6 +6,6 @@ import os
|
|||
app.static_folder = os.path.join(os.getcwd(), "static")
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host=cfg("debug", "debug-host"),
|
||||
port=cfgi("debug", "debug-port"),
|
||||
app.run(host=cfg("dispatch.sr.ht", "debug-host"),
|
||||
port=cfgi("dispatch.sr.ht", "debug-port"),
|
||||
debug=True)
|
||||
|
|
Loading…
Reference in New Issue