~miconoco/woo

woo/bin/woo -rwxr-xr-x 4.0 KiB
0b1818b1Christoph Polcin build 3 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/bin/sh
# https://git.sr.ht/~miconoco/woo

export WOO=1

set -euf

WOOWS="${WOOWS:-${PWD:?PWD}}"
MANIFEST="${WOOWS}/manifest"

_usage() {
        cat <<EOF
Usage: $(basename "$0") COMMAND | -
Loops over tracked projects and
executes the COMMAND in thier root directory.

 -
   Reads the COMMAND / SCRIPT from stdin.

BUILT-INS

 snapshot
   Scans the workspace for GIT projects and
   records thier state in the manifest.

 list [PATTERN]
   Lists the manifest content.

 reset [PATTERN]
   Resets projects to thier manifest state, removes changes!

PATTERN
   Limits the execution scope to a specific project.

ENVIRONMENT
   WOOWS    = ${WOOWS:-unset}
   MANIFEST = WOOWS/manifest
   WOO      = 1

   WOOLOOP  = 1
   MOD      = path to project
   MURL     = remote URL
   MREF     = ref
   MNREF    = named ref
   MUPS     = remote tracking branch
EOF
    exit "${1:-0}"
}


_bel() {
    [ -z "${SILENT:-}" ] && [ -t 1 ] && printf '\a' || :
}

_ignore() { #mod {{{
    grep -q -m 1 -- "^/*${1}\$" "${WOOWS}/.wooignore" >/dev/null 2>&1
} # }}}

_snapshot_repo() { # mod {{{
    mod="${1:-MOD}"
    murl="$(2>/dev/null git -C "${WOOWS}/${mod}" ls-remote --get-url || printf '')"
    read -r mref mnref mups <<EOF
        $(2>/dev/null git -C "${WOOWS}/${mod}" \
            for-each-ref --format="%(objectname:short) %(refname:short) %(upstream:short)" \
            "$(2>/dev/null git -C "${WOOWS}/${mod}"  symbolic-ref -q HEAD)" \
        )
EOF
    printf "%s;%s;%s;%s;%s\n" "$mod" "$murl" "$mref" "$mnref" "$mups"
} # }}}

_snapshot() { # {{{
    # TODO reuse manifest selection
    while IFS= read -r mod
    do
        [ -z "${mod}" ] && continue
        _ignore "${mod}" && continue
        _snapshot_repo "${mod}" | tee -a "${MANIFEST}"
    done <<EOF
$(
    cd "${WOOWS}" || exit 1
    : > "${MANIFEST}"
    find . \
	    -mindepth 2 -maxdepth 5 \
	    -type d -name '.git' -prune \
    | sed 's/^\.\///;s/\.git$//' \
    | sort -u \
    | awk 'BEGIN { old = "^-----" }
        $0 ~ old { next }
        { old = "^"$0; sub(/\/$/, "", $0); print $0 }'
)
EOF
} # }}}

_reset() { # PATTERN... {{{
    while IFS=';' read -r mod murl mref mnref mups
    do
        [ -z "${mref}" ] && continue
        _info "${mod} @ ${mref}"
        (
            cd "${mod}" || exit 1
            git reset --keep "$mref"
            git status --short --branch --ahead-behind
        )
    done <<EOF
$(_list "$@")
EOF
} # }}}

_list() { # PATTERN... {{{
	[ ! -f "${MANIFEST}" ] && return 1

	if [ $# -gt 0 ]; then
        pat="$(printf "%s" "$*" | sed 's:^\s*::;s:\s*$::;s:\s:\\|:g')"
    else
        pat="."
    fi

    [ "${WOO_LOOP_PRE:=0}" = 1 ] && _snapshot_repo "."

    grep -- "$pat" "${MANIFEST}"

    [ "${WOO_LOOP_POST:=0}" = 1 ] && _snapshot_repo "."
} # }}}

_info() {
    [ -n "${SILENT:-}" ] && return 0
    [ -t 1 ] \
        && printf "%b## %s%b\n" "\033[1;33m" "$*" "\033[0m" \
        || printf "## %s\n" "$*"
}

_for() { # command|FILE {{{

	[ $# -eq 0 ] && _usage 1

    # TODO support pattern ++ cmd

    while IFS=';' read -r mod murl mref mnref mups
    do
        [ -z "${mod}" ] && continue
        _ignore "${mod}" && continue
        _info "${mod} @ ${mref}"
        (
            cd "${WOOWS}/${mod}" 2>/dev/null || {
                mkdir -p "${WOOWS}/${mod}"
                cd "${WOOWS}/${mod}" || exit 1
            }
            WOOLOOP=1 \
            MANIFEST="${MANIFEST}" \
            WOOWS="${WOOWS}" \
            MOD="${mod}" \
            MURL="${murl}" \
            MREF="${mref}" \
            MNREF="${mnref}" \
            MUPS="${mups}" \
            "$@"
        ) || { [ "${FAIL:-0}" = "1" ] && exit 1;  }
        printf '\n'
    done <<EOF
$(_list)
EOF
} # }}}

_exec_stdin() { # {{{
    tf="$(mktemp)"
    # TODO print instead of delete
    trap 'rm -f $tf' EXIT
    cat - > "$tf"
    chmod +x "$tf"
    [ -s "$tf" ] && _for "$tf" "$@"
} # }}}


case "${1:---help}" in
    list|snapshot|reset)
        cmd="$1"
        shift
        "_${cmd}" "$@"
    ;;

    -h|--help)
        _usage
    ;;

    -)
        shift
        _exec_stdin "$@"
     ;;

    *)
        _for "$@"
    ;;
esac