epm-restore 10.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
#!/bin/sh
#
# Copyright (C) 2020  Etersoft
# Copyright (C) 2020  Vitaly Lipatov <lav@etersoft.ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

load_helper epm-sh-altlinux
load_helper epm-assure

23
# FIXME: python modules are packaged into python packages, but we have only module names and rpm package names instead of python packages
24 25
# enable python3dest(PEP-503 normalized name) provides
# https://bugzilla.altlinux.org/show_bug.cgi?id=39003
26 27
__epm_filter_pip_to_rpm()
{
28
    tr "A-Z" "a-z" | sed -e "s|_|-|g" -e "s|^python[-_]||" \
29
        -e "s|bs4|beautifulsoup4|" \
30
        -e "s|pillow|Pillow|" \
31 32
        -e "s|sqlalchemy|SQLAlchemy|" \
        -e "s|flask-SQLAlchemy|flask_sqlalchemy|" \
33
        -e "s|redis|redis-py|" \
34
        -e "s|pyjwt|jwt|" \
35
        -e "s|pymonetdb|monetdb|" \
36
        -e "s|pyyaml|yaml|" \
37
        -e "s|flask-migrate|Flask-Migrate|" \
38 39
        -e "s|twisted|twisted-core|" \
        -e "s|pymacaroons|pymacaroons-pynacl|" \
40
        -e "s|pygments|Pygments|" \
41 42 43 44
        -e "s|memcached|memcache|" \
        -e "s|pyopenssl|OpenSSL|"
}

45 46 47 48
# TODO: remove me
fill_sign()
{
    local sign="$1"
49
    echo "$2" | grep -E -- "$sign[[:space:]]*[0-9.]+?" | sed -E -e "s|.*$sign[[:space:]]*([0-9.]+?).*|\1|"
50 51 52
}


53 54 55
# macro pkg caseline
__epm_pi_sign_to_rpm()
{
56 57 58 59
    local t="$1"
    local l="$2"
    local equal="$3"
    [ -n "$equal" ] || equal=">="
60 61 62 63 64 65 66 67 68 69

    local pi=''
    local sign ll
    for sign in "<=" "<" ">=" ">" "==" "!="; do
        ll=$(fill_sign "$sign" "$l")
        [ -n "$ll" ] || continue
        [ "$sign" = "==" ] && sign="$equal"
        [ "$sign" = "!=" ] && sign=">="
        [ -n "$pi" ] && pi="$pi
"
70
        pi="$pi$t $sign $ll"
71
    done
72
    [ -n "$pi" ] || pi="$t"
73 74 75
    echo "$pi"
}

76
__epm_get_array_name()
77
{
78 79
    echo "$*" | grep "=" | head -n1 | sed -e 's| *=.*||'
}
80

81 82 83 84 85
__epm_lineprint_python_array()
{
    local a="$*"
    [ -n "$a" ] || return
    local name="$(__epm_get_array_name "$a")"
86
    (echo "$a" | sed -E -e 's@(\]|\)).*@\1@' ; echo "print('\n'.join($name))" ) | ( a= python - || a= python3 )
87 88 89 90 91 92 93 94
}

# translate pip requirement lines to rpm notation
# (compare signs and package names)
__epm_restore_convert_to_rpm_notation()
{
    local equal="$1"
    local l
95
    while read l ; do
96
        if echo "$l" | grep -q "; *python_version *< *['\"]3" ; then
97 98 99 100 101
            [ -n "$verbose" ] && warning "    $t is python2 only requirement, skipped"
            continue
        fi
        # drop various "python_version > '3.5'"
        l="$(echo "$l" | sed -e "s| *;.*||")"
102
        if echo "$l" | grep -qE "^ *#" || [ -z "$l" ] ; then
103 104
            continue
        fi
105
        local t="$(echo "$l" | sed -E -e "s|[[:space:]]*[<>!=]+.*||" -e "s| *#.*||" | __epm_filter_pip_to_rpm)"
106
        [ -n "$t" ] || continue
107 108 109 110 111
        # until new section
        if echo "$l" | grep -qE "^\[" ; then
            break
        fi
        # if dependency_links URLs, use egg name
112 113 114 115
        if echo "$l" | grep -qE "://" ; then
            if echo "$l" | grep -q "#egg=" ; then
                t="$(echo "$l" | sed -e "s|.*#egg=||" -e "s|\[.*||" | __epm_filter_pip_to_rpm)"
            else
116
                warning "    skipping URL $l ..."
117 118 119
                continue
            fi
        fi
120

121 122 123 124 125 126 127 128 129
        __epm_pi_sign_to_rpm "$t" "$l" "$equal"
    done
}

__epm_restore_pip()
{
    local req_file="$1"
    local reqmacro
    local ilist
130

131
    if [ -n "$dryrun" ] ; then
132
        reqmacro="%py3_use"
133
        basename "$req_file" | egrep -q "(dev|test)" && reqmacro="%py3_buildrequires"
134 135 136
        echo
        echo "# generated by epm --restore --dry-run from $(basename $(dirname $(realpath $req_file)))/$req_file"
        cat $req_file | __epm_restore_convert_to_rpm_notation | sed -e "s|^|$reqmacro |"
137
        return
138 139 140
    else
        info "Install requirements from $req_file ..."
        ilist="$(cat $req_file | __epm_restore_convert_to_rpm_notation | cut -d' ' -f 1 | sed -e "s|^|python3-module-|")"
141 142
    fi

143
    ilist="$(estrlist list $ilist)"
144 145 146
    docmd epm install $ilist
}

147 148
__eresection()
{
149
    rhas "$1" "[[:space:]]*$2[[:space:]]*=[[:space:]]*[\[(]"
150 151
}

152 153 154 155 156 157 158
__epm_restore_setup_py()
{
    local req_file="$1"
    if [ -z "$dryrun" ] ; then
        info "Install requirements from $req_file ..."
    fi

159
    local ar=''
160 161
    local ilist=''
    local reqmacro
162
    local section=''
163 164 165 166
    while read l ; do
        if rhas "$l" "^ *#" ; then
            continue
        fi
167 168 169 170
        # start of section
        if __eresection "$l" "REQUIREMENTS" ; then
            reqmacro="%py3_use"
            section="$l"
171
        fi
172
        if __eresection "$l" "install_requires" ; then
173
            reqmacro="%py3_use"
174
            section="$l"
175
        fi
176
        if __eresection "$l" "setup_requires" ; then
177
            reqmacro="%py3_buildrequires"
178 179 180 181 182 183 184 185 186 187 188 189
            section="$l"
        fi
        if __eresection "$l" "tests_require" ; then
            reqmacro="%py3_buildrequires"
            section="$l"
        fi
        if [ -n "$section" ] ; then
            ar="$ar
$l"
        fi

        # not end of section
190
        if [ -z "$section" ] || ! rhas "$l" "(\]|\)),*" ; then
191 192
            continue
        fi
193 194 195 196 197 198 199 200 201 202

        if [ -n "$dryrun" ] ; then
            echo
            echo "# generated by epm --restore --dry-run from $(basename $(dirname $(realpath $req_file)))/$req_file $(__epm_get_array_name "$section")"
            __epm_lineprint_python_array "$ar" | __epm_restore_convert_to_rpm_notation ">=" | sed -e "s|^|$reqmacro |"
        else
            ilist="$ilist $(__epm_lineprint_python_array "$ar" | __epm_restore_convert_to_rpm_notation ">=" | cut -d' ' -f 1 | sed -e "s|^|python3-module-|")"
        fi
        section=''
        ar=''
203 204 205 206 207
    done < $req_file

    if [ -n "$dryrun" ] ; then
        return
    fi
208

209
    ilist="$(estrlist list $ilist)"
210
    docmd epm install $ilist
211 212
}

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
__epm_print_npm_list()
{
    local reqmacro="$1"
    local req_file="$2"
    local l
    while read l ; do
        # "tap": "^14.10.7"
        echo "$l" | grep -q '"\(.*\)": "\(.*\)"' || continue
        local name="$(echo "$l" | sed -e 's|.*"\(.*\)": ".*|\1|')"
        [ -z "$name" ] && continue
        local ver="$(echo "$l" | sed -e 's|.*"\(.*\)": "\(.*\)".*|\2|')" #'
        [ -z "$name" ] && continue

        if [ -n "$dryrun" ] ; then
            local pi=''
            local sign
            if echo "$ver" | grep -q "^\^" ; then
                sign=">="
            else
                sign="="
            fi
            ll=$(echo "$ver" | sed -e 's|^[^~]||')
            pi="$pi$reqmacro node-$name $sign $ll"
#         [ -n "$pi" ] && pi="$pi
#"
#           [ -n "$pi" ] || pi="$pi$reqmacro: node-$t"
            echo "$pi"
            continue
        else
            local pi="node-$name"
            #echo "    $l -> $name -> $pi"
        fi
        [ -n "$name" ] || continue
        ilist="$ilist $pi"
    done < $req_file

    [ -n "$dryrun" ] || echo "$ilist"
}

__epm_restore_npm()
{
    local req_file="$1"

    epm assure jq

    if [ -n "$dryrun" ] ; then
        local lt=$(mktemp)
        a= jq .dependencies <$req_file >$lt
        echo
        echo "# generated by epm --restore --dry-run from $(basename $(dirname $(realpath $req_file)))/$req_file"
        __epm_print_npm_list "Requires:" $lt

        echo
        echo "# devDependencies generated by epm --restore --dry-run from $(basename $(dirname $(realpath $req_file)))/$req_file"
        a= jq .devDependencies <$req_file >$lt
        __epm_print_npm_list "BuildRequires:" $lt
        rm -f $lt
        return
    fi

    info "Install requirements from $req_file ..."
    local lt=$(mktemp)
    a= jq .dependencies <$req_file >$lt
    ilist="$(__epm_print_npm_list "" $lt)"
    a= jq .devDependencies <$req_file >$lt
    ilist="$ilist $(__epm_print_npm_list "" $lt)"
    rm -f $lt
    docmd epm install $ilist
}

283 284 285 286
__epm_restore_by()
{
    local req_file="$1"
    [ -s "$req_file" ] || return
287 288 289 290 291

    if file $req_file | grep -q "ELF [3264]*-bit LSB executable" ; then
        assure_exists ldd-requires
        showcmd ldd-requires $req_file
        local TOINSTALL="$(a= ldd-requires $req_file | grep "^apt-get install" | sed -e "s|^apt-get install ||")"
292 293 294 295
        if [ -n "$dryrun" ] ; then
            estrlist list $TOINSTALL
            return
        fi
296
        [ -n "$TOINSTALL" ] || { info "There are no missed packages is found for $req_file binary." ; return ; }
297
        docmd epm install $TOINSTALL
298 299 300
        return
    fi

301
    case $(basename $req_file) in
302
        requirements.txt|dev-requirements.txt|requirements-dev.txt|requirements_dev.txt|requirements_test.txt|requirements-test.txt|test-requirements.txt|requires.txt)
303 304
            [ -s "$req_file" ] && __epm_restore_pip "$req_file"
            ;;
305
        setup.py|python_dependencies.py)
306 307
            [ -s "$req_file" ] && __epm_restore_setup_py "$req_file"
            ;;
308 309 310
        package.json)
            [ -s "$req_file" ] && __epm_restore_npm "$req_file"
            ;;
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
        Gemfile|package.json)
            info "$req_file support is not implemented yet"
            ;;
    esac
}

epm_restore()
{
    req_file="$pkg_filenames"
    if [ -n "$pkg_urls" ] && echo "$pkg_urls" | grep -qE "^https?://" ; then
        req_file="$(basename "$pkg_urls")"
        #assure eget
        [ -r "$req_file" ] && fatal "File $req_file is already exists in $(pwd)"
        info "Downloading '$req_file' from '$pkg_urls' ..."
        eget "$pkg_urls"
        [ -s "$req_file" ] || fatal "Can't download $req_file from '$pkg_urls'"
    fi

    if [ -n "$req_file" ] ; then
        __epm_restore_by $req_file
        return
    fi

334 335
# TODO: nowhere works: python3 setup.py --requires

336
    # if run with empty args
337
    for i in requirements.txt requirements_dev.txt requirements-dev.txt dev-requirements.txt requirements-test.txt requirements_test.txt test-requirements.txt Gemfile requires.txt package.json setup.py python_dependencies.py; do
338 339 340 341
        __epm_restore_by $i
    done

}