Browse Source

Ports: Add a 'dev' mode that uses git to autogenerate patches for ports

Ali Mohammad Pur 3 years ago
parent
commit
d543db0286
1 changed files with 203 additions and 5 deletions
  1. 203 5
      Ports/.port_include.sh

+ 203 - 5
Ports/.port_include.sh

@@ -80,6 +80,21 @@ shift
 
 : "${workdir:=$port-$version}"
 
+cleanup_git() {
+    echo "WARNING: Reverting changes to $workdir as we are in dev mode!"
+    run git clean -xffd >/dev/null 2>&1
+}
+
+# Use the local git target repo as the workdir
+# Make sure to clean it up afterwards
+if [ -n "${IN_SERENITY_PORT_DEV:-}" ]; then
+    echo "WARNING: All changes to the workdir in the current state (inside ./package.sh dev) are temporary!"
+    echo "         They will be reverted once the command exits!"
+    nongit_workdir="$workdir"
+    workdir=".$workdir-git"
+    trap "run cleanup_git" EXIT
+fi
+
 run_nocd() {
     echo "+ $@ (nocd)" >&2
     ("$@")
@@ -354,7 +369,7 @@ func_defined pre_patch || pre_patch() {
 
 func_defined patch_internal || patch_internal() {
     # patch if it was not yet patched (applying patches multiple times doesn't work!)
-    if [ -d patches ]; then
+    if [ -z "${IN_SERENITY_PORT_DEV:-}" ] && [ -d patches ]; then
         for filepath in patches/*.patch; do
             filename=$(basename $filepath)
             if [ ! -f "$workdir"/.${filename}_applied ]; then
@@ -386,7 +401,11 @@ func_defined post_install || post_install() {
     echo
 }
 func_defined clean || clean() {
-    rm -rf "$workdir" *.out
+    if [ -z "${IN_SERENITY_PORT_DEV:-}" ]; then
+        rm -rf "$workdir" *.out
+    else
+        rm -rf "$nongit_workdir" *.out
+    fi
 }
 func_defined clean_dist || clean_dist() {
     OLDIFS=$IFS
@@ -398,7 +417,11 @@ func_defined clean_dist || clean_dist() {
     done
 }
 func_defined clean_all || clean_all() {
-    rm -rf "$workdir" *.out
+    if [ -z "${IN_SERENITY_PORT_DEV:-}" ]; then
+        rm -rf "$workdir" *.out
+    else
+        rm -rf "$nongit_workdir" *.out
+    fi
     OLDIFS=$IFS
     IFS=$'\n'
     for f in $files; do
@@ -551,6 +574,168 @@ do_shell() {
     echo "End of package shell. Back to the User shell."
 }
 
+do_generate_patch_readme() {
+    if [ ! -d patches ]; then
+        >&2 echo "Error: Port $port does not have any patches"
+        exit 1
+    fi
+
+    if [ -f patches/ReadMe.md  ]; then
+        read -N1 -rp \
+            "A ReadMe.md already exists, overwrite? (N/y) " should_overwrite
+        echo
+        if [ "${should_overwrite,,}" != y ]; then
+            >&2 echo "Not overwriting Ports/$port/patches/ReadMe.md"
+            exit 0
+        fi
+    fi
+
+    rm -fr .patches.tmp
+    mkdir .patches.tmp
+
+    echo "# Patches for $port on SerenityOS" > patches/ReadMe.md
+    echo >> patches/ReadMe.md
+
+    pushd patches
+
+    local tempdir="../.patches.tmp"
+    local count=0
+    for patch in *.patch; do
+        git mailinfo \
+            "$tempdir/$patch.msg" \
+            /dev/null \
+            < "$patch" \
+            > "$tempdir/$patch.info" \
+            2> "$tempdir/$patch.error" \
+        || {
+            rc=$?
+            >&2 echo "Failed to extract patch info from $patch"
+            >&2 echo "git returned $rc and said:"
+            >&2 cat "$tempdir/$patch.error"
+            exit 1
+        }
+
+        (
+            grep 'Subject: ' "$tempdir/$patch.info" | sed -e 's/Subject: \(.*\)$/\1/'
+            echo
+            cat "$tempdir/$patch.msg"
+        ) > "$tempdir/$patch.desc"
+
+
+        if [ ! -s "$tempdir/$patch.desc" ]; then
+            >&2 echo "WARNING: $patch does not contain a valid git patch or is missing a commit message, and is going to be skipped!"
+            continue
+        fi
+
+        {
+            echo "## \`$patch\`"
+            echo
+            cat "$tempdir/$patch.desc"
+            echo
+        } >> ReadMe.md
+        count=$((count + 1))
+    done
+
+    popd
+
+    >&2 echo "Successfully generated entries for $count patch(es) in patches/ReadMe.md."
+}
+
+launch_user_shell() {
+    env \
+        IN_SERENITY_PORT_DEV="$port" \
+        "${SHELL:-bash}" || \
+    true
+}
+
+prompt_yes_no() {
+    read -N1 -rp \
+        "$1 (N/y) " result
+    2>&1 echo
+    if [ "${result,,}" == y ]; then
+        return 0
+    else
+        return 1
+    fi
+}
+
+do_dev() {
+    if [ -n "${IN_SERENITY_PORT_DEV:-}"  ]; then
+        >&2 echo "Error: Already in dev environment for $IN_SERENITY_PORT_DEV"
+        exit 1
+    fi
+
+    git_repo=".$workdir-git"
+    [ -d "$git_repo" ] || (
+        mv "$workdir" "$git_repo"
+        pushd "$git_repo"
+        if [ ! -d "$git_repo/.git" ]; then
+            git init .
+            git add .
+            git commit -a -m 'Initial import'
+        fi
+        # Make it allow pushes from other local checkouts
+        git config receive.denyCurrentBranch ignore
+        # Import patches as commits, or ask the user to commit them
+        # if they're not git patches already.
+        if [ -d ../patches ] && [ -n "$(find ../patches -maxdepth 1 -name '*.patch' -print -quit)" ]; then
+            for patch in ../patches/*.patch; do
+                if [ -f "$workdir/.$(basename $patch).applied" ]; then
+                    continue
+                fi
+
+                echo "Importing patch $patch..."
+                git am "$patch" || {
+                    git am --abort >/dev/null 2>&1 || true
+                    git apply < $patch || {
+                        # The patch didn't apply, oh no!
+                        # Ask the user to figure it out :shrug:
+                        >&2 echo "- This patch does not apply, you'll be dropped into a shell to investigate and fix this, quit the shell when the problem is resolved."
+                        launch_user_shell
+                    }
+                    git add -A
+                    if prompt_yes_no "- This patch does not appear to be a git patch, would you like to manually commit its changes?"; then
+                        >&2 echo "Apply any changes you want, commit them into the current repo and quit this shell to continue."
+
+                        launch_user_shell
+                    else
+                        git commit --verbose
+                    fi
+                }
+            done
+        fi
+        popd
+    )
+
+    [ -d "$git_repo" ] && [ ! -d "$workdir" ] && {
+        git clone "$git_repo" "$workdir"
+    }
+
+    [ -d "$workdir/.git" ] || {
+        >&2 echo "$workdir does not appear to be a git repository, if you did this manually, you're on your own"
+        if prompt_yes_no "Otherwise, press 'y' to remove that directory and clone it again"; then
+            rm -fr "$workdir"
+            git clone "$git_repo" "$workdir"
+        else
+            exit 1
+        fi
+    }
+
+    local first_hash="$(git -C "$git_repo" rev-list --max-parents=0 HEAD)"
+
+    launch_user_shell
+
+    local current_hash="$(git -C "$git_repo" rev-parse HEAD)"
+
+    # If the hashes are the same, we have no patches, otherwise generate patches
+    if [ "$first_hash" != "$current_hash" ]; then
+        >&2 echo "Note: Regenerating patches as there are some commits in the port repo (started at $first_hash, now is $current_hash)"
+        rm -fr patches/*.patch
+        git -C "$git_repo" format-patch "$first_hash" -o "$(realpath patches)"
+        do_generate_patch_readme
+    fi
+}
+
 NO_GPG=false
 parse_arguments() {
     if [ -z "${1:-}" ]; then
@@ -558,7 +743,7 @@ parse_arguments() {
         return
     fi
     case "$1" in
-        fetch|patch|shell|configure|build|install|installdepends|clean|clean_dist|clean_all|uninstall|showproperty)
+        fetch|patch|shell|configure|build|install|installdepends|clean|clean_dist|clean_all|uninstall|showproperty|generate_patch_readme)
             method=$1
             shift
             do_${method} "$@"
@@ -575,8 +760,21 @@ parse_arguments() {
             export PS1="(serenity):\w$ "
             bash --norc
             ;;
+        dev)
+            shift
+            if [ "${1:-}" != "--no-depends" ]; then
+                do_installdepends
+            fi
+            if [ -d "$workdir" ] && [ ! -d "$workdir/.git" ]; then
+                if prompt_yes_no "- Would you like to clean the working direcory (i.e. ./package.sh clean)?"; then
+                    do_clean
+                fi
+            fi
+            do_fetch
+            do_dev
+            ;;
         *)
-            >&2 echo "I don't understand $1! Supported arguments: fetch, patch, configure, build, install, installdepends, interactive, clean, clean_dist, clean_all, uninstall, showproperty."
+            >&2 echo "I don't understand $1! Supported arguments: fetch, patch, configure, build, install, installdepends, interactive, clean, clean_dist, clean_all, uninstall, showproperty, generate_patch_readme, dev."
             exit 1
             ;;
     esac