vouchington-tooling 0.7.0 → 0.7.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vouchington-tooling",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Vouchington CLI and extractable tooling libraries.",
5
5
  "homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
6
6
  "bugs": {
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ echo "usage: backfill-slash-release-tags.sh --package NAME [--package NAME]... [--dry-run] [--write] [--push] [--remote NAME]" >&2
6
+ }
7
+
8
+ packages=()
9
+ explicit_dry=0
10
+ do_write=0
11
+ do_push=0
12
+ remote=origin
13
+
14
+ while [ $# -gt 0 ]; do
15
+ case "$1" in
16
+ --package)
17
+ [ $# -ge 2 ] || { usage; exit 2; }
18
+ packages+=("$2")
19
+ shift 2
20
+ ;;
21
+ --dry-run)
22
+ explicit_dry=1
23
+ shift
24
+ ;;
25
+ --write)
26
+ do_write=1
27
+ shift
28
+ ;;
29
+ --push)
30
+ do_push=1
31
+ shift
32
+ ;;
33
+ --remote)
34
+ [ $# -ge 2 ] || { usage; exit 2; }
35
+ remote="$2"
36
+ shift 2
37
+ ;;
38
+ -h | --help)
39
+ usage
40
+ exit 2
41
+ ;;
42
+ *)
43
+ usage
44
+ exit 2
45
+ ;;
46
+ esac
47
+ done
48
+
49
+ if [ "${#packages[@]}" -eq 0 ]; then
50
+ usage
51
+ exit 2
52
+ fi
53
+
54
+ if [ "$explicit_dry" -eq 1 ] && { [ "$do_write" -eq 1 ] || [ "$do_push" -eq 1 ]; }; then
55
+ echo "cannot combine --dry-run with --write or --push" >&2
56
+ exit 2
57
+ fi
58
+
59
+ dry_run=1
60
+ if [ "$do_write" -eq 1 ] || [ "$do_push" -eq 1 ]; then
61
+ dry_run=0
62
+ fi
63
+
64
+ semver='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'
65
+ push_refs=()
66
+
67
+ for package in "${packages[@]}"; do
68
+ if [[ ! "$package" =~ ^[A-Za-z0-9._-]+$ ]]; then
69
+ echo "invalid package name: ${package}" >&2
70
+ exit 2
71
+ fi
72
+
73
+ while IFS= read -r hyphen_tag; do
74
+ [ -n "$hyphen_tag" ] || continue
75
+ version="${hyphen_tag#"${package}-v"}"
76
+ if [[ ! "$version" =~ $semver ]]; then
77
+ echo "hyphen tag ${hyphen_tag} is not <package>-v<semver>" >&2
78
+ exit 1
79
+ fi
80
+
81
+ slash_tag="${package}/v${version}"
82
+ commit="$(git rev-parse "refs/tags/${hyphen_tag}^{commit}")"
83
+
84
+ if git rev-parse -q --verify "refs/tags/${slash_tag}" >/dev/null; then
85
+ existing="$(git rev-parse "refs/tags/${slash_tag}^{commit}")"
86
+ if [ "$existing" != "$commit" ]; then
87
+ echo "slash tag ${slash_tag} points at ${existing}, expected ${commit}" >&2
88
+ exit 1
89
+ fi
90
+ echo "skip ${slash_tag} (already at ${commit})"
91
+ continue
92
+ fi
93
+
94
+ echo "create ${slash_tag} -> ${commit}"
95
+ if [ "$dry_run" -eq 0 ]; then
96
+ git tag -a "${slash_tag}" -m "${package} v${version}" "$commit"
97
+ push_refs+=("refs/tags/${slash_tag}")
98
+ fi
99
+ done < <(git tag --list "${package}-v*")
100
+ done
101
+
102
+ if [ "$do_push" -eq 0 ]; then
103
+ exit 0
104
+ fi
105
+
106
+ if [ "${#push_refs[@]}" -eq 0 ]; then
107
+ echo "nothing to push"
108
+ exit 0
109
+ fi
110
+
111
+ git push --atomic "$remote" "${push_refs[@]}"
@@ -12,25 +12,32 @@ merge.
12
12
 
13
13
  A stack is a sequence of branches, each submitted as its own pull request, where every PR but the
14
14
  bottom-most one targets the PR below it as its base branch instead of targeting the default branch
15
- directly. That parent-branch base is the hazard: a mid-stack PR's base ref is an unmerged branch,
16
- not the default branch, so a generic "merge this PR" action taken on a mid-stack layer can land it
17
- into that unmerged parent instead of where it needs to go. Before treating any merge action on a
18
- stacked PR as safe, confirm its actual base branch, and treat a base that is not the default branch
19
- as a sign the PR is mid-stack.
15
+ directly. GitHub tracks a chain like this as a single stack, and its own documentation on merging
16
+ stacked pull requests is explicit about what a merge then does: "the selected pull request and all
17
+ unmerged pull requests below it land on the base branch together as a single operation, ordered
18
+ from the bottom up," and merging one is only possible once everything below it already satisfies
19
+ whatever this repository requires to merge — you cannot merge an isolated middle layer on its own.
20
+ That is dedicated stack-merge behavior, not the ordinary single-branch merge semantics a generic
21
+ "merge this PR" action assumes, and it comes with its own restrictions: GitHub does not support
22
+ auto-merge for stacked pull requests at all, and a plain, non-stack-aware merge mechanism is not
23
+ guaranteed to reproduce this cascade correctly. A mid-stack PR's base ref being an unmerged branch,
24
+ not the default branch, is the sign to slow down and confirm the merge path in use actually
25
+ understands stacks before treating it as routine.
20
26
 
21
27
  Drain a stack from the bottom, one layer at a time, merging each bottom-most layer as soon as it
22
28
  becomes ready rather than waiting for every layer above it to be ready first. A stack should stay as
23
29
  short as it can be: every layer that remains unmerged keeps accumulating rebase surface, CI cost,
24
30
  and drift against the default branch, and that cost compounds for every layer still stacked above
25
- it.
31
+ it. Because stacked pull requests do not auto-merge, draining only happens through a deliberate
32
+ merge action taken each time a layer becomes ready — nothing lands on its own just because it is
33
+ ready.
26
34
 
27
- Only merging the bottom-most layer into the default branch actually drains anything: that merge
28
- lands the bottom layer, and the parent-branch bases above it can now be retargeted onto the default
29
- branch as that layer closes. Merging a higher layer instead lands it into its own parent branch, not
30
- the default branch, and leaves every layer below it exactly as open and unmerged as before no
31
- progress toward the default branch has been made, even though a PR closed. So whatever merge
32
- selector or target is used to drain the stack must name the bottom-most unmerged layer specifically,
33
- not an arbitrary or more convenient one.
35
+ Merging a given layer lands that layer and every unmerged layer below it in the same operation, so
36
+ whatever merge target is used must name the bottom-most unmerged layer specifically, not a higher
37
+ one, to land exactly the increment intended for that drain step: naming a higher layer either merges
38
+ more than intended, when everything below also happens to be ready, or is simply unavailable, when
39
+ it is not. Once a layer merges, GitHub retargets the next unmerged layer onto the default branch
40
+ directly, so it becomes the new bottom — treat it the same way on the next drain step.
34
41
 
35
42
  When a drain stalls — something on the stack needs human attention, or a layer closes without
36
43
  merging — but the bottom-most layer is otherwise ready to merge, stop before yielding: report the