cockpit/bots/npm-update

138 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This file is part of Cockpit.
#
# Copyright (C) 2017 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
# To use this example add a line to an issue with the "bot" label
#
# * [ ] npm-update patternfly
#
# Dependencies that are either fragile (minor updates break)
# or are part of our public Javascript API and need caution
FRAGILE = [
"jquery",
"patternfly",
"paternfly-bootstrap-combobox",
# version 0.32.2 switches to babel 7, which causes build failures with our babel 6 build system
"react-bootstrap",
]
import collections
import json
import os
import random
import sys
import subprocess
sys.dont_write_bytecode = True
import task
BOTS = os.path.abspath(os.path.dirname(__file__))
BASE = os.path.normpath(os.path.join(BOTS, ".."))
def package_json(data=None, package=None, version=None):
package_path = os.path.join(BASE, "package.json")
if data is None:
with open(package_path, "r") as f:
return json.load(f, object_pairs_hook=collections.OrderedDict)
else:
if package:
data = dict(data, dependencies=dict(data["dependencies"], **{ package: version }))
with open(package_path, "w") as f:
json.dump(data, f, indent=2, separators=(',', ': '))
f.write("\n")
def map_dict(dependencies, function):
items = [ ]
for (name, value) in dependencies.items():
items.append((name, function(name, value)))
return collections.OrderedDict(items)
def execute(*args):
if task.verbose:
sys.stderr.write("+ " + " ".join(args) + "\n")
subprocess.check_call(args, cwd=BASE)
def output(*args):
if task.verbose:
sys.stderr.write("+ " + " ".join(args) + "\n")
return subprocess.check_output(args, cwd=BASE, universal_newlines=True)
def run(specified_package, verbose=False, **kwargs):
# Force all current dependencies in place
execute("npm", "install")
orig_package_json = package_json()
if specified_package:
packages = [ specified_package ]
else:
packages = list(orig_package_json["dependencies"].keys())
random.shuffle(packages)
def prefix(name, version):
if not version[0].isdigit():
return version
if name == specified_package or name not in FRAGILE:
return "^" + version
return "~" + version
for package in packages:
orig_version = orig_package_json["dependencies"][package]
upgradeable_version = prefix(package, orig_version)
if orig_version == upgradeable_version:
continue
# Run npm upgrade for our package
#
package_json(orig_package_json, package, upgradeable_version)
execute("npm", "upgrade", "--save", package)
# Check if that did an upgrade
new_version = package_json()["dependencies"][package].lstrip("^~")
if new_version != orig_version:
# Write out the original package.json with the updated version
package_json(orig_package_json, package, new_version)
# Create a pull request from these changes
title = "package.json: Update {0} package dependency".format(package)
branch = task.branch(package, title, pathspec="package.json", **kwargs)
# List of files that probably touch this package
lines = output("git", "grep", "-w", package)
comment = "Please manually check features related to these files:\n\n```\n{0}```".format(lines)
if branch:
kwargs["title"] = title
pull = task.pull(branch, **kwargs)
task.comment(pull, comment)
break
# Undo our changes
package_json(orig_package_json)
if __name__ == '__main__':
task.main(function=run, title="Upgrade a node dependency")