-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a very trivial brancher to start pushing fake branches to test. For now those branches are just a copy of net-next. Signed-off-by: Jakub Kicinski <[email protected]>
- Loading branch information
Showing
2 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
#!/usr/bin/env python3 | ||
# SPDX-License-Identifier: GPL-2.0 | ||
|
||
import configparser | ||
import datetime | ||
import json | ||
import os | ||
import time | ||
|
||
from core import NIPA_DIR | ||
from core import log, log_open_sec, log_end_sec, log_init | ||
from core import Tree | ||
|
||
""" | ||
Config: | ||
[trees] | ||
net-next=net-next, net-next, origin, origin/main | ||
[target] | ||
public_url=https://github.com/linux-netdev/testing.git | ||
[email protected]:linux-netdev/testing.git | ||
branch_pfx=net-next- | ||
[output] | ||
branches=branches.json | ||
""" | ||
|
||
|
||
def hour_timestamp(when=None) -> int: | ||
if when is None: | ||
when = datetime.datetime.now(datetime.UTC) | ||
ts = when.timestamp() | ||
return int(ts / (60 * 60)) | ||
|
||
|
||
def dump_branches(config, state) -> None: | ||
log_open_sec("Update branches manifest") | ||
pub_url = config.get('target', 'public_url') | ||
|
||
data = [] | ||
for name, val in state["branches"].items(): | ||
data.append({"branch": name, "date": val, "url": pub_url + " " + name}) | ||
|
||
branches = config.get("output", "branches") | ||
with open(branches, 'w') as fp: | ||
json.dump(data, fp) | ||
log_end_sec() | ||
|
||
|
||
def create_new(config, state, tree, tgt_remote) -> None: | ||
now = datetime.datetime.now(datetime.UTC) | ||
pfx = config.get("target", "branch_pfx") | ||
branch_name = pfx + datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%d--%H-%M") | ||
|
||
log_open_sec("Fetching latest net-next") | ||
tree.git_fetch(tree.remote) | ||
tree.git_reset(tree.branch, hard=True) | ||
log_end_sec() | ||
|
||
state["branches"][branch_name] = now.isoformat() | ||
|
||
log_open_sec("Pushing out") | ||
tree.git_push(tgt_remote, "HEAD:" + branch_name) | ||
log_end_sec() | ||
|
||
|
||
def reap_old(config, state, tree, tgt_remote) -> None: | ||
now = datetime.datetime.now(datetime.UTC) | ||
pfx = config.get("target", "branch_pfx") | ||
|
||
log_open_sec("Clean up old branches") | ||
tree.git_fetch(tgt_remote) | ||
|
||
branches = tree.git(['branch', '-a']) | ||
branches = branches.split('\n') | ||
r_tgt_pfx = 'remotes/' + tgt_remote + '/' | ||
|
||
found = set() | ||
for br in branches: | ||
br = br.strip() | ||
if not br.startswith(r_tgt_pfx + pfx): | ||
continue | ||
br = br[len(r_tgt_pfx):] | ||
found.add(br) | ||
if br not in state["branches"]: | ||
tree.git_push(tgt_remote, ':' + br) | ||
continue | ||
when = datetime.datetime.fromisoformat(state["branches"][br]) | ||
if now - when > datetime.timedelta(days=5): | ||
tree.git_push(tgt_remote, ':' + br) | ||
del state["branches"][br] | ||
continue | ||
state_has = set(state["branches"].keys()) | ||
lost = state_has.difference(found) | ||
for br in lost: | ||
log_open_sec("Removing lost branch " + br + " from state") | ||
del state["branches"][br] | ||
log_end_sec() | ||
log_end_sec() | ||
|
||
|
||
def main_loop(config, state, tree, tgt_remote) -> None: | ||
now = datetime.datetime.now(datetime.UTC) | ||
now_h = hour_timestamp(now) | ||
if now_h - state["last"] < 3: | ||
time.sleep(20) | ||
return | ||
|
||
reap_old(config, state, tree, tgt_remote) | ||
create_new(config, state, tree, tgt_remote) | ||
|
||
state["last"] = now_h | ||
|
||
dump_branches(config, state) | ||
|
||
|
||
def prep_remote(config, tree) -> str: | ||
tgt_tree = config.get('target', 'push_url') | ||
|
||
log_open_sec("Prep remote") | ||
remotes = tree.remotes() | ||
for r in remotes: | ||
if remotes[r]["push"] == tgt_tree: | ||
log("Found remote, it is " + r) | ||
return r | ||
|
||
log("Remote not found, adding") | ||
|
||
if "brancher" in remotes: | ||
log("Remote 'brancher' already exists with different URL") | ||
raise Exception("Remote exists with different URL") | ||
|
||
tree.git(['remote', 'add', 'brancher', tgt_tree]) | ||
log_end_sec() | ||
|
||
|
||
def main() -> None: | ||
config = configparser.ConfigParser() | ||
config.read(['nipa.config', 'pw.config', 'brancher.config']) | ||
|
||
log_init(config.get('log', 'type', fallback='stdout'), | ||
config.get('log', 'file', fallback=None)) | ||
|
||
state = {} | ||
if os.path.exists("brancher.state"): | ||
with open("brancher.state") as fp: | ||
state = json.load(fp) | ||
|
||
if "last" not in state: | ||
state["last"] = 0 | ||
if "branches" not in state: | ||
state["branches"] = {} | ||
|
||
tree_obj = None | ||
tree_dir = config.get('dirs', 'trees', fallback=os.path.join(NIPA_DIR, "../")) | ||
for tree in config['trees']: | ||
opts = [x.strip() for x in config['trees'][tree].split(',')] | ||
prefix = opts[0] | ||
fspath = opts[1] | ||
remote = opts[2] | ||
branch = opts[3] | ||
src = os.path.join(tree_dir, fspath) | ||
# name, pfx, fspath, remote=None, branch=None | ||
tree_obj = Tree(tree, prefix, src, remote=remote, branch=branch) | ||
tree = tree_obj | ||
|
||
tgt_remote = prep_remote(config, tree) | ||
|
||
try: | ||
while True: | ||
main_loop(config, state, tree, tgt_remote) | ||
finally: | ||
with open('brancher.state', 'w') as f: | ||
json.dump(state, f) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |