devflow-kit 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +438 -1
- package/README.md +15 -11
- package/dist/agents/code.md +9 -15
- package/dist/agents/design.md +1 -1
- package/dist/agents/fix.md +11 -13
- package/dist/agents/git.md +1 -1
- package/dist/agents/harden.md +1 -1
- package/dist/agents/refactor.md +7 -10
- package/dist/agents/review.md +6 -4
- package/dist/agents/skim.md +2 -2
- package/dist/agents/triage.md +2 -2
- package/dist/cli/commands/capture.js +1 -1
- package/dist/cli/commands/curation-move-io.js +102 -0
- package/dist/cli/commands/curation-move-step.js +374 -0
- package/dist/cli/commands/{learning.js → curation.js} +302 -99
- package/dist/cli/commands/gate-level-prompts.js +426 -0
- package/dist/cli/commands/gates.js +299 -0
- package/dist/cli/commands/init-seed.js +16 -5
- package/dist/cli/commands/init.js +263 -54
- package/dist/cli/commands/memory.js +20 -2
- package/dist/cli/commands/prompt-io.js +37 -0
- package/dist/cli/commands/uninstall.js +81 -9
- package/dist/cli.js +4 -2
- package/dist/commands/bug-analysis.md +25 -36
- package/dist/commands/code-review.md +77 -73
- package/dist/commands/debug.md +5 -5
- package/dist/commands/dynamic-build.md +76 -83
- package/dist/commands/dynamic-plan.md +5 -5
- package/dist/commands/dynamic-profile.md +5 -5
- package/dist/commands/dynamic-tickets.md +5 -5
- package/dist/commands/explore.md +6 -6
- package/dist/commands/implement.md +68 -102
- package/dist/commands/plan.md +19 -29
- package/dist/commands/release.md +5 -5
- package/dist/commands/research.md +6 -6
- package/dist/commands/resolve.md +29 -31
- package/dist/commands/self-review.md +5 -13
- package/dist/core/assets.js +20 -20
- package/dist/core/curate-tuning-config.js +5 -6
- package/dist/core/curation-move-notice.js +84 -0
- package/dist/core/curation-move-skip.js +36 -0
- package/dist/core/curation-move.js +35 -0
- package/dist/core/curation-queue-cleanup.js +134 -0
- package/dist/core/curation-store.js +98 -0
- package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
- package/dist/core/echo-value.js +48 -0
- package/dist/core/evidence-policy.js +18 -6
- package/dist/core/feature-switch.js +95 -19
- package/dist/core/fs-atomic.js +1 -1
- package/dist/core/gate-level.js +221 -0
- package/dist/core/git.js +16 -14
- package/dist/core/ledger-root.js +82 -22
- package/dist/core/linked-path.js +4 -4
- package/dist/core/manifest-lock.js +238 -0
- package/dist/core/manifest.js +84 -24
- package/dist/core/migrations.js +1 -13
- package/dist/core/model-discovery.js +7 -1
- package/dist/core/observations.js +2 -2
- package/dist/core/plugins.js +15 -15
- package/dist/core/pool.js +26 -0
- package/dist/core/project-paths.js +103 -34
- package/dist/core/queue-drain.js +2 -2
- package/dist/core/tracked-areas.js +35 -0
- package/dist/core/tracker.js +6 -34
- package/dist/{learning-off → curation-off}/agents/code.md +9 -15
- package/dist/{learning-off → curation-off}/agents/fix.md +11 -13
- package/dist/{learning-off → curation-off}/agents/refactor.md +7 -10
- package/dist/{learning-off → curation-off}/agents/review.md +5 -3
- package/dist/{learning-off → curation-off}/agents/triage.md +1 -1
- package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
- package/dist/{learning-off → curation-off}/commands/code-review.md +74 -70
- package/dist/{learning-off → curation-off}/commands/debug.md +2 -2
- package/dist/{learning-off → curation-off}/commands/dynamic-build.md +73 -80
- package/dist/{learning-off → curation-off}/commands/explore.md +2 -2
- package/dist/{learning-off → curation-off}/commands/implement.md +65 -99
- package/dist/{learning-off → curation-off}/commands/plan.md +15 -25
- package/dist/{learning-off → curation-off}/commands/resolve.md +26 -28
- package/dist/{learning-off → curation-off}/commands/self-review.md +2 -10
- package/dist/hud/components/{learning-counts.js → curation-counts.js} +36 -17
- package/dist/hud/config.js +1 -1
- package/dist/hud/index.js +6 -6
- package/dist/hud/render.js +3 -3
- package/dist/skills/git/references/pr/validate-branch.md +8 -5
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +20 -14
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +20 -20
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +20 -20
- package/dist/targets/claude-code/{learning-install.js → curation-install.js} +99 -67
- package/dist/targets/claude-code/installer.js +15 -15
- package/dist/targets/claude-code/language-stamp.js +5 -5
- package/dist/targets/claude-code/post-install.js +2 -2
- package/dist/targets/claude-code/tracker-servers.js +1 -1
- package/package.json +3 -2
- package/src/assets/agents/_partials/_code.mds +16 -17
- package/src/assets/agents/code.mds +4 -7
- package/src/assets/agents/curate.md +12 -14
- package/src/assets/agents/debug.mds +6 -6
- package/src/assets/agents/design.mds +18 -18
- package/src/assets/agents/fix.mds +6 -5
- package/src/assets/agents/git.mds +1 -1
- package/src/assets/agents/harden.mds +7 -7
- package/src/assets/agents/knowledge.mds +23 -23
- package/src/assets/agents/refactor.mds +2 -2
- package/src/assets/agents/research.mds +15 -15
- package/src/assets/agents/review.mds +45 -43
- package/src/assets/agents/skim.mds +11 -11
- package/src/assets/agents/synthesize.md +2 -2
- package/src/assets/agents/triage.mds +22 -22
- package/src/assets/agents/validate.md +13 -26
- package/src/assets/commands/_partials/_decisions.mds +13 -13
- package/src/assets/commands/_partials/_engine.mds +8 -8
- package/src/assets/commands/_partials/_knowledge.mds +29 -15
- package/src/assets/commands/_partials/_preamble.mds +4 -4
- package/src/assets/commands/_partials/_settings.mds +3 -3
- package/src/assets/commands/_partials/_wave.mds +17 -24
- package/src/assets/commands/bug-analysis.mds +37 -48
- package/src/assets/commands/code-review.mds +90 -86
- package/src/assets/commands/debug.mds +14 -14
- package/src/assets/commands/dynamic-build.mds +78 -78
- package/src/assets/commands/dynamic-plan.mds +22 -22
- package/src/assets/commands/dynamic-profile.mds +2 -2
- package/src/assets/commands/dynamic-tickets.mds +8 -8
- package/src/assets/commands/explore.mds +20 -20
- package/src/assets/commands/implement.mds +92 -118
- package/src/assets/commands/plan.mds +41 -51
- package/src/assets/commands/release.mds +12 -12
- package/src/assets/commands/research.mds +19 -19
- package/src/assets/commands/resolve.mds +56 -50
- package/src/assets/commands/self-review.mds +20 -20
- package/src/assets/mds/git/_ops.mds +3 -1
- package/src/assets/mds/git/_ops_pr.mds +1 -1
- package/src/assets/mds/git/_ops_steps.mds +13 -7
- package/src/assets/mds/git/_pr.mds +7 -4
- package/src/assets/mds/tracker/_common.mds +23 -12
- package/src/assets/mds/tracker/_github.mds +2 -9
- package/src/assets/mds/tracker/_jira.mds +2 -15
- package/src/assets/mds/tracker/_linear.mds +2 -15
- package/src/assets/rules/quality.md +1 -0
- package/src/assets/scripts/ci-wait.cjs +22 -4
- package/src/assets/scripts/claude-md-audit.cjs +66 -51
- package/src/assets/scripts/hooks/background-memory-update +406 -97
- package/src/assets/scripts/hooks/capture-prompt +12 -12
- package/src/assets/scripts/hooks/capture-question +11 -11
- package/src/assets/scripts/hooks/capture-turn +12 -12
- package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
- package/src/assets/scripts/hooks/curation-move +293 -0
- package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
- package/src/assets/scripts/hooks/ensure-proxy +4 -4
- package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
- package/src/assets/scripts/hooks/git-marker +81 -0
- package/src/assets/scripts/hooks/json-helper.cjs +145 -51
- package/src/assets/scripts/hooks/json-parse +19 -4
- package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
- package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +242 -118
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
- package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +34 -22
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +259 -0
- package/src/assets/scripts/hooks/memory-permission +358 -0
- package/src/assets/scripts/hooks/memory-worker +1 -1
- package/src/assets/scripts/hooks/pre-compact-memory +6 -16
- package/src/assets/scripts/hooks/queue-append +228 -103
- package/src/assets/scripts/hooks/resolve-project-root +2 -2
- package/src/assets/scripts/hooks/session-start-context +145 -71
- package/src/assets/scripts/hooks/session-start-memory +27 -46
- package/src/assets/scripts/lib/project-config.cjs +31 -9
- package/src/assets/scripts/resolve-base.cjs +1043 -0
- package/src/assets/scripts/resolve-settings.cjs +238 -46
- package/src/assets/skills/agent-references/SKILL.md +9 -2
- package/src/assets/skills/agent-references/references/commands/code-review-levels.md +19 -0
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +27 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
- package/src/assets/skills/agent-references/references/commands/plan-levels.md +40 -0
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +24 -0
- package/src/assets/skills/agent-references/references/synthesize/planning.md +9 -22
- package/src/assets/skills/agent-references/references/synthesize/review.md +11 -11
- package/src/assets/skills/apply-decisions/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +7 -7
- package/src/assets/skills/docs-framework/references/patterns.md +3 -4
- package/src/assets/skills/quality-gates/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
- package/templates/lint-pack/.lint-pack.conf +86 -0
- package/templates/lint-pack/README.md +190 -0
- package/templates/lint-pack/lint-pack.sh +905 -0
- package/dist/core/learning-queue-cleanup.js +0 -27
- package/dist/core/learning-store.js +0 -61
- /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
|
@@ -0,0 +1,905 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# lint-pack.sh: a project-owned lint pack. Copy it into your repository (for
|
|
4
|
+
# example as scripts/lint-pack.sh) and wire it to a `lint-pack` build-file entry.
|
|
5
|
+
# devflow ships this template. It never runs the template itself, and it never
|
|
6
|
+
# installs or updates your copy. At the `lean` gate level, Validate runs your
|
|
7
|
+
# wired copy. The README is templates/lint-pack/README.md in devflow-kit.
|
|
8
|
+
#
|
|
9
|
+
# Usage: bash scripts/lint-pack.sh <base-sha>
|
|
10
|
+
#
|
|
11
|
+
# <base-sha> a full 40-digit lower-case commit id that is an ancestor of HEAD.
|
|
12
|
+
#
|
|
13
|
+
# It reads the committed change <base-sha>..HEAD and the config blob that
|
|
14
|
+
# `.lint-pack.conf` holds at HEAD (data only: it is never sourced and never
|
|
15
|
+
# evaluated). Three checks are off until the config enables them:
|
|
16
|
+
#
|
|
17
|
+
# stale-symbol a symbol the branch removed from the source globs, that is on
|
|
18
|
+
# no added line in the source globs and in no tracked file
|
|
19
|
+
# outside the doc and help globs, but that a doc or help line
|
|
20
|
+
# at HEAD still names
|
|
21
|
+
# plan-id configured POSIX ERE patterns (plan IDs, tombstone phrases)
|
|
22
|
+
# that match as whole words on added lines
|
|
23
|
+
# function-size diagnostics of a configured linter that fall inside added hunks
|
|
24
|
+
#
|
|
25
|
+
# Output (stdout only; stderr stays empty):
|
|
26
|
+
#
|
|
27
|
+
# <check-id> <path>:<line> <id> one record per match, sorted, at most 50
|
|
28
|
+
# +<k> more records beyond the first 50
|
|
29
|
+
# pack-changed <path> a pack file the branch changed; at most 20, then
|
|
30
|
+
# +<k> more; these lines also print before an error
|
|
31
|
+
# line
|
|
32
|
+
# pack-dirty <path> a pack file that differs from HEAD in the work
|
|
33
|
+
# tree or the index, or that is untracked; at most
|
|
34
|
+
# 20, then +<k> more; these lines print after the
|
|
35
|
+
# pack-changed lines, and also before an error line
|
|
36
|
+
# skipped-binary <path> at most 10, then +<k> more
|
|
37
|
+
# symbol-cap <n> | unsafe-tokens <n> | unmatched-diagnostics <n>
|
|
38
|
+
# dropped-long-lines <n> counts, printed only when not zero
|
|
39
|
+
# base <sha> the commit the run compared: the line just before
|
|
40
|
+
# the LINT_PACK line, on every run that got past the
|
|
41
|
+
# base checks, an error exit included
|
|
42
|
+
# LINT_PACK: clean | no-config exit 0
|
|
43
|
+
# LINT_PACK: matches <n> exit 1
|
|
44
|
+
# LINT_PACK: error <reason> exit 2
|
|
45
|
+
#
|
|
46
|
+
# Error reasons: usage, git-too-old, not-a-repo, bad-base, base-not-ancestor,
|
|
47
|
+
# bad-config <n>, bad-pattern <id>, git-failed, interrupted, linter-missing,
|
|
48
|
+
# linter-exit <n>.
|
|
49
|
+
#
|
|
50
|
+
# bad-config <n> is the line number in the config; 0 means that .lint-pack.conf
|
|
51
|
+
# at HEAD is not a regular file (for example a symlink or a directory)
|
|
52
|
+
# git-too-old git is older than 2.30; a version string that cannot be read is
|
|
53
|
+
# git-failed
|
|
54
|
+
# interrupted the run got SIGTERM or SIGINT, for example from a timeout
|
|
55
|
+
# linter-exit the linter exited with a status outside the accepted set, or with
|
|
56
|
+
# any non-zero status and no "<path>:<line>:" line in its output
|
|
57
|
+
#
|
|
58
|
+
# The pack files are the config, this script, any .gitattributes, and the build
|
|
59
|
+
# files package.json, Makefile, makefile and GNUmakefile (GNU make reads the last
|
|
60
|
+
# two before Makefile). A build file counts only when a changed line names
|
|
61
|
+
# lint-pack or BASE; for pack-dirty, an untracked build file counts too.
|
|
62
|
+
#
|
|
63
|
+
# Needs bash 3.2 or newer and git 2.30 or newer. No line of added text is ever
|
|
64
|
+
# printed. The last line is always the LINT_PACK line.
|
|
65
|
+
|
|
66
|
+
set -u -o pipefail
|
|
67
|
+
|
|
68
|
+
LC_ALL=C
|
|
69
|
+
export LC_ALL
|
|
70
|
+
unset GIT_LITERAL_PATHSPECS GIT_NOGLOB_PATHSPECS GIT_GLOB_PATHSPECS GIT_ICASE_PATHSPECS
|
|
71
|
+
# GIT_DIFF_OPTS overrides -U0 and would add context lines to the diff.
|
|
72
|
+
unset GIT_DIFF_OPTS
|
|
73
|
+
unset GREP_OPTIONS GREP_COLOR GREP_COLORS
|
|
74
|
+
# A set CDPATH makes a relative cd print the directory it chose.
|
|
75
|
+
unset CDPATH
|
|
76
|
+
|
|
77
|
+
# Fixed bounds: every loop below runs over a file, an array or one of these.
|
|
78
|
+
MAX_CONF_LINES=1000
|
|
79
|
+
MAX_RECORDS=50
|
|
80
|
+
MAX_SYMBOLS=500
|
|
81
|
+
MAX_BINARY=10
|
|
82
|
+
MAX_PACK=20
|
|
83
|
+
MIN_GIT_MAJOR=2
|
|
84
|
+
MIN_GIT_MINOR=30
|
|
85
|
+
|
|
86
|
+
TAB=$(printf '\t')
|
|
87
|
+
KEY_RE='^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$'
|
|
88
|
+
ID_RE='^[a-z0-9-]{1,32}$'
|
|
89
|
+
BASE_RE='^[0-9a-f]{40}$'
|
|
90
|
+
BLANK_RE='^[[:space:]]*$'
|
|
91
|
+
NUM_RE='^[0-9]{1,3}$'
|
|
92
|
+
BAD_ESCAPE_RE='\\[A-Za-z0-9<>]'
|
|
93
|
+
GIT_VERSION_RE='^([0-9]+)\.([0-9]+)'
|
|
94
|
+
# A diagnostic starts with a path and holds no colon before its line number. An
|
|
95
|
+
# indented stack frame, and a "node:internal/..." prefix, are not diagnostics.
|
|
96
|
+
DIAGNOSTIC_RE='^[^[:space:]][^:]*:[0-9]+:'
|
|
97
|
+
|
|
98
|
+
# Every git call: no pager, and quoted paths in git's C-quoted form.
|
|
99
|
+
g() { command git --no-pager -c core.quotePath=true "$@"; }
|
|
100
|
+
DIFF_PINS=(--no-ext-diff --no-textconv --no-color --no-relative --src-prefix=a/ --dst-prefix=b/
|
|
101
|
+
--diff-algorithm=myers --inter-hunk-context=0 --indent-heuristic)
|
|
102
|
+
# The build files that run the pack, and the changed lines in them that count.
|
|
103
|
+
BUILD_FILES=(package.json Makefile makefile GNUmakefile)
|
|
104
|
+
BUILD_LINE_RE='lint-pack|BASE'
|
|
105
|
+
# A submodule file is not a tracked file of this repository: never search it.
|
|
106
|
+
GREP_PINS=(--no-color --no-column --full-name --no-recurse-submodules)
|
|
107
|
+
|
|
108
|
+
TMP=''
|
|
109
|
+
DONE=0
|
|
110
|
+
ROOT=''
|
|
111
|
+
BASE=''
|
|
112
|
+
SELF_ABS=''
|
|
113
|
+
SELF_REL=''
|
|
114
|
+
BASE_OK=0
|
|
115
|
+
FLAGS=''
|
|
116
|
+
PC_READY=0
|
|
117
|
+
SEEN=' '
|
|
118
|
+
ANY_ENABLED=0
|
|
119
|
+
|
|
120
|
+
STALE_ENABLED=false
|
|
121
|
+
STALE_LINE=0
|
|
122
|
+
STALE_SOURCE=()
|
|
123
|
+
STALE_DOC=()
|
|
124
|
+
STALE_HELP=()
|
|
125
|
+
STALE_SYMBOL=()
|
|
126
|
+
STALE_ALLOW=()
|
|
127
|
+
STALE_MIN=6
|
|
128
|
+
IDS_ENABLED=false
|
|
129
|
+
IDS_LINE=0
|
|
130
|
+
IDS_ID=()
|
|
131
|
+
IDS_RE=()
|
|
132
|
+
IDS_ALLOW=()
|
|
133
|
+
SIZE_ENABLED=false
|
|
134
|
+
SIZE_LINE=0
|
|
135
|
+
SIZE_ARG=()
|
|
136
|
+
SIZE_OK=()
|
|
137
|
+
SIZE_ALLOW=()
|
|
138
|
+
SIZE_MATCH=''
|
|
139
|
+
|
|
140
|
+
SYMBOL_CAP_N=0
|
|
141
|
+
UNSAFE_N=0
|
|
142
|
+
UNMATCHED_N=0
|
|
143
|
+
DROPPED_N=0
|
|
144
|
+
|
|
145
|
+
# ---- Exit handling ----
|
|
146
|
+
|
|
147
|
+
# File descriptor 9 is the real stdout. Every result line goes to it, never to fd 1:
|
|
148
|
+
# a redirect that is active when a signal lands (a `g ... >file` call, the result
|
|
149
|
+
# block) would take the lines, and the last line would not be the LINT_PACK line.
|
|
150
|
+
exec 9>&1
|
|
151
|
+
|
|
152
|
+
# The pack-changed and pack-dirty lines, once they are built. An error exit prints
|
|
153
|
+
# them too, so a branch that breaks its own pack still shows that it changed it.
|
|
154
|
+
report_pack_lines() {
|
|
155
|
+
if [ "$PC_READY" = 1 ]; then
|
|
156
|
+
PC_READY=0
|
|
157
|
+
cat "$TMP/pc.out" 2>/dev/null
|
|
158
|
+
fi
|
|
159
|
+
return 0
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# The base the run compared, once the base checks have passed. It is the line just
|
|
163
|
+
# before the result line, an error result included.
|
|
164
|
+
report_base() {
|
|
165
|
+
if [ "$BASE_OK" = 1 ]; then printf 'base %s\n' "$BASE"; fi
|
|
166
|
+
return 0
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# The error result on the real stdout: the pack lines, the base line, the reason.
|
|
170
|
+
print_error() {
|
|
171
|
+
report_pack_lines
|
|
172
|
+
report_base
|
|
173
|
+
printf 'LINT_PACK: error %s\n' "$1"
|
|
174
|
+
} >&9
|
|
175
|
+
|
|
176
|
+
# One error, one reason from the closed set. Only the pack lines and the base line
|
|
177
|
+
# come before it on stdout.
|
|
178
|
+
die_error() {
|
|
179
|
+
print_error "$1"
|
|
180
|
+
DONE=1
|
|
181
|
+
exit 2
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# Remove the temp dir. A run that ends without its result line is a git-failed.
|
|
185
|
+
# A signal that lands now changes nothing, and it must not cut the removal short.
|
|
186
|
+
on_exit() {
|
|
187
|
+
local rc=$?
|
|
188
|
+
trap '' TERM INT
|
|
189
|
+
if [ "$DONE" != 1 ]; then
|
|
190
|
+
print_error git-failed
|
|
191
|
+
rc=2
|
|
192
|
+
fi
|
|
193
|
+
if [ -n "$TMP" ]; then rm -rf "$TMP"; fi
|
|
194
|
+
exit "$rc"
|
|
195
|
+
}
|
|
196
|
+
trap on_exit EXIT
|
|
197
|
+
|
|
198
|
+
# A timeout wrapper ends a run with SIGTERM, a user with SIGINT: say so, and let
|
|
199
|
+
# the exit trap remove the temp dir. Further signals are ignored meanwhile, and so
|
|
200
|
+
# is every signal once the result is final.
|
|
201
|
+
on_signal() {
|
|
202
|
+
trap '' TERM INT
|
|
203
|
+
if [ "$DONE" = 1 ]; then return 0; fi
|
|
204
|
+
die_error interrupted
|
|
205
|
+
}
|
|
206
|
+
trap on_signal TERM INT
|
|
207
|
+
|
|
208
|
+
# ---- Small helpers ----
|
|
209
|
+
|
|
210
|
+
# Print the line count of a file without wc's padding.
|
|
211
|
+
count_lines() {
|
|
212
|
+
local n
|
|
213
|
+
n=$(wc -l <"$1") || return 1
|
|
214
|
+
printf '%s' "${n//[[:space:]]/}"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# Keep the lines of $1 that are at most 200 bytes in $2; count the others.
|
|
218
|
+
bound_lines() {
|
|
219
|
+
local rc k
|
|
220
|
+
command grep -a -E -v -e '^.{201}' "$1" >"$2"
|
|
221
|
+
rc=$?
|
|
222
|
+
[ "$rc" -le 1 ] || die_error git-failed
|
|
223
|
+
k=$(command grep -a -c -E -e '^.{201}' "$1")
|
|
224
|
+
rc=$?
|
|
225
|
+
[ "$rc" -le 1 ] || die_error git-failed
|
|
226
|
+
DROPPED_N=$((DROPPED_N + k))
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# ---- Config (data, one key=value per line) ----
|
|
230
|
+
|
|
231
|
+
cfg_fail() { die_error "bad-config $1"; }
|
|
232
|
+
|
|
233
|
+
# A scalar key may appear once.
|
|
234
|
+
cfg_once() {
|
|
235
|
+
case "$SEEN" in *" $1 "*) cfg_fail "$2" ;; esac
|
|
236
|
+
SEEN="$SEEN$1 "
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
cfg_bool() {
|
|
240
|
+
case "$1" in true | false) ;; *) cfg_fail "$2" ;; esac
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
cfg_value() {
|
|
244
|
+
[ -n "$1" ] || cfg_fail "$2"
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
# A whole number of at most three digits that is not above $3.
|
|
248
|
+
cfg_number() {
|
|
249
|
+
[[ $1 =~ $NUM_RE ]] || cfg_fail "$2"
|
|
250
|
+
[ $((10#$1)) -le "$3" ] || cfg_fail "$2"
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
read_config() {
|
|
254
|
+
local line n=0 key val id pat
|
|
255
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
256
|
+
n=$((n + 1))
|
|
257
|
+
[ "$n" -le "$MAX_CONF_LINES" ] || cfg_fail "$n"
|
|
258
|
+
line=${line%$'\r'}
|
|
259
|
+
case "$line" in '' | '#'*) continue ;; esac
|
|
260
|
+
if [[ $line =~ $BLANK_RE ]]; then continue; fi
|
|
261
|
+
case "$line" in *=*) ;; *) cfg_fail "$n" ;; esac
|
|
262
|
+
key=${line%%=*}
|
|
263
|
+
val=${line#*=}
|
|
264
|
+
[[ $key =~ $KEY_RE ]] || cfg_fail "$n"
|
|
265
|
+
case "$key" in
|
|
266
|
+
stale.enabled)
|
|
267
|
+
cfg_once "$key" "$n"; cfg_bool "$val" "$n"
|
|
268
|
+
STALE_ENABLED=$val; STALE_LINE=$n ;;
|
|
269
|
+
stale.source)
|
|
270
|
+
cfg_value "$val" "$n"; STALE_SOURCE[${#STALE_SOURCE[@]}]=$val ;;
|
|
271
|
+
stale.doc)
|
|
272
|
+
cfg_value "$val" "$n"; STALE_DOC[${#STALE_DOC[@]}]=$val ;;
|
|
273
|
+
stale.help)
|
|
274
|
+
cfg_value "$val" "$n"; STALE_HELP[${#STALE_HELP[@]}]=$val ;;
|
|
275
|
+
stale.symbol)
|
|
276
|
+
cfg_value "$val" "$n"; STALE_SYMBOL[${#STALE_SYMBOL[@]}]=$val ;;
|
|
277
|
+
stale.min-length)
|
|
278
|
+
cfg_once "$key" "$n"; cfg_number "$val" "$n" 64
|
|
279
|
+
[ $((10#$val)) -ge 1 ] || cfg_fail "$n"
|
|
280
|
+
STALE_MIN=$((10#$val)) ;;
|
|
281
|
+
stale.allow)
|
|
282
|
+
cfg_value "$val" "$n"; STALE_ALLOW[${#STALE_ALLOW[@]}]=$val ;;
|
|
283
|
+
ids.enabled)
|
|
284
|
+
cfg_once "$key" "$n"; cfg_bool "$val" "$n"
|
|
285
|
+
IDS_ENABLED=$val; IDS_LINE=$n ;;
|
|
286
|
+
ids.pattern)
|
|
287
|
+
case "$val" in *' '*) ;; *) cfg_fail "$n" ;; esac
|
|
288
|
+
id=${val%% *}
|
|
289
|
+
pat=${val#* }
|
|
290
|
+
[[ $id =~ $ID_RE ]] || cfg_fail "$n"
|
|
291
|
+
[[ $pat =~ $BLANK_RE ]] && cfg_fail "$n"
|
|
292
|
+
IDS_ID[${#IDS_ID[@]}]=$id
|
|
293
|
+
IDS_RE[${#IDS_RE[@]}]=$pat ;;
|
|
294
|
+
ids.allow)
|
|
295
|
+
cfg_value "$val" "$n"; IDS_ALLOW[${#IDS_ALLOW[@]}]=$val ;;
|
|
296
|
+
size.enabled)
|
|
297
|
+
cfg_once "$key" "$n"; cfg_bool "$val" "$n"
|
|
298
|
+
SIZE_ENABLED=$val; SIZE_LINE=$n ;;
|
|
299
|
+
size.arg)
|
|
300
|
+
cfg_value "$val" "$n"; SIZE_ARG[${#SIZE_ARG[@]}]=$val ;;
|
|
301
|
+
size.match)
|
|
302
|
+
cfg_once "$key" "$n"; cfg_value "$val" "$n"
|
|
303
|
+
SIZE_MATCH=$val ;;
|
|
304
|
+
size.ok-exit)
|
|
305
|
+
cfg_number "$val" "$n" 255
|
|
306
|
+
SIZE_OK[${#SIZE_OK[@]}]=$((10#$val)) ;;
|
|
307
|
+
size.allow)
|
|
308
|
+
cfg_value "$val" "$n"; SIZE_ALLOW[${#SIZE_ALLOW[@]}]=$val ;;
|
|
309
|
+
*) cfg_fail "$n" ;;
|
|
310
|
+
esac
|
|
311
|
+
done <"$TMP/conf"
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
# An enabled check needs its keys; a disabled check is ignored whole.
|
|
315
|
+
finish_config() {
|
|
316
|
+
if [ "$STALE_ENABLED" = true ]; then
|
|
317
|
+
[ "${#STALE_SOURCE[@]}" -gt 0 ] && [ "${#STALE_SYMBOL[@]}" -gt 0 ] || cfg_fail "$STALE_LINE"
|
|
318
|
+
ANY_ENABLED=1
|
|
319
|
+
else
|
|
320
|
+
STALE_SOURCE=(); STALE_DOC=(); STALE_HELP=(); STALE_SYMBOL=(); STALE_ALLOW=()
|
|
321
|
+
fi
|
|
322
|
+
if [ "$IDS_ENABLED" = true ]; then
|
|
323
|
+
[ "${#IDS_ID[@]}" -gt 0 ] || cfg_fail "$IDS_LINE"
|
|
324
|
+
ANY_ENABLED=1
|
|
325
|
+
else
|
|
326
|
+
IDS_ID=(); IDS_RE=(); IDS_ALLOW=()
|
|
327
|
+
fi
|
|
328
|
+
if [ "$SIZE_ENABLED" = true ]; then
|
|
329
|
+
[ "${#SIZE_ARG[@]}" -gt 0 ] || cfg_fail "$SIZE_LINE"
|
|
330
|
+
ANY_ENABLED=1
|
|
331
|
+
else
|
|
332
|
+
SIZE_ARG=(); SIZE_OK=(); SIZE_ALLOW=(); SIZE_MATCH=''
|
|
333
|
+
fi
|
|
334
|
+
if [ "${#STALE_DOC[@]}" -eq 0 ]; then STALE_DOC[0]='*.md'; fi
|
|
335
|
+
if [ "${#SIZE_OK[@]}" -eq 0 ]; then SIZE_OK[0]=0; SIZE_OK[1]=1; fi
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
# The whole-word wrapper of a plan-ID pattern.
|
|
339
|
+
wrap_pattern() {
|
|
340
|
+
printf '%s' "(^|[^A-Za-z0-9_])($1)([^A-Za-z0-9_]|\$)"
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
# POSIX ERE only: no \d \w \s \b, no back-reference, no regex the grep engine
|
|
344
|
+
# rejects, no pattern that matches the empty string. $3 is 1 for a wrapped one.
|
|
345
|
+
validate_pattern() {
|
|
346
|
+
local rc
|
|
347
|
+
if [[ $2 =~ $BAD_ESCAPE_RE ]]; then die_error "bad-pattern $1"; fi
|
|
348
|
+
command grep -a -E -e "($2)" </dev/null >/dev/null 2>&1
|
|
349
|
+
rc=$?
|
|
350
|
+
[ "$rc" -le 1 ] || die_error "bad-pattern $1"
|
|
351
|
+
if [ "$3" = 1 ]; then
|
|
352
|
+
command grep -a -E -e "$(wrap_pattern "$2")" </dev/null >/dev/null 2>&1
|
|
353
|
+
rc=$?
|
|
354
|
+
[ "$rc" -le 1 ] || die_error "bad-pattern $1"
|
|
355
|
+
fi
|
|
356
|
+
command grep -a -E -e "^($2)\$" <<<'' >/dev/null 2>&1
|
|
357
|
+
rc=$?
|
|
358
|
+
[ "$rc" -eq 1 ] || die_error "bad-pattern $1"
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
validate_patterns() {
|
|
362
|
+
local i p
|
|
363
|
+
for p in ${STALE_SYMBOL[@]+"${STALE_SYMBOL[@]}"}; do
|
|
364
|
+
validate_pattern stale-symbol "$p" 0
|
|
365
|
+
done
|
|
366
|
+
i=0
|
|
367
|
+
while [ "$i" -lt "${#IDS_ID[@]}" ]; do
|
|
368
|
+
validate_pattern "${IDS_ID[$i]}" "${IDS_RE[$i]}" 1
|
|
369
|
+
i=$((i + 1))
|
|
370
|
+
done
|
|
371
|
+
if [ -n "$SIZE_MATCH" ]; then validate_pattern size-match "$SIZE_MATCH" 0; fi
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
# ---- Paths and flags ----
|
|
375
|
+
|
|
376
|
+
# Path of this script, resolved before the first cd.
|
|
377
|
+
resolve_self() {
|
|
378
|
+
local dir
|
|
379
|
+
dir=$(cd "$(dirname -- "$0")" 2>/dev/null && pwd -P) || return 0
|
|
380
|
+
SELF_ABS="$dir/$(basename -- "$0")"
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# Does raw path $1 match any of the globs after it? A glob is a shell glob in
|
|
384
|
+
# which * also matches /. The pattern is data: case never runs it.
|
|
385
|
+
glob_match() {
|
|
386
|
+
local path=$1 glob
|
|
387
|
+
shift
|
|
388
|
+
for glob in "$@"; do
|
|
389
|
+
case "$path" in $glob) return 0 ;; esac
|
|
390
|
+
done
|
|
391
|
+
return 1
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
# FLAGS for raw path $1: = then one letter per property.
|
|
395
|
+
# O own file of the pack P pack-changed by itself S stale source
|
|
396
|
+
# D doc or help glob A stale allow B ids allow C size allow
|
|
397
|
+
classify_path() {
|
|
398
|
+
FLAGS='='
|
|
399
|
+
if [ "$1" = '.lint-pack.conf' ]; then FLAGS="${FLAGS}OP"; fi
|
|
400
|
+
if [ -n "$SELF_REL" ] && [ "$1" = "$SELF_REL" ]; then FLAGS="${FLAGS}OP"; fi
|
|
401
|
+
case "$1" in .gitattributes | */.gitattributes) FLAGS="${FLAGS}P" ;; esac
|
|
402
|
+
if glob_match "$1" ${STALE_SOURCE[@]+"${STALE_SOURCE[@]}"}; then FLAGS="${FLAGS}S"; fi
|
|
403
|
+
if glob_match "$1" ${STALE_DOC[@]+"${STALE_DOC[@]}"} ${STALE_HELP[@]+"${STALE_HELP[@]}"}; then FLAGS="${FLAGS}D"; fi
|
|
404
|
+
if glob_match "$1" ${STALE_ALLOW[@]+"${STALE_ALLOW[@]}"}; then FLAGS="${FLAGS}A"; fi
|
|
405
|
+
if glob_match "$1" ${IDS_ALLOW[@]+"${IDS_ALLOW[@]}"}; then FLAGS="${FLAGS}B"; fi
|
|
406
|
+
if glob_match "$1" ${SIZE_ALLOW[@]+"${SIZE_ALLOW[@]}"}; then FLAGS="${FLAGS}C"; fi
|
|
407
|
+
return 0
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
# Join a quoted listing ($1, one path per line) with the same listing made with
|
|
411
|
+
# -z ($2, raw paths): line n of one is entry n of the other. Writes a map of
|
|
412
|
+
# quoted path, raw path, flags (tab separated) to $3. $4 is 1 to drop the
|
|
413
|
+
# "HEAD:" prefix that git grep adds.
|
|
414
|
+
build_map() {
|
|
415
|
+
local q r rq
|
|
416
|
+
: >"$3"
|
|
417
|
+
exec 3<"$1" 4<"$2" 5>>"$3"
|
|
418
|
+
while IFS= read -r q <&3; do
|
|
419
|
+
IFS= read -r -d '' r <&4 || die_error git-failed
|
|
420
|
+
if [ "$4" = 1 ]; then q=${q#HEAD:}; r=${r#HEAD:}; fi
|
|
421
|
+
classify_path "$r"
|
|
422
|
+
rq=$r
|
|
423
|
+
case "$rq" in *"$TAB"* | *$'\n'*) rq='' ;; esac
|
|
424
|
+
printf '%s\t%s\t%s\n' "$q" "$rq" "$FLAGS" >&5
|
|
425
|
+
done
|
|
426
|
+
if IFS= read -r -d '' r <&4; then die_error git-failed; fi
|
|
427
|
+
exec 3<&- 4<&- 5>&-
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
# ---- Diffs ----
|
|
431
|
+
|
|
432
|
+
# Every changed path, old and new side of a rename included, with its flags.
|
|
433
|
+
build_diffmap() {
|
|
434
|
+
g diff "${DIFF_PINS[@]}" --no-renames --name-only --end-of-options "$BASE" HEAD -- \
|
|
435
|
+
>"$TMP/dm.q" 2>/dev/null || die_error git-failed
|
|
436
|
+
g diff "${DIFF_PINS[@]}" --no-renames --name-only -z --end-of-options "$BASE" HEAD -- \
|
|
437
|
+
>"$TMP/dm.z" 2>/dev/null || die_error git-failed
|
|
438
|
+
build_map "$TMP/dm.q" "$TMP/dm.z" "$TMP/dm.map" 0
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
# The "+<k> more" line for the lines of file $1 beyond the first $MAX_PACK, if any.
|
|
442
|
+
more_after_cap() {
|
|
443
|
+
local total
|
|
444
|
+
total=$(count_lines "$1") || die_error git-failed
|
|
445
|
+
if [ "$total" -gt "$MAX_PACK" ]; then printf '+%s more\n' $((total - MAX_PACK)); fi
|
|
446
|
+
return 0
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
# The pack files the branch changed, sorted: the config, this script and any
|
|
450
|
+
# .gitattributes by name, a build file when a changed line names lint-pack or BASE.
|
|
451
|
+
# A config renamed away or deleted shows as its old path. Then the pack files that
|
|
452
|
+
# differ from HEAD in the work tree. The lines need only the changed paths and fixed
|
|
453
|
+
# names, so they are built before the config is read and every later error exit
|
|
454
|
+
# prints them. At most $MAX_PACK lines of each kind print, then "+<k> more". The
|
|
455
|
+
# build-file diff reads a binary file as text, so a NUL byte hides no line.
|
|
456
|
+
pack_changed() {
|
|
457
|
+
command awk -F"$TAB" 'index($3, "P") > 0 { print $1 }' "$TMP/dm.map" >"$TMP/pc.txt" || die_error git-failed
|
|
458
|
+
g diff "${DIFF_PINS[@]}" --no-renames --name-only --text "-G$BUILD_LINE_RE" --end-of-options "$BASE" HEAD -- \
|
|
459
|
+
"${BUILD_FILES[@]}" >>"$TMP/pc.txt" 2>/dev/null || die_error git-failed
|
|
460
|
+
command sort -u "$TMP/pc.txt" >"$TMP/pc.sorted" || die_error git-failed
|
|
461
|
+
command head -n "$MAX_PACK" "$TMP/pc.sorted" | command sed 's/^/pack-changed /' >"$TMP/pc.lines" || die_error git-failed
|
|
462
|
+
more_after_cap "$TMP/pc.sorted" >>"$TMP/pc.lines"
|
|
463
|
+
pack_dirty
|
|
464
|
+
bound_lines "$TMP/pc.lines" "$TMP/pc.out"
|
|
465
|
+
PC_READY=1
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
# The pack files that differ from HEAD in the work tree or the index, or that are
|
|
469
|
+
# untracked and not ignored, sorted, appended to the pack lines. The script is a
|
|
470
|
+
# literal pathspec: its name may hold glob characters. A build file counts only for
|
|
471
|
+
# a changed line that names lint-pack or BASE, or when it is untracked.
|
|
472
|
+
pack_dirty() {
|
|
473
|
+
local side
|
|
474
|
+
local -a mode specs
|
|
475
|
+
specs=(':(literal).lint-pack.conf' .gitattributes '*/.gitattributes')
|
|
476
|
+
if [ -n "$SELF_REL" ]; then specs[${#specs[@]}]=":(literal)$SELF_REL"; fi
|
|
477
|
+
: >"$TMP/pd.txt"
|
|
478
|
+
for side in tree index; do
|
|
479
|
+
mode=()
|
|
480
|
+
if [ "$side" = index ]; then mode=(--cached); fi
|
|
481
|
+
g diff "${DIFF_PINS[@]}" --no-renames --name-only ${mode[@]+"${mode[@]}"} --end-of-options HEAD -- \
|
|
482
|
+
"${specs[@]}" >>"$TMP/pd.txt" 2>/dev/null || die_error git-failed
|
|
483
|
+
g diff "${DIFF_PINS[@]}" --no-renames --name-only --text "-G$BUILD_LINE_RE" ${mode[@]+"${mode[@]}"} --end-of-options HEAD -- \
|
|
484
|
+
"${BUILD_FILES[@]}" >>"$TMP/pd.txt" 2>/dev/null || die_error git-failed
|
|
485
|
+
done
|
|
486
|
+
g ls-files --others --exclude-standard -- "${specs[@]}" "${BUILD_FILES[@]}" >>"$TMP/pd.txt" 2>/dev/null || die_error git-failed
|
|
487
|
+
command sort -u "$TMP/pd.txt" >"$TMP/pd.sorted" || die_error git-failed
|
|
488
|
+
command head -n "$MAX_PACK" "$TMP/pd.sorted" | command sed 's/^/pack-dirty /' >>"$TMP/pc.lines" || die_error git-failed
|
|
489
|
+
more_after_cap "$TMP/pd.sorted" >>"$TMP/pc.lines"
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
# One -U0 diff, split by an awk pass into the streams each check reads.
|
|
493
|
+
diff_main() {
|
|
494
|
+
local ids_on=0 size_on=0
|
|
495
|
+
if [ "$IDS_ENABLED" = true ]; then ids_on=1; fi
|
|
496
|
+
if [ "$SIZE_ENABLED" = true ]; then size_on=1; fi
|
|
497
|
+
g diff "${DIFF_PINS[@]}" -M -U0 --end-of-options "$BASE" HEAD -- >"$TMP/main.diff" 2>/dev/null || die_error git-failed
|
|
498
|
+
command awk -v MAP="$TMP/dm.map" -v IDS_ON="$ids_on" -v SIZE_ON="$size_on" \
|
|
499
|
+
-v RM="$TMP/rm.txt" -v AD1="$TMP/ad1.txt" -v AD2T="$TMP/ad2.txt" -v AD2M="$TMP/ad2.meta" -v RNG="$TMP/ranges.tsv" \
|
|
500
|
+
-f "$TMP/split.awk" "$TMP/main.diff" || die_error git-failed
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
# ---- Check 1: stale symbols ----
|
|
504
|
+
|
|
505
|
+
# Every match of every stale.symbol pattern in $1, one per line, into $2.
|
|
506
|
+
extract_tokens() {
|
|
507
|
+
local pat rc
|
|
508
|
+
: >"$2"
|
|
509
|
+
for pat in ${STALE_SYMBOL[@]+"${STALE_SYMBOL[@]}"}; do
|
|
510
|
+
command grep -a -o -E -e "$pat" "$1" >>"$2"
|
|
511
|
+
rc=$?
|
|
512
|
+
[ "$rc" -le 1 ] || die_error "bad-pattern stale-symbol"
|
|
513
|
+
done
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
check_stale() {
|
|
517
|
+
local total rc
|
|
518
|
+
extract_tokens "$TMP/rm.txt" "$TMP/rm.tok"
|
|
519
|
+
extract_tokens "$TMP/ad1.txt" "$TMP/ad.tok"
|
|
520
|
+
command sort -u "$TMP/rm.tok" >"$TMP/rm.sorted" || die_error git-failed
|
|
521
|
+
command sort -u "$TMP/ad.tok" >"$TMP/ad.sorted" || die_error git-failed
|
|
522
|
+
command awk -v MIN="$STALE_MIN" -v UNSAFE="$TMP/unsafe.n" -f "$TMP/safe.awk" "$TMP/rm.sorted" >"$TMP/rm.safe" || die_error git-failed
|
|
523
|
+
UNSAFE_N=$(cat "$TMP/unsafe.n") || die_error git-failed
|
|
524
|
+
command comm -23 "$TMP/rm.safe" "$TMP/ad.sorted" >"$TMP/cand" || die_error git-failed
|
|
525
|
+
total=$(count_lines "$TMP/cand") || die_error git-failed
|
|
526
|
+
if [ "$total" -gt "$MAX_SYMBOLS" ]; then SYMBOL_CAP_N=$((total - MAX_SYMBOLS)); fi
|
|
527
|
+
command head -n "$MAX_SYMBOLS" "$TMP/cand" >"$TMP/tokens" || die_error git-failed
|
|
528
|
+
[ -s "$TMP/tokens" ] || return 0
|
|
529
|
+
|
|
530
|
+
# Three passes over HEAD only: the files that hold a symbol, quoted and raw,
|
|
531
|
+
# for their flags; then the lines. git grep never leaves the repository.
|
|
532
|
+
g grep -l -F -w "${GREP_PINS[@]}" -f "$TMP/tokens" HEAD -- >"$TMP/hf.q" 2>/dev/null
|
|
533
|
+
rc=$?
|
|
534
|
+
[ "$rc" -le 1 ] || die_error git-failed
|
|
535
|
+
g grep -l -z -F -w "${GREP_PINS[@]}" -f "$TMP/tokens" HEAD -- >"$TMP/hf.z" 2>/dev/null
|
|
536
|
+
rc=$?
|
|
537
|
+
[ "$rc" -le 1 ] || die_error git-failed
|
|
538
|
+
build_map "$TMP/hf.q" "$TMP/hf.z" "$TMP/hf.map" 1
|
|
539
|
+
g grep -n -o -F -w "${GREP_PINS[@]}" -f "$TMP/tokens" HEAD -- >"$TMP/hits" 2>/dev/null
|
|
540
|
+
rc=$?
|
|
541
|
+
[ "$rc" -le 1 ] || die_error git-failed
|
|
542
|
+
command awk -v TOKENS="$TMP/tokens" -v FMAP="$TMP/hf.map" -v SB="$TMP/sb1.txt" -v REC="$TMP/rec.stale" \
|
|
543
|
+
-f "$TMP/hits.awk" "$TMP/hits" || die_error git-failed
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
# ---- Check 2: plan IDs and tombstones ----
|
|
547
|
+
|
|
548
|
+
check_ids() {
|
|
549
|
+
local i=0 id rc
|
|
550
|
+
while [ "$i" -lt "${#IDS_ID[@]}" ]; do
|
|
551
|
+
id=${IDS_ID[$i]}
|
|
552
|
+
command grep -a -n -E -e "$(wrap_pattern "${IDS_RE[$i]}")" "$TMP/ad2.txt" >"$TMP/ids.hit"
|
|
553
|
+
rc=$?
|
|
554
|
+
case "$rc" in
|
|
555
|
+
0)
|
|
556
|
+
command cut -d: -f1 "$TMP/ids.hit" >"$TMP/ids.nums" || die_error git-failed
|
|
557
|
+
command awk -F"$TAB" -v ID="$id" \
|
|
558
|
+
'NR == FNR { HIT[$1] = 1; next } (FNR in HIT) { print "plan-id\t" $1 "\t" $2 "\t" ID }' \
|
|
559
|
+
"$TMP/ids.nums" "$TMP/ad2.meta" >>"$TMP/rec.ids" || die_error git-failed ;;
|
|
560
|
+
1) ;;
|
|
561
|
+
*) die_error "bad-pattern $id" ;;
|
|
562
|
+
esac
|
|
563
|
+
i=$((i + 1))
|
|
564
|
+
done
|
|
565
|
+
# A changed file that git shows as binary is not searched: say so.
|
|
566
|
+
g diff "${DIFF_PINS[@]}" --no-renames --numstat --end-of-options "$BASE" HEAD -- >"$TMP/numstat" 2>/dev/null || die_error git-failed
|
|
567
|
+
command awk -F"$TAB" '
|
|
568
|
+
NR == FNR { FL[$1] = $3; next }
|
|
569
|
+
$1 == "-" && $2 == "-" {
|
|
570
|
+
f = ($3 in FL) ? FL[$3] : "="
|
|
571
|
+
if (index(f, "O") == 0 && index(f, "B") == 0) print $3
|
|
572
|
+
}' "$TMP/dm.map" "$TMP/numstat" >>"$TMP/sb2.txt" || die_error git-failed
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
# ---- Check 3: function size ----
|
|
576
|
+
|
|
577
|
+
check_size() {
|
|
578
|
+
local rc ok e
|
|
579
|
+
# The linter is argv, one word per config line, run from the root. No shell.
|
|
580
|
+
# The outer redirect keeps bash's own "Terminated" note off stderr when a signal
|
|
581
|
+
# ends the linter's subshell. The linter does not get fd 9, so a daemon that it
|
|
582
|
+
# leaves behind never holds the caller's pipe open.
|
|
583
|
+
{ ( cd "$ROOT" && "${SIZE_ARG[@]}" ) </dev/null >"$TMP/lint.out" 2>&1 9>&-; } 2>/dev/null
|
|
584
|
+
rc=$?
|
|
585
|
+
case "$rc" in 126 | 127) die_error linter-missing ;; esac
|
|
586
|
+
ok=0
|
|
587
|
+
for e in ${SIZE_OK[@]+"${SIZE_OK[@]}"}; do
|
|
588
|
+
if [ "$rc" -eq "$e" ]; then ok=1; fi
|
|
589
|
+
done
|
|
590
|
+
[ "$ok" = 1 ] || die_error "linter-exit $rc"
|
|
591
|
+
# A non-zero status without one "<path>:<line>:" line is not a lint result: the
|
|
592
|
+
# linter did not run (a failed fetch, a missing module). Never read it as clean.
|
|
593
|
+
if [ "$rc" -ne 0 ]; then
|
|
594
|
+
command grep -a -q -E -e "$DIAGNOSTIC_RE" "$TMP/lint.out"
|
|
595
|
+
case "$?" in 0) ;; 1) die_error "linter-exit $rc" ;; *) die_error git-failed ;; esac
|
|
596
|
+
fi
|
|
597
|
+
if [ -n "$SIZE_MATCH" ]; then
|
|
598
|
+
command grep -a -E -e "$SIZE_MATCH" "$TMP/lint.out" >"$TMP/lint.sel"
|
|
599
|
+
rc=$?
|
|
600
|
+
[ "$rc" -le 1 ] || die_error "bad-pattern size-match"
|
|
601
|
+
else
|
|
602
|
+
cp "$TMP/lint.out" "$TMP/lint.sel" || die_error git-failed
|
|
603
|
+
fi
|
|
604
|
+
ROOT_FOR_AWK=$ROOT command awk -v MAP="$TMP/dm.map" -v RNG="$TMP/ranges.tsv" \
|
|
605
|
+
-v REC="$TMP/rec.size" -v UNM="$TMP/unmatched.n" \
|
|
606
|
+
-f "$TMP/size.awk" "$TMP/lint.sel" || die_error git-failed
|
|
607
|
+
UNMATCHED_N=$(cat "$TMP/unmatched.n") || die_error git-failed
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
# ---- Result ----
|
|
611
|
+
|
|
612
|
+
emit() {
|
|
613
|
+
local total more state code sb_total sb_more
|
|
614
|
+
command sort -t "$TAB" -k1,1 -k2,2 -k3,3n -k4,4 -u "$TMP/rec.stale" "$TMP/rec.ids" "$TMP/rec.size" >"$TMP/rec.sorted" || die_error git-failed
|
|
615
|
+
total=$(count_lines "$TMP/rec.sorted") || die_error git-failed
|
|
616
|
+
command head -n "$MAX_RECORDS" "$TMP/rec.sorted" >"$TMP/rec.head" || die_error git-failed
|
|
617
|
+
command awk -F"$TAB" '{ print $1 " " $2 ":" $3 " " $4 }' "$TMP/rec.head" >"$TMP/rec.lines" || die_error git-failed
|
|
618
|
+
bound_lines "$TMP/rec.lines" "$TMP/rec.out"
|
|
619
|
+
more=0
|
|
620
|
+
if [ "$total" -gt "$MAX_RECORDS" ]; then more=$((total - MAX_RECORDS)); fi
|
|
621
|
+
|
|
622
|
+
cat "$TMP/sb1.txt" "$TMP/sb2.txt" | command sort -u >"$TMP/sb.sorted" || die_error git-failed
|
|
623
|
+
sb_total=$(count_lines "$TMP/sb.sorted") || die_error git-failed
|
|
624
|
+
command head -n "$MAX_BINARY" "$TMP/sb.sorted" | command sed 's/^/skipped-binary /' >"$TMP/sb.lines" || die_error git-failed
|
|
625
|
+
bound_lines "$TMP/sb.lines" "$TMP/sb.out"
|
|
626
|
+
sb_more=0
|
|
627
|
+
if [ "$sb_total" -gt "$MAX_BINARY" ]; then sb_more=$((sb_total - MAX_BINARY)); fi
|
|
628
|
+
|
|
629
|
+
if [ "$total" -gt 0 ]; then
|
|
630
|
+
state="matches $total"; code=1
|
|
631
|
+
elif [ "$ANY_ENABLED" = 1 ]; then
|
|
632
|
+
state=clean; code=0
|
|
633
|
+
else
|
|
634
|
+
state=no-config; code=0
|
|
635
|
+
fi
|
|
636
|
+
|
|
637
|
+
{
|
|
638
|
+
cat "$TMP/rec.out"
|
|
639
|
+
if [ "$more" -gt 0 ]; then printf '+%s more\n' "$more"; fi
|
|
640
|
+
cat "$TMP/pc.out"
|
|
641
|
+
cat "$TMP/sb.out"
|
|
642
|
+
if [ "$sb_more" -gt 0 ]; then printf '+%s more\n' "$sb_more"; fi
|
|
643
|
+
if [ "$SYMBOL_CAP_N" -gt 0 ]; then printf 'symbol-cap %s\n' "$SYMBOL_CAP_N"; fi
|
|
644
|
+
if [ "$UNSAFE_N" -gt 0 ]; then printf 'unsafe-tokens %s\n' "$UNSAFE_N"; fi
|
|
645
|
+
if [ "$UNMATCHED_N" -gt 0 ]; then printf 'unmatched-diagnostics %s\n' "$UNMATCHED_N"; fi
|
|
646
|
+
if [ "$DROPPED_N" -gt 0 ]; then printf 'dropped-long-lines %s\n' "$DROPPED_N"; fi
|
|
647
|
+
report_base
|
|
648
|
+
printf 'LINT_PACK: %s\n' "$state"
|
|
649
|
+
} >"$TMP/result.txt"
|
|
650
|
+
# The result is final: a signal that lands now changes nothing, and it must not
|
|
651
|
+
# cut the print short. The print goes to the real stdout.
|
|
652
|
+
trap '' TERM INT
|
|
653
|
+
DONE=1
|
|
654
|
+
cat "$TMP/result.txt" >&9
|
|
655
|
+
exit "$code"
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
# ---- The awk passes (POSIX awk: no interval expressions) ----
|
|
659
|
+
|
|
660
|
+
write_awk_programs() {
|
|
661
|
+
# Split the -U0 diff. A hunk header gives the exact line counts, so a content
|
|
662
|
+
# line that starts with "--- " or "+++ " is never read as a file header.
|
|
663
|
+
cat >"$TMP/split.awk" <<'AWK' || die_error git-failed
|
|
664
|
+
function flagsof(p) { return (p in FL) ? FL[p] : "=" }
|
|
665
|
+
function unprefix(p) {
|
|
666
|
+
sub(/\t$/, "", p)
|
|
667
|
+
if (p == "/dev/null") return ""
|
|
668
|
+
if (substr(p, 1, 1) == "\"") return "\"" substr(p, 4)
|
|
669
|
+
return substr(p, 3)
|
|
670
|
+
}
|
|
671
|
+
function removed(text, fl) {
|
|
672
|
+
fl = flagsof(oldp)
|
|
673
|
+
if (index(fl, "O") == 0 && index(fl, "S") > 0) print text > RM
|
|
674
|
+
}
|
|
675
|
+
function added(text, fl, ln) {
|
|
676
|
+
fl = flagsof(newp)
|
|
677
|
+
ln = nextnew
|
|
678
|
+
nextnew++
|
|
679
|
+
if (index(fl, "O") > 0) return
|
|
680
|
+
if (index(fl, "S") > 0) print text > AD1
|
|
681
|
+
if (IDS_ON == 1 && index(fl, "B") == 0) {
|
|
682
|
+
print text > AD2T
|
|
683
|
+
print newp "\t" ln > AD2M
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function hunk(line, hdr, parts, o, n, oa, na, fl) {
|
|
687
|
+
if (!match(line, /^@@ -[0-9]+(,[0-9]+)? \+[0-9]+(,[0-9]+)? @@/)) return
|
|
688
|
+
hdr = substr(line, 4, RLENGTH - 6)
|
|
689
|
+
split(hdr, parts, " ")
|
|
690
|
+
o = substr(parts[1], 2)
|
|
691
|
+
n = substr(parts[2], 2)
|
|
692
|
+
if (index(o, ",") > 0) { split(o, oa, ","); remold = oa[2] + 0 } else remold = 1
|
|
693
|
+
if (index(n, ",") > 0) { split(n, na, ","); nextnew = na[1] + 0; remnew = na[2] + 0 } else { nextnew = n + 0; remnew = 1 }
|
|
694
|
+
if (SIZE_ON == 1 && remnew > 0) {
|
|
695
|
+
fl = flagsof(newp)
|
|
696
|
+
if (index(fl, "O") == 0 && index(fl, "C") == 0) print newp "\t" nextnew "\t" (nextnew + remnew - 1) > RNG
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
BEGIN {
|
|
700
|
+
while ((getline l < MAP) > 0) {
|
|
701
|
+
split(l, f, "\t")
|
|
702
|
+
FL[f[1]] = f[3]
|
|
703
|
+
}
|
|
704
|
+
close(MAP)
|
|
705
|
+
}
|
|
706
|
+
{
|
|
707
|
+
line = $0
|
|
708
|
+
if (remold > 0 || remnew > 0) {
|
|
709
|
+
c = substr(line, 1, 1)
|
|
710
|
+
if (c == "-" && remold > 0) { remold--; removed(substr(line, 2)); next }
|
|
711
|
+
if (c == "+" && remnew > 0) { remnew--; added(substr(line, 2)); next }
|
|
712
|
+
if (c == "\\") next
|
|
713
|
+
remold = 0
|
|
714
|
+
remnew = 0
|
|
715
|
+
}
|
|
716
|
+
if (substr(line, 1, 4) == "--- ") { oldp = unprefix(substr(line, 5)); next }
|
|
717
|
+
if (substr(line, 1, 4) == "+++ ") { newp = unprefix(substr(line, 5)); next }
|
|
718
|
+
if (substr(line, 1, 11) == "diff --git ") { oldp = ""; newp = ""; next }
|
|
719
|
+
if (substr(line, 1, 3) == "@@ ") hunk(line)
|
|
720
|
+
}
|
|
721
|
+
AWK
|
|
722
|
+
|
|
723
|
+
# Keep safe symbols of the minimum length; count the unsafe ones.
|
|
724
|
+
cat >"$TMP/safe.awk" <<'AWK' || die_error git-failed
|
|
725
|
+
{
|
|
726
|
+
t = $0
|
|
727
|
+
if (length(t) < MIN) next
|
|
728
|
+
if (length(t) > 64 || t !~ "^[A-Za-z0-9_.:/-]+$") { unsafe++; next }
|
|
729
|
+
print t
|
|
730
|
+
}
|
|
731
|
+
END { print unsafe + 0 > UNSAFE }
|
|
732
|
+
AWK
|
|
733
|
+
|
|
734
|
+
# Read "HEAD:<path>:<line>:<symbol>" lines. A path may hold colons, so the
|
|
735
|
+
# split is the first ":<digits>:" whose rest is a known symbol. A hit in a
|
|
736
|
+
# code file kills the symbol; a hit in a doc or help file is a candidate.
|
|
737
|
+
cat >"$TMP/hits.awk" <<'AWK' || die_error git-failed
|
|
738
|
+
function flagsof(p) { return (p in FL) ? FL[p] : "=" }
|
|
739
|
+
BEGIN {
|
|
740
|
+
while ((getline l < TOKENS) > 0) TOK[l] = 1
|
|
741
|
+
close(TOKENS)
|
|
742
|
+
while ((getline l < FMAP) > 0) {
|
|
743
|
+
split(l, f, "\t")
|
|
744
|
+
FL[f[1]] = f[3]
|
|
745
|
+
}
|
|
746
|
+
close(FMAP)
|
|
747
|
+
}
|
|
748
|
+
{
|
|
749
|
+
line = $0
|
|
750
|
+
len = length(line)
|
|
751
|
+
if (substr(line, 1, 17) == "Binary file HEAD:" && substr(line, len - 7) == " matches") {
|
|
752
|
+
name = substr(line, 18, len - 25)
|
|
753
|
+
if (index(flagsof(name), "O") == 0) print name > SB
|
|
754
|
+
next
|
|
755
|
+
}
|
|
756
|
+
if (substr(line, 1, 5) != "HEAD:") next
|
|
757
|
+
rest = substr(line, 6)
|
|
758
|
+
rlen = length(rest)
|
|
759
|
+
start = 1
|
|
760
|
+
while (start <= rlen) {
|
|
761
|
+
s = substr(rest, start)
|
|
762
|
+
if (!match(s, /:[0-9]+:/)) break
|
|
763
|
+
pos = start + RSTART - 1
|
|
764
|
+
llen = RLENGTH
|
|
765
|
+
t = substr(rest, pos + llen)
|
|
766
|
+
if (t in TOK) {
|
|
767
|
+
path = substr(rest, 1, pos - 1)
|
|
768
|
+
lineno = substr(rest, pos + 1, llen - 2)
|
|
769
|
+
fl = flagsof(path)
|
|
770
|
+
if (index(fl, "O") > 0) break
|
|
771
|
+
if (index(fl, "D") > 0) {
|
|
772
|
+
if (index(fl, "A") == 0) { nc++; CP[nc] = path; CL[nc] = lineno; CT[nc] = t }
|
|
773
|
+
} else KILL[t] = 1
|
|
774
|
+
break
|
|
775
|
+
}
|
|
776
|
+
start = pos + 1
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
END {
|
|
780
|
+
for (i = 1; i <= nc; i++) {
|
|
781
|
+
if (!(CT[i] in KILL)) print "stale-symbol\t" CP[i] "\t" CL[i] "\t" CT[i] > REC
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
AWK
|
|
785
|
+
|
|
786
|
+
# Keep a diagnostic "<path>:<line>:..." that lies inside an added hunk.
|
|
787
|
+
cat >"$TMP/size.awk" <<'AWK' || die_error git-failed
|
|
788
|
+
BEGIN {
|
|
789
|
+
while ((getline l < MAP) > 0) {
|
|
790
|
+
split(l, f, "\t")
|
|
791
|
+
if (f[2] != "") { RQ[f[2]] = f[1]; RF[f[2]] = f[3] }
|
|
792
|
+
}
|
|
793
|
+
close(MAP)
|
|
794
|
+
while ((getline l < RNG) > 0) {
|
|
795
|
+
split(l, f, "\t")
|
|
796
|
+
RANGE[f[1]] = RANGE[f[1]] " " f[2] "-" f[3]
|
|
797
|
+
}
|
|
798
|
+
close(RNG)
|
|
799
|
+
rp = ENVIRON["ROOT_FOR_AWK"] "/"
|
|
800
|
+
}
|
|
801
|
+
{
|
|
802
|
+
line = $0
|
|
803
|
+
if (!match(line, /:[0-9]+:/)) next
|
|
804
|
+
p = substr(line, 1, RSTART - 1)
|
|
805
|
+
ln = substr(line, RSTART + 1, RLENGTH - 2) + 0
|
|
806
|
+
if (index(p, rp) == 1) p = substr(p, length(rp) + 1)
|
|
807
|
+
else if (substr(p, 1, 2) == "./") p = substr(p, 3)
|
|
808
|
+
if (!(p in RQ)) { unmatched++; next }
|
|
809
|
+
q = RQ[p]
|
|
810
|
+
fl = RF[p]
|
|
811
|
+
if (index(fl, "O") > 0 || index(fl, "C") > 0) next
|
|
812
|
+
n = split(RANGE[q], r, " ")
|
|
813
|
+
for (i = 1; i <= n; i++) {
|
|
814
|
+
split(r[i], se, "-")
|
|
815
|
+
if (ln >= se[1] + 0 && ln <= se[2] + 0) {
|
|
816
|
+
print "function-size\t" q "\t" ln "\tfunction-size" > REC
|
|
817
|
+
break
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
END { print unmatched + 0 > UNM }
|
|
822
|
+
AWK
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
# ---- Main ----
|
|
826
|
+
|
|
827
|
+
# The config is the blob at HEAD, never the work tree. The tree says whether it
|
|
828
|
+
# exists, so a blob that will not read is git-failed and never no-config. Only a
|
|
829
|
+
# regular file is a config. The map is rebuilt once the globs are known.
|
|
830
|
+
load_config() {
|
|
831
|
+
local mode rest
|
|
832
|
+
g ls-tree HEAD -- .lint-pack.conf >"$TMP/conf.entry" 2>/dev/null || die_error git-failed
|
|
833
|
+
[ -s "$TMP/conf.entry" ] || return 0
|
|
834
|
+
IFS=' ' read -r mode rest <"$TMP/conf.entry" || die_error git-failed
|
|
835
|
+
case "$mode" in 100644 | 100755) ;; *) cfg_fail 0 ;; esac
|
|
836
|
+
g cat-file blob HEAD:.lint-pack.conf >"$TMP/conf" 2>/dev/null || die_error git-failed
|
|
837
|
+
read_config
|
|
838
|
+
finish_config
|
|
839
|
+
validate_patterns
|
|
840
|
+
build_map "$TMP/dm.q" "$TMP/dm.z" "$TMP/dm.map" 0
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
# git 2.30 or newer: the base checks use --end-of-options, which an older git
|
|
844
|
+
# rejects, and that would read as bad-base. Compare numbers, never strings.
|
|
845
|
+
check_git_version() {
|
|
846
|
+
local out major minor
|
|
847
|
+
out=$(command git --version 2>/dev/null) || die_error git-failed
|
|
848
|
+
out=${out#git version }
|
|
849
|
+
[[ $out =~ $GIT_VERSION_RE ]] || die_error git-failed
|
|
850
|
+
major=$((10#${BASH_REMATCH[1]}))
|
|
851
|
+
minor=$((10#${BASH_REMATCH[2]}))
|
|
852
|
+
if [ "$major" -lt "$MIN_GIT_MAJOR" ]; then die_error git-too-old; fi
|
|
853
|
+
if [ "$major" -eq "$MIN_GIT_MAJOR" ] && [ "$minor" -lt "$MIN_GIT_MINOR" ]; then die_error git-too-old; fi
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
main() {
|
|
857
|
+
local top rc
|
|
858
|
+
[ "$#" -eq 1 ] || die_error usage
|
|
859
|
+
resolve_self
|
|
860
|
+
command -v git >/dev/null 2>&1 || die_error git-failed
|
|
861
|
+
check_git_version
|
|
862
|
+
top=$(command git rev-parse --show-toplevel 2>/dev/null) || die_error not-a-repo
|
|
863
|
+
[ -n "$top" ] || die_error not-a-repo
|
|
864
|
+
cd "$top" 2>/dev/null || die_error not-a-repo
|
|
865
|
+
ROOT=$(pwd -P) || die_error git-failed
|
|
866
|
+
|
|
867
|
+
BASE=$1
|
|
868
|
+
[[ $BASE =~ $BASE_RE ]] || die_error bad-base
|
|
869
|
+
# The commit the argument names, so the base line shows what the run compared.
|
|
870
|
+
BASE=$(g rev-parse --verify --quiet --end-of-options "${BASE}^{commit}" 2>/dev/null) || die_error bad-base
|
|
871
|
+
[[ $BASE =~ $BASE_RE ]] || die_error bad-base
|
|
872
|
+
g merge-base --is-ancestor "$BASE" HEAD >/dev/null 2>&1
|
|
873
|
+
rc=$?
|
|
874
|
+
case "$rc" in 0) BASE_OK=1 ;; 1) die_error base-not-ancestor ;; *) die_error git-failed ;; esac
|
|
875
|
+
|
|
876
|
+
TMP=$(mktemp -d "${TMPDIR:-/tmp}/lint-pack.XXXXXX") || die_error git-failed
|
|
877
|
+
case "$SELF_ABS" in "$ROOT"/*) SELF_REL=${SELF_ABS#"$ROOT"/} ;; esac
|
|
878
|
+
|
|
879
|
+
# The pack-changed lines come before the config, so an error exit shows them.
|
|
880
|
+
build_diffmap
|
|
881
|
+
pack_changed
|
|
882
|
+
load_config
|
|
883
|
+
|
|
884
|
+
: >"$TMP/rec.stale"
|
|
885
|
+
: >"$TMP/rec.ids"
|
|
886
|
+
: >"$TMP/rec.size"
|
|
887
|
+
: >"$TMP/sb1.txt"
|
|
888
|
+
: >"$TMP/sb2.txt"
|
|
889
|
+
: >"$TMP/rm.txt"
|
|
890
|
+
: >"$TMP/ad1.txt"
|
|
891
|
+
: >"$TMP/ad2.txt"
|
|
892
|
+
: >"$TMP/ad2.meta"
|
|
893
|
+
: >"$TMP/ranges.tsv"
|
|
894
|
+
write_awk_programs
|
|
895
|
+
|
|
896
|
+
if [ "$ANY_ENABLED" = 1 ]; then
|
|
897
|
+
diff_main
|
|
898
|
+
if [ "$STALE_ENABLED" = true ]; then check_stale; fi
|
|
899
|
+
if [ "$IDS_ENABLED" = true ]; then check_ids; fi
|
|
900
|
+
if [ "$SIZE_ENABLED" = true ]; then check_size; fi
|
|
901
|
+
fi
|
|
902
|
+
emit
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
main "$@"
|