Merge pull request 'merge-after-ci: rewrite according to #814' (#882) from DavHau-dave into main
checks / check-links (push) Successful in 21s Details
checks / checks (push) Successful in 31s Details
checks / checks-impure (push) Successful in 1m51s Details

This commit is contained in:
clan-bot 2024-02-27 11:35:26 +00:00
commit 2ccf32c36b
4 changed files with 541 additions and 18 deletions

View File

@ -30,6 +30,10 @@
"--" # this argument is ignored by bash
];
includes = [ "*.nix" ];
excludes = [
# Was copied from nixpkgs. Keep diff minimal to simplify upstreaming.
"pkgs/builders/script-writers.nix"
];
};
treefmt.settings.formatter.python = {
command = "sh";

View File

@ -0,0 +1,488 @@
{ pkgs, buildPackages, lib, stdenv, libiconv, mkNugetDeps, mkNugetSource, gixy, makeWrapper, }:
let
inherit (lib)
concatMapStringsSep
elem
escapeShellArg
last
optionalString
stringLength
strings
types
;
in
rec {
# Base implementation for non-compiled executables.
# Takes an interpreter, for example `${lib.getExe pkgs.bash}`
#
# Examples:
# writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; }
# makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world"
makeScriptWriter = { interpreter, check ? "", makeWrapperArgs ? [], }: nameOrPath: content:
assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
assert lib.or (types.path.check content) (types.str.check content);
let
name = last (builtins.split "/" nameOrPath);
in
pkgs.runCommandLocal name (
{
inherit makeWrapperArgs;
nativeBuildInputs = [
makeWrapper
];
}
// lib.optionalAttrs (nameOrPath == "/bin/${name}") {
meta.mainProgram = name;
}
// (
if (types.str.check content) then {
inherit content interpreter;
passAsFile = [ "content" ];
} else {
inherit interpreter;
contentPath = content;
}
)
)
''
# On darwin a script cannot be used as an interpreter in a shebang but
# there doesn't seem to be a limit to the size of shebang and multiple
# arguments to the interpreter are allowed.
if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter
then
wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3)
# Get first word from the line (note: xargs echo remove leading spaces)
wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1)
if isScript $wrapperInterpreter
then
echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported."
exit 1
fi
# This should work as long as wrapperInterpreter is a shell, which is
# the case for programs wrapped with makeWrapper, like
# python3.withPackages etc.
interpreterLine="$wrapperInterpreterLine $interpreter"
else
interpreterLine=$interpreter
fi
echo "#! $interpreterLine" > $out
cat "$contentPath" >> $out
${optionalString (check != "") ''
${check} $out
''}
chmod +x $out
# Relocate executable if path was specified instead of name.
# Only in this case wrapProgram is applied, as it wouldn't work with a
# single executable file under $out.
${optionalString (types.path.check nameOrPath) ''
mv $out tmp
mkdir -p $out/$(dirname "${nameOrPath}")
mv tmp $out/${nameOrPath}
wrapProgram $out/${nameOrPath} ''${makeWrapperArgs[@]}
''}
'';
# Base implementation for compiled executables.
# Takes a compile script, which in turn takes the name as an argument.
#
# Examples:
# writeSimpleC = makeBinWriter { compileScript = name: "gcc -o $out $contentPath"; }
makeBinWriter = { compileScript, strip ? true }: nameOrPath: content:
assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
assert lib.or (types.path.check content) (types.str.check content);
let
name = last (builtins.split "/" nameOrPath);
in
pkgs.runCommand name ((if (types.str.check content) then {
inherit content;
passAsFile = [ "content" ];
} else {
contentPath = content;
}) // lib.optionalAttrs (nameOrPath == "/bin/${name}") {
meta.mainProgram = name;
}) ''
${compileScript}
${lib.optionalString strip
"${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"}
# Sometimes binaries produced for darwin (e. g. by GHC) won't be valid
# mach-o executables from the get-go, but need to be corrected somehow
# which is done by fixupPhase.
${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"}
${optionalString (types.path.check nameOrPath) ''
mv $out tmp
mkdir -p $out/$(dirname "${nameOrPath}")
mv tmp $out/${nameOrPath}
''}
'';
# Like writeScript but the first line is a shebang to bash
#
# Example:
# writeBash "example" ''
# echo hello world
# ''
writeBash = makeScriptWriter {
interpreter = "${lib.getExe pkgs.bash}";
};
# Like writeScriptBin but the first line is a shebang to bash
writeBashBin = name:
writeBash "/bin/${name}";
# Like writeScript but the first line is a shebang to dash
#
# Example:
# writeDash "example" ''
# echo hello world
# ''
writeDash = makeScriptWriter {
interpreter = "${lib.getExe pkgs.dash}";
};
# Like writeScriptBin but the first line is a shebang to dash
writeDashBin = name:
writeDash "/bin/${name}";
# Like writeScript but the first line is a shebang to fish
#
# Example:
# writeFish "example" ''
# echo hello world
# ''
writeFish = makeScriptWriter {
interpreter = "${lib.getExe pkgs.fish} --no-config";
check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only
};
# Like writeScriptBin but the first line is a shebang to fish
writeFishBin = name:
writeFish "/bin/${name}";
# writeHaskell takes a name, an attrset with libraries and haskell version (both optional)
# and some haskell source code and returns an executable.
#
# Example:
# writeHaskell "missiles" { libraries = [ pkgs.haskellPackages.acme-missiles ]; } ''
# import Acme.Missiles
#
# main = launchMissiles
# '';
writeHaskell = name: {
libraries ? [],
ghc ? pkgs.ghc,
ghcArgs ? [],
threadedRuntime ? true,
strip ? true
}:
let
appendIfNotSet = el: list: if elem el list then list else list ++ [ el ];
ghcArgs' = if threadedRuntime then appendIfNotSet "-threaded" ghcArgs else ghcArgs;
in makeBinWriter {
compileScript = ''
cp $contentPath tmp.hs
${(ghc.withPackages (_: libraries ))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs
mv tmp $out
'';
inherit strip;
} name;
# writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin)
writeHaskellBin = name:
writeHaskell "/bin/${name}";
# Like writeScript but the first line is a shebang to nu
#
# Example:
# writeNu "example" ''
# echo hello world
# ''
writeNu = makeScriptWriter {
interpreter = "${lib.getExe pkgs.nushell} --no-config-file";
};
# Like writeScriptBin but the first line is a shebang to nu
writeNuBin = name:
writeNu "/bin/${name}";
# makeRubyWriter takes ruby and compatible rubyPackages and produces ruby script writer,
# If any libraries are specified, ruby.withPackages is used as interpreter, otherwise the "bare" ruby is used.
makeRubyWriter = ruby: rubyPackages: buildRubyPackages: name: { libraries ? [], ... } @ args:
makeScriptWriter (
(builtins.reamoveAttrs args ["libraries"])
// {
interpreter =
if libraries == []
then "${ruby}/bin/ruby"
else "${(ruby.withPackages (ps: libraries))}/bin/ruby";
# Rubocop doesnt seem to like running in this fashion.
#check = (writeDash "rubocop.sh" ''
# exec ${lib.getExe buildRubyPackages.rubocop} "$1"
#'');
}
) name;
# Like writeScript but the first line is a shebang to ruby
#
# Example:
# writeRuby "example" ''
# puts "hello world"
# ''
writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages;
writeRubyBin = name:
writeRuby "/bin/${name}";
# makeLuaWriter takes lua and compatible luaPackages and produces lua script writer,
# which validates the script with luacheck at build time. If any libraries are specified,
# lua.withPackages is used as interpreter, otherwise the "bare" lua is used.
makeLuaWriter = lua: luaPackages: buildLuaPackages: name: { libraries ? [], ... } @ args:
makeScriptWriter (
(builtins.removeAttrs args ["libraries"])
// {
interpreter = lua.interpreter;
# if libraries == []
# then lua.interpreter
# else (lua.withPackages (ps: libraries)).interpreter
# This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this.
check = (writeDash "luacheck.sh" ''
exec ${buildLuaPackages.luacheck}/bin/luacheck "$1"
'');
}
) name;
# writeLua takes a name an attributeset with libraries and some lua source code and
# returns an executable (should also work with luajit)
#
# Example:
# writeLua "test_lua" { libraries = [ pkgs.luaPackages.say ]; } ''
# s = require("say")
# s:set_namespace("en")
#
# s:set('money', 'I have %s dollars')
# s:set('wow', 'So much money!')
#
# print(s('money', {1000})) -- I have 1000 dollars
#
# s:set_namespace("fr") -- switch to french!
# s:set('wow', "Tant d'argent!")
#
# print(s('wow')) -- Tant d'argent!
# s:set_namespace("en") -- switch back to english!
# print(s('wow')) -- So much money!
# ''
writeLua = makeLuaWriter pkgs.lua pkgs.luaPackages buildPackages.luaPackages;
writeLuaBin = name:
writeLua "/bin/${name}";
writeRust = name: {
rustc ? pkgs.rustc,
rustcArgs ? [],
strip ? true
}:
let
darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ];
in
makeBinWriter {
compileScript = ''
cp "$contentPath" tmp.rs
PATH=${lib.makeBinPath [pkgs.gcc]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs
'';
inherit strip;
} name;
writeRustBin = name:
writeRust "/bin/${name}";
# writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and
# returns an executable
#
# Example:
# writeJS "example" { libraries = [ pkgs.nodePackages.uglify-js ]; } ''
# var UglifyJS = require("uglify-js");
# var code = "function add(first, second) { return first + second; }";
# var result = UglifyJS.minify(code);
# console.log(result.code);
# ''
writeJS = name: { libraries ? [] }: content:
let
node-env = pkgs.buildEnv {
name = "node";
paths = libraries;
pathsToLink = [
"/lib/node_modules"
];
};
in writeDash name ''
export NODE_PATH=${node-env}/lib/node_modules
exec ${lib.getExe pkgs.nodejs} ${pkgs.writeText "js" content} "$@"
'';
# writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin)
writeJSBin = name:
writeJS "/bin/${name}";
awkFormatNginx = builtins.toFile "awkFormat-nginx.awk" ''
awk -f
{sub(/^[ \t]+/,"");idx=0}
/\{/{ctx++;idx=1}
/\}/{ctx--}
{id="";for(i=idx;i<ctx;i++)id=sprintf("%s%s", id, "\t");printf "%s%s\n", id, $0}
'';
writeNginxConfig = name: text: pkgs.runCommandLocal name {
inherit text;
passAsFile = [ "text" ];
nativeBuildInputs = [ gixy ];
} /* sh */ ''
# nginx-config-formatter has an error - https://github.com/1connect/nginx-config-formatter/issues/16
awk -f ${awkFormatNginx} "$textPath" | sed '/^\s*$/d' > $out
gixy $out
'';
# writePerl takes a name an attributeset with libraries and some perl sourcecode and
# returns an executable
#
# Example:
# writePerl "example" { libraries = [ pkgs.perlPackages.boolean ]; } ''
# use boolean;
# print "Howdy!\n" if true;
# ''
writePerl = name: { libraries ? [], ... } @ args:
makeScriptWriter (
(builtins.removeAttrs args ["libraries"])
// {
interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}";
}
) name;
# writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin)
writePerlBin = name:
writePerl "/bin/${name}";
# makePythonWriter takes python and compatible pythonPackages and produces python script writer,
# which validates the script with flake8 at build time. If any libraries are specified,
# python.withPackages is used as interpreter, otherwise the "bare" python is used.
makePythonWriter = python: pythonPackages: buildPythonPackages: name: { libraries ? [], flakeIgnore ? [], ... } @ args:
let
ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}";
in
makeScriptWriter
(
(builtins.removeAttrs args ["libraries" "flakeIgnore"])
// {
interpreter =
if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then
if libraries == []
then python.interpreter
else (python.withPackages (ps: libraries)).interpreter
else python.interpreter
;
check = optionalString python.isPy3k (writeDash "pythoncheck.sh" ''
exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1"
'');
}
)
name;
# writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and
# returns an executable
#
# Example:
# writePyPy2 "test_pypy2" { libraries = [ pkgs.pypy2Packages.enum ]; } ''
# from enum import Enum
#
# class Test(Enum):
# a = "success"
#
# print Test.a
# ''
writePyPy2 = makePythonWriter pkgs.pypy2 pkgs.pypy2Packages buildPackages.pypy2Packages;
# writePyPy2Bin takes the same arguments as writePyPy2 but outputs a directory (like writeScriptBin)
writePyPy2Bin = name:
writePyPy2 "/bin/${name}";
# writePython3 takes a name an attributeset with libraries and some python3 sourcecode and
# returns an executable
#
# Example:
# writePython3 "test_python3" { libraries = [ pkgs.python3Packages.pyyaml ]; } ''
# import yaml
#
# y = yaml.load("""
# - test: success
# """)
# print(y[0]['test'])
# ''
writePython3 = makePythonWriter pkgs.python3 pkgs.python3Packages buildPackages.python3Packages;
# writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin)
writePython3Bin = name:
writePython3 "/bin/${name}";
# writePyPy3 takes a name an attributeset with libraries and some pypy3 sourcecode and
# returns an executable
#
# Example:
# writePyPy3 "test_pypy3" { libraries = [ pkgs.pypy3Packages.pyyaml ]; } ''
# import yaml
#
# y = yaml.load("""
# - test: success
# """)
# print(y[0]['test'])
# ''
writePyPy3 = makePythonWriter pkgs.pypy3 pkgs.pypy3Packages buildPackages.pypy3Packages;
# writePyPy3Bin takes the same arguments as writePyPy3 but outputs a directory (like writeScriptBin)
writePyPy3Bin = name:
writePyPy3 "/bin/${name}";
makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [], ... } @ args: nameOrPath:
let
fname = last (builtins.split "/" nameOrPath);
path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx";
_nugetDeps = mkNugetDeps { name = "${fname}-nuget-deps"; nugetDeps = libraries; };
nuget-source = mkNugetSource {
name = "${fname}-nuget-source";
description = "A Nuget source with the dependencies for ${fname}";
deps = [ _nugetDeps ];
};
fsi = writeBash "fsi" ''
export HOME=$NIX_BUILD_TOP/.home
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_NOLOGO=1
script="$1"; shift
${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script"
'';
in content: makeScriptWriter (
(builtins.removeAttrs args ["dotnet-sdk" "fsi-flags" "libraries"])
// {
interpreter = fsi;
}
) path
''
#i "nuget: ${nuget-source}/lib"
${ content }
exit 0
'';
writeFSharp =
makeFSharpWriter {};
writeFSharpBin = name:
writeFSharp "/bin/${name}";
}

View File

@ -1,26 +1,33 @@
{ writeShellApplication
{ bash
, callPackage
, coreutils
, bash
, git
, tea
, lib
, nix
, openssh
, tea
, tea-create-pr
, ...
}:
writeShellApplication {
name = "merge-after-ci";
runtimeInputs = [
bash
coreutils
git
tea
openssh
tea-create-pr
let
writers = callPackage ../builders/script-writers.nix { };
in
writers.writePython3Bin "merge-after-ci"
{
makeWrapperArgs = [
"--prefix"
"PATH"
":"
(lib.makeBinPath [
bash
coreutils
git
nix
openssh
tea
tea-create-pr
])
];
text = ''
remoteName="''${1:-origin}"
targetBranch="''${2:-main}"
shift && shift
tea-create-pr "$remoteName" "$targetBranch" --assignees clan-bot "$@"
'';
}
./merge-after-ci.py

View File

@ -0,0 +1,24 @@
import argparse
import subprocess
parser = argparse.ArgumentParser()
parser.add_argument("--reviewers", nargs="*")
parser.add_argument("--no-review", action="store_true")
parser.add_argument("args", nargs="*")
args = parser.parse_args()
# complain if neither --reviewers nor --no-review is given
if not args.reviewers and not args.no_review:
parser.error("either --reviewers or --no-review must be given")
subprocess.run(
[
"tea-create-pr",
"origin",
"main",
"--assignees",
"clan-bot",
*([*args.reviewers] if args.reviewers else []),
*args.args,
]
)